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 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-subx-name:  # (handle array byte)
  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 #     type ids will be less than 0x10000 (MAX_TYPE_ID).
  367 
  368 Tree-left:  # either type-id or (addr tree type-id)
  369   0/imm32
  370 Tree-right:  # (addr tree type-id)
  371   4/imm32
  372 Tree-size:  # (addr int)
  373   8/imm32
  374 
  375 # Types
  376 
  377 Max-type-id:
  378   0x10000/imm32
  379 
  380 Type-id:  # (stream (address array byte))
  381   0x1c/imm32/write
  382   0/imm32/read
  383   0x100/imm32/length
  384   # data
  385   "literal"/imm32  # 0
  386   "int"/imm32  # 1
  387   "addr"/imm32  # 2
  388   "array"/imm32  # 3
  389   "handle"/imm32  # 4
  390   "boolean"/imm32  # 5
  391   "constant"/imm32  # 6: like a literal, but replaced with its value in Var-offset
  392   "offset"/imm32  # 7: (offset T) is guaranteed to be a 32-bit multiple of size-of(T)
  393   0/imm32
  394   # 0x20
  395   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  396   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  397   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  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 
  403 # Program->types contains some typeinfo for each type definition.
  404 # Types contain vars with types, but can't specify registers.
  405 Typeinfo-id:  # type-id
  406   0/imm32
  407 Typeinfo-fields:  # (handle table string (handle typeinfo-entry))
  408   4/imm32
  409 # Total size must be >= 0
  410 # During parsing it may take on two additional values:
  411 #   -2: not yet initialized
  412 #   -1: in process of being computed
  413 # See populate-mu-type-sizes for details.
  414 Typeinfo-total-size-in-bytes:  # int
  415   8/imm32
  416 Typeinfo-next:  # (handle typeinfo)
  417   0xc/imm32
  418 Typeinfo-size:  # (addr int)
  419   0x10/imm32
  420 
  421 # Each entry in the typeinfo->fields table has a pointer to a string and a
  422 # pointer to a typeinfo-entry.
  423 Typeinfo-fields-row-size:  # (addr int)
  424   8/imm32
  425 
  426 # typeinfo-entry objects have information about a field in a single record type
  427 #
  428 # each field of a type is represented using two var's:
  429 #   1. the input var: expected type of the field; convenient for creating using parse-var-with-type
  430 #   2. the output var: a constant containing the byte offset; convenient for code-generation
  431 # computing the output happens after parsing; in the meantime we preserve the
  432 # order of fields in the 'index' field.
  433 Typeinfo-entry-input-var:  # (handle var)
  434   0/imm32
  435 Typeinfo-entry-index:  # int
  436   4/imm32
  437 Typeinfo-entry-output-var:  # (handle var)
  438   8/imm32
  439 Typeinfo-entry-size:  # (addr int)
  440   0xc/imm32
  441 
  442 == code
  443 
  444 Entry:
  445     # . prologue
  446     89/<- %ebp 4/r32/esp
  447     (new-segment *Heap-size Heap)
  448     # if (argv[1] == "test') run-tests()
  449     {
  450       # if (argc <= 1) break
  451       81 7/subop/compare *ebp 1/imm32
  452       7e/jump-if-<= break/disp8
  453       # if (argv[1] != "test") break
  454       (kernel-string-equal? *(ebp+8) "test")  # => eax
  455       3d/compare-eax-and 0/imm32/false
  456       74/jump-if-= break/disp8
  457       #
  458       (run-tests)
  459       # syscall(exit, *Num-test-failures)
  460       8b/-> *Num-test-failures 3/r32/ebx
  461       eb/jump $mu-main:end/disp8
  462     }
  463     # otherwise convert Stdin
  464     (convert-mu Stdin Stdout)
  465     (flush Stdout)
  466     # syscall(exit, 0)
  467     bb/copy-to-ebx 0/imm32
  468 $mu-main:end:
  469     b8/copy-to-eax 1/imm32/exit
  470     cd/syscall 0x80/imm8
  471 
  472 convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
  473     # . prologue
  474     55/push-ebp
  475     89/<- %ebp 4/r32/esp
  476     # initialize global data structures
  477     c7 0/subop/copy *Next-block-index 1/imm32
  478     c7 0/subop/copy *Type-id 0x1c/imm32  # stream-write
  479     c7 0/subop/copy *_Program-functions 0/imm32
  480     c7 0/subop/copy *_Program-types 0/imm32
  481     #
  482     (parse-mu *(ebp+8))
  483     (populate-mu-type-sizes)
  484     (check-mu-types)
  485     (emit-subx *(ebp+0xc))
  486 $convert-mu:end:
  487     # . epilogue
  488     89/<- %esp 5/r32/ebp
  489     5d/pop-to-ebp
  490     c3/return
  491 
  492 test-convert-empty-input:
  493     # empty input => empty output
  494     # . prologue
  495     55/push-ebp
  496     89/<- %ebp 4/r32/esp
  497     # setup
  498     (clear-stream _test-input-stream)
  499     (clear-stream $_test-input-buffered-file->buffer)
  500     (clear-stream _test-output-stream)
  501     (clear-stream $_test-output-buffered-file->buffer)
  502     #
  503     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  504     (flush _test-output-buffered-file)
  505     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
  506     # . epilogue
  507     89/<- %esp 5/r32/ebp
  508     5d/pop-to-ebp
  509     c3/return
  510 
  511 test-convert-function-skeleton:
  512     # . prologue
  513     55/push-ebp
  514     89/<- %ebp 4/r32/esp
  515     # setup
  516     (clear-stream _test-input-stream)
  517     (clear-stream $_test-input-buffered-file->buffer)
  518     (clear-stream _test-output-stream)
  519     (clear-stream $_test-output-buffered-file->buffer)
  520     #
  521     (write _test-input-stream "fn foo {\n")
  522     (write _test-input-stream "}\n")
  523     # convert
  524     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  525     (flush _test-output-buffered-file)
  526 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  532     # check output
  533     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
  534     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
  535     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
  536     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
  537     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
  538     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
  539     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
  540     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
  541     # . epilogue
  542     89/<- %esp 5/r32/ebp
  543     5d/pop-to-ebp
  544     c3/return
  545 
  546 test-convert-multiple-function-skeletons:
  547     # . prologue
  548     55/push-ebp
  549     89/<- %ebp 4/r32/esp
  550     # setup
  551     (clear-stream _test-input-stream)
  552     (clear-stream $_test-input-buffered-file->buffer)
  553     (clear-stream _test-output-stream)
  554     (clear-stream $_test-output-buffered-file->buffer)
  555     #
  556     (write _test-input-stream "fn foo {\n")
  557     (write _test-input-stream "}\n")
  558     (write _test-input-stream "fn bar {\n")
  559     (write _test-input-stream "}\n")
  560     # convert
  561     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  562     (flush _test-output-buffered-file)
  563 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  569     # check first function
  570     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
  571     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
  572     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
  573     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
  574     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
  575     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
  576     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
  577     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
  578     # check second function
  579     (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
  580     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
  581     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
  582     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
  583     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
  584     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
  585     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
  586     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
  587     # . epilogue
  588     89/<- %esp 5/r32/ebp
  589     5d/pop-to-ebp
  590     c3/return
  591 
  592 test-convert-function-with-arg:
  593     # . prologue
  594     55/push-ebp
  595     89/<- %ebp 4/r32/esp
  596     # setup
  597     (clear-stream _test-input-stream)
  598     (clear-stream $_test-input-buffered-file->buffer)
  599     (clear-stream _test-output-stream)
  600     (clear-stream $_test-output-buffered-file->buffer)
  601     #
  602     (write _test-input-stream "fn foo n: int {\n")
  603     (write _test-input-stream "}\n")
  604     # convert
  605     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  606     (flush _test-output-buffered-file)
  607 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  613     # check output
  614     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
  615     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
  616     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
  617     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
  618     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
  619     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
  620     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
  621     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
  622     # . epilogue
  623     89/<- %esp 5/r32/ebp
  624     5d/pop-to-ebp
  625     c3/return
  626 
  627 test-convert-function-with-arg-and-body:
  628     # . prologue
  629     55/push-ebp
  630     89/<- %ebp 4/r32/esp
  631     # setup
  632     (clear-stream _test-input-stream)
  633     (clear-stream $_test-input-buffered-file->buffer)
  634     (clear-stream _test-output-stream)
  635     (clear-stream $_test-output-buffered-file->buffer)
  636     #
  637     (write _test-input-stream "fn foo n: int {\n")
  638     (write _test-input-stream "  increment n\n")
  639     (write _test-input-stream "}\n")
  640     # convert
  641     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  642     (flush _test-output-buffered-file)
  643 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  649     # check output
  650     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
  651     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
  652     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
  653     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
  654     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
  655     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
  656     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
  657     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
  658     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
  659     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
  660     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
  661     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
  662     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
  663     # . epilogue
  664     89/<- %esp 5/r32/ebp
  665     5d/pop-to-ebp
  666     c3/return
  667 
  668 test-convert-function-distinguishes-args:
  669     # . prologue
  670     55/push-ebp
  671     89/<- %ebp 4/r32/esp
  672     # setup
  673     (clear-stream _test-input-stream)
  674     (clear-stream $_test-input-buffered-file->buffer)
  675     (clear-stream _test-output-stream)
  676     (clear-stream $_test-output-buffered-file->buffer)
  677     #
  678     (write _test-input-stream "fn foo a: int, b: int {\n")
  679     (write _test-input-stream "  increment b\n")
  680     (write _test-input-stream "}\n")
  681     # convert
  682     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  683     (flush _test-output-buffered-file)
  684 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  690     # check output
  691     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
  692     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
  693     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
  694     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
  695     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
  696     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
  697     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
  698     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
  699     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
  700     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
  701     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
  702     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
  703     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
  704     # . epilogue
  705     89/<- %esp 5/r32/ebp
  706     5d/pop-to-ebp
  707     c3/return
  708 
  709 test-convert-function-returns-result:
  710     # . prologue
  711     55/push-ebp
  712     89/<- %ebp 4/r32/esp
  713     # setup
  714     (clear-stream _test-input-stream)
  715     (clear-stream $_test-input-buffered-file->buffer)
  716     (clear-stream _test-output-stream)
  717     (clear-stream $_test-output-buffered-file->buffer)
  718     #
  719     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
  720     (write _test-input-stream "  result <- copy a\n")
  721     (write _test-input-stream "  result <- increment\n")
  722     (write _test-input-stream "}\n")
  723     # convert
  724     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  725     (flush _test-output-buffered-file)
  726 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  732     # check output
  733     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-returns-result/0")
  734     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-returns-result/1")
  735     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-returns-result/2")
  736     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
  737     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-returns-result/4")
  738     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-returns-result/5")
  739     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/6")
  740     (check-next-stream-line-equal _test-output-stream "    40/increment-eax"    "F - test-convert-function-returns-result/7")
  741     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-returns-result/8")
  742     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-returns-result/9")
  743     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-returns-result/10")
  744     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/11")
  745     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-returns-result/12")
  746     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-returns-result/13")
  747     # . epilogue
  748     89/<- %esp 5/r32/ebp
  749     5d/pop-to-ebp
  750     c3/return
  751 
  752 test-convert-function-literal-arg:
  753     # . prologue
  754     55/push-ebp
  755     89/<- %ebp 4/r32/esp
  756     # setup
  757     (clear-stream _test-input-stream)
  758     (clear-stream $_test-input-buffered-file->buffer)
  759     (clear-stream _test-output-stream)
  760     (clear-stream $_test-output-buffered-file->buffer)
  761     #
  762     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
  763     (write _test-input-stream "  result <- copy a\n")
  764     (write _test-input-stream "  result <- add 1\n")
  765     (write _test-input-stream "}\n")
  766     # convert
  767     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  768     (flush _test-output-buffered-file)
  769 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  775     # check output
  776     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg/0")
  777     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg/1")
  778     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg/2")
  779     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg/3")
  780     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg/4")
  781     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg/5")
  782     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-literal-arg/6")
  783     (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-literal-arg/7")
  784     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg/8")
  785     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg/9")
  786     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg/10")
  787     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg/11")
  788     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg/12")
  789     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg/13")
  790     # . epilogue
  791     89/<- %esp 5/r32/ebp
  792     5d/pop-to-ebp
  793     c3/return
  794 
  795 test-convert-function-literal-arg-2:
  796     # . prologue
  797     55/push-ebp
  798     89/<- %ebp 4/r32/esp
  799     # setup
  800     (clear-stream _test-input-stream)
  801     (clear-stream $_test-input-buffered-file->buffer)
  802     (clear-stream _test-output-stream)
  803     (clear-stream $_test-output-buffered-file->buffer)
  804     #
  805     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
  806     (write _test-input-stream "  result <- copy a\n")
  807     (write _test-input-stream "  result <- add 1\n")
  808     (write _test-input-stream "}\n")
  809     # convert
  810     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  811     (flush _test-output-buffered-file)
  812 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  818     # check output
  819     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg-2/0")
  820     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg-2/1")
  821     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg-2/2")
  822     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg-2/3")
  823     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg-2/4")
  824     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg-2/5")
  825     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-literal-arg-2/6")
  826     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-literal-arg-2/7")
  827     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg-2/8")
  828     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg-2/9")
  829     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg-2/10")
  830     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg-2/11")
  831     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg-2/12")
  832     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg-2/13")
  833     # . epilogue
  834     89/<- %esp 5/r32/ebp
  835     5d/pop-to-ebp
  836     c3/return
  837 
  838 test-convert-function-call-with-literal-arg:
  839     # . prologue
  840     55/push-ebp
  841     89/<- %ebp 4/r32/esp
  842     # setup
  843     (clear-stream _test-input-stream)
  844     (clear-stream $_test-input-buffered-file->buffer)
  845     (clear-stream _test-output-stream)
  846     (clear-stream $_test-output-buffered-file->buffer)
  847     #
  848     (write _test-input-stream "fn main -> result/ebx: int {\n")
  849     (write _test-input-stream "  result <- do-add 3 4\n")
  850     (write _test-input-stream "}\n")
  851     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
  852     (write _test-input-stream "  result <- copy a\n")
  853     (write _test-input-stream "  result <- add b\n")
  854     (write _test-input-stream "}\n")
  855     # convert
  856     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  857     (flush _test-output-buffered-file)
  858 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  864     # check output
  865     (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
  866     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
  867     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
  868     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
  869     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
  870     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
  871     (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/6")
  872     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/7")
  873     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
  874     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/9")
  875     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/10")
  876     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/11")
  877     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/12")
  878     (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/13")
  879     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/14")
  880     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/15")
  881     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/16")
  882     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/17")
  883     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/18")
  884     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/19")
  885     (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/20")
  886     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/21")
  887     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/22")
  888     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/23")
  889     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/24")
  890     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/25")
  891     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/26")
  892     # . epilogue
  893     89/<- %esp 5/r32/ebp
  894     5d/pop-to-ebp
  895     c3/return
  896 
  897 test-convert-function-with-local-var-in-mem:
  898     # . prologue
  899     55/push-ebp
  900     89/<- %ebp 4/r32/esp
  901     # setup
  902     (clear-stream _test-input-stream)
  903     (clear-stream $_test-input-buffered-file->buffer)
  904     (clear-stream _test-output-stream)
  905     (clear-stream $_test-output-buffered-file->buffer)
  906     #
  907     (write _test-input-stream "fn foo {\n")
  908     (write _test-input-stream "  var x: int\n")
  909     (write _test-input-stream "  increment x\n")
  910     (write _test-input-stream "}\n")
  911     # convert
  912     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  913     (flush _test-output-buffered-file)
  914 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  920     # check output
  921     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
  922     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
  923     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
  924     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
  925     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
  926     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
  927     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
  928     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
  929     (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")
  930     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
  931     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
  932     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
  933     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
  934     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
  935     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
  936     # . epilogue
  937     89/<- %esp 5/r32/ebp
  938     5d/pop-to-ebp
  939     c3/return
  940 
  941 test-convert-function-with-local-var-in-reg:
  942     # . prologue
  943     55/push-ebp
  944     89/<- %ebp 4/r32/esp
  945     # setup
  946     (clear-stream _test-input-stream)
  947     (clear-stream $_test-input-buffered-file->buffer)
  948     (clear-stream _test-output-stream)
  949     (clear-stream $_test-output-buffered-file->buffer)
  950     #
  951     (write _test-input-stream "fn foo {\n")
  952     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
  953     (write _test-input-stream "  x <- increment\n")
  954     (write _test-input-stream "}\n")
  955     # convert
  956     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  957     (flush _test-output-buffered-file)
  958 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
  964     # check output
  965     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
  966     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
  967     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
  968     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
  969     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
  970     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
  971     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
  972     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
  973     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
  974     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
  975     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
  976     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
  977     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
  978     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
  979     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
  980     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
  981     # . epilogue
  982     89/<- %esp 5/r32/ebp
  983     5d/pop-to-ebp
  984     c3/return
  985 
  986 test-convert-function-with-second-local-var-in-same-reg:
  987     # . prologue
  988     55/push-ebp
  989     89/<- %ebp 4/r32/esp
  990     # setup
  991     (clear-stream _test-input-stream)
  992     (clear-stream $_test-input-buffered-file->buffer)
  993     (clear-stream _test-output-stream)
  994     (clear-stream $_test-output-buffered-file->buffer)
  995     #
  996     (write _test-input-stream "fn foo {\n")
  997     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
  998     (write _test-input-stream "  var y/ecx: int <- copy 4\n")
  999     (write _test-input-stream "  y <- increment\n")
 1000     (write _test-input-stream "}\n")
 1001     # convert
 1002     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1003     (flush _test-output-buffered-file)
 1004 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1010     # check output
 1011     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-second-local-var-in-same-reg/0")
 1012     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-second-local-var-in-same-reg/1")
 1013     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-second-local-var-in-same-reg/2")
 1014     (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")
 1015     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-second-local-var-in-same-reg/4")
 1016     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-second-local-var-in-same-reg/5")
 1017     (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")
 1018     (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")
 1019     (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")
 1020     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-second-local-var-in-same-reg/9")
 1021     (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")
 1022     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-second-local-var-in-same-reg/11")
 1023     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-second-local-var-in-same-reg/12")
 1024     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-second-local-var-in-same-reg/13")
 1025     (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")
 1026     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-second-local-var-in-same-reg/15")
 1027     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-second-local-var-in-same-reg/16")
 1028     # . epilogue
 1029     89/<- %esp 5/r32/ebp
 1030     5d/pop-to-ebp
 1031     c3/return
 1032 
 1033 test-convert-function-with-local-var-dereferenced:
 1034     # . prologue
 1035     55/push-ebp
 1036     89/<- %ebp 4/r32/esp
 1037     # setup
 1038     (clear-stream _test-input-stream)
 1039     (clear-stream $_test-input-buffered-file->buffer)
 1040     (clear-stream _test-output-stream)
 1041     (clear-stream $_test-output-buffered-file->buffer)
 1042     #
 1043     (write _test-input-stream "fn foo {\n")
 1044     (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
 1045     (write _test-input-stream "  increment *x\n")
 1046     (write _test-input-stream "}\n")
 1047     # convert
 1048     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1049     (flush _test-output-buffered-file)
 1050 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1056     # check output
 1057     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
 1058     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
 1059     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
 1060     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
 1061     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
 1062     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
 1063     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
 1064     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
 1065     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
 1066     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
 1067     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
 1068     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
 1069     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
 1070     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
 1071     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
 1072     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
 1073     # . epilogue
 1074     89/<- %esp 5/r32/ebp
 1075     5d/pop-to-ebp
 1076     c3/return
 1077 
 1078 test-convert-compare-register-with-literal:
 1079     # . prologue
 1080     55/push-ebp
 1081     89/<- %ebp 4/r32/esp
 1082     # setup
 1083     (clear-stream _test-input-stream)
 1084     (clear-stream $_test-input-buffered-file->buffer)
 1085     (clear-stream _test-output-stream)
 1086     (clear-stream $_test-output-buffered-file->buffer)
 1087     #
 1088     (write _test-input-stream "fn foo {\n")
 1089     (write _test-input-stream "  var x/ecx: int <- copy 0\n")
 1090     (write _test-input-stream "  compare x, 0\n")
 1091     (write _test-input-stream "}\n")
 1092     # convert
 1093     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1094     (flush _test-output-buffered-file)
 1095 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1101     # check output
 1102     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
 1103     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
 1104     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
 1105     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
 1106     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
 1107     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
 1108     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 1109     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
 1110     (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
 1111     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 1112     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
 1113     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
 1114     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
 1115     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
 1116     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
 1117     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
 1118     # . epilogue
 1119     89/<- %esp 5/r32/ebp
 1120     5d/pop-to-ebp
 1121     c3/return
 1122 
 1123 test-convert-function-with-local-var-in-block:
 1124     # . prologue
 1125     55/push-ebp
 1126     89/<- %ebp 4/r32/esp
 1127     # setup
 1128     (clear-stream _test-input-stream)
 1129     (clear-stream $_test-input-buffered-file->buffer)
 1130     (clear-stream _test-output-stream)
 1131     (clear-stream $_test-output-buffered-file->buffer)
 1132     #
 1133     (write _test-input-stream "fn foo {\n")
 1134     (write _test-input-stream "  {\n")
 1135     (write _test-input-stream "    var x: int\n")
 1136     (write _test-input-stream "    increment x\n")
 1137     (write _test-input-stream "  }\n")
 1138     (write _test-input-stream "}\n")
 1139     # convert
 1140     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1141     (flush _test-output-buffered-file)
 1142 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1148     # check output
 1149     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
 1150     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
 1151     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
 1152     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
 1153     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
 1154     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
 1155     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
 1156     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
 1157     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
 1158     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
 1159     (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")
 1160     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
 1161     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
 1162     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
 1163     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
 1164     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
 1165     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
 1166     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
 1167     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
 1168     # . epilogue
 1169     89/<- %esp 5/r32/ebp
 1170     5d/pop-to-ebp
 1171     c3/return
 1172 
 1173 test-convert-function-with-local-var-in-named-block:
 1174     # . prologue
 1175     55/push-ebp
 1176     89/<- %ebp 4/r32/esp
 1177     # setup
 1178     (clear-stream _test-input-stream)
 1179     (clear-stream $_test-input-buffered-file->buffer)
 1180     (clear-stream _test-output-stream)
 1181     (clear-stream $_test-output-buffered-file->buffer)
 1182     #
 1183     (write _test-input-stream "fn foo {\n")
 1184     (write _test-input-stream "  $bar: {\n")
 1185     (write _test-input-stream "    var x: int\n")
 1186     (write _test-input-stream "    increment x\n")
 1187     (write _test-input-stream "  }\n")
 1188     (write _test-input-stream "}\n")
 1189     # convert
 1190     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1191     (flush _test-output-buffered-file)
 1192 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1198     # check output
 1199     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
 1200     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
 1201     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
 1202     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
 1203     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
 1204     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
 1205     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
 1206     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
 1207     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
 1208     (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")
 1209     (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")
 1210     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
 1211     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
 1212     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
 1213     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
 1214     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
 1215     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
 1216     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
 1217     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
 1218     # . epilogue
 1219     89/<- %esp 5/r32/ebp
 1220     5d/pop-to-ebp
 1221     c3/return
 1222 
 1223 test-always-shadow-outermost-reg-vars-in-function:
 1224     # . prologue
 1225     55/push-ebp
 1226     89/<- %ebp 4/r32/esp
 1227     # setup
 1228     (clear-stream _test-input-stream)
 1229     (clear-stream $_test-input-buffered-file->buffer)
 1230     (clear-stream _test-output-stream)
 1231     (clear-stream $_test-output-buffered-file->buffer)
 1232     #
 1233     (write _test-input-stream "fn foo {\n")
 1234     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1235     (write _test-input-stream "}\n")
 1236     # convert
 1237     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1238     (flush _test-output-buffered-file)
 1239 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1245     # check output
 1246     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-always-shadow-outermost-reg-vars-in-function/0")
 1247     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-always-shadow-outermost-reg-vars-in-function/1")
 1248     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-always-shadow-outermost-reg-vars-in-function/2")
 1249     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-always-shadow-outermost-reg-vars-in-function/3")
 1250     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-always-shadow-outermost-reg-vars-in-function/4")
 1251     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-always-shadow-outermost-reg-vars-in-function/5")
 1252     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 1253     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-always-shadow-outermost-reg-vars-in-function/8")
 1254     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 1255     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-always-shadow-outermost-reg-vars-in-function/12")
 1256     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-always-shadow-outermost-reg-vars-in-function/13")
 1257     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-always-shadow-outermost-reg-vars-in-function/14")
 1258     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-always-shadow-outermost-reg-vars-in-function/15")
 1259     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-always-shadow-outermost-reg-vars-in-function/16")
 1260     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-always-shadow-outermost-reg-vars-in-function/17")
 1261     # . epilogue
 1262     89/<- %esp 5/r32/ebp
 1263     5d/pop-to-ebp
 1264     c3/return
 1265 
 1266 _pending-test-clobber-dead-local:
 1267     # . prologue
 1268     55/push-ebp
 1269     89/<- %ebp 4/r32/esp
 1270     # setup
 1271     (clear-stream _test-input-stream)
 1272     (clear-stream $_test-input-buffered-file->buffer)
 1273     (clear-stream _test-output-stream)
 1274     (clear-stream $_test-output-buffered-file->buffer)
 1275     #
 1276     (write _test-input-stream "fn foo {\n")
 1277     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1278     (write _test-input-stream "  {\n")
 1279     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1280     (write _test-input-stream "  }\n")
 1281     (write _test-input-stream "}\n")
 1282     # convert
 1283     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1284     (flush _test-output-buffered-file)
 1285 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1291     # check output
 1292     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-clobber-dead-local/0")
 1293     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-clobber-dead-local/1")
 1294     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-clobber-dead-local/2")
 1295     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-clobber-dead-local/3")
 1296     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-clobber-dead-local/4")
 1297     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-clobber-dead-local/5")
 1298     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-clobber-dead-local/6")
 1299     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-clobber-dead-local/7")
 1300     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-clobber-dead-local/8")
 1301     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-clobber-dead-local/9")
 1302     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-clobber-dead-local/10")  # no push/pop here
 1303     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-clobber-dead-local/11")
 1304     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-clobber-dead-local/12")
 1305     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-clobber-dead-local/13")
 1306     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-clobber-dead-local/14")
 1307     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-clobber-dead-local/15")
 1308     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-clobber-dead-local/16")
 1309     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-clobber-dead-local/17")
 1310     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-clobber-dead-local/18")
 1311     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-clobber-dead-local/19")
 1312     # . epilogue
 1313     89/<- %esp 5/r32/ebp
 1314     5d/pop-to-ebp
 1315     c3/return
 1316 
 1317 test-shadow-live-local:
 1318     # . prologue
 1319     55/push-ebp
 1320     89/<- %ebp 4/r32/esp
 1321     # setup
 1322     (clear-stream _test-input-stream)
 1323     (clear-stream $_test-input-buffered-file->buffer)
 1324     (clear-stream _test-output-stream)
 1325     (clear-stream $_test-output-buffered-file->buffer)
 1326     #
 1327     (write _test-input-stream "fn foo {\n")
 1328     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1329     (write _test-input-stream "  {\n")
 1330     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1331     (write _test-input-stream "  }\n")
 1332     (write _test-input-stream "  x <- increment\n")
 1333     (write _test-input-stream "}\n")
 1334     # convert
 1335     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1336     (flush _test-output-buffered-file)
 1337 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1343     # check output
 1344     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-local/0")
 1345     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-local/1")
 1346     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-local/2")
 1347     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-local/3")
 1348     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-local/4")
 1349     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-local/5")
 1350     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-live-local/6")
 1351     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-live-local/7")
 1352     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-local/8")
 1353     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-local/9")
 1354     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-local/10")
 1355     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-local/11")
 1356     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-local/12")
 1357     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-local/13")
 1358     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-local/14")
 1359     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-local/15")
 1360     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-live-local/16")
 1361     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-local/17")
 1362     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-local/18")
 1363     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-local/19")
 1364     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-local/20")
 1365     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-local/21")
 1366     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-local/21")
 1367     # . epilogue
 1368     89/<- %esp 5/r32/ebp
 1369     5d/pop-to-ebp
 1370     c3/return
 1371 
 1372 test-shadow-live-output:
 1373     # . prologue
 1374     55/push-ebp
 1375     89/<- %ebp 4/r32/esp
 1376     # setup
 1377     (clear-stream _test-input-stream)
 1378     (clear-stream $_test-input-buffered-file->buffer)
 1379     (clear-stream _test-output-stream)
 1380     (clear-stream $_test-output-buffered-file->buffer)
 1381     #
 1382     (write _test-input-stream "fn foo -> x/ecx: int {\n")
 1383     (write _test-input-stream "  x <- copy 3\n")
 1384     (write _test-input-stream "  {\n")
 1385     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1386     (write _test-input-stream "  }\n")
 1387     (write _test-input-stream "  x <- increment\n")
 1388     (write _test-input-stream "}\n")
 1389     # convert
 1390     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1391     (flush _test-output-buffered-file)
 1392 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1398     # check output
 1399     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-output/0")
 1400     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-output/1")
 1401     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-output/2")
 1402     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-output/3")
 1403     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-output/4")
 1404     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-output/5")
 1405     (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
 1406     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-output/8")
 1407     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-output/9")
 1408     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-output/10")
 1409     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-output/11")
 1410     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-output/12")
 1411     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-output/13")
 1412     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-output/14")
 1413     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-output/15")
 1414     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-output/17")
 1415     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-output/18")
 1416     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-output/19")
 1417     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-output/20")
 1418     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-output/21")
 1419     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-output/21")
 1420     # . epilogue
 1421     89/<- %esp 5/r32/ebp
 1422     5d/pop-to-ebp
 1423     c3/return
 1424 
 1425 _pending-test-local-clobbered-by-output:
 1426     # also doesn't spill
 1427     # . prologue
 1428     55/push-ebp
 1429     89/<- %ebp 4/r32/esp
 1430     # setup
 1431     (clear-stream _test-input-stream)
 1432     (clear-stream $_test-input-buffered-file->buffer)
 1433     (clear-stream _test-output-stream)
 1434     (clear-stream $_test-output-buffered-file->buffer)
 1435     #
 1436     (write _test-input-stream "fn foo -> x/ecx: int {\n")
 1437     (write _test-input-stream "  var y/ecx: int <- copy 4\n")
 1438     (write _test-input-stream "  x <- copy y\n")
 1439     (write _test-input-stream "}\n")
 1440     # convert
 1441     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1442     (flush _test-output-buffered-file)
 1443 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1449     # check output
 1450     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-local-clobbered-by-output/0")
 1451     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-local-clobbered-by-output/1")
 1452     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-local-clobbered-by-output/2")
 1453     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-local-clobbered-by-output/3")
 1454     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-local-clobbered-by-output/4")
 1455     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-local-clobbered-by-output/5")
 1456     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-local-clobbered-by-output/6")
 1457     (check-next-stream-line-equal _test-output-stream "    89/copy-to %ecx 0x00000001/r32"  "F - test-local-clobbered-by-output/7")
 1458     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-local-clobbered-by-output/8")
 1459     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-local-clobbered-by-output/9")
 1460     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-local-clobbered-by-output/10")
 1461     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-local-clobbered-by-output/11")
 1462     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-local-clobbered-by-output/12")
 1463     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-local-clobbered-by-output/13")
 1464     # . epilogue
 1465     89/<- %esp 5/r32/ebp
 1466     5d/pop-to-ebp
 1467     c3/return
 1468 
 1469 test-convert-function-with-branches-in-block:
 1470     # . prologue
 1471     55/push-ebp
 1472     89/<- %ebp 4/r32/esp
 1473     # setup
 1474     (clear-stream _test-input-stream)
 1475     (clear-stream $_test-input-buffered-file->buffer)
 1476     (clear-stream _test-output-stream)
 1477     (clear-stream $_test-output-buffered-file->buffer)
 1478     #
 1479     (write _test-input-stream "fn foo x: int {\n")
 1480     (write _test-input-stream "  {\n")
 1481     (write _test-input-stream "    break-if->=\n")
 1482     (write _test-input-stream "    loop-if-addr<\n")
 1483     (write _test-input-stream "    increment x\n")
 1484     (write _test-input-stream "    loop\n")
 1485     (write _test-input-stream "  }\n")
 1486     (write _test-input-stream "}\n")
 1487     # convert
 1488     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1489     (flush _test-output-buffered-file)
 1490 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1496     # check output
 1497     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
 1498     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
 1499     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
 1500     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
 1501     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
 1502     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
 1503     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
 1504     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
 1505     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
 1506     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
 1507     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
 1508     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
 1509     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
 1510     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
 1511     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
 1512     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
 1513     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
 1514     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
 1515     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
 1516     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
 1517     # . epilogue
 1518     89/<- %esp 5/r32/ebp
 1519     5d/pop-to-ebp
 1520     c3/return
 1521 
 1522 test-convert-function-with-branches-in-named-block:
 1523     # . prologue
 1524     55/push-ebp
 1525     89/<- %ebp 4/r32/esp
 1526     # setup
 1527     (clear-stream _test-input-stream)
 1528     (clear-stream $_test-input-buffered-file->buffer)
 1529     (clear-stream _test-output-stream)
 1530     (clear-stream $_test-output-buffered-file->buffer)
 1531     #
 1532     (write _test-input-stream "fn foo x: int {\n")
 1533     (write _test-input-stream "  $bar: {\n")
 1534     (write _test-input-stream "    break-if->= $bar\n")
 1535     (write _test-input-stream "    loop-if-addr< $bar\n")
 1536     (write _test-input-stream "    increment x\n")
 1537     (write _test-input-stream "    loop\n")
 1538     (write _test-input-stream "  }\n")
 1539     (write _test-input-stream "}\n")
 1540     # convert
 1541     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1542     (flush _test-output-buffered-file)
 1543 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1549     # check output
 1550     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
 1551     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
 1552     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
 1553     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
 1554     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
 1555     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
 1556     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
 1557     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
 1558     (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")
 1559     (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")
 1560     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
 1561     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
 1562     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
 1563     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
 1564     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
 1565     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
 1566     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
 1567     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
 1568     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
 1569     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
 1570     # . epilogue
 1571     89/<- %esp 5/r32/ebp
 1572     5d/pop-to-ebp
 1573     c3/return
 1574 
 1575 test-convert-function-with-var-in-nested-block:
 1576     # . prologue
 1577     55/push-ebp
 1578     89/<- %ebp 4/r32/esp
 1579     # setup
 1580     (clear-stream _test-input-stream)
 1581     (clear-stream $_test-input-buffered-file->buffer)
 1582     (clear-stream _test-output-stream)
 1583     (clear-stream $_test-output-buffered-file->buffer)
 1584     #
 1585     (write _test-input-stream "fn foo x: int {\n")
 1586     (write _test-input-stream "  {\n")
 1587     (write _test-input-stream "    {\n")
 1588     (write _test-input-stream "      var x: int\n")
 1589     (write _test-input-stream "      increment x\n")
 1590     (write _test-input-stream "    }\n")
 1591     (write _test-input-stream "  }\n")
 1592     (write _test-input-stream "}\n")
 1593     # convert
 1594     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1595     (flush _test-output-buffered-file)
 1596 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1602     # check output
 1603     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
 1604     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
 1605     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
 1606     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
 1607     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
 1608     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
 1609     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
 1610     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
 1611     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
 1612     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
 1613     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
 1614     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
 1615     (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")
 1616     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
 1617     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
 1618     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
 1619     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
 1620     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
 1621     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
 1622     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
 1623     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
 1624     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
 1625     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
 1626     # . epilogue
 1627     89/<- %esp 5/r32/ebp
 1628     5d/pop-to-ebp
 1629     c3/return
 1630 
 1631 test-convert-function-with-multiple-vars-in-nested-blocks:
 1632     # . prologue
 1633     55/push-ebp
 1634     89/<- %ebp 4/r32/esp
 1635     # setup
 1636     (clear-stream _test-input-stream)
 1637     (clear-stream $_test-input-buffered-file->buffer)
 1638     (clear-stream _test-output-stream)
 1639     (clear-stream $_test-output-buffered-file->buffer)
 1640     #
 1641     (write _test-input-stream "fn foo x: int {\n")
 1642     (write _test-input-stream "  {\n")
 1643     (write _test-input-stream "    var x/eax: int <- copy 0\n")
 1644     (write _test-input-stream "    {\n")
 1645     (write _test-input-stream "      var y: int\n")
 1646     (write _test-input-stream "      x <- add y\n")
 1647     (write _test-input-stream "    }\n")
 1648     (write _test-input-stream "  }\n")
 1649     (write _test-input-stream "}\n")
 1650     # convert
 1651     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1652     (flush _test-output-buffered-file)
 1653 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1659     # check output
 1660     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
 1661     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
 1662     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
 1663     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
 1664     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
 1665     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
 1666     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
 1667     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
 1668     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
 1669     (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")
 1670     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
 1671     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
 1672     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
 1673     (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")
 1674     (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")
 1675     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
 1676     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
 1677     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
 1678     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
 1679     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
 1680     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
 1681     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
 1682     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
 1683     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
 1684     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
 1685     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
 1686     # . epilogue
 1687     89/<- %esp 5/r32/ebp
 1688     5d/pop-to-ebp
 1689     c3/return
 1690 
 1691 test-convert-function-with-branches-and-local-vars:
 1692     # A conditional 'break' after a 'var' in a block is converted into a
 1693     # nested block that performs all necessary cleanup before jumping. This
 1694     # results in some ugly code duplication.
 1695     # . prologue
 1696     55/push-ebp
 1697     89/<- %ebp 4/r32/esp
 1698     # setup
 1699     (clear-stream _test-input-stream)
 1700     (clear-stream $_test-input-buffered-file->buffer)
 1701     (clear-stream _test-output-stream)
 1702     (clear-stream $_test-output-buffered-file->buffer)
 1703     #
 1704     (write _test-input-stream "fn foo {\n")
 1705     (write _test-input-stream "  {\n")
 1706     (write _test-input-stream "    var x: int\n")
 1707     (write _test-input-stream "    break-if->=\n")
 1708     (write _test-input-stream "    increment x\n")
 1709     (write _test-input-stream "  }\n")
 1710     (write _test-input-stream "}\n")
 1711     # convert
 1712     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1713     (flush _test-output-buffered-file)
 1714 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1720     # check output
 1721     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
 1722     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
 1723     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
 1724     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
 1725     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
 1726     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
 1727     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
 1728     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
 1729     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
 1730     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
 1731     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
 1732     (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")
 1733     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
 1734     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
 1735     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
 1736     (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")
 1737     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
 1738     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
 1739     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
 1740     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
 1741     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
 1742     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
 1743     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
 1744     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
 1745     # . epilogue
 1746     89/<- %esp 5/r32/ebp
 1747     5d/pop-to-ebp
 1748     c3/return
 1749 
 1750 test-convert-function-with-conditional-loops-and-local-vars:
 1751     # A conditional 'loop' after a 'var' in a block is converted into a nested
 1752     # block that performs all necessary cleanup before jumping. This results
 1753     # in some ugly code duplication.
 1754     # . prologue
 1755     55/push-ebp
 1756     89/<- %ebp 4/r32/esp
 1757     # setup
 1758     (clear-stream _test-input-stream)
 1759     (clear-stream $_test-input-buffered-file->buffer)
 1760     (clear-stream _test-output-stream)
 1761     (clear-stream $_test-output-buffered-file->buffer)
 1762     #
 1763     (write _test-input-stream "fn foo {\n")
 1764     (write _test-input-stream "  {\n")
 1765     (write _test-input-stream "    var x: int\n")
 1766     (write _test-input-stream "    loop-if->=\n")
 1767     (write _test-input-stream "    increment x\n")
 1768     (write _test-input-stream "  }\n")
 1769     (write _test-input-stream "}\n")
 1770     # convert
 1771     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1772     (flush _test-output-buffered-file)
 1773 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1779     # check output
 1780     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
 1781     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
 1782     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
 1783     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
 1784     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
 1785     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
 1786     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
 1787     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
 1788     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
 1789     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
 1790     (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")
 1791     (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")
 1792     (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")
 1793     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
 1794     (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")
 1795     (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")
 1796     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
 1797     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
 1798     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
 1799     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
 1800     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
 1801     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
 1802     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
 1803     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
 1804     # . epilogue
 1805     89/<- %esp 5/r32/ebp
 1806     5d/pop-to-ebp
 1807     c3/return
 1808 
 1809 test-convert-function-with-unconditional-loops-and-local-vars:
 1810     # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
 1811     # regular block cleanup. Any instructions after 'loop' are dead and
 1812     # therefore skipped.
 1813     # . prologue
 1814     55/push-ebp
 1815     89/<- %ebp 4/r32/esp
 1816     # setup
 1817     (clear-stream _test-input-stream)
 1818     (clear-stream $_test-input-buffered-file->buffer)
 1819     (clear-stream _test-output-stream)
 1820     (clear-stream $_test-output-buffered-file->buffer)
 1821     #
 1822     (write _test-input-stream "fn foo {\n")
 1823     (write _test-input-stream "  {\n")
 1824     (write _test-input-stream "    var x: int\n")
 1825     (write _test-input-stream "    loop\n")
 1826     (write _test-input-stream "    increment x\n")
 1827     (write _test-input-stream "  }\n")
 1828     (write _test-input-stream "}\n")
 1829     # convert
 1830     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1831     (flush _test-output-buffered-file)
 1832 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1838     # check output
 1839     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
 1840     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
 1841     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
 1842     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
 1843     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
 1844     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
 1845     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
 1846     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
 1847     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
 1848     (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")
 1849     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
 1850     # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
 1851     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
 1852     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
 1853     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
 1854     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
 1855     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
 1856     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
 1857     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
 1858     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
 1859     # . epilogue
 1860     89/<- %esp 5/r32/ebp
 1861     5d/pop-to-ebp
 1862     c3/return
 1863 
 1864 test-convert-function-with-branches-and-loops-and-local-vars:
 1865     # . prologue
 1866     55/push-ebp
 1867     89/<- %ebp 4/r32/esp
 1868     # setup
 1869     (clear-stream _test-input-stream)
 1870     (clear-stream $_test-input-buffered-file->buffer)
 1871     (clear-stream _test-output-stream)
 1872     (clear-stream $_test-output-buffered-file->buffer)
 1873     #
 1874     (write _test-input-stream "fn foo {\n")
 1875     (write _test-input-stream "  {\n")
 1876     (write _test-input-stream "    var x: int\n")
 1877     (write _test-input-stream "    break-if->=\n")
 1878     (write _test-input-stream "    increment x\n")
 1879     (write _test-input-stream "    loop\n")
 1880     (write _test-input-stream "  }\n")
 1881     (write _test-input-stream "}\n")
 1882     # convert
 1883     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1884     (flush _test-output-buffered-file)
 1885 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1891     # check output
 1892     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
 1893     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
 1894     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
 1895     (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")
 1896     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
 1897     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
 1898     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
 1899     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
 1900     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
 1901     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
 1902     (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")
 1903     (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")
 1904     (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")
 1905     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
 1906     (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")
 1907     (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")
 1908     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
 1909     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
 1910     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
 1911     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
 1912     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
 1913     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
 1914     (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")
 1915     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
 1916     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
 1917     # . epilogue
 1918     89/<- %esp 5/r32/ebp
 1919     5d/pop-to-ebp
 1920     c3/return
 1921 
 1922 test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
 1923     # . prologue
 1924     55/push-ebp
 1925     89/<- %ebp 4/r32/esp
 1926     # setup
 1927     (clear-stream _test-input-stream)
 1928     (clear-stream $_test-input-buffered-file->buffer)
 1929     (clear-stream _test-output-stream)
 1930     (clear-stream $_test-output-buffered-file->buffer)
 1931     #
 1932     (write _test-input-stream "fn foo {\n")
 1933     (write _test-input-stream "  a: {\n")
 1934     (write _test-input-stream "    var x: int\n")
 1935     (write _test-input-stream "    {\n")
 1936     (write _test-input-stream "      var y: int\n")
 1937     (write _test-input-stream "      break-if->= a\n")
 1938     (write _test-input-stream "      increment x\n")
 1939     (write _test-input-stream "      loop\n")
 1940     (write _test-input-stream "    }\n")
 1941     (write _test-input-stream "  }\n")
 1942     (write _test-input-stream "}\n")
 1943     # convert
 1944     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1945     (flush _test-output-buffered-file)
 1946 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1952     # check output
 1953     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
 1954     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
 1955     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
 1956     (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")
 1957     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
 1958     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
 1959     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
 1960     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
 1961     (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")
 1962     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
 1963     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
 1964     (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")
 1965     (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
 1966     (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")
 1967     (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")
 1968     (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")
 1969     (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")
 1970     (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
 1971     (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")
 1972     (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")
 1973     (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")
 1974     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
 1975     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
 1976     (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")
 1977     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
 1978     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
 1979     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
 1980     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
 1981     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
 1982     (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")
 1983     (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")
 1984     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
 1985     # . epilogue
 1986     89/<- %esp 5/r32/ebp
 1987     5d/pop-to-ebp
 1988     c3/return
 1989 
 1990 test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
 1991     # . prologue
 1992     55/push-ebp
 1993     89/<- %ebp 4/r32/esp
 1994     # setup
 1995     (clear-stream _test-input-stream)
 1996     (clear-stream $_test-input-buffered-file->buffer)
 1997     (clear-stream _test-output-stream)
 1998     (clear-stream $_test-output-buffered-file->buffer)
 1999     #
 2000     (write _test-input-stream "fn foo {\n")
 2001     (write _test-input-stream "  a: {\n")
 2002     (write _test-input-stream "    var x: int\n")
 2003     (write _test-input-stream "    {\n")
 2004     (write _test-input-stream "      var y: int\n")
 2005     (write _test-input-stream "      break a\n")
 2006     (write _test-input-stream "      increment x\n")
 2007     (write _test-input-stream "    }\n")
 2008     (write _test-input-stream "  }\n")
 2009     (write _test-input-stream "}\n")
 2010     # convert
 2011     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2012     (flush _test-output-buffered-file)
 2013 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2019     # check output
 2020     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
 2021     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
 2022     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
 2023     (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")
 2024     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
 2025     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
 2026     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
 2027     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
 2028     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
 2029     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
 2030     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
 2031     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
 2032     (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")
 2033     (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")
 2034     (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")
 2035     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
 2036     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
 2037     (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")
 2038     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
 2039     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
 2040     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
 2041     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
 2042     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
 2043     (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")
 2044     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
 2045     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
 2046     # . epilogue
 2047     89/<- %esp 5/r32/ebp
 2048     5d/pop-to-ebp
 2049     c3/return
 2050 
 2051 test-convert-function-with-unconditional-break-and-local-vars:
 2052     # . prologue
 2053     55/push-ebp
 2054     89/<- %ebp 4/r32/esp
 2055     # setup
 2056     (clear-stream _test-input-stream)
 2057     (clear-stream $_test-input-buffered-file->buffer)
 2058     (clear-stream _test-output-stream)
 2059     (clear-stream $_test-output-buffered-file->buffer)
 2060     #
 2061     (write _test-input-stream "fn foo {\n")
 2062     (write _test-input-stream "  {\n")
 2063     (write _test-input-stream "    var x: int\n")
 2064     (write _test-input-stream "    {\n")
 2065     (write _test-input-stream "      var y: int\n")
 2066     (write _test-input-stream "      break\n")
 2067     (write _test-input-stream "      increment x\n")
 2068     (write _test-input-stream "    }\n")
 2069     (write _test-input-stream "  }\n")
 2070     (write _test-input-stream "}\n")
 2071     # convert
 2072     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2073     (flush _test-output-buffered-file)
 2074 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2080     # check output
 2081     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
 2082     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
 2083     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
 2084     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
 2085     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
 2086     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
 2087     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
 2088     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
 2089     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
 2090     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
 2091     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
 2092     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
 2093     (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")
 2094     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
 2095     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
 2096     (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")
 2097     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
 2098     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
 2099     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
 2100     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
 2101     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
 2102     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
 2103     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
 2104     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
 2105     # . epilogue
 2106     89/<- %esp 5/r32/ebp
 2107     5d/pop-to-ebp
 2108     c3/return
 2109 
 2110 test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
 2111     # . prologue
 2112     55/push-ebp
 2113     89/<- %ebp 4/r32/esp
 2114     # setup
 2115     (clear-stream _test-input-stream)
 2116     (clear-stream $_test-input-buffered-file->buffer)
 2117     (clear-stream _test-output-stream)
 2118     (clear-stream $_test-output-buffered-file->buffer)
 2119     #
 2120     (write _test-input-stream "fn foo {\n")
 2121     (write _test-input-stream "  a: {\n")
 2122     (write _test-input-stream "    var x: int\n")
 2123     (write _test-input-stream "    {\n")
 2124     (write _test-input-stream "      var y: int\n")
 2125     (write _test-input-stream "      loop a\n")
 2126     (write _test-input-stream "      increment x\n")
 2127     (write _test-input-stream "    }\n")
 2128     (write _test-input-stream "  }\n")
 2129     (write _test-input-stream "}\n")
 2130     # convert
 2131     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2132     (flush _test-output-buffered-file)
 2133 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2139     # check output
 2140     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
 2141     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
 2142     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
 2143     (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")
 2144     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
 2145     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
 2146     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
 2147     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
 2148     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
 2149     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
 2150     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
 2151     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
 2152     (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")
 2153     (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")
 2154     (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")
 2155     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
 2156     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
 2157     (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")
 2158     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
 2159     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
 2160     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
 2161     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
 2162     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
 2163     (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")
 2164     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
 2165     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
 2166     # . epilogue
 2167     89/<- %esp 5/r32/ebp
 2168     5d/pop-to-ebp
 2169     c3/return
 2170 
 2171 test-convert-length-of-array:
 2172     # . prologue
 2173     55/push-ebp
 2174     89/<- %ebp 4/r32/esp
 2175     # setup
 2176     (clear-stream _test-input-stream)
 2177     (clear-stream $_test-input-buffered-file->buffer)
 2178     (clear-stream _test-output-stream)
 2179     (clear-stream $_test-output-buffered-file->buffer)
 2180     #
 2181     (write _test-input-stream "fn foo a: (addr array int) {\n")
 2182     (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
 2183     (write _test-input-stream "  var c/eax: int <- length b\n")
 2184     (write _test-input-stream "}\n")
 2185     # convert
 2186     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2187     (flush _test-output-buffered-file)
 2188 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2194     # check output
 2195     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
 2196     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
 2197     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
 2198     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
 2199     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
 2200     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
 2201     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
 2202     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
 2203     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
 2204     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
 2205     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
 2206     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
 2207     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
 2208     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
 2209     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
 2210     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
 2211     # . epilogue
 2212     89/<- %esp 5/r32/ebp
 2213     5d/pop-to-ebp
 2214     c3/return
 2215 
 2216 test-convert-index-into-array:
 2217     # . prologue
 2218     55/push-ebp
 2219     89/<- %ebp 4/r32/esp
 2220     # setup
 2221     (clear-stream _test-input-stream)
 2222     (clear-stream $_test-input-buffered-file->buffer)
 2223     (clear-stream _test-output-stream)
 2224     (clear-stream $_test-output-buffered-file->buffer)
 2225     #
 2226     (write _test-input-stream "fn foo {\n")
 2227     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2228     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2229     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2230     (write _test-input-stream "}\n")
 2231     # convert
 2232     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2233     (flush _test-output-buffered-file)
 2234 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2240     # check output
 2241     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array/0")
 2242     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array/1")
 2243     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array/2")
 2244     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array/3")
 2245     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array/4")
 2246     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array/5")
 2247     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array/6")
 2248     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array/7")
 2249     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array/8")
 2250     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array/9")
 2251     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000002 + 4) 0x00000000/r32"  "F - test-convert-index-into-array/11")
 2252     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array/13")
 2253     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array/14")
 2254     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array/15")
 2255     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array/16")
 2256     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array/17")
 2257     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array/18")
 2258     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array/19")
 2259     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array/20")
 2260     # . epilogue
 2261     89/<- %esp 5/r32/ebp
 2262     5d/pop-to-ebp
 2263     c3/return
 2264 
 2265 test-convert-index-into-array-with-literal:
 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 arr/eax: (addr array int) <- copy 0\n")
 2277     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\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-index-into-array-with-literal/0")
 2290     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-with-literal/1")
 2291     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-with-literal/2")
 2292     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-with-literal/3")
 2293     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-with-literal/4")
 2294     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-with-literal/5")
 2295     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-with-literal/6")
 2296     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-with-literal/7")
 2297                                                                                  # 2 * 4 bytes/elem + 4 bytes for length = offset 12
 2298     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x0000000c) 0x00000000/r32"  "F - test-convert-index-into-array-with-literal/8")
 2299     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-with-literal/9")
 2300     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-with-literal/10")
 2301     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-with-literal/11")
 2302     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-with-literal/12")
 2303     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-with-literal/13")
 2304     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-with-literal/14")
 2305     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-with-literal/15")
 2306     # . epilogue
 2307     89/<- %esp 5/r32/ebp
 2308     5d/pop-to-ebp
 2309     c3/return
 2310 
 2311 test-convert-index-into-array-using-offset:
 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 {\n")
 2322     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2323     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2324     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2325     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2326     (write _test-input-stream "}\n")
 2327     # convert
 2328     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2329     (flush _test-output-buffered-file)
 2330 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2336     # check output
 2337     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset/0")
 2338     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset/1")
 2339     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset/2")
 2340     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset/3")
 2341     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset/4")
 2342     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset/5")
 2343     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset/6")
 2344     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset/7")
 2345     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset/8")
 2346     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-using-offset/9")
 2347     (check-next-stream-line-equal _test-output-stream "    69/multiply 0x00000004/imm32 %ecx 0x00000001/r32"  "F - test-convert-index-into-array-using-offset/10")
 2348     (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")
 2349     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset/12")
 2350     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset/13")
 2351     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset/14")
 2352     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset/15")
 2353     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset/16")
 2354     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset/17")
 2355     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset/18")
 2356     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset/19")
 2357     # . epilogue
 2358     89/<- %esp 5/r32/ebp
 2359     5d/pop-to-ebp
 2360     c3/return
 2361 
 2362 test-convert-index-into-array-using-offset-on-stack:
 2363     # . prologue
 2364     55/push-ebp
 2365     89/<- %ebp 4/r32/esp
 2366     # setup
 2367     (clear-stream _test-input-stream)
 2368     (clear-stream $_test-input-buffered-file->buffer)
 2369     (clear-stream _test-output-stream)
 2370     (clear-stream $_test-output-buffered-file->buffer)
 2371     #
 2372     (write _test-input-stream "fn foo {\n")
 2373     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2374     (write _test-input-stream "  var idx: int\n")
 2375     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2376     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2377     (write _test-input-stream "}\n")
 2378     # convert
 2379     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2380     (flush _test-output-buffered-file)
 2381 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2387     # check output
 2388     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset-on-stack/0")
 2389     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset-on-stack/1")
 2390     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset-on-stack/2")
 2391     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset-on-stack/3")
 2392     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset-on-stack/4")
 2393     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset-on-stack/5")
 2394     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset-on-stack/6")
 2395     (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")
 2396     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-using-offset-on-stack/8")
 2397     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset-on-stack/9")
 2398     (check-next-stream-line-equal _test-output-stream "    69/multiply 0x00000004/imm32 *(ebp+0xfffffff8) 0x00000001/r32"  "F - test-convert-index-into-array-using-offset-on-stack/10")
 2399     (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")
 2400     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset-on-stack/12")
 2401     (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")
 2402     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset-on-stack/14")
 2403     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset-on-stack/15")
 2404     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset-on-stack/16")
 2405     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset-on-stack/17")
 2406     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset-on-stack/18")
 2407     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset-on-stack/19")
 2408     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset-on-stack/20")
 2409     # . epilogue
 2410     89/<- %esp 5/r32/ebp
 2411     5d/pop-to-ebp
 2412     c3/return
 2413 
 2414 test-convert-function-and-type-definition:
 2415     # . prologue
 2416     55/push-ebp
 2417     89/<- %ebp 4/r32/esp
 2418     # setup
 2419     (clear-stream _test-input-stream)
 2420     (clear-stream $_test-input-buffered-file->buffer)
 2421     (clear-stream _test-output-stream)
 2422     (clear-stream $_test-output-buffered-file->buffer)
 2423     #
 2424     (write _test-input-stream "fn foo a: (addr t) {\n")
 2425     (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
 2426     (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
 2427     (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
 2428     (write _test-input-stream "}\n")
 2429     (write _test-input-stream "type t {\n")
 2430     (write _test-input-stream "  x: int\n")
 2431     (write _test-input-stream "  y: int\n")
 2432     (write _test-input-stream "}\n")
 2433     # convert
 2434     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2435     (flush _test-output-buffered-file)
 2436 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2442     # check output
 2443     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
 2444     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
 2445     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
 2446     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
 2447     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
 2448     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
 2449     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
 2450     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
 2451     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
 2452     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
 2453     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
 2454     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
 2455     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
 2456     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
 2457     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
 2458     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
 2459     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
 2460     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
 2461     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
 2462     # . epilogue
 2463     89/<- %esp 5/r32/ebp
 2464     5d/pop-to-ebp
 2465     c3/return
 2466 
 2467 test-convert-function-with-local-var-with-user-defined-type:
 2468     # . prologue
 2469     55/push-ebp
 2470     89/<- %ebp 4/r32/esp
 2471     # setup
 2472     (clear-stream _test-input-stream)
 2473     (clear-stream $_test-input-buffered-file->buffer)
 2474     (clear-stream _test-output-stream)
 2475     (clear-stream $_test-output-buffered-file->buffer)
 2476     #
 2477     (write _test-input-stream "fn foo {\n")
 2478     (write _test-input-stream "  var a: t\n")
 2479     (write _test-input-stream "}\n")
 2480     (write _test-input-stream "type t {\n")
 2481     (write _test-input-stream "  x: int\n")
 2482     (write _test-input-stream "  y: int\n")
 2483     (write _test-input-stream "}\n")
 2484     # convert
 2485     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2486     (flush _test-output-buffered-file)
 2487 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2493     # check output
 2494     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
 2495     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
 2496     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
 2497     (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")
 2498     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
 2499     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
 2500     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
 2501     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
 2502     (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")
 2503     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
 2504     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
 2505     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
 2506     (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")
 2507     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
 2508     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
 2509     # . epilogue
 2510     89/<- %esp 5/r32/ebp
 2511     5d/pop-to-ebp
 2512     c3/return
 2513 
 2514 test-convert-get-of-type-on-stack:
 2515     # . prologue
 2516     55/push-ebp
 2517     89/<- %ebp 4/r32/esp
 2518     # setup
 2519     (clear-stream _test-input-stream)
 2520     (clear-stream $_test-input-buffered-file->buffer)
 2521     (clear-stream _test-output-stream)
 2522     (clear-stream $_test-output-buffered-file->buffer)
 2523     #
 2524     (write _test-input-stream "fn foo {\n")
 2525     (write _test-input-stream "  var a: t\n")
 2526     (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
 2527     (write _test-input-stream "}\n")
 2528     (write _test-input-stream "type t {\n")
 2529     (write _test-input-stream "  x: int\n")
 2530     (write _test-input-stream "  y: int\n")
 2531     (write _test-input-stream "}\n")
 2532     # convert
 2533     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2534     (flush _test-output-buffered-file)
 2535 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2541     # check output
 2542     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-of-type-on-stack/0")
 2543     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-of-type-on-stack/1")
 2544     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-of-type-on-stack/2")
 2545     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-of-type-on-stack/3")
 2546     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-of-type-on-stack/4")
 2547     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-of-type-on-stack/5")
 2548     # var a
 2549     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/6")
 2550     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/7")
 2551     # var c
 2552     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-of-type-on-stack/8")
 2553     # get
 2554     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000001/r32"  "F - test-convert-get-of-type-on-stack/9")
 2555     # reclaim c
 2556     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-of-type-on-stack/10")
 2557     # reclaim a
 2558     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-get-of-type-on-stack/11")
 2559     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-of-type-on-stack/12")
 2560     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-of-type-on-stack/13")
 2561     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-of-type-on-stack/14")
 2562     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-of-type-on-stack/15")
 2563     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-of-type-on-stack/16")
 2564     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-of-type-on-stack/17")
 2565     # . epilogue
 2566     89/<- %esp 5/r32/ebp
 2567     5d/pop-to-ebp
 2568     c3/return
 2569 
 2570 test-convert-array-of-user-defined-types:
 2571     # . prologue
 2572     55/push-ebp
 2573     89/<- %ebp 4/r32/esp
 2574     # setup
 2575     (clear-stream _test-input-stream)
 2576     (clear-stream $_test-input-buffered-file->buffer)
 2577     (clear-stream _test-output-stream)
 2578     (clear-stream $_test-output-buffered-file->buffer)
 2579     #
 2580     (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
 2581     (write _test-input-stream "  x: int\n")
 2582     (write _test-input-stream "  y: int\n")
 2583     (write _test-input-stream "}\n")
 2584     (write _test-input-stream "fn foo {\n")
 2585     (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
 2586     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2587     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2588     (write _test-input-stream "}\n")
 2589     # convert
 2590     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2591     (flush _test-output-buffered-file)
 2592 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2598     # check output
 2599     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-array-of-user-defined-types/0")
 2600     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-array-of-user-defined-types/1")
 2601     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-array-of-user-defined-types/2")
 2602     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-array-of-user-defined-types/3")
 2603     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-array-of-user-defined-types/4")
 2604     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-array-of-user-defined-types/5")
 2605     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-array-of-user-defined-types/6")
 2606     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-array-of-user-defined-types/7")
 2607     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-array-of-user-defined-types/8")
 2608     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-array-of-user-defined-types/9")
 2609     (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")
 2610     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-array-of-user-defined-types/13")
 2611     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-array-of-user-defined-types/14")
 2612     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-array-of-user-defined-types/15")
 2613     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-array-of-user-defined-types/16")
 2614     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-array-of-user-defined-types/17")
 2615     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-array-of-user-defined-types/18")
 2616     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-array-of-user-defined-types/19")
 2617     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-array-of-user-defined-types/20")
 2618     # . epilogue
 2619     89/<- %esp 5/r32/ebp
 2620     5d/pop-to-ebp
 2621     c3/return
 2622 
 2623 #######################################################
 2624 # Parsing
 2625 #######################################################
 2626 
 2627 parse-mu:  # in: (addr buffered-file)
 2628     # pseudocode
 2629     #   var curr-function: (addr (handle function)) = Program->functions
 2630     #   var curr-type: (addr (handle typeinfo)) = Program->types
 2631     #   var line: (stream byte 512)
 2632     #   var word-slice: slice
 2633     #   while true                                  # line loop
 2634     #     clear-stream(line)
 2635     #     read-line-buffered(in, line)
 2636     #     if (line->write == 0) break               # end of file
 2637     #     word-slice = next-mu-token(line)
 2638     #     if slice-empty?(word-slice)               # end of line
 2639     #       continue
 2640     #     else if slice-starts-with?(word-slice, "#")  # comment
 2641     #       continue                                # end of line
 2642     #     else if slice-equal?(word-slice, "fn")
 2643     #       var new-function: (handle function) = allocate(function)
 2644     #       var vars: (stack (addr var) 256)
 2645     #       populate-mu-function-header(in, new-function, vars)
 2646     #       populate-mu-function-body(in, new-function, vars)
 2647     #       assert(vars->top == 0)
 2648     #       *curr-function = new-function
 2649     #       curr-function = &new-function->next
 2650     #     else if slice-equal?(word-slice, "type")
 2651     #       word-slice = next-mu-token(line)
 2652     #       type-id = pos-or-insert-slice(Type-id, word-slice)
 2653     #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
 2654     #       assert(next-word(line) == "{")
 2655     #       populate-mu-type(in, new-type)
 2656     #     else
 2657     #       abort()
 2658     #
 2659     # . prologue
 2660     55/push-ebp
 2661     89/<- %ebp 4/r32/esp
 2662     # . save registers
 2663     50/push-eax
 2664     51/push-ecx
 2665     52/push-edx
 2666     53/push-ebx
 2667     56/push-esi
 2668     57/push-edi
 2669     # var line/ecx: (stream byte 512)
 2670     81 5/subop/subtract %esp 0x200/imm32
 2671     68/push 0x200/imm32/length
 2672     68/push 0/imm32/read
 2673     68/push 0/imm32/write
 2674     89/<- %ecx 4/r32/esp
 2675     # var word-slice/edx: slice
 2676     68/push 0/imm32/end
 2677     68/push 0/imm32/start
 2678     89/<- %edx 4/r32/esp
 2679     # var curr-function/edi: (addr (handle function))
 2680     bf/copy-to-edi _Program-functions/imm32
 2681     # var curr-type/esi: (addr (handle typeinfo))
 2682     be/copy-to-esi _Program-types/imm32
 2683     # var vars/ebx: (stack (addr var) 256)
 2684     81 5/subop/subtract %esp 0x400/imm32
 2685     68/push 0x400/imm32/length
 2686     68/push 0/imm32/top
 2687     89/<- %ebx 4/r32/esp
 2688     {
 2689 $parse-mu:line-loop:
 2690       (clear-stream %ecx)
 2691       (read-line-buffered *(ebp+8) %ecx)
 2692       # if (line->write == 0) break
 2693       81 7/subop/compare *ecx 0/imm32
 2694       0f 84/jump-if-= break/disp32
 2695 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
 2701       (next-mu-token %ecx %edx)
 2702       # if slice-empty?(word-slice) continue
 2703       (slice-empty? %edx)
 2704       3d/compare-eax-and 0/imm32/false
 2705       0f 85/jump-if-!= loop/disp32
 2706       # if (*word-slice->start == "#") continue
 2707       # . eax = *word-slice->start
 2708       8b/-> *edx 0/r32/eax
 2709       8a/copy-byte *eax 0/r32/AL
 2710       81 4/subop/and %eax 0xff/imm32
 2711       # . if (eax == '#') continue
 2712       3d/compare-eax-and 0x23/imm32/hash
 2713       0f 84/jump-if-= loop/disp32
 2714       # if (slice-equal?(word-slice, "fn")) parse a function
 2715       {
 2716 $parse-mu:fn:
 2717         (slice-equal? %edx "fn")
 2718         3d/compare-eax-and 0/imm32/false
 2719         0f 84/jump-if-= break/disp32
 2720         # var new-function/eax: (handle function) = populate-mu-function(line, in, vars)
 2721         (allocate Heap *Function-size)  # => eax
 2722         (zero-out %eax *Function-size)
 2723         (clear-stack %ebx)
 2724         (populate-mu-function-header %ecx %eax %ebx)
 2725         (populate-mu-function-body *(ebp+8) %eax %ebx)
 2726         # *curr-function = new-function
 2727         89/<- *edi 0/r32/eax
 2728         # curr-function = &new-function->next
 2729         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
 2730         e9/jump $parse-mu:line-loop/disp32
 2731       }
 2732       # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
 2733       {
 2734 $parse-mu:type:
 2735         (slice-equal? %edx "type")
 2736         3d/compare-eax-and 0/imm32
 2737         0f 84/jump-if-= break/disp32
 2738         (next-mu-token %ecx %edx)
 2739         # var type-id/eax: int
 2740         (pos-or-insert-slice Type-id %edx)  # => eax
 2741         # var new-type/eax: (handle typeinfo)
 2742         (find-or-create-typeinfo %eax)  # => eax
 2743         # TODO: ensure that 'line' has nothing else but '{'
 2744         (populate-mu-type *(ebp+8) %eax)  # => eax
 2745         e9/jump $parse-mu:line-loop/disp32
 2746       }
 2747       # otherwise abort
 2748       e9/jump $parse-mu:error1/disp32
 2749     } # end line loop
 2750 $parse-mu:end:
 2751     # . reclaim locals
 2752     81 0/subop/add %esp 0x630/imm32
 2753     # . restore registers
 2754     5f/pop-to-edi
 2755     5e/pop-to-esi
 2756     5b/pop-to-ebx
 2757     5a/pop-to-edx
 2758     59/pop-to-ecx
 2759     58/pop-to-eax
 2760     # . epilogue
 2761     89/<- %esp 5/r32/ebp
 2762     5d/pop-to-ebp
 2763     c3/return
 2764 
 2765 $parse-mu:error1:
 2766     # error("unexpected top-level command: " word-slice "\n")
 2767     (write-buffered Stderr "unexpected top-level command: ")
 2768     (write-slice-buffered Stderr %edx)
 2769     (write-buffered Stderr "\n")
 2770     (flush Stderr)
 2771     # . syscall(exit, 1)
 2772     bb/copy-to-ebx  1/imm32
 2773     b8/copy-to-eax  1/imm32/exit
 2774     cd/syscall  0x80/imm8
 2775     # never gets here
 2776 
 2777 $parse-mu:error2:
 2778     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
 2779     (print-int32-buffered Stderr *ebx)
 2780     (write-buffered Stderr " vars not reclaimed after fn '")
 2781     (write-slice-buffered Stderr *eax)  # Function-name
 2782     (write-buffered Stderr "'\n")
 2783     (flush Stderr)
 2784     # . syscall(exit, 1)
 2785     bb/copy-to-ebx  1/imm32
 2786     b8/copy-to-eax  1/imm32/exit
 2787     cd/syscall  0x80/imm8
 2788     # never gets here
 2789 
 2790 # scenarios considered:
 2791 # ✗ fn foo  # no block
 2792 # ✓ fn foo {
 2793 # ✗ fn foo { {
 2794 # ✗ fn foo { }
 2795 # ✗ fn foo { } {
 2796 # ✗ fn foo x {
 2797 # ✗ fn foo x: {
 2798 # ✓ fn foo x: int {
 2799 # ✓ fn foo x: int {
 2800 # ✓ fn foo x: int -> y/eax: int {
 2801 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
 2802     # pseudocode:
 2803     #   var name: slice
 2804     #   next-mu-token(first-line, name)
 2805     #   assert(name not in '{' '}' '->')
 2806     #   out->name = slice-to-string(name)
 2807     #   ## inouts
 2808     #   while true
 2809     #     ## name
 2810     #     name = next-mu-token(first-line)
 2811     #     if (name == '{') goto done
 2812     #     if (name == '->') break
 2813     #     assert(name != '}')
 2814     #     var v: (handle var) = parse-var-with-type(name, first-line)
 2815     #     assert(v->register == null)
 2816     #     # v->block-depth is implicitly 0
 2817     #     out->inouts = append(out->inouts, v)
 2818     #     push(vars, v)
 2819     #   ## outputs
 2820     #   while true
 2821     #     ## name
 2822     #     name = next-mu-token(first-line)
 2823     #     assert(name not in '{' '}' '->')
 2824     #     var v: (handle var) = parse-var-with-type(name, first-line)
 2825     #     assert(v->register != null)
 2826     #     out->outputs = append(out->outputs, v)
 2827     #   done:
 2828     #
 2829     # . prologue
 2830     55/push-ebp
 2831     89/<- %ebp 4/r32/esp
 2832     # . save registers
 2833     50/push-eax
 2834     51/push-ecx
 2835     52/push-edx
 2836     53/push-ebx
 2837     57/push-edi
 2838     # edi = out
 2839     8b/-> *(ebp+0xc) 7/r32/edi
 2840     # var word-slice/ecx: slice
 2841     68/push 0/imm32/end
 2842     68/push 0/imm32/start
 2843     89/<- %ecx 4/r32/esp
 2844     # read function name
 2845     (next-mu-token *(ebp+8) %ecx)
 2846     # error checking
 2847     # TODO: error if name starts with 'break' or 'loop'
 2848     # if (word-slice == '{') abort
 2849     (slice-equal? %ecx "{")   # => eax
 2850     3d/compare-eax-and 0/imm32/false
 2851     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 2852     # if (word-slice == '->') abort
 2853     (slice-equal? %ecx "->")   # => eax
 2854     3d/compare-eax-and 0/imm32/false
 2855     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 2856     # if (word-slice == '}') abort
 2857     (slice-equal? %ecx "}")   # => eax
 2858     3d/compare-eax-and 0/imm32/false
 2859     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 2860     # save function name
 2861     (slice-to-string Heap %ecx)  # => eax
 2862     89/<- *edi 0/r32/eax  # Function-name
 2863     # initialize default subx-name as well
 2864     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
 2865     # save function inouts
 2866     {
 2867 $populate-mu-function-header:check-for-inout:
 2868       (next-mu-token *(ebp+8) %ecx)
 2869       # if (word-slice == '{') goto done
 2870       (slice-equal? %ecx "{")   # => eax
 2871       3d/compare-eax-and 0/imm32/false
 2872       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
 2873       # if (word-slice == '->') break
 2874       (slice-equal? %ecx "->")   # => eax
 2875       3d/compare-eax-and 0/imm32/false
 2876       0f 85/jump-if-!= break/disp32
 2877       # if (word-slice == '}') abort
 2878       (slice-equal? %ecx "}")   # => eax
 2879       3d/compare-eax-and 0/imm32/false
 2880       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 2881       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
 2882       (parse-var-with-type %ecx *(ebp+8))  # => eax
 2883       89/<- %ebx 0/r32/eax
 2884       # assert(v->register == null)
 2885       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 2886       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
 2887       # v->block-depth is implicitly 0
 2888       #
 2889       # out->inouts = append(out->inouts, v)
 2890       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
 2891       89/<- *(edi+8) 0/r32/eax  # Function-inouts
 2892       # push(vars, v)
 2893       (push *(ebp+0x10) %ebx)
 2894       #
 2895       e9/jump loop/disp32
 2896     }
 2897     # save function outputs
 2898     {
 2899 $populate-mu-function-header:check-for-out:
 2900       (next-mu-token *(ebp+8) %ecx)
 2901       # if (word-slice == '{') break
 2902       (slice-equal? %ecx "{")   # => eax
 2903       3d/compare-eax-and 0/imm32/false
 2904       0f 85/jump-if-!= break/disp32
 2905       # if (word-slice == '->') abort
 2906       (slice-equal? %ecx "->")   # => eax
 2907       3d/compare-eax-and 0/imm32/false
 2908       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 2909       # if (word-slice == '}') abort
 2910       (slice-equal? %ecx "}")   # => eax
 2911       3d/compare-eax-and 0/imm32/false
 2912       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 2913       #
 2914       (parse-var-with-type %ecx *(ebp+8))  # => eax
 2915       89/<- %ebx 0/r32/eax
 2916       # assert(var->register != null)
 2917       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 2918       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
 2919       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
 2920       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
 2921       e9/jump loop/disp32
 2922     }
 2923 $populate-mu-function-header:done:
 2924     (check-no-tokens-left *(ebp+8))
 2925 $populate-mu-function-header:end:
 2926     # . reclaim locals
 2927     81 0/subop/add %esp 8/imm32
 2928     # . restore registers
 2929     5f/pop-to-edi
 2930     5b/pop-to-ebx
 2931     5a/pop-to-edx
 2932     59/pop-to-ecx
 2933     58/pop-to-eax
 2934     # . epilogue
 2935     89/<- %esp 5/r32/ebp
 2936     5d/pop-to-ebp
 2937     c3/return
 2938 
 2939 $populate-mu-function-header:error1:
 2940     # error("function header not in form 'fn <name> {'")
 2941     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
 2942     (flush Stderr)
 2943     (rewind-stream *(ebp+8))
 2944     (write-stream 2 *(ebp+8))
 2945     (write-buffered Stderr "'\n")
 2946     (flush Stderr)
 2947     # . syscall(exit, 1)
 2948     bb/copy-to-ebx  1/imm32
 2949     b8/copy-to-eax  1/imm32/exit
 2950     cd/syscall  0x80/imm8
 2951     # never gets here
 2952 
 2953 $populate-mu-function-header:error2:
 2954     # error("function input '" var "' cannot be in a register")
 2955     (write-buffered Stderr "function input '")
 2956     (write-buffered Stderr *ebx)  # Var-name
 2957     (write-buffered Stderr "' cannot be in a register")
 2958     (flush Stderr)
 2959     # . syscall(exit, 1)
 2960     bb/copy-to-ebx  1/imm32
 2961     b8/copy-to-eax  1/imm32/exit
 2962     cd/syscall  0x80/imm8
 2963     # never gets here
 2964 
 2965 $populate-mu-function-header:error3:
 2966     # error("function input '" var "' must be in a register")
 2967     (write-buffered Stderr "function input '")
 2968     (write-buffered Stderr *eax)  # Var-name
 2969     (write-buffered Stderr " must be in a register'")
 2970     (flush Stderr)
 2971     (rewind-stream *(ebp+8))
 2972     (write-stream 2 *(ebp+8))
 2973     (write-buffered Stderr "'\n")
 2974     (flush Stderr)
 2975     # . syscall(exit, 1)
 2976     bb/copy-to-ebx  1/imm32
 2977     b8/copy-to-eax  1/imm32/exit
 2978     cd/syscall  0x80/imm8
 2979     # never gets here
 2980 
 2981 test-function-header-with-arg:
 2982     # . prologue
 2983     55/push-ebp
 2984     89/<- %ebp 4/r32/esp
 2985     # setup
 2986     (clear-stream _test-input-stream)
 2987     (write _test-input-stream "foo n: int {\n")
 2988     # var result/ecx: function
 2989     2b/subtract-> *Function-size 4/r32/esp
 2990     89/<- %ecx 4/r32/esp
 2991     (zero-out %ecx *Function-size)
 2992     # var vars/ebx: (stack (addr var) 16)
 2993     81 5/subop/subtract %esp 0x10/imm32
 2994     68/push 0x10/imm32/length
 2995     68/push 0/imm32/top
 2996     89/<- %ebx 4/r32/esp
 2997     # convert
 2998     (populate-mu-function-header _test-input-stream %ecx %ebx)
 2999     # check result
 3000     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
 3001     # edx: (handle list var) = result->inouts
 3002     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3003     # ebx: (handle var) = result->inouts->value
 3004     8b/-> *edx 3/r32/ebx  # List-value
 3005     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
 3006     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3007     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
 3008     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
 3009     # . epilogue
 3010     89/<- %esp 5/r32/ebp
 3011     5d/pop-to-ebp
 3012     c3/return
 3013 
 3014 test-function-header-with-multiple-args:
 3015     # . prologue
 3016     55/push-ebp
 3017     89/<- %ebp 4/r32/esp
 3018     # setup
 3019     (clear-stream _test-input-stream)
 3020     (write _test-input-stream "foo a: int, b: int c: int {\n")
 3021     # result/ecx: (handle function)
 3022     2b/subtract-> *Function-size 4/r32/esp
 3023     89/<- %ecx 4/r32/esp
 3024     (zero-out %ecx *Function-size)
 3025     # var vars/ebx: (stack (addr var) 16)
 3026     81 5/subop/subtract %esp 0x10/imm32
 3027     68/push 0x10/imm32/length
 3028     68/push 0/imm32/top
 3029     89/<- %ebx 4/r32/esp
 3030     # convert
 3031     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3032     # check result
 3033     (check-strings-equal *ecx "foo")  # Function-name
 3034     # edx: (handle list var) = result->inouts
 3035     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3036 $test-function-header-with-multiple-args:inout0:
 3037     # ebx: (handle var) = result->inouts->value
 3038     8b/-> *edx 3/r32/ebx  # List-value
 3039     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
 3040     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3041     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
 3042     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
 3043     # edx = result->inouts->next
 3044     8b/-> *(edx+4) 2/r32/edx  # List-next
 3045 $test-function-header-with-multiple-args:inout1:
 3046     # ebx = result->inouts->next->value
 3047     8b/-> *edx 3/r32/ebx  # List-value
 3048     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
 3049     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3050     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
 3051     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
 3052     # edx = result->inouts->next->next
 3053     8b/-> *(edx+4) 2/r32/edx  # List-next
 3054 $test-function-header-with-multiple-args:inout2:
 3055     # ebx = result->inouts->next->next->value
 3056     8b/-> *edx 3/r32/ebx  # List-value
 3057     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
 3058     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3059     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
 3060     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
 3061     # . epilogue
 3062     89/<- %esp 5/r32/ebp
 3063     5d/pop-to-ebp
 3064     c3/return
 3065 
 3066 test-function-with-multiple-args-and-outputs:
 3067     # . prologue
 3068     55/push-ebp
 3069     89/<- %ebp 4/r32/esp
 3070     # setup
 3071     (clear-stream _test-input-stream)
 3072     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
 3073     # result/ecx: (handle function)
 3074     2b/subtract-> *Function-size 4/r32/esp
 3075     89/<- %ecx 4/r32/esp
 3076     (zero-out %ecx *Function-size)
 3077     # var vars/ebx: (stack (addr var) 16)
 3078     81 5/subop/subtract %esp 0x10/imm32
 3079     68/push 0x10/imm32/length
 3080     68/push 0/imm32/top
 3081     89/<- %ebx 4/r32/esp
 3082     # convert
 3083     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3084     # check result
 3085     (check-strings-equal *ecx "foo")  # Function-name
 3086     # edx: (handle list var) = result->inouts
 3087     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3088     # ebx: (handle var) = result->inouts->value
 3089     8b/-> *edx 3/r32/ebx  # List-value
 3090     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
 3091     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3092     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
 3093     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
 3094     # edx = result->inouts->next
 3095     8b/-> *(edx+4) 2/r32/edx  # List-next
 3096     # ebx = result->inouts->next->value
 3097     8b/-> *edx 3/r32/ebx  # List-value
 3098     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
 3099     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3100     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
 3101     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
 3102     # edx = result->inouts->next->next
 3103     8b/-> *(edx+4) 2/r32/edx  # List-next
 3104     # ebx = result->inouts->next->next->value
 3105     8b/-> *edx 3/r32/ebx  # List-value
 3106     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
 3107     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3108     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
 3109     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
 3110     # edx: (handle list var) = result->outputs
 3111     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
 3112     # ebx: (handle var) = result->outputs->value
 3113     8b/-> *edx 3/r32/ebx  # List-value
 3114     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
 3115     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3116     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3117     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
 3118     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
 3119     # edx = result->outputs->next
 3120     8b/-> *(edx+4) 2/r32/edx  # List-next
 3121     # ebx = result->outputs->next->value
 3122     8b/-> *edx 3/r32/ebx  # List-value
 3123     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
 3124     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3125     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3126     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
 3127     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
 3128     # . epilogue
 3129     89/<- %esp 5/r32/ebp
 3130     5d/pop-to-ebp
 3131     c3/return
 3132 
 3133 # format for variables with types
 3134 #   x: int
 3135 #   x: int,
 3136 #   x/eax: int
 3137 #   x/eax: int,
 3138 # ignores at most one trailing comma
 3139 # WARNING: modifies name
 3140 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
 3141     # pseudocode:
 3142     #   var s: slice
 3143     #   if (!slice-ends-with(name, ":"))
 3144     #     abort
 3145     #   --name->end to skip ':'
 3146     #   next-token-from-slice(name->start, name->end, '/', s)
 3147     #   result = new-var-from-slice(s)
 3148     #   ## register
 3149     #   next-token-from-slice(s->end, name->end, '/', s)
 3150     #   if (!slice-empty?(s))
 3151     #     v->register = slice-to-string(s)
 3152     #   ## type
 3153     #   var type: (handle tree type-id) = parse-type(first-line)
 3154     #   v->type = type
 3155     #   return v
 3156     #
 3157     # . prologue
 3158     55/push-ebp
 3159     89/<- %ebp 4/r32/esp
 3160     # . save registers
 3161     51/push-ecx
 3162     52/push-edx
 3163     53/push-ebx
 3164     56/push-esi
 3165     57/push-edi
 3166     # esi = name
 3167     8b/-> *(ebp+8) 6/r32/esi
 3168     # if (!slice-ends-with?(name, ":")) abort
 3169     8b/-> *(esi+4) 1/r32/ecx  # Slice-end
 3170     49/decrement-ecx
 3171     8a/copy-byte *ecx 1/r32/CL
 3172     81 4/subop/and %ecx 0xff/imm32
 3173     81 7/subop/compare %ecx 0x3a/imm32/colon
 3174     0f 85/jump-if-!= $parse-var-with-type:abort/disp32
 3175     # --name->end to skip ':'
 3176     ff 1/subop/decrement *(esi+4)
 3177     # var s/ecx: slice
 3178     68/push 0/imm32/end
 3179     68/push 0/imm32/start
 3180     89/<- %ecx 4/r32/esp
 3181 $parse-var-with-type:parse-name:
 3182     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
 3183 $parse-var-with-type:create-var:
 3184     # edi = new-var-from-slice(s)
 3185     (new-var-from-slice Heap %ecx)  # => eax
 3186     89/<- %edi 0/r32/eax
 3187     # save v->register
 3188 $parse-var-with-type:save-register:
 3189     # s = next-token(...)
 3190     (next-token-from-slice *(ecx+4) *(esi+4) 0x2f %ecx)  # s->end, name->end, '/'
 3191     # if (!slice-empty?(s)) v->register = slice-to-string(s)
 3192     {
 3193 $parse-var-with-type:write-register:
 3194       (slice-empty? %ecx)  # => eax
 3195       3d/compare-eax-and 0/imm32/false
 3196       75/jump-if-!= break/disp8
 3197       (slice-to-string Heap %ecx)
 3198       89/<- *(edi+0x10) 0/r32/eax  # Var-register
 3199     }
 3200 $parse-var-with-type:save-type:
 3201     (parse-type Heap *(ebp+0xc))  # => eax
 3202 #?     (write-buffered Stderr "saving to var ")
 3203 #?     (print-int32-buffered Stderr %edi)
 3204 #?     (write-buffered Stderr Newline)
 3205 #?     (flush Stderr)
 3206     89/<- *(edi+4) 0/r32/eax  # Var-type
 3207 $parse-var-with-type:end:
 3208     # return result
 3209     89/<- %eax 7/r32/edi
 3210     # . reclaim locals
 3211     81 0/subop/add %esp 8/imm32
 3212     # . restore registers
 3213     5f/pop-to-edi
 3214     5e/pop-to-esi
 3215     5b/pop-to-ebx
 3216     5a/pop-to-edx
 3217     59/pop-to-ecx
 3218     # . epilogue
 3219     89/<- %esp 5/r32/ebp
 3220     5d/pop-to-ebp
 3221     c3/return
 3222 
 3223 $parse-var-with-type:abort:
 3224     # error("var should have form 'name: type' in '" line "'\n")
 3225     (write-buffered Stderr "var should have form 'name: type' in '")
 3226     (flush Stderr)
 3227     (rewind-stream *(ebp+0xc))
 3228     (write-stream 2 *(ebp+0xc))
 3229     (write-buffered Stderr "'\n")
 3230     (flush Stderr)
 3231     # . syscall(exit, 1)
 3232     bb/copy-to-ebx  1/imm32
 3233     b8/copy-to-eax  1/imm32/exit
 3234     cd/syscall  0x80/imm8
 3235     # never gets here
 3236 
 3237 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3238     # pseudocode:
 3239     #   var s: slice = next-mu-token(in)
 3240     #   assert s != ""
 3241     #   assert s != "->"
 3242     #   assert s != "{"
 3243     #   assert s != "}"
 3244     #   if s == ")"
 3245     #     return 0
 3246     #   result = allocate(Tree)
 3247     #   zero-out(result, *Tree-size)
 3248     #   if s != "("
 3249     #     result->left = pos-or-insert-slice(Type-id, s)
 3250     #     return
 3251     #   result->left = parse-type(ad, in)
 3252     #   result->right = parse-type-tree(ad, in)
 3253     #
 3254     # . prologue
 3255     55/push-ebp
 3256     89/<- %ebp 4/r32/esp
 3257     # . save registers
 3258     51/push-ecx
 3259     52/push-edx
 3260     # var s/ecx: slice
 3261     68/push 0/imm32
 3262     68/push 0/imm32
 3263     89/<- %ecx 4/r32/esp
 3264     # s = next-mu-token(in)
 3265     (next-mu-token *(ebp+0xc) %ecx)
 3266 #?     (write-buffered Stderr "tok: ")
 3267 #?     (write-slice-buffered Stderr %ecx)
 3268 #?     (write-buffered Stderr "$\n")
 3269 #?     (flush Stderr)
 3270     # assert s != ""
 3271     (slice-equal? %ecx "")
 3272     3d/compare-eax-and 0/imm32/false
 3273     0f 85/jump-if-!= $parse-type:abort/disp32
 3274     # assert s != "{"
 3275     (slice-equal? %ecx "{")
 3276     3d/compare-eax-and 0/imm32/false
 3277     0f 85/jump-if-!= $parse-type:abort/disp32
 3278     # assert s != "}"
 3279     (slice-equal? %ecx "}")
 3280     3d/compare-eax-and 0/imm32/false
 3281     0f 85/jump-if-!= $parse-type:abort/disp32
 3282     # assert s != "->"
 3283     (slice-equal? %ecx "->")
 3284     3d/compare-eax-and 0/imm32/false
 3285     0f 85/jump-if-!= $parse-type:abort/disp32
 3286     # if (s == ")") return 0
 3287     (slice-equal? %ecx ")")
 3288     3d/compare-eax-and 0/imm32/false
 3289     b8/copy-to-eax 0/imm32
 3290     0f 85/jump-if-!= $parse-type:end/disp32
 3291     # var result/edx: (handle tree type-id)
 3292     (allocate *(ebp+8) *Tree-size)  # => eax
 3293     (zero-out %eax *Tree-size)
 3294     89/<- %edx 0/r32/eax
 3295     {
 3296       # if (s != "(") break
 3297       (slice-equal? %ecx "(")
 3298       3d/compare-eax-and 0/imm32/false
 3299       75/jump-if-!= break/disp8
 3300       # result->left = pos-or-insert-slice(Type-id, s)
 3301       (pos-or-insert-slice Type-id %ecx)  # => eax
 3302 #?       (write-buffered Stderr "=> {")
 3303 #?       (print-int32-buffered Stderr %eax)
 3304 #?       (write-buffered Stderr ", 0}\n")
 3305 #?       (flush Stderr)
 3306       89/<- *edx 0/r32/eax  # Tree-left
 3307       e9/jump $parse-type:return-edx/disp32
 3308     }
 3309     # otherwise s == "("
 3310     # result->left = parse-type(ad, in)
 3311     (parse-type *(ebp+8) *(ebp+0xc))
 3312 #?     (write-buffered Stderr "=> {")
 3313 #?     (print-int32-buffered Stderr %eax)
 3314     89/<- *edx 0/r32/eax  # Tree-left
 3315     # result->right = parse-type-tree(ad, in)
 3316     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3317 #?     (write-buffered Stderr Space)
 3318 #?     (print-int32-buffered Stderr %eax)
 3319 #?     (write-buffered Stderr "}\n")
 3320 #?     (flush Stderr)
 3321     89/<- *(edx+4) 0/r32/eax  # Tree-right
 3322 $parse-type:return-edx:
 3323     89/<- %eax 2/r32/edx
 3324 $parse-type:end:
 3325     # . reclaim locals
 3326     81 0/subop/add %esp 8/imm32
 3327     # . restore registers
 3328     5a/pop-to-edx
 3329     59/pop-to-ecx
 3330     # . epilogue
 3331     89/<- %esp 5/r32/ebp
 3332     5d/pop-to-ebp
 3333     c3/return
 3334 
 3335 $parse-type:abort:
 3336     # error("unexpected token when parsing type: '" s "'\n")
 3337     (write-buffered Stderr "unexpected token when parsing type: '")
 3338     (write-slice-buffered Stderr %ecx)
 3339     (write-buffered Stderr "'\n")
 3340     (flush Stderr)
 3341     # . syscall(exit, 1)
 3342     bb/copy-to-ebx  1/imm32
 3343     b8/copy-to-eax  1/imm32/exit
 3344     cd/syscall  0x80/imm8
 3345     # never gets here
 3346 
 3347 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3348     # pseudocode:
 3349     #   var tmp: (handle tree type-id) = parse-type(ad, in)
 3350     #   if tmp == 0
 3351     #     return 0
 3352     #   result = allocate(Tree)
 3353     #   zero-out(result, *Tree-size)
 3354     #   result->left = tmp
 3355     #   result->right = parse-type-tree(ad, in)
 3356     #
 3357     # . prologue
 3358     55/push-ebp
 3359     89/<- %ebp 4/r32/esp
 3360     # . save registers
 3361     51/push-ecx
 3362     52/push-edx
 3363     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
 3364     (parse-type *(ebp+8) *(ebp+0xc))
 3365     # if (tmp == 0) return tmp
 3366     3d/compare-eax-and 0/imm32
 3367     74/jump-if-= $parse-type-tree:end/disp8
 3368     # var tmp2/ecx = tmp
 3369     89/<- %ecx 0/r32/eax
 3370     # var result/edx: (handle tree type-id)
 3371     (allocate *(ebp+8) *Tree-size)  # => eax
 3372     (zero-out %eax *Tree-size)
 3373     89/<- %edx 0/r32/eax
 3374     # result->left = tmp2
 3375     89/<- *edx 1/r32/ecx  # Tree-left
 3376     # result->right = parse-type-tree(ad, in)
 3377     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3378     89/<- *(edx+4) 0/r32/eax  # Tree-right
 3379 $parse-type-tree:return-edx:
 3380     89/<- %eax 2/r32/edx
 3381 $parse-type-tree:end:
 3382     # . restore registers
 3383     5a/pop-to-edx
 3384     59/pop-to-ecx
 3385     # . epilogue
 3386     89/<- %esp 5/r32/ebp
 3387     5d/pop-to-ebp
 3388     c3/return
 3389 
 3390 next-mu-token:  # in: (addr stream byte), out: (addr slice)
 3391     # pseudocode:
 3392     # start:
 3393     #   skip-chars-matching-whitespace(in)
 3394     #   if in->read >= in->write              # end of in
 3395     #     out = {0, 0}
 3396     #     return
 3397     #   out->start = &in->data[in->read]
 3398     #   var curr-byte/eax: byte = in->data[in->read]
 3399     #   if curr->byte == ','                  # comment token
 3400     #     ++in->read
 3401     #     goto start
 3402     #   if curr-byte == '#'                   # comment
 3403     #     goto done                             # treat as eof
 3404     #   if curr-byte == '"'                   # string literal
 3405     #     skip-string(in)
 3406     #     goto done                           # no metadata
 3407     #   if curr-byte == '('
 3408     #     ++in->read
 3409     #     goto done
 3410     #   if curr-byte == ')'
 3411     #     ++in->read
 3412     #     goto done
 3413     #   # read a word
 3414     #   while true
 3415     #     if in->read >= in->write
 3416     #       break
 3417     #     curr-byte = in->data[in->read]
 3418     #     if curr-byte == ' '
 3419     #       break
 3420     #     if curr-byte == '\r'
 3421     #       break
 3422     #     if curr-byte == '\n'
 3423     #       break
 3424     #     if curr-byte == '('
 3425     #       break
 3426     #     if curr-byte == ')'
 3427     #       break
 3428     #     if curr-byte == ','
 3429     #       break
 3430     #     ++in->read
 3431     # done:
 3432     #   out->end = &in->data[in->read]
 3433     #
 3434     # . prologue
 3435     55/push-ebp
 3436     89/<- %ebp 4/r32/esp
 3437     # . save registers
 3438     50/push-eax
 3439     51/push-ecx
 3440     56/push-esi
 3441     57/push-edi
 3442     # esi = in
 3443     8b/-> *(ebp+8) 6/r32/esi
 3444     # edi = out
 3445     8b/-> *(ebp+0xc) 7/r32/edi
 3446 $next-mu-token:start:
 3447     (skip-chars-matching-whitespace %esi)
 3448 $next-mu-token:check0:
 3449     # if (in->read >= in->write) return out = {0, 0}
 3450     # . ecx = in->read
 3451     8b/-> *(esi+4) 1/r32/ecx
 3452     # . if (ecx >= in->write) return out = {0, 0}
 3453     3b/compare 1/r32/ecx *esi
 3454     c7 0/subop/copy *edi 0/imm32
 3455     c7 0/subop/copy *(edi+4) 0/imm32
 3456     0f 8d/jump-if->= $next-mu-token:end/disp32
 3457     # out->start = &in->data[in->read]
 3458     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3459     89/<- *edi 0/r32/eax
 3460     # var curr-byte/eax: byte = in->data[in->read]
 3461     31/xor %eax 0/r32/eax
 3462     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3463     {
 3464 $next-mu-token:check-for-comma:
 3465       # if (curr-byte != ',') break
 3466       3d/compare-eax-and 0x2c/imm32/comma
 3467       75/jump-if-!= break/disp8
 3468       # ++in->read
 3469       ff 0/subop/increment *(esi+4)
 3470       # restart
 3471       e9/jump $next-mu-token:start/disp32
 3472     }
 3473     {
 3474 $next-mu-token:check-for-comment:
 3475       # if (curr-byte != '#') break
 3476       3d/compare-eax-and 0x23/imm32/pound
 3477       75/jump-if-!= break/disp8
 3478       # return eof
 3479       e9/jump $next-mu-token:done/disp32
 3480     }
 3481     {
 3482 $next-mu-token:check-for-string-literal:
 3483       # if (curr-byte != '"') break
 3484       3d/compare-eax-and 0x22/imm32/dquote
 3485       75/jump-if-!= break/disp8
 3486       (skip-string %esi)
 3487       # return
 3488       e9/jump $next-mu-token:done/disp32
 3489     }
 3490     {
 3491 $next-mu-token:check-for-open-paren:
 3492       # if (curr-byte != '(') break
 3493       3d/compare-eax-and 0x28/imm32/open-paren
 3494       75/jump-if-!= break/disp8
 3495       # ++in->read
 3496       ff 0/subop/increment *(esi+4)
 3497       # return
 3498       e9/jump $next-mu-token:done/disp32
 3499     }
 3500     {
 3501 $next-mu-token:check-for-close-paren:
 3502       # if (curr-byte != ')') break
 3503       3d/compare-eax-and 0x29/imm32/close-paren
 3504       75/jump-if-!= break/disp8
 3505       # ++in->read
 3506       ff 0/subop/increment *(esi+4)
 3507       # return
 3508       e9/jump $next-mu-token:done/disp32
 3509     }
 3510     {
 3511 $next-mu-token:regular-word-without-metadata:
 3512       # if (in->read >= in->write) break
 3513       # . ecx = in->read
 3514       8b/-> *(esi+4) 1/r32/ecx
 3515       # . if (ecx >= in->write) break
 3516       3b/compare *esi 1/r32/ecx
 3517       7d/jump-if->= break/disp8
 3518       # var c/eax: byte = in->data[in->read]
 3519       31/xor %eax 0/r32/eax
 3520       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3521       # if (c == ' ') break
 3522       3d/compare-eax-and 0x20/imm32/space
 3523       74/jump-if-= break/disp8
 3524       # if (c == '\r') break
 3525       3d/compare-eax-and 0xd/imm32/carriage-return
 3526       74/jump-if-= break/disp8
 3527       # if (c == '\n') break
 3528       3d/compare-eax-and 0xa/imm32/newline
 3529       74/jump-if-= break/disp8
 3530       # if (c == '(') break
 3531       3d/compare-eax-and 0x28/imm32/open-paren
 3532       0f 84/jump-if-= break/disp32
 3533       # if (c == ')') break
 3534       3d/compare-eax-and 0x29/imm32/close-paren
 3535       0f 84/jump-if-= break/disp32
 3536       # if (c == ',') break
 3537       3d/compare-eax-and 0x2c/imm32/comma
 3538       0f 84/jump-if-= break/disp32
 3539       # ++in->read
 3540       ff 0/subop/increment *(esi+4)
 3541       #
 3542       e9/jump loop/disp32
 3543     }
 3544 $next-mu-token:done:
 3545     # out->end = &in->data[in->read]
 3546     8b/-> *(esi+4) 1/r32/ecx
 3547     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3548     89/<- *(edi+4) 0/r32/eax
 3549 $next-mu-token:end:
 3550     # . restore registers
 3551     5f/pop-to-edi
 3552     5e/pop-to-esi
 3553     59/pop-to-ecx
 3554     58/pop-to-eax
 3555     # . epilogue
 3556     89/<- %esp 5/r32/ebp
 3557     5d/pop-to-ebp
 3558     c3/return
 3559 
 3560 pos-or-insert-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 3561     # . prologue
 3562     55/push-ebp
 3563     89/<- %ebp 4/r32/esp
 3564     # if (pos-slice(arr, s) != -1) return it
 3565     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 3566     3d/compare-eax-and -1/imm32
 3567     75/jump-if-!= $pos-or-insert-slice:end/disp8
 3568 $pos-or-insert-slice:insert:
 3569     (slice-to-string Heap *(ebp+0xc))  # => eax
 3570     (write-int *(ebp+8) %eax)
 3571     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 3572 $pos-or-insert-slice:end:
 3573     # . epilogue
 3574     89/<- %esp 5/r32/ebp
 3575     5d/pop-to-ebp
 3576     c3/return
 3577 
 3578 # return the index in an array of strings matching 's', -1 if not found
 3579 # index is denominated in elements, not bytes
 3580 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 3581     # . prologue
 3582     55/push-ebp
 3583     89/<- %ebp 4/r32/esp
 3584     # . save registers
 3585     51/push-ecx
 3586     52/push-edx
 3587     53/push-ebx
 3588     56/push-esi
 3589 #?     (write-buffered Stderr "pos-slice: ")
 3590 #?     (write-slice-buffered Stderr *(ebp+0xc))
 3591 #?     (write-buffered Stderr "\n")
 3592 #?     (flush Stderr)
 3593     # esi = arr
 3594     8b/-> *(ebp+8) 6/r32/esi
 3595     # var index/ecx: int = 0
 3596     b9/copy-to-ecx 0/imm32
 3597     # var curr/edx: (addr (addr array byte)) = arr->data
 3598     8d/copy-address *(esi+0xc) 2/r32/edx
 3599     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
 3600     8b/-> *esi 3/r32/ebx
 3601     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
 3602     {
 3603 #?       (write-buffered Stderr "  ")
 3604 #?       (print-int32-buffered Stderr %ecx)
 3605 #?       (write-buffered Stderr "\n")
 3606 #?       (flush Stderr)
 3607       # if (curr >= max) return -1
 3608       39/compare %edx 3/r32/ebx
 3609       b8/copy-to-eax -1/imm32
 3610       73/jump-if-addr>= $pos-slice:end/disp8
 3611       # if (slice-equal?(s, *curr)) break
 3612       (slice-equal? *(ebp+0xc) *edx)  # => eax
 3613       3d/compare-eax-and 0/imm32/false
 3614       75/jump-if-!= break/disp8
 3615       # ++index
 3616       41/increment-ecx
 3617       # curr += 4
 3618       81 0/subop/add %edx 4/imm32
 3619       #
 3620       eb/jump loop/disp8
 3621     }
 3622     # return index
 3623     89/<- %eax 1/r32/ecx
 3624 $pos-slice:end:
 3625 #?     (write-buffered Stderr "=> ")
 3626 #?     (print-int32-buffered Stderr %eax)
 3627 #?     (write-buffered Stderr "\n")
 3628     # . restore registers
 3629     5e/pop-to-esi
 3630     5b/pop-to-ebx
 3631     5a/pop-to-edx
 3632     59/pop-to-ecx
 3633     # . epilogue
 3634     89/<- %esp 5/r32/ebp
 3635     5d/pop-to-ebp
 3636     c3/return
 3637 
 3638 test-parse-var-with-type:
 3639     # . prologue
 3640     55/push-ebp
 3641     89/<- %ebp 4/r32/esp
 3642     # (eax..ecx) = "x:"
 3643     b8/copy-to-eax "x:"/imm32
 3644     8b/-> *eax 1/r32/ecx
 3645     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3646     05/add-to-eax 4/imm32
 3647     # var slice/ecx: slice = {eax, ecx}
 3648     51/push-ecx
 3649     50/push-eax
 3650     89/<- %ecx 4/r32/esp
 3651     # _test-input-stream contains "int"
 3652     (clear-stream _test-input-stream)
 3653     (write _test-input-stream "int")
 3654     #
 3655     (parse-var-with-type %ecx _test-input-stream)
 3656     8b/-> *eax 2/r32/edx  # Var-name
 3657     (check-strings-equal %edx "x" "F - test-var-with-type/name")
 3658     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3659     (check-ints-equal *edx 1 "F - test-var-with-type/type")
 3660     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
 3661     # . epilogue
 3662     89/<- %esp 5/r32/ebp
 3663     5d/pop-to-ebp
 3664     c3/return
 3665 
 3666 test-parse-var-with-type-and-register:
 3667     # . prologue
 3668     55/push-ebp
 3669     89/<- %ebp 4/r32/esp
 3670     # (eax..ecx) = "x/eax:"
 3671     b8/copy-to-eax "x/eax:"/imm32
 3672     8b/-> *eax 1/r32/ecx
 3673     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3674     05/add-to-eax 4/imm32
 3675     # var slice/ecx: slice = {eax, ecx}
 3676     51/push-ecx
 3677     50/push-eax
 3678     89/<- %ecx 4/r32/esp
 3679     # _test-input-stream contains "int"
 3680     (clear-stream _test-input-stream)
 3681     (write _test-input-stream "int")
 3682     #
 3683     (parse-var-with-type %ecx _test-input-stream)
 3684     8b/-> *eax 2/r32/edx  # Var-name
 3685     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
 3686     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3687     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
 3688     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3689     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
 3690     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
 3691     # . epilogue
 3692     89/<- %esp 5/r32/ebp
 3693     5d/pop-to-ebp
 3694     c3/return
 3695 
 3696 test-parse-var-with-trailing-characters:
 3697     # . prologue
 3698     55/push-ebp
 3699     89/<- %ebp 4/r32/esp
 3700     # (eax..ecx) = "x:"
 3701     b8/copy-to-eax "x:"/imm32
 3702     8b/-> *eax 1/r32/ecx
 3703     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3704     05/add-to-eax 4/imm32
 3705     # var slice/ecx: slice = {eax, ecx}
 3706     51/push-ecx
 3707     50/push-eax
 3708     89/<- %ecx 4/r32/esp
 3709     # _test-input-stream contains "int,"
 3710     (clear-stream _test-input-stream)
 3711     (write _test-input-stream "int,")
 3712     #
 3713     (parse-var-with-type %ecx _test-input-stream)
 3714     8b/-> *eax 2/r32/edx  # Var-name
 3715     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
 3716     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3717     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
 3718     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3719     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
 3720     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
 3721     # . epilogue
 3722     89/<- %esp 5/r32/ebp
 3723     5d/pop-to-ebp
 3724     c3/return
 3725 
 3726 test-parse-var-with-register-and-trailing-characters:
 3727     # . prologue
 3728     55/push-ebp
 3729     89/<- %ebp 4/r32/esp
 3730     # (eax..ecx) = "x/eax:"
 3731     b8/copy-to-eax "x/eax:"/imm32
 3732     8b/-> *eax 1/r32/ecx
 3733     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3734     05/add-to-eax 4/imm32
 3735     # var slice/ecx: slice = {eax, ecx}
 3736     51/push-ecx
 3737     50/push-eax
 3738     89/<- %ecx 4/r32/esp
 3739     # _test-input-stream contains "int,"
 3740     (clear-stream _test-input-stream)
 3741     (write _test-input-stream "int,")
 3742     #
 3743     (parse-var-with-type %ecx _test-input-stream)
 3744     8b/-> *eax 2/r32/edx  # Var-name
 3745     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
 3746     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3747     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
 3748     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3749     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
 3750     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
 3751     # . epilogue
 3752     89/<- %esp 5/r32/ebp
 3753     5d/pop-to-ebp
 3754     c3/return
 3755 
 3756 test-parse-var-with-compound-type:
 3757     # . prologue
 3758     55/push-ebp
 3759     89/<- %ebp 4/r32/esp
 3760     # (eax..ecx) = "x:"
 3761     b8/copy-to-eax "x:"/imm32
 3762     8b/-> *eax 1/r32/ecx
 3763     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3764     05/add-to-eax 4/imm32
 3765     # var slice/ecx: slice = {eax, ecx}
 3766     51/push-ecx
 3767     50/push-eax
 3768     89/<- %ecx 4/r32/esp
 3769     # _test-input-stream contains "(addr int)"
 3770     (clear-stream _test-input-stream)
 3771     (write _test-input-stream "(addr int)")
 3772     #
 3773     (parse-var-with-type %ecx _test-input-stream)
 3774     8b/-> *eax 2/r32/edx  # Var-name
 3775     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
 3776     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3777     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
 3778     # var type/edx: (handle tree type-id) = var->type
 3779     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3780     # type->left == atom(addr)
 3781     8b/-> *edx 0/r32/eax  # Atom-value
 3782     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
 3783     # type->right->left == atom(int)
 3784     8b/-> *(edx+4) 2/r32/edx  # Tree-right
 3785     8b/-> *edx 0/r32/eax  # Tree-left
 3786     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
 3787     # type->right->right == null
 3788     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
 3789     # . epilogue
 3790     89/<- %esp 5/r32/ebp
 3791     5d/pop-to-ebp
 3792     c3/return
 3793 
 3794 # identifier starts with a letter or '$' or '_'
 3795 # no constraints at the moment on later letters
 3796 # all we really want to do so far is exclude '{', '}' and '->'
 3797 is-identifier?:  # in: (addr slice) -> result/eax: boolean
 3798     # . prologue
 3799     55/push-ebp
 3800     89/<- %ebp 4/r32/esp
 3801     # if (slice-empty?(in)) return false
 3802     (slice-empty? *(ebp+8))  # => eax
 3803     3d/compare-eax-and 0/imm32/false
 3804     75/jump-if-!= $is-identifier?:false/disp8
 3805     # var c/eax: byte = *in->start
 3806     8b/-> *(ebp+8) 0/r32/eax
 3807     8b/-> *eax 0/r32/eax
 3808     8a/copy-byte *eax 0/r32/AL
 3809     81 4/subop/and %eax 0xff/imm32
 3810     # if (c == '$') return true
 3811     3d/compare-eax-and 0x24/imm32/$
 3812     74/jump-if-= $is-identifier?:true/disp8
 3813     # if (c == '_') return true
 3814     3d/compare-eax-and 0x5f/imm32/_
 3815     74/jump-if-= $is-identifier?:true/disp8
 3816     # drop case
 3817     25/and-eax-with 0x5f/imm32
 3818     # if (c < 'A') return false
 3819     3d/compare-eax-and 0x41/imm32/A
 3820     7c/jump-if-< $is-identifier?:false/disp8
 3821     # if (c > 'Z') return false
 3822     3d/compare-eax-and 0x5a/imm32/Z
 3823     7f/jump-if-> $is-identifier?:false/disp8
 3824     # otherwise return true
 3825 $is-identifier?:true:
 3826     b8/copy-to-eax 1/imm32/true
 3827     eb/jump $is-identifier?:end/disp8
 3828 $is-identifier?:false:
 3829     b8/copy-to-eax 0/imm32/false
 3830 $is-identifier?:end:
 3831     # . epilogue
 3832     89/<- %esp 5/r32/ebp
 3833     5d/pop-to-ebp
 3834     c3/return
 3835 
 3836 test-is-identifier-dollar:
 3837     # . prologue
 3838     55/push-ebp
 3839     89/<- %ebp 4/r32/esp
 3840     # (eax..ecx) = "$a"
 3841     b8/copy-to-eax "$a"/imm32
 3842     8b/-> *eax 1/r32/ecx
 3843     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3844     05/add-to-eax 4/imm32
 3845     # var slice/ecx: slice = {eax, ecx}
 3846     51/push-ecx
 3847     50/push-eax
 3848     89/<- %ecx 4/r32/esp
 3849     #
 3850     (is-identifier? %ecx)
 3851     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
 3852     # . epilogue
 3853     89/<- %esp 5/r32/ebp
 3854     5d/pop-to-ebp
 3855     c3/return
 3856 
 3857 test-is-identifier-underscore:
 3858     # . prologue
 3859     55/push-ebp
 3860     89/<- %ebp 4/r32/esp
 3861     # (eax..ecx) = "_a"
 3862     b8/copy-to-eax "_a"/imm32
 3863     8b/-> *eax 1/r32/ecx
 3864     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3865     05/add-to-eax 4/imm32
 3866     # var slice/ecx: slice = {eax, ecx}
 3867     51/push-ecx
 3868     50/push-eax
 3869     89/<- %ecx 4/r32/esp
 3870     #
 3871     (is-identifier? %ecx)
 3872     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
 3873     # . epilogue
 3874     89/<- %esp 5/r32/ebp
 3875     5d/pop-to-ebp
 3876     c3/return
 3877 
 3878 test-is-identifier-a:
 3879     # . prologue
 3880     55/push-ebp
 3881     89/<- %ebp 4/r32/esp
 3882     # (eax..ecx) = "a$"
 3883     b8/copy-to-eax "a$"/imm32
 3884     8b/-> *eax 1/r32/ecx
 3885     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3886     05/add-to-eax 4/imm32
 3887     # var slice/ecx: slice = {eax, ecx}
 3888     51/push-ecx
 3889     50/push-eax
 3890     89/<- %ecx 4/r32/esp
 3891     #
 3892     (is-identifier? %ecx)
 3893     (check-ints-equal %eax 1 "F - test-is-identifier-a")
 3894     # . epilogue
 3895     89/<- %esp 5/r32/ebp
 3896     5d/pop-to-ebp
 3897     c3/return
 3898 
 3899 test-is-identifier-z:
 3900     # . prologue
 3901     55/push-ebp
 3902     89/<- %ebp 4/r32/esp
 3903     # (eax..ecx) = "z$"
 3904     b8/copy-to-eax "z$"/imm32
 3905     8b/-> *eax 1/r32/ecx
 3906     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3907     05/add-to-eax 4/imm32
 3908     # var slice/ecx: slice = {eax, ecx}
 3909     51/push-ecx
 3910     50/push-eax
 3911     89/<- %ecx 4/r32/esp
 3912     #
 3913     (is-identifier? %ecx)
 3914     (check-ints-equal %eax 1 "F - test-is-identifier-z")
 3915     # . epilogue
 3916     89/<- %esp 5/r32/ebp
 3917     5d/pop-to-ebp
 3918     c3/return
 3919 
 3920 test-is-identifier-A:
 3921     # . prologue
 3922     55/push-ebp
 3923     89/<- %ebp 4/r32/esp
 3924     # (eax..ecx) = "A$"
 3925     b8/copy-to-eax "A$"/imm32
 3926     8b/-> *eax 1/r32/ecx
 3927     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3928     05/add-to-eax 4/imm32
 3929     # var slice/ecx: slice = {eax, ecx}
 3930     51/push-ecx
 3931     50/push-eax
 3932     89/<- %ecx 4/r32/esp
 3933     #
 3934     (is-identifier? %ecx)
 3935     (check-ints-equal %eax 1 "F - test-is-identifier-A")
 3936     # . epilogue
 3937     89/<- %esp 5/r32/ebp
 3938     5d/pop-to-ebp
 3939     c3/return
 3940 
 3941 test-is-identifier-Z:
 3942     # . prologue
 3943     55/push-ebp
 3944     89/<- %ebp 4/r32/esp
 3945     # (eax..ecx) = "Z$"
 3946     b8/copy-to-eax "Z$"/imm32
 3947     8b/-> *eax 1/r32/ecx
 3948     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3949     05/add-to-eax 4/imm32
 3950     # var slice/ecx: slice = {eax, ecx}
 3951     51/push-ecx
 3952     50/push-eax
 3953     89/<- %ecx 4/r32/esp
 3954     #
 3955     (is-identifier? %ecx)
 3956     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
 3957     # . epilogue
 3958     89/<- %esp 5/r32/ebp
 3959     5d/pop-to-ebp
 3960     c3/return
 3961 
 3962 test-is-identifier-@:
 3963     # character before 'A' is invalid
 3964     # . prologue
 3965     55/push-ebp
 3966     89/<- %ebp 4/r32/esp
 3967     # (eax..ecx) = "@a"
 3968     b8/copy-to-eax "@a"/imm32
 3969     8b/-> *eax 1/r32/ecx
 3970     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3971     05/add-to-eax 4/imm32
 3972     # var slice/ecx: slice = {eax, ecx}
 3973     51/push-ecx
 3974     50/push-eax
 3975     89/<- %ecx 4/r32/esp
 3976     #
 3977     (is-identifier? %ecx)
 3978     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 3979     # . epilogue
 3980     89/<- %esp 5/r32/ebp
 3981     5d/pop-to-ebp
 3982     c3/return
 3983 
 3984 test-is-identifier-square-bracket:
 3985     # character after 'Z' is invalid
 3986     # . prologue
 3987     55/push-ebp
 3988     89/<- %ebp 4/r32/esp
 3989     # (eax..ecx) = "[a"
 3990     b8/copy-to-eax "[a"/imm32
 3991     8b/-> *eax 1/r32/ecx
 3992     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3993     05/add-to-eax 4/imm32
 3994     # var slice/ecx: slice = {eax, ecx}
 3995     51/push-ecx
 3996     50/push-eax
 3997     89/<- %ecx 4/r32/esp
 3998     #
 3999     (is-identifier? %ecx)
 4000     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4001     # . epilogue
 4002     89/<- %esp 5/r32/ebp
 4003     5d/pop-to-ebp
 4004     c3/return
 4005 
 4006 test-is-identifier-backtick:
 4007     # character before 'a' is invalid
 4008     # . prologue
 4009     55/push-ebp
 4010     89/<- %ebp 4/r32/esp
 4011     # (eax..ecx) = "`a"
 4012     b8/copy-to-eax "`a"/imm32
 4013     8b/-> *eax 1/r32/ecx
 4014     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4015     05/add-to-eax 4/imm32
 4016     # var slice/ecx: slice = {eax, ecx}
 4017     51/push-ecx
 4018     50/push-eax
 4019     89/<- %ecx 4/r32/esp
 4020     #
 4021     (is-identifier? %ecx)
 4022     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
 4023     # . epilogue
 4024     89/<- %esp 5/r32/ebp
 4025     5d/pop-to-ebp
 4026     c3/return
 4027 
 4028 test-is-identifier-curly-brace-open:
 4029     # character after 'z' is invalid; also used for blocks
 4030     # . prologue
 4031     55/push-ebp
 4032     89/<- %ebp 4/r32/esp
 4033     # (eax..ecx) = "{a"
 4034     b8/copy-to-eax "{a"/imm32
 4035     8b/-> *eax 1/r32/ecx
 4036     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4037     05/add-to-eax 4/imm32
 4038     # var slice/ecx: slice = {eax, ecx}
 4039     51/push-ecx
 4040     50/push-eax
 4041     89/<- %ecx 4/r32/esp
 4042     #
 4043     (is-identifier? %ecx)
 4044     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
 4045     # . epilogue
 4046     89/<- %esp 5/r32/ebp
 4047     5d/pop-to-ebp
 4048     c3/return
 4049 
 4050 test-is-identifier-curly-brace-close:
 4051     # . prologue
 4052     55/push-ebp
 4053     89/<- %ebp 4/r32/esp
 4054     # (eax..ecx) = "}a"
 4055     b8/copy-to-eax "}a"/imm32
 4056     8b/-> *eax 1/r32/ecx
 4057     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4058     05/add-to-eax 4/imm32
 4059     # var slice/ecx: slice = {eax, ecx}
 4060     51/push-ecx
 4061     50/push-eax
 4062     89/<- %ecx 4/r32/esp
 4063     #
 4064     (is-identifier? %ecx)
 4065     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
 4066     # . epilogue
 4067     89/<- %esp 5/r32/ebp
 4068     5d/pop-to-ebp
 4069     c3/return
 4070 
 4071 test-is-identifier-hyphen:
 4072     # disallow leading '-' since '->' has special meaning
 4073     # . prologue
 4074     55/push-ebp
 4075     89/<- %ebp 4/r32/esp
 4076     # (eax..ecx) = "-a"
 4077     b8/copy-to-eax "-a"/imm32
 4078     8b/-> *eax 1/r32/ecx
 4079     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4080     05/add-to-eax 4/imm32
 4081     # var slice/ecx: slice = {eax, ecx}
 4082     51/push-ecx
 4083     50/push-eax
 4084     89/<- %ecx 4/r32/esp
 4085     #
 4086     (is-identifier? %ecx)
 4087     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
 4088     # . epilogue
 4089     89/<- %esp 5/r32/ebp
 4090     5d/pop-to-ebp
 4091     c3/return
 4092 
 4093 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
 4094     # . prologue
 4095     55/push-ebp
 4096     89/<- %ebp 4/r32/esp
 4097     # . save registers
 4098     50/push-eax
 4099     56/push-esi
 4100     57/push-edi
 4101     # esi = in
 4102     8b/-> *(ebp+8) 6/r32/esi
 4103     # edi = out
 4104     8b/-> *(ebp+0xc) 7/r32/edi
 4105     # var eax: (handle block) = parse-mu-block(in, vars, fn)
 4106     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
 4107     # out->body = eax
 4108     89/<- *(edi+0x10) 0/r32/eax  # Function-body
 4109 $populate-mu-function-body:end:
 4110     # . restore registers
 4111     5f/pop-to-edi
 4112     5e/pop-to-esi
 4113     58/pop-to-eax
 4114     # . epilogue
 4115     89/<- %esp 5/r32/ebp
 4116     5d/pop-to-ebp
 4117     c3/return
 4118 
 4119 # parses a block, assuming that the leading '{' has already been read by the caller
 4120 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
 4121     # pseudocode:
 4122     #   var line: (stream byte 512)
 4123     #   var word-slice: slice
 4124     #   result/eax = allocate(Heap, Stmt-size)
 4125     #   result->tag = 0/block
 4126     #   result->name = some unique name
 4127     #   while true                                  # line loop
 4128     #     clear-stream(line)
 4129     #     read-line-buffered(in, line)
 4130     #     if (line->write == 0) break               # end of file
 4131     #     word-slice = next-mu-token(line)
 4132     #     if slice-empty?(word-slice)               # end of line
 4133     #       continue
 4134     #     else if slice-starts-with?(word-slice, "#")
 4135     #       continue
 4136     #     else if slice-equal?(word-slice, "{")
 4137     #       assert(no-tokens-in(line))
 4138     #       block = parse-mu-block(in, vars, fn)
 4139     #       append-to-block(result, block)
 4140     #     else if slice-equal?(word-slice, "}")
 4141     #       break
 4142     #     else if slice-ends-with?(word-slice, ":")
 4143     #       # TODO: error-check the rest of 'line'
 4144     #       --word-slice->end to skip ':'
 4145     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
 4146     #       append-to-block(result, named-block)
 4147     #     else if slice-equal?(word-slice, "var")
 4148     #       var-def = parse-mu-var-def(line, vars)
 4149     #       append-to-block(result, var-def)
 4150     #     else
 4151     #       stmt = parse-mu-stmt(line, vars, fn)
 4152     #       append-to-block(result, stmt)
 4153     #   return result
 4154     #
 4155     # . prologue
 4156     55/push-ebp
 4157     89/<- %ebp 4/r32/esp
 4158     # . save registers
 4159     51/push-ecx
 4160     52/push-edx
 4161     53/push-ebx
 4162     57/push-edi
 4163     # var line/ecx: (stream byte 512)
 4164     81 5/subop/subtract %esp 0x200/imm32
 4165     68/push 0x200/imm32/length
 4166     68/push 0/imm32/read
 4167     68/push 0/imm32/write
 4168     89/<- %ecx 4/r32/esp
 4169     # var word-slice/edx: slice
 4170     68/push 0/imm32/end
 4171     68/push 0/imm32/start
 4172     89/<- %edx 4/r32/esp
 4173     # edi = result
 4174     (allocate Heap *Stmt-size)  # => eax
 4175     (zero-out %eax *Stmt-size)
 4176     89/<- %edi 0/r32/eax
 4177     # set result->tag
 4178     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
 4179     # set result->var
 4180     (new-block-name *(ebp+0x10))  # => eax
 4181     89/<- *(edi+8) 0/r32/eax  # Block-var
 4182     # push result->var to vars
 4183     (push *(ebp+0xc) %eax)
 4184     {
 4185 $parse-mu-block:line-loop:
 4186       # line = read-line-buffered(in)
 4187       (clear-stream %ecx)
 4188       (read-line-buffered *(ebp+8) %ecx)
 4189 #?       (write-buffered Stderr "line: ")
 4190 #?       (write-stream-data Stderr %ecx)
 4191 #?       (write-buffered Stderr Newline)
 4192 #?       (flush Stderr)
 4193       # if (line->write == 0) break
 4194       81 7/subop/compare *ecx 0/imm32
 4195       0f 84/jump-if-= break/disp32
 4196       # word-slice = next-mu-token(line)
 4197       (next-mu-token %ecx %edx)
 4198 #?       (write-buffered Stderr "word: ")
 4199 #?       (write-slice-buffered Stderr %edx)
 4200 #?       (write-buffered Stderr Newline)
 4201 #?       (flush Stderr)
 4202       # if slice-empty?(word-slice) continue
 4203       (slice-empty? %edx)
 4204       3d/compare-eax-and 0/imm32/false
 4205       0f 85/jump-if-!= loop/disp32
 4206       # if (slice-starts-with?(word-slice, '#') continue
 4207       # . eax = *word-slice->start
 4208       8b/-> *edx 0/r32/eax
 4209       8a/copy-byte *eax 0/r32/AL
 4210       81 4/subop/and %eax 0xff/imm32
 4211       # . if (eax == '#') continue
 4212       3d/compare-eax-and 0x23/imm32/hash
 4213       0f 84/jump-if-= loop/disp32
 4214       # if slice-equal?(word-slice, "{")
 4215       {
 4216 $parse-mu-block:check-for-block:
 4217         (slice-equal? %edx "{")
 4218         3d/compare-eax-and 0/imm32/false
 4219         74/jump-if-= break/disp8
 4220         (check-no-tokens-left %ecx)
 4221         # parse new block and append
 4222         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4223         (append-to-block Heap %edi %eax)
 4224         e9/jump $parse-mu-block:line-loop/disp32
 4225       }
 4226       # if slice-equal?(word-slice, "}") break
 4227 $parse-mu-block:check-for-end:
 4228       (slice-equal? %edx "}")
 4229       3d/compare-eax-and 0/imm32/false
 4230       0f 85/jump-if-!= break/disp32
 4231       # if slice-ends-with?(word-slice, ":") parse named block and append
 4232       {
 4233 $parse-mu-block:check-for-named-block:
 4234         # . eax = *(word-slice->end-1)
 4235         8b/-> *(edx+4) 0/r32/eax
 4236         48/decrement-eax
 4237         8a/copy-byte *eax 0/r32/AL
 4238         81 4/subop/and %eax 0xff/imm32
 4239         # . if (eax != ':') break
 4240         3d/compare-eax-and 0x3a/imm32/colon
 4241         0f 85/jump-if-!= break/disp32
 4242         # TODO: error-check the rest of 'line'
 4243         #
 4244         # skip ':'
 4245         ff 1/subop/decrement *(edx+4)  # Slice-end
 4246         #
 4247         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4248         (append-to-block Heap %edi %eax)
 4249         e9/jump $parse-mu-block:line-loop/disp32
 4250       }
 4251       # if slice-equal?(word-slice, "var")
 4252       {
 4253 $parse-mu-block:check-for-var:
 4254         (slice-equal? %edx "var")
 4255         3d/compare-eax-and 0/imm32/false
 4256         74/jump-if-= break/disp8
 4257         #
 4258         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
 4259         (append-to-block Heap %edi %eax)
 4260         e9/jump $parse-mu-block:line-loop/disp32
 4261       }
 4262 $parse-mu-block:regular-stmt:
 4263       # otherwise
 4264       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4265       (append-to-block Heap %edi %eax)
 4266       e9/jump loop/disp32
 4267     } # end line loop
 4268     #
 4269     (pop *(ebp+0xc))  # => eax
 4270     # return result
 4271     89/<- %eax 7/r32/edi
 4272 $parse-mu-block:end:
 4273     # . reclaim locals
 4274     81 0/subop/add %esp 0x214/imm32
 4275     # . restore registers
 4276     5f/pop-to-edi
 4277     5b/pop-to-ebx
 4278     5a/pop-to-edx
 4279     59/pop-to-ecx
 4280     # . epilogue
 4281     89/<- %esp 5/r32/ebp
 4282     5d/pop-to-ebp
 4283     c3/return
 4284 
 4285 $parse-mu-block:abort:
 4286     # error("'{' or '}' should be on its own line, but got '")
 4287     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4288     (rewind-stream %ecx)
 4289     (write-stream 2 %ecx)
 4290     (write-buffered Stderr "'\n")
 4291     (flush Stderr)
 4292     # . syscall(exit, 1)
 4293     bb/copy-to-ebx  1/imm32
 4294     b8/copy-to-eax  1/imm32/exit
 4295     cd/syscall  0x80/imm8
 4296     # never gets here
 4297 
 4298 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
 4299     # . prologue
 4300     55/push-ebp
 4301     89/<- %ebp 4/r32/esp
 4302     # . save registers
 4303     51/push-ecx
 4304     52/push-edx
 4305     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
 4306     8b/-> *(ebp+8) 0/r32/eax
 4307     8b/-> *eax 0/r32/eax  # Function-name
 4308     8b/-> *eax 0/r32/eax  # String-length
 4309     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
 4310     89/<- %ecx 0/r32/eax
 4311     # var name/edx: (stream byte n)
 4312     29/subtract %esp 1/r32/ecx
 4313     ff 6/subop/push %ecx
 4314     68/push 0/imm32/read
 4315     68/push 0/imm32/write
 4316     89/<- %edx 4/r32/esp
 4317     (clear-stream %edx)
 4318     # eax = fn->name
 4319     8b/-> *(ebp+8) 0/r32/eax
 4320     8b/-> *eax 0/r32/eax  # Function-name
 4321     # construct result using Next-block-index (and increment it)
 4322     (write %edx "$")
 4323     (write %edx %eax)
 4324     (write %edx ":")
 4325     (print-int32 %edx *Next-block-index)
 4326     ff 0/subop/increment *Next-block-index
 4327     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
 4328     # . eax = name->write
 4329     8b/-> *edx 0/r32/eax
 4330     # . edx = name->data
 4331     8d/copy-address *(edx+0xc) 2/r32/edx
 4332     # . eax = name->write + name->data
 4333     01/add %eax 2/r32/edx
 4334     # . push {edx, eax}
 4335     ff 6/subop/push %eax
 4336     ff 6/subop/push %edx
 4337     89/<- %eax 4/r32/esp
 4338     # result->var = new literal(s)
 4339     (new-literal Heap %eax)  # => eax
 4340 $new-block-name:end:
 4341     # . reclaim locals
 4342     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
 4343     81 0/subop/add %ecx 8/imm32  # slice
 4344     01/add %esp 1/r32/ecx
 4345     # . restore registers
 4346     5a/pop-to-edx
 4347     59/pop-to-ecx
 4348     # . epilogue
 4349     89/<- %esp 5/r32/ebp
 4350     5d/pop-to-ebp
 4351     c3/return
 4352 
 4353 == data
 4354 
 4355 # Global state added to each var record when parsing a function
 4356 Next-block-index:  # (addr int)
 4357     1/imm32
 4358 
 4359 == code
 4360 
 4361 check-no-tokens-left:  # line: (addr stream byte)
 4362     # . prologue
 4363     55/push-ebp
 4364     89/<- %ebp 4/r32/esp
 4365     # . save registers
 4366     50/push-eax
 4367     51/push-ecx
 4368     # var s/ecx: slice
 4369     68/push 0/imm32/end
 4370     68/push 0/imm32/start
 4371     89/<- %ecx 4/r32/esp
 4372     #
 4373     (next-mu-token *(ebp+8) %ecx)
 4374     # if slice-empty?(s) return
 4375     (slice-empty? %ecx)
 4376     3d/compare-eax-and 0/imm32/false
 4377     75/jump-if-!= $check-no-tokens-left:end/disp8
 4378     # if (slice-starts-with?(s, '#') return
 4379     # . eax = *s->start
 4380     8b/-> *edx 0/r32/eax
 4381     8a/copy-byte *eax 0/r32/AL
 4382     81 4/subop/and %eax 0xff/imm32
 4383     # . if (eax == '#') continue
 4384     3d/compare-eax-and 0x23/imm32/hash
 4385     74/jump-if-= $check-no-tokens-left:end/disp8
 4386     # abort
 4387     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4388     (rewind-stream %ecx)
 4389     (write-stream 2 %ecx)
 4390     (write-buffered Stderr "'\n")
 4391     (flush Stderr)
 4392     # . syscall(exit, 1)
 4393     bb/copy-to-ebx  1/imm32
 4394     b8/copy-to-eax  1/imm32/exit
 4395     cd/syscall  0x80/imm8
 4396     # never gets here
 4397 $check-no-tokens-left:end:
 4398     # . reclaim locals
 4399     81 0/subop/add %esp 8/imm32
 4400     # . restore registers
 4401     59/pop-to-ecx
 4402     58/pop-to-eax
 4403     # . epilogue
 4404     89/<- %esp 5/r32/ebp
 4405     5d/pop-to-ebp
 4406     c3/return
 4407 
 4408 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4409     # pseudocode:
 4410     #   var v: (handle var) = new-literal(name)
 4411     #   push(vars, v)
 4412     #   result = parse-mu-block(in, vars, fn)
 4413     #   pop(vars)
 4414     #   result->name = s
 4415     #   return result
 4416     #
 4417     # . prologue
 4418     55/push-ebp
 4419     89/<- %ebp 4/r32/esp
 4420     # . save registers
 4421     51/push-ecx
 4422     # var v/ecx: (handle var)
 4423     (new-literal Heap *(ebp+8))  # => eax
 4424     89/<- %ecx 0/r32/eax
 4425     # push(vars, v)
 4426     (push *(ebp+0x10) %ecx)
 4427     # eax = result
 4428     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
 4429     # pop the var
 4430     50/push-eax
 4431     (pop *(ebp+0x10))  # => eax
 4432     58/pop-to-eax
 4433     # result->tag = named-block
 4434     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
 4435     # result->var = v
 4436     89/<- *(eax+8) 1/r32/ecx  # Block-var
 4437 $parse-mu-named-block:end:
 4438     # . restore registers
 4439     59/pop-to-ecx
 4440     # . epilogue
 4441     89/<- %esp 5/r32/ebp
 4442     5d/pop-to-ebp
 4443     c3/return
 4444 
 4445 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
 4446     # . prologue
 4447     55/push-ebp
 4448     89/<- %ebp 4/r32/esp
 4449     # . save registers
 4450     51/push-ecx
 4451     52/push-edx
 4452     # var word-slice/ecx: slice
 4453     68/push 0/imm32/end
 4454     68/push 0/imm32/start
 4455     89/<- %ecx 4/r32/esp
 4456     # var v/edx: (handle var) = parse-var-with-type(line)
 4457     (next-mu-token *(ebp+8) %ecx)
 4458     (parse-var-with-type %ecx *(ebp+8))  # => eax
 4459     89/<- %edx 0/r32/eax
 4460     #
 4461     (push *(ebp+0xc) %edx)
 4462     # either v has no register and there's no more to this line
 4463     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
 4464     3d/compare-eax-and 0/imm32
 4465     {
 4466       75/jump-if-!= break/disp8
 4467       # TODO: ensure that there's nothing else on this line
 4468       (new-var-def Heap %edx)  # => eax
 4469       eb/jump $parse-mu-var-def:end/disp8
 4470     }
 4471     # or v has a register and there's more to this line
 4472     {
 4473       74/jump-if-= break/disp8
 4474       # ensure that the next word is '<-'
 4475       (next-mu-token *(ebp+8) %ecx)
 4476       (slice-equal? %ecx "<-")  # => eax
 4477       3d/compare-eax-and 0/imm32/false
 4478       74/jump-if-= $parse-mu-var-def:abort/disp8
 4479       #
 4480       (new-reg-var-def Heap %edx)  # => eax
 4481       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
 4482     }
 4483 $parse-mu-var-def:end:
 4484     # . reclaim locals
 4485     81 0/subop/add %esp 8/imm32
 4486     # . restore registers
 4487     5a/pop-to-edx
 4488     59/pop-to-ecx
 4489     # . epilogue
 4490     89/<- %esp 5/r32/ebp
 4491     5d/pop-to-ebp
 4492     c3/return
 4493 
 4494 $parse-mu-var-def:abort:
 4495     (rewind-stream *(ebp+8))
 4496     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
 4497     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
 4498     (flush Stderr)
 4499     (write-stream 2 *(ebp+8))
 4500     (write-buffered Stderr "'\n")
 4501     (flush Stderr)
 4502     # . syscall(exit, 1)
 4503     bb/copy-to-ebx  1/imm32
 4504     b8/copy-to-eax  1/imm32/exit
 4505     cd/syscall  0x80/imm8
 4506     # never gets here
 4507 
 4508 test-parse-mu-var-def:
 4509     # 'var n: int'
 4510     # . prologue
 4511     55/push-ebp
 4512     89/<- %ebp 4/r32/esp
 4513     # setup
 4514     (clear-stream _test-input-stream)
 4515     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
 4516     # var vars/ecx: (stack (addr var) 4)
 4517     81 5/subop/subtract %esp 0x10/imm32
 4518     68/push 0x10/imm32/length
 4519     68/push 0/imm32/top
 4520     89/<- %ecx 4/r32/esp
 4521     (clear-stack %ecx)
 4522     # convert
 4523     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 4524     # check result
 4525     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
 4526     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
 4527     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
 4528     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
 4529     # ensure type is int
 4530     8b/-> *(eax+4) 0/r32/eax  # Var-type
 4531     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
 4532     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
 4533     # . epilogue
 4534     89/<- %esp 5/r32/ebp
 4535     5d/pop-to-ebp
 4536     c3/return
 4537 
 4538 test-parse-mu-reg-var-def:
 4539     # 'var n/eax: int <- copy 0'
 4540     # . prologue
 4541     55/push-ebp
 4542     89/<- %ebp 4/r32/esp
 4543     # setup
 4544     (clear-stream _test-input-stream)
 4545     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
 4546     # var vars/ecx: (stack (addr var) 4)
 4547     81 5/subop/subtract %esp 0x10/imm32
 4548     68/push 0x10/imm32/length
 4549     68/push 0/imm32/top
 4550     89/<- %ecx 4/r32/esp
 4551     (clear-stack %ecx)
 4552     # convert
 4553     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 4554     # check result
 4555     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
 4556     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
 4557     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
 4558     8b/-> *eax 0/r32/eax  # Stmt-var-value
 4559     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
 4560     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
 4561     # ensure type is int
 4562     8b/-> *(eax+4) 0/r32/eax  # Var-type
 4563     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
 4564     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
 4565     # . epilogue
 4566     89/<- %esp 5/r32/ebp
 4567     5d/pop-to-ebp
 4568     c3/return
 4569 
 4570 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4571     # pseudocode:
 4572     #   var name: slice
 4573     #   result = allocate(Heap, Stmt-size)
 4574     #   if stmt-has-outputs?(line)
 4575     #     while true
 4576     #       name = next-mu-token(line)
 4577     #       if (name == '<-') break
 4578     #       assert(is-identifier?(name))
 4579     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
 4580     #       result->outputs = append(result->outputs, v)
 4581     #   add-operation-and-inputs-to-stmt(result, line, vars)
 4582     #
 4583     # . prologue
 4584     55/push-ebp
 4585     89/<- %ebp 4/r32/esp
 4586     # . save registers
 4587     51/push-ecx
 4588     52/push-edx
 4589     57/push-edi
 4590     # var name/ecx: slice
 4591     68/push 0/imm32/end
 4592     68/push 0/imm32/start
 4593     89/<- %ecx 4/r32/esp
 4594     # var is-deref?/edx: boolean = false
 4595     ba/copy-to-edx 0/imm32/false
 4596     # result/edi: (handle stmt)
 4597     (allocate Heap *Stmt-size)  # => eax
 4598     (zero-out %eax *Stmt-size)
 4599     89/<- %edi 0/r32/eax
 4600     # result->tag = 1/stmt
 4601     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
 4602     {
 4603       (stmt-has-outputs? *(ebp+8))
 4604       3d/compare-eax-and 0/imm32/false
 4605       0f 84/jump-if-= break/disp32
 4606       {
 4607 $parse-mu-stmt:read-outputs:
 4608         # name = next-mu-token(line)
 4609         (next-mu-token *(ebp+8) %ecx)
 4610         # if slice-empty?(word-slice) break
 4611         (slice-empty? %ecx)  # => eax
 4612         3d/compare-eax-and 0/imm32/false
 4613         0f 85/jump-if-!= break/disp32
 4614         # if (name == "<-") break
 4615         (slice-equal? %ecx "<-")  # => eax
 4616         3d/compare-eax-and 0/imm32/false
 4617         0f 85/jump-if-!= break/disp32
 4618         # is-deref? = false
 4619         ba/copy-to-edx 0/imm32/false
 4620         # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 4621         8b/-> *ecx 0/r32/eax  # Slice-start
 4622         8a/copy-byte *eax 0/r32/AL
 4623         81 4/subop/and %eax 0xff/imm32
 4624         3d/compare-eax-and 0x2a/imm32/asterisk
 4625         {
 4626           75/jump-if-!= break/disp8
 4627           ff 0/subop/increment *ecx
 4628           ba/copy-to-edx 1/imm32/true
 4629         }
 4630         # assert(is-identifier?(name))
 4631         (is-identifier? %ecx)  # => eax
 4632         3d/compare-eax-and 0/imm32/false
 4633         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
 4634         # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
 4635         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4636         (append-stmt-var Heap %eax *(edi+0xc) %edx)  # Stmt1-outputs => eax
 4637         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
 4638         e9/jump loop/disp32
 4639       }
 4640     }
 4641     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
 4642 $parse-mu-stmt:end:
 4643     # return result
 4644     89/<- %eax 7/r32/edi
 4645     # . reclaim locals
 4646     81 0/subop/add %esp 8/imm32
 4647     # . restore registers
 4648     5f/pop-to-edi
 4649     5a/pop-to-edx
 4650     59/pop-to-ecx
 4651     # . epilogue
 4652     89/<- %esp 5/r32/ebp
 4653     5d/pop-to-ebp
 4654     c3/return
 4655 
 4656 $parse-mu-stmt:abort:
 4657     # error("invalid identifier '" name "'\n")
 4658     (write-buffered Stderr "invalid identifier '")
 4659     (write-slice-buffered Stderr %ecx)
 4660     (write-buffered Stderr "'\n")
 4661     (flush Stderr)
 4662     # . syscall(exit, 1)
 4663     bb/copy-to-ebx  1/imm32
 4664     b8/copy-to-eax  1/imm32/exit
 4665     cd/syscall  0x80/imm8
 4666     # never gets here
 4667 
 4668 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
 4669     # pseudocode:
 4670     #   stmt->name = slice-to-string(next-mu-token(line))
 4671     #   while true
 4672     #     name = next-mu-token(line)
 4673     #     v = lookup-var-or-literal(name)
 4674     #     stmt->inouts = append(stmt->inouts, v)
 4675     #
 4676     # . prologue
 4677     55/push-ebp
 4678     89/<- %ebp 4/r32/esp
 4679     # . save registers
 4680     50/push-eax
 4681     51/push-ecx
 4682     52/push-edx
 4683     53/push-ebx
 4684     57/push-edi
 4685     # edi = stmt
 4686     8b/-> *(ebp+8) 7/r32/edi
 4687     # var name/ecx: slice
 4688     68/push 0/imm32/end
 4689     68/push 0/imm32/start
 4690     89/<- %ecx 4/r32/esp
 4691     # var is-deref?/edx: boolean = false
 4692     ba/copy-to-edx 0/imm32/false
 4693 $add-operation-and-inputs-to-stmt:read-operation:
 4694     (next-mu-token *(ebp+0xc) %ecx)
 4695     (slice-to-string Heap %ecx)  # => eax
 4696     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
 4697     # var is-get?/ebx: boolean = (name == "get")
 4698     (slice-equal? %ecx "get")  # => eax
 4699     89/<- %ebx 0/r32/eax
 4700     {
 4701 $add-operation-and-inputs-to-stmt:read-inouts:
 4702       # name = next-mu-token(line)
 4703       (next-mu-token *(ebp+0xc) %ecx)
 4704       # if slice-empty?(word-slice) break
 4705       (slice-empty? %ecx)  # => eax
 4706       3d/compare-eax-and 0/imm32/false
 4707       0f 85/jump-if-!= break/disp32
 4708       # if (name == "<-") abort
 4709       (slice-equal? %ecx "<-")
 4710       3d/compare-eax-and 0/imm32/false
 4711       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
 4712       # if (is-get? && second operand) lookup or create offset
 4713       {
 4714         81 7/subop/compare %ebx 0/imm32/false
 4715         74/jump-if-= break/disp8
 4716         81 7/subop/compare *(edi+8) 0/imm32  # Stmt1-inouts or Regvardef-inouts
 4717         74/jump-if-= break/disp8
 4718         (lookup-or-create-constant *(edi+8) %ecx)  # Stmt1-inouts => eax
 4719 #?         (write-buffered Stderr "creating new output var ")
 4720 #?         (print-int32-buffered Stderr %eax)
 4721 #?         (write-buffered Stderr " for field called ")
 4722 #?         (write-slice-buffered Stderr %ecx)
 4723 #?         (write-buffered Stderr Newline)
 4724 #?         (flush Stderr)
 4725         e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
 4726       }
 4727       # is-deref? = false
 4728       ba/copy-to-edx 0/imm32/false
 4729       # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 4730       8b/-> *ecx 0/r32/eax  # Slice-start
 4731       8a/copy-byte *eax 0/r32/AL
 4732       81 4/subop/and %eax 0xff/imm32
 4733       3d/compare-eax-and 0x2a/imm32/asterisk
 4734       {
 4735         75/jump-if-!= break/disp8
 4736 $add-operation-and-inputs-to-stmt:inout-is-deref:
 4737         ff 0/subop/increment *ecx
 4738         ba/copy-to-edx 1/imm32/true
 4739       }
 4740       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
 4741 $add-operation-and-inputs-to-stmt:save-var:
 4742       (append-stmt-var Heap %eax *(edi+8) %edx)  # Stmt1-inouts or Regvardef-inouts => eax
 4743       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 4744       e9/jump loop/disp32
 4745     }
 4746 $add-operation-and-inputs-to-stmt:end:
 4747     # . reclaim locals
 4748     81 0/subop/add %esp 8/imm32
 4749     # . restore registers
 4750     5f/pop-to-edi
 4751     5b/pop-to-ebx
 4752     5a/pop-to-edx
 4753     59/pop-to-ecx
 4754     58/pop-to-eax
 4755     # . epilogue
 4756     89/<- %esp 5/r32/ebp
 4757     5d/pop-to-ebp
 4758     c3/return
 4759 
 4760 $add-operation-and-inputs-to-stmt:abort:
 4761     # error("invalid statement '" line "'\n")
 4762     (rewind-stream *(ebp+8))
 4763     (write-buffered Stderr "invalid identifier '")
 4764     (flush Stderr)
 4765     (write-stream 2 *(ebp+8))
 4766     (write-buffered Stderr "'\n")
 4767     (flush Stderr)
 4768     # . syscall(exit, 1)
 4769     bb/copy-to-ebx  1/imm32
 4770     b8/copy-to-eax  1/imm32/exit
 4771     cd/syscall  0x80/imm8
 4772     # never gets here
 4773 
 4774 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
 4775     # . prologue
 4776     55/push-ebp
 4777     89/<- %ebp 4/r32/esp
 4778     # . save registers
 4779     51/push-ecx
 4780     # var word-slice/ecx: slice
 4781     68/push 0/imm32/end
 4782     68/push 0/imm32/start
 4783     89/<- %ecx 4/r32/esp
 4784     # result = false
 4785     b8/copy-to-eax 0/imm32/false
 4786     (rewind-stream *(ebp+8))
 4787     {
 4788       (next-mu-token *(ebp+8) %ecx)
 4789       # if slice-empty?(word-slice) break
 4790       (slice-empty? %ecx)
 4791       3d/compare-eax-and 0/imm32/false
 4792       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 4793       0f 85/jump-if-!= break/disp32
 4794       # if slice-starts-with?(word-slice, '#') break
 4795       # . eax = *word-slice->start
 4796       8b/-> *ecx 0/r32/eax
 4797       8a/copy-byte *eax 0/r32/AL
 4798       81 4/subop/and %eax 0xff/imm32
 4799       # . if (eax == '#') break
 4800       3d/compare-eax-and 0x23/imm32/hash
 4801       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 4802       0f 84/jump-if-= break/disp32
 4803       # if slice-equal?(word-slice, '<-') return true
 4804       (slice-equal? %ecx "<-")
 4805       3d/compare-eax-and 0/imm32/false
 4806       74/jump-if-= loop/disp8
 4807       b8/copy-to-eax 1/imm32/true
 4808     }
 4809 $stmt-has-outputs:end:
 4810     (rewind-stream *(ebp+8))
 4811     # . reclaim locals
 4812     81 0/subop/add %esp 8/imm32
 4813     # . restore registers
 4814     59/pop-to-ecx
 4815     # . epilogue
 4816     89/<- %esp 5/r32/ebp
 4817     5d/pop-to-ebp
 4818     c3/return
 4819 
 4820 # if 'name' starts with a digit, create a new literal var for it
 4821 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
 4822 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 4823     # . prologue
 4824     55/push-ebp
 4825     89/<- %ebp 4/r32/esp
 4826     # . save registers
 4827     51/push-ecx
 4828     56/push-esi
 4829     # esi = name
 4830     8b/-> *(ebp+8) 6/r32/esi
 4831     # if slice-empty?(name) abort
 4832     (slice-empty? %esi)  # => eax
 4833     3d/compare-eax-and 0/imm32/false
 4834     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
 4835     # var c/ecx: byte = *name->start
 4836     8b/-> *esi 1/r32/ecx
 4837     8a/copy-byte *ecx 1/r32/CL
 4838     81 4/subop/and %ecx 0xff/imm32
 4839     # if is-decimal-digit?(c) return new var(name)
 4840     {
 4841       (is-decimal-digit? %ecx)  # => eax
 4842       3d/compare-eax-and 0/imm32/false
 4843       74/jump-if-= break/disp8
 4844       (new-literal-integer Heap %esi)  # => eax
 4845       eb/jump $lookup-var-or-literal:end/disp8
 4846     }
 4847     # else if (c == '"') return new var(name)
 4848     {
 4849       81 7/subop/compare %ecx 0x22/imm32/dquote
 4850       75/jump-if-!= break/disp8
 4851       (new-literal Heap %esi)  # => eax
 4852       eb/jump $lookup-var-or-literal:end/disp8
 4853     }
 4854     # otherwise return lookup-var(name, vars)
 4855     {
 4856       (lookup-var %esi *(ebp+0xc))  # => eax
 4857     }
 4858 $lookup-var-or-literal:end:
 4859     # . restore registers
 4860     5e/pop-to-esi
 4861     59/pop-to-ecx
 4862     # . epilogue
 4863     89/<- %esp 5/r32/ebp
 4864     5d/pop-to-ebp
 4865     c3/return
 4866 
 4867 $lookup-var-or-literal:abort:
 4868     (write-buffered Stderr "empty variable!")
 4869     (flush Stderr)
 4870     # . syscall(exit, 1)
 4871     bb/copy-to-ebx  1/imm32
 4872     b8/copy-to-eax  1/imm32/exit
 4873     cd/syscall  0x80/imm8
 4874     # never gets here
 4875 
 4876 # return first 'name' from the top (back) of 'vars' and abort if not found
 4877 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 4878     # . prologue
 4879     55/push-ebp
 4880     89/<- %ebp 4/r32/esp
 4881     # var target/eax: (handle array byte) = slice-to-string(name)
 4882     (slice-to-string Heap *(ebp+8))  # => eax
 4883     #
 4884     (lookup-var-helper %eax *(ebp+0xc))  # => eax
 4885     # if (result == 0) abort
 4886     3d/compare-eax-and 0/imm32
 4887     74/jump-if-= $lookup-var:abort/disp8
 4888 $lookup-var:end:
 4889     # . epilogue
 4890     89/<- %esp 5/r32/ebp
 4891     5d/pop-to-ebp
 4892     c3/return
 4893 
 4894 $lookup-var:abort:
 4895     (write-buffered Stderr "unknown variable '")
 4896     (write-slice-buffered Stderr *(ebp+8))
 4897     (write-buffered Stderr "'\n")
 4898     (flush Stderr)
 4899     # . syscall(exit, 1)
 4900     bb/copy-to-ebx  1/imm32
 4901     b8/copy-to-eax  1/imm32/exit
 4902     cd/syscall  0x80/imm8
 4903     # never gets here
 4904 
 4905 # return first 'name' from the top (back) of 'vars', and 0/null if not found
 4906 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
 4907     # pseudocode:
 4908     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
 4909     #   var min = vars->data
 4910     #   while curr >= min
 4911     #     var v: (handle var) = *curr
 4912     #     if v->name == name
 4913     #       return v
 4914     #   return 0
 4915     #
 4916     # . prologue
 4917     55/push-ebp
 4918     89/<- %ebp 4/r32/esp
 4919     # . save registers
 4920     52/push-edx
 4921     53/push-ebx
 4922     56/push-esi
 4923     # esi = vars
 4924     8b/-> *(ebp+0xc) 6/r32/esi
 4925     # ebx = vars->top
 4926     8b/-> *esi 3/r32/ebx
 4927     # if (vars->top > vars->length) abort
 4928     3b/compare 0/r32/eax *(esi+4)
 4929     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
 4930     # var min/edx: (addr handle var) = vars->data
 4931     8d/copy-address *(esi+8) 2/r32/edx
 4932     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
 4933     81 5/subop/subtract %ebx 4/imm32
 4934     8d/copy-address *(esi+ebx+8) 3/r32/ebx
 4935     {
 4936       # if (curr < min) return 0
 4937       39/compare %ebx 2/r32/edx
 4938       b8/copy-to-eax 0/imm32
 4939       0f 82/jump-if-addr< break/disp32
 4940       # var v/eax: (handle var) = *curr
 4941       8b/-> *ebx 0/r32/eax
 4942       # if (v->name == name) return v
 4943       (string-equal? *eax *(ebp+8))  # Var-name
 4944       3d/compare-eax-and 0/imm32/false
 4945       8b/-> *ebx 0/r32/eax
 4946       75/jump-if-!= break/disp8
 4947       # curr -= 4
 4948       81 5/subop/subtract %ebx 4/imm32
 4949       e9/jump loop/disp32
 4950     }
 4951 $lookup-var-helper:end:
 4952     # . restore registers
 4953     5e/pop-to-esi
 4954     5b/pop-to-ebx
 4955     5a/pop-to-edx
 4956     # . epilogue
 4957     89/<- %esp 5/r32/ebp
 4958     5d/pop-to-ebp
 4959     c3/return
 4960 
 4961 $lookup-var-helper:error1:
 4962     (write-buffered Stderr "malformed stack when looking up '")
 4963     (write-slice-buffered Stderr *(ebp+8))
 4964     (write-buffered Stderr "'\n")
 4965     (flush Stderr)
 4966     # . syscall(exit, 1)
 4967     bb/copy-to-ebx  1/imm32
 4968     b8/copy-to-eax  1/imm32/exit
 4969     cd/syscall  0x80/imm8
 4970     # never gets here
 4971 
 4972 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
 4973 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
 4974     # . prologue
 4975     55/push-ebp
 4976     89/<- %ebp 4/r32/esp
 4977     # . save registers
 4978     51/push-ecx
 4979     # var target/ecx: (handle array byte) = slice-to-string(name)
 4980     (slice-to-string Heap *(ebp+8))  # => eax
 4981     89/<- %ecx 0/r32/eax
 4982     #
 4983     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
 4984     {
 4985       # if (result != 0) return
 4986       3d/compare-eax-and 0/imm32
 4987       75/jump-if-!= break/disp8
 4988       # if name is one of fn's outputs, return it
 4989       {
 4990         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
 4991         3d/compare-eax-and 0/imm32
 4992         # otherwise abort
 4993         0f 84/jump-if-!= $lookup-var:abort/disp32
 4994       }
 4995     }
 4996 $lookup-or-define-var:end:
 4997     # . restore registers
 4998     59/pop-to-ecx
 4999     # . epilogue
 5000     89/<- %esp 5/r32/ebp
 5001     5d/pop-to-ebp
 5002     c3/return
 5003 
 5004 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
 5005     # . prologue
 5006     55/push-ebp
 5007     89/<- %ebp 4/r32/esp
 5008     # . save registers
 5009     51/push-ecx
 5010     # var curr/ecx: (handle list var) = fn->outputs
 5011     8b/-> *(ebp+8) 1/r32/ecx
 5012     8b/-> *(ecx+0xc) 1/r32/ecx
 5013     # while curr != null
 5014     {
 5015       81 7/subop/compare %ecx 0/imm32
 5016       74/jump-if-= break/disp8
 5017       # var v: (handle var) = *curr
 5018       8b/-> *ecx 0/r32/eax  # List-value
 5019       # if (curr->name == name) return curr
 5020       50/push-eax
 5021       (string-equal? *eax *(ebp+0xc))
 5022       3d/compare-eax-and 0/imm32/false
 5023       58/pop-to-eax
 5024       75/jump-if-!= $find-in-function-outputs:end/disp8
 5025       # curr = curr->next
 5026       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 5027       eb/jump loop/disp8
 5028     }
 5029     b8/copy-to-eax 0/imm32
 5030 $find-in-function-outputs:end:
 5031     # . restore registers
 5032     59/pop-to-ecx
 5033     # . epilogue
 5034     89/<- %esp 5/r32/ebp
 5035     5d/pop-to-ebp
 5036     c3/return
 5037 
 5038 test-parse-mu-stmt:
 5039     # . prologue
 5040     55/push-ebp
 5041     89/<- %ebp 4/r32/esp
 5042     # setup
 5043     (clear-stream _test-input-stream)
 5044     (write _test-input-stream "increment n\n")
 5045     # var vars/ecx: (stack (addr var) 4)
 5046     81 5/subop/subtract %esp 0x10/imm32
 5047     68/push 0x10/imm32/length
 5048     68/push 0/imm32/top
 5049     89/<- %ecx 4/r32/esp
 5050     (clear-stack %ecx)
 5051     # var v/edx: var
 5052     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5053     89/<- %edx 4/r32/esp
 5054     (zero-out %edx 0x14)  # Var-size
 5055     # v->name = "n"
 5056     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5057     #
 5058     (push %ecx %edx)
 5059     # convert
 5060     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5061     # check result
 5062     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
 5063     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
 5064     # edx: (handle list var) = result->inouts
 5065     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5066     # ebx: (handle var) = result->inouts->value
 5067     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5068     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
 5069     # . epilogue
 5070     89/<- %esp 5/r32/ebp
 5071     5d/pop-to-ebp
 5072     c3/return
 5073 
 5074 test-parse-mu-stmt-with-comma:
 5075     # . prologue
 5076     55/push-ebp
 5077     89/<- %ebp 4/r32/esp
 5078     # setup
 5079     (clear-stream _test-input-stream)
 5080     (write _test-input-stream "copy-to n, 3\n")
 5081     # var vars/ecx: (stack (addr var) 4)
 5082     81 5/subop/subtract %esp 0x10/imm32
 5083     68/push 0x10/imm32/length
 5084     68/push 0/imm32/top
 5085     89/<- %ecx 4/r32/esp
 5086     (clear-stack %ecx)
 5087     # var v/edx: var
 5088     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5089     89/<- %edx 4/r32/esp
 5090     (zero-out %edx 0x14)  # Var-size
 5091     # v->name = "n"
 5092     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5093     #
 5094     (push %ecx %edx)
 5095     # convert
 5096     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5097     # check result
 5098     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
 5099     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
 5100     # edx: (handle list var) = result->inouts
 5101     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5102     # ebx: (handle var) = result->inouts->value
 5103     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5104     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
 5105     # . epilogue
 5106     89/<- %esp 5/r32/ebp
 5107     5d/pop-to-ebp
 5108     c3/return
 5109 
 5110 new-function:  # ad: (addr allocation-descriptor), name: (addr array byte), subx-name: (addr array byte), inouts: (handle list var), outputs: (handle list var), body: (handle block), next: (handle function) -> result/eax: (handle function)
 5111     # . prologue
 5112     55/push-ebp
 5113     89/<- %ebp 4/r32/esp
 5114     # . save registers
 5115     51/push-ecx
 5116     #
 5117     (allocate *(ebp+8) *Function-size)  # => eax
 5118     8b/-> *(ebp+0xc) 1/r32/ecx
 5119     89/<- *eax 1/r32/ecx  # Function-name
 5120     8b/-> *(ebp+0x10) 1/r32/ecx
 5121     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
 5122     8b/-> *(ebp+0x14) 1/r32/ecx
 5123     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
 5124     8b/-> *(ebp+0x18) 1/r32/ecx
 5125     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
 5126     8b/-> *(ebp+0x1c) 1/r32/ecx
 5127     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
 5128     8b/-> *(ebp+0x20) 1/r32/ecx
 5129     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
 5130 $new-function:end:
 5131     # . restore registers
 5132     59/pop-to-ecx
 5133     # . epilogue
 5134     89/<- %esp 5/r32/ebp
 5135     5d/pop-to-ebp
 5136     c3/return
 5137 
 5138 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte) -> result/eax: (handle var)
 5139     # . prologue
 5140     55/push-ebp
 5141     89/<- %ebp 4/r32/esp
 5142     # . save registers
 5143     51/push-ecx
 5144     #
 5145     (allocate *(ebp+8) *Var-size)  # => eax
 5146     (zero-out %eax *Var-size)
 5147     8b/-> *(ebp+0xc) 1/r32/ecx
 5148     89/<- *eax 1/r32/ecx  # Var-name
 5149 $new-var:end:
 5150     # . restore registers
 5151     59/pop-to-ecx
 5152     # . epilogue
 5153     89/<- %esp 5/r32/ebp
 5154     5d/pop-to-ebp
 5155     c3/return
 5156 
 5157 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5158     # . prologue
 5159     55/push-ebp
 5160     89/<- %ebp 4/r32/esp
 5161     # . save registers
 5162     51/push-ecx
 5163     # if (!is-hex-int?(name)) abort
 5164     (is-hex-int? *(ebp+0xc))  # => eax
 5165     3d/compare-eax-and 0/imm32/false
 5166     0f 84/jump-if-= $new-literal-integer:abort/disp32
 5167     # var type/ecx: (handle tree type-id) = new type()
 5168     (allocate *(ebp+8) *Tree-size)  # => eax
 5169     (zero-out %eax *Tree-size)  # default type is 'literal'
 5170     89/<- %ecx 0/r32/eax
 5171     # result = new var(s)
 5172     (new-var-from-slice *(ebp+8) *(ebp+0xc))  # => eax
 5173     89/<- *(eax+4) 1/r32/ecx
 5174 $new-literal-integer:end:
 5175     # . restore registers
 5176     59/pop-to-ecx
 5177     # . epilogue
 5178     89/<- %esp 5/r32/ebp
 5179     5d/pop-to-ebp
 5180     c3/return
 5181 
 5182 $new-literal-integer:abort:
 5183     (write-buffered Stderr "variable cannot begin with a digit '")
 5184     (write-slice-buffered Stderr *(ebp+0xc))
 5185     (write-buffered Stderr "'\n")
 5186     (flush Stderr)
 5187     # . syscall(exit, 1)
 5188     bb/copy-to-ebx  1/imm32
 5189     b8/copy-to-eax  1/imm32/exit
 5190     cd/syscall  0x80/imm8
 5191     # never gets here
 5192 
 5193 new-literal:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5194     # . prologue
 5195     55/push-ebp
 5196     89/<- %ebp 4/r32/esp
 5197     # . save registers
 5198     51/push-ecx
 5199     52/push-edx
 5200     # var s/ecx: (addr array byte)
 5201     (slice-to-string Heap *(ebp+0xc))  # => eax
 5202     89/<- %ecx 0/r32/eax
 5203     # type = new type()
 5204     (allocate *(ebp+8) *Tree-size)  # => eax
 5205     (zero-out %eax *Tree-size)  # default type is 'literal'
 5206     89/<- %edx 0/r32/eax
 5207     # eax = result
 5208     (new-var *(ebp+8) %ecx)  # => eax
 5209     # result->type = type
 5210     89/<- *(eax+4) 2/r32/edx  # Var-type
 5211 $new-literal:end:
 5212     # . restore registers
 5213     5a/pop-to-edx
 5214     59/pop-to-ecx
 5215     # . epilogue
 5216     89/<- %esp 5/r32/ebp
 5217     5d/pop-to-ebp
 5218     c3/return
 5219 
 5220 new-var-from-slice:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5221     # . prologue
 5222     55/push-ebp
 5223     89/<- %ebp 4/r32/esp
 5224     # . save registers
 5225     51/push-ecx
 5226     # result = new-var(slice-to-string(name))
 5227     (slice-to-string Heap *(ebp+0xc))  # => eax
 5228     (new-var *(ebp+8) %eax)
 5229 $new-var-from-slice:end:
 5230     # . restore registers
 5231     59/pop-to-ecx
 5232     # . epilogue
 5233     89/<- %esp 5/r32/ebp
 5234     5d/pop-to-ebp
 5235     c3/return
 5236 
 5237 new-block:  # ad: (addr allocation-descriptor), data: (handle list stmt) -> result/eax: (handle stmt)
 5238     # . prologue
 5239     55/push-ebp
 5240     89/<- %ebp 4/r32/esp
 5241     # . save registers
 5242     51/push-ecx
 5243     #
 5244     (allocate *(ebp+8) *Stmt-size)  # => eax
 5245     (zero-out %eax *Stmt-size)
 5246     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
 5247     8b/-> *(ebp+0xc) 1/r32/ecx
 5248     89/<- *(eax+4) 1/r32/ecx  # Block-stmts
 5249 $new-block:end:
 5250     # . restore registers
 5251     59/pop-to-ecx
 5252     # . epilogue
 5253     89/<- %esp 5/r32/ebp
 5254     5d/pop-to-ebp
 5255     c3/return
 5256 
 5257 new-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5258     # . prologue
 5259     55/push-ebp
 5260     89/<- %ebp 4/r32/esp
 5261     # . save registers
 5262     51/push-ecx
 5263     #
 5264     (allocate *(ebp+8) *Stmt-size)  # => eax
 5265     (zero-out %eax *Stmt-size)
 5266     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
 5267     # result->var = var
 5268     8b/-> *(ebp+0xc) 1/r32/ecx
 5269     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
 5270 $new-var-def:end:
 5271     # . restore registers
 5272     59/pop-to-ecx
 5273     # . epilogue
 5274     89/<- %esp 5/r32/ebp
 5275     5d/pop-to-ebp
 5276     c3/return
 5277 
 5278 new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5279     # . prologue
 5280     55/push-ebp
 5281     89/<- %ebp 4/r32/esp
 5282     # . save registers
 5283     51/push-ecx
 5284     57/push-edi
 5285     # ecx = var
 5286     8b/-> *(ebp+0xc) 1/r32/ecx
 5287     # edi = result
 5288     (allocate *(ebp+8) *Stmt-size)  # => eax
 5289     89/<- %edi 0/r32/eax
 5290     (zero-out %edi *Stmt-size)
 5291     # set tag
 5292     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
 5293     # set output
 5294     (append-stmt-var Heap %ecx *(edi+0xc) 0)  # Regvardef-outputs => eax
 5295     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
 5296 $new-reg-var-def:end:
 5297     89/<- %eax 7/r32/edi
 5298     # . restore registers
 5299     5f/pop-to-edi
 5300     59/pop-to-ecx
 5301     # . epilogue
 5302     89/<- %esp 5/r32/ebp
 5303     5d/pop-to-ebp
 5304     c3/return
 5305 
 5306 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
 5307     # . prologue
 5308     55/push-ebp
 5309     89/<- %ebp 4/r32/esp
 5310     # . save registers
 5311     51/push-ecx
 5312     #
 5313     (allocate *(ebp+8) *List-size)  # => eax
 5314     (zero-out %eax *List-size)
 5315     8b/-> *(ebp+0xc) 1/r32/ecx
 5316     89/<- *eax 1/r32/ecx  # List-value
 5317     # if (list == null) return result
 5318     81 7/subop/compare *(ebp+0x10) 0/imm32
 5319     74/jump-if-= $append-list:end/disp8
 5320     # otherwise append
 5321     # var curr/ecx = list
 5322     8b/-> *(ebp+0x10) 1/r32/ecx
 5323     # while (curr->next != null) curr = curr->next
 5324     {
 5325       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5326       74/jump-if-= break/disp8
 5327       # curr = curr->next
 5328       8b/-> *(ecx+4) 1/r32/ecx
 5329       eb/jump loop/disp8
 5330     }
 5331     # curr->next = result
 5332     89/<- *(ecx+4) 0/r32/eax
 5333     # return list
 5334     8b/-> *(ebp+0x10) 0/r32/eax
 5335 $append-list:end:
 5336     # . restore registers
 5337     59/pop-to-ecx
 5338     # . epilogue
 5339     89/<- %esp 5/r32/ebp
 5340     5d/pop-to-ebp
 5341     c3/return
 5342 
 5343 append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
 5344     # . prologue
 5345     55/push-ebp
 5346     89/<- %ebp 4/r32/esp
 5347     # . save registers
 5348     51/push-ecx
 5349     #
 5350     (allocate *(ebp+8) *Stmt-var-size)  # => eax
 5351     (zero-out %eax *Stmt-var-size)
 5352     8b/-> *(ebp+0xc) 1/r32/ecx
 5353     89/<- *eax 1/r32/ecx  # Stmt-var-value
 5354     8b/-> *(ebp+0x14) 1/r32/ecx
 5355     89/<- *(eax+8) 1/r32/ecx  # Stmt-var-is-deref
 5356     # if (list == null) return result
 5357     81 7/subop/compare *(ebp+0x10) 0/imm32
 5358     74/jump-if-= $append-stmt-var:end/disp8
 5359     # otherwise append
 5360     # var curr/ecx: (handle stmt-var) = vars
 5361     8b/-> *(ebp+0x10) 1/r32/ecx
 5362     # while (curr->next != null) curr = curr->next
 5363     {
 5364       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5365       74/jump-if-= break/disp8
 5366       # curr = curr->next
 5367       8b/-> *(ecx+4) 1/r32/ecx
 5368       eb/jump loop/disp8
 5369     }
 5370     # curr->next = result
 5371     89/<- *(ecx+4) 0/r32/eax
 5372     # return vars
 5373     8b/-> *(ebp+0x10) 0/r32/eax
 5374 $append-stmt-var:end:
 5375     # . restore registers
 5376     59/pop-to-ecx
 5377     # . epilogue
 5378     89/<- %esp 5/r32/ebp
 5379     5d/pop-to-ebp
 5380     c3/return
 5381 
 5382 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
 5383     # . prologue
 5384     55/push-ebp
 5385     89/<- %ebp 4/r32/esp
 5386     # . save registers
 5387     56/push-esi
 5388     # esi = block
 5389     8b/-> *(ebp+0xc) 6/r32/esi
 5390     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-stmts
 5391     89/<- *(esi+4) 0/r32/eax  # Block-stmts
 5392 $append-to-block:end:
 5393     # . restore registers
 5394     5e/pop-to-esi
 5395     # . epilogue
 5396     89/<- %esp 5/r32/ebp
 5397     5d/pop-to-ebp
 5398     c3/return
 5399 
 5400 ## Parsing types
 5401 # We need to create metadata on user-defined types, and we need to use this
 5402 # metadata as we parse instructions.
 5403 # However, we also want to allow types to be used before their definitions.
 5404 # This means we can't ever assume any type data structures exist.
 5405 
 5406 lookup-or-create-constant:  # container: (handle stmt-var), field-name: (addr slice) -> result/eax: (handle var)
 5407     # . prologue
 5408     55/push-ebp
 5409     89/<- %ebp 4/r32/esp
 5410     # . save registers
 5411     56/push-esi
 5412     # var container-type/esi: type-id
 5413     (container-type *(ebp+8))  # => eax
 5414 #?     (write-buffered Stderr "lookup-or-create-constant: container type-id: ")
 5415 #?     (print-int32-buffered Stderr %eax)
 5416 #?     (write-buffered Stderr Newline)
 5417 #?     (flush Stderr)
 5418     89/<- %esi 0/r32/eax
 5419     # var typeinfo/eax: (addr typeinfo)
 5420     (find-or-create-typeinfo %esi)  # => eax
 5421 #?     (write-buffered Stderr "lookup-or-create-constant: typeinfo: ")
 5422 #?     (print-int32-buffered Stderr %eax)
 5423 #?     (write-buffered Stderr Newline)
 5424 #?     (flush Stderr)
 5425     # result = find-or-create-typeinfo-output-var(typeinfo, field-name)
 5426     (find-or-create-typeinfo-output-var %eax *(ebp+0xc))  # => eax
 5427 $lookup-or-create-constant:end:
 5428     # . restore registers
 5429     5e/pop-to-esi
 5430     # . epilogue
 5431     89/<- %esp 5/r32/ebp
 5432     5d/pop-to-ebp
 5433     c3/return
 5434 
 5435 # if addr var:
 5436 #   container->var->type->right->left->value
 5437 # otherwise
 5438 #   container->var->type->value
 5439 container-type:  # container: (handle stmt-var) -> result/eax: type-id
 5440     # . prologue
 5441     55/push-ebp
 5442     89/<- %ebp 4/r32/esp
 5443     #
 5444     8b/-> *(ebp+8) 0/r32/eax
 5445     8b/-> *eax 0/r32/eax  # Stmt-var-value
 5446     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5447     {
 5448       81 7/subop/compare *(eax+4) 0/imm32
 5449       74/jump-if-= break/disp8
 5450       8b/-> *(eax+4) 0/r32/eax  # Tree-right
 5451       8b/-> *eax 0/r32/eax  # Tree-left
 5452     }
 5453     8b/-> *eax 0/r32/eax  # Atom-value
 5454 $container-type:end:
 5455     # . epilogue
 5456     89/<- %esp 5/r32/ebp
 5457     5d/pop-to-ebp
 5458     c3/return
 5459 
 5460 find-or-create-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5461     # . prologue
 5462     55/push-ebp
 5463     89/<- %ebp 4/r32/esp
 5464     # . save registers
 5465     51/push-ecx
 5466     # eax = find-typeinfo(t)
 5467     (find-typeinfo *(ebp+8))  # => eax
 5468     {
 5469       # if (curr != 0) break
 5470       3d/compare-eax-and 0/imm32
 5471       75/jump-if-!= break/disp8
 5472 $find-or-create-typeinfo:create:
 5473       (allocate Heap *Typeinfo-size)  # => eax
 5474       (zero-out %eax *Typeinfo-size)
 5475       # result->id = t
 5476       8b/-> *(ebp+8) 1/r32/ecx
 5477       89/<- *eax 1/r32/ecx  # Typeinfo-id
 5478       # result->fields = new table
 5479       # . ecx = new table
 5480       50/push-eax
 5481       (new-stream Heap 0x40 *Typeinfo-fields-row-size)  # => eax
 5482       89/<- %ecx 0/r32/eax
 5483       58/pop-to-eax
 5484       # . result->fields = ecx
 5485       89/<- *(eax+4) 1/r32/ecx  # Typeinfo-fields
 5486       # result->next = Program->types
 5487       8b/-> *_Program-types 1/r32/ecx
 5488       89/<- *(eax+0xc) 1/r32/ecx  # Typeinfo-next
 5489       # Program->types = result
 5490       89/<- *_Program-types 0/r32/eax
 5491     }
 5492 $find-or-create-typeinfo:end:
 5493     # . restore registers
 5494     59/pop-to-ecx
 5495     # . epilogue
 5496     89/<- %esp 5/r32/ebp
 5497     5d/pop-to-ebp
 5498     c3/return
 5499 
 5500 find-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5501     # . prologue
 5502     55/push-ebp
 5503     89/<- %ebp 4/r32/esp
 5504     # . save registers
 5505     51/push-ecx
 5506     # ecx = t
 5507     8b/-> *(ebp+8) 1/r32/ecx
 5508     # var curr/eax: (handle typeinfo) = Program->types
 5509     8b/-> *_Program-types 0/r32/eax
 5510     {
 5511       # if (curr == 0) break
 5512       3d/compare-eax-and 0/imm32
 5513       74/jump-if-= break/disp8
 5514       # if (curr->id == t) return curr
 5515       39/compare *eax 1/r32/ecx  # Typeinfo-id
 5516       0f 84/jump-if-= $find-or-create-typeinfo:end/disp32
 5517       # curr = curr->next
 5518       8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-next
 5519       #
 5520       eb/jump loop/disp8
 5521     }
 5522 $find-typeinfo:end:
 5523     # . restore registers
 5524     59/pop-to-ecx
 5525     # . epilogue
 5526     89/<- %esp 5/r32/ebp
 5527     5d/pop-to-ebp
 5528     c3/return
 5529 
 5530 find-or-create-typeinfo-output-var:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle var)
 5531     # . prologue
 5532     55/push-ebp
 5533     89/<- %ebp 4/r32/esp
 5534     # . save registers
 5535     51/push-ecx
 5536     56/push-esi
 5537     # esi = find-or-create-typeinfo-fields(T, f)
 5538     (find-or-create-typeinfo-fields *(ebp+8) *(ebp+0xc))  # => eax
 5539     89/<- %esi 0/r32/eax
 5540     # if output var doesn't exist, create it
 5541     {
 5542       81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-entry-output-var
 5543       75/jump-if-!= break/disp8
 5544       # var type/eax: (handle tree type-id) = new var("dummy name", constant type, -1 offset)
 5545       (allocate Heap *Tree-size)  # => eax
 5546       c7 0/subop/copy *eax 6/imm32/constant  # Atom-value
 5547       c7 0/subop/copy *(eax+4) 0/imm32  # Tree-right
 5548       89/<- %ecx 0/r32/eax
 5549       # eax = result
 5550       (new-var Heap "field")  # => eax
 5551       # result->type = type
 5552       89/<- *(eax+4) 1/r32/ecx  # Var-type
 5553       # result->offset isn't filled out yet
 5554       c7 0/subop/copy *(eax+0xc) -1/imm32/uninitialized  # Var-offset
 5555       # save result as output var
 5556       89/<- *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5557     }
 5558     # return the output var
 5559     8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5560 $find-or-create-typeinfo-output-var:end:
 5561     # . restore registers
 5562     5e/pop-to-esi
 5563     59/pop-to-ecx
 5564     # . epilogue
 5565     89/<- %esp 5/r32/ebp
 5566     5d/pop-to-ebp
 5567     c3/return
 5568 
 5569 find-or-create-typeinfo-fields:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle typeinfo-entry)
 5570     # . prologue
 5571     55/push-ebp
 5572     89/<- %ebp 4/r32/esp
 5573     # . save registers
 5574     56/push-esi
 5575     # esi = T->fields
 5576     8b/-> *(ebp+8) 6/r32/esi
 5577     8b/-> *(esi+4) 6/r32/esi  # Typeinfo-fields
 5578     # esi = get-or-insert(T->fields, f)
 5579     (leaky-get-or-insert-slice %esi *(ebp+0xc) *Typeinfo-fields-row-size)  # => eax
 5580     89/<- %esi 0/r32/eax
 5581     # if typeinfo-entry doesn't exist, allocate it
 5582     {
 5583       81 7/subop/compare *esi 0/imm32  # output var
 5584       75/jump-if-!= break/disp8
 5585       (allocate Heap *Typeinfo-entry-size)  # => eax
 5586       (zero-out %eax *Typeinfo-entry-size)
 5587       89/<- *esi 0/r32/eax
 5588     }
 5589     # eax = T->fields[f]->entry
 5590     8b/-> *esi 0/r32/eax
 5591 $find-or-create-typeinfo-fields:end:
 5592     # . restore registers
 5593     5e/pop-to-esi
 5594     # . epilogue
 5595     89/<- %esp 5/r32/ebp
 5596     5d/pop-to-ebp
 5597     c3/return
 5598 
 5599 populate-mu-type:  # in: (addr stream byte), t: (handle typeinfo)
 5600     # pseudocode:
 5601     #   var line: (stream byte 512)
 5602     #   curr-index = 0
 5603     #   while true
 5604     #     clear-stream(line)
 5605     #     read-line-buffered(in, line)
 5606     #     if line->write == 0
 5607     #       abort
 5608     #     word-slice = next-mu-token(line)
 5609     #     if slice-empty?(word-slice)               # end of line
 5610     #       continue
 5611     #     if slice-equal?(word-slice, "}")
 5612     #       break
 5613     #     var v: (handle var) = parse-var-with-type(word-slice, line)
 5614     #     var r: (handle typeinfo-fields) = find-or-create-typeinfo-fields(t, word-slice/v->name)
 5615     #     TODO: ensure that r->first is null
 5616     #     r->index = curr-index
 5617     #     curr-index++
 5618     #     r->input-var = v
 5619     #     if r->output-var == 0
 5620     #       r->output-var = new literal
 5621     #     TODO: ensure nothing else in line
 5622     # t->total-size-in-bytes = -2 (not yet initialized)
 5623     #
 5624     # . prologue
 5625     55/push-ebp
 5626     89/<- %ebp 4/r32/esp
 5627     # var curr-index: int at *(ebp-4)
 5628     68/push 0/imm32
 5629     # . save registers
 5630     50/push-eax
 5631     51/push-ecx
 5632     52/push-edx
 5633     53/push-ebx
 5634     56/push-esi
 5635     57/push-edi
 5636     # edi = t
 5637     8b/-> *(ebp+0xc) 7/r32/edi
 5638     # var line/ecx: (stream byte 512)
 5639     81 5/subop/subtract %esp 0x200/imm32
 5640     68/push 0x200/imm32/length
 5641     68/push 0/imm32/read
 5642     68/push 0/imm32/write
 5643     89/<- %ecx 4/r32/esp
 5644     # var word-slice/edx: slice
 5645     68/push 0/imm32/end
 5646     68/push 0/imm32/start
 5647     89/<- %edx 4/r32/esp
 5648     {
 5649 $populate-mu-type:line-loop:
 5650       (clear-stream %ecx)
 5651       (read-line-buffered *(ebp+8) %ecx)
 5652       # if (line->write == 0) abort
 5653       81 7/subop/compare *ecx 0/imm32
 5654       0f 84/jump-if-= $populate-mu-type:abort/disp32
 5655 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
 5661       (next-mu-token %ecx %edx)
 5662       # if slice-empty?(word-slice) continue
 5663       (slice-empty? %edx)  # => eax
 5664       3d/compare-eax-and 0/imm32
 5665       0f 85/jump-if-!= loop/disp32
 5666       # if slice-equal?(word-slice, "}") break
 5667       (slice-equal? %edx "}")
 5668       3d/compare-eax-and 0/imm32
 5669       0f 85/jump-if-!= break/disp32
 5670 $populate-mu-type:parse-element:
 5671       # var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
 5672       (parse-var-with-type %edx %ecx)  # => eax
 5673       89/<- %esi 0/r32/eax
 5674 $populate-mu-type:create-typeinfo-fields:
 5675       # var r/ebx: (handle typeinfo-entry)
 5676       (find-or-create-typeinfo-fields %edi %edx)  # => eax
 5677       89/<- %ebx 0/r32/eax
 5678 #?       (write-buffered Stderr "var ")
 5679 #?       (write-buffered Stderr *esi)  # Var-name
 5680 #?       (write-buffered Stderr " is at index ")
 5681 #?       (print-int32-buffered Stderr *(ebp-4))
 5682 #?       (write-buffered Stderr Newline)
 5683 #?       (flush Stderr)
 5684       # r->index = curr-index
 5685       8b/-> *(ebp-4) 0/r32/eax
 5686       89/<- *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 5687       # ++curr-index
 5688       ff 0/subop/increment *(ebp-4)
 5689 $populate-mu-type:set-input-type:
 5690       # r->input-var = v
 5691       89/<- *ebx 6/r32/esi  # Typeinfo-entry-input-var
 5692       {
 5693 $populate-mu-type:create-output-type:
 5694         # if (r->output-var == 0) create a new var with some placeholder data
 5695         81 7/subop/compare *(ebx+8) 0/imm32  # Typeinfo-entry-output-var
 5696         75/jump-if-!= break/disp8
 5697         (new-literal Heap %edx)  # => eax
 5698         89/<- *(ebx+8) 0/r32/eax  # Typeinfo-entry-output-var
 5699       }
 5700       e9/jump loop/disp32
 5701     }
 5702 $populate-mu-type:invalidate-total-size-in-bytes:
 5703     # Offsets and total size may not be accurate here since we may not yet
 5704     # have encountered the element types.
 5705     # We'll recompute them separately after parsing the entire program.
 5706     c7 0/subop/copy *(edi+8) -2/imm32/uninitialized  # Typeinfo-total-size-in-bytes
 5707 $populate-mu-type:end:
 5708     # . reclaim locals
 5709     81 0/subop/add %esp 0x214/imm32
 5710     # . restore registers
 5711     5f/pop-to-edi
 5712     5e/pop-to-esi
 5713     5b/pop-to-ebx
 5714     5a/pop-to-edx
 5715     59/pop-to-ecx
 5716     58/pop-to-eax
 5717     # reclaim curr-index
 5718     81 0/subop/add %esp 4/imm32
 5719     # . epilogue
 5720     89/<- %esp 5/r32/ebp
 5721     5d/pop-to-ebp
 5722     c3/return
 5723 
 5724 $populate-mu-type:abort:
 5725     # error("unexpected top-level command: " word-slice "\n")
 5726     (write-buffered Stderr "incomplete type definition '")
 5727     (type-name *edi)  # Typeinfo-id => eax
 5728     (write-buffered Stderr %eax)
 5729     (write-buffered Stderr "\n")
 5730     (flush Stderr)
 5731     # . syscall(exit, 1)
 5732     bb/copy-to-ebx  1/imm32
 5733     b8/copy-to-eax  1/imm32/exit
 5734     cd/syscall  0x80/imm8
 5735     # never gets here
 5736 
 5737 type-name:  # index: int -> result/eax: (addr array byte)
 5738     # . prologue
 5739     55/push-ebp
 5740     89/<- %ebp 4/r32/esp
 5741     #
 5742     (index Type-id *(ebp+8))
 5743 $type-name:end:
 5744     # . epilogue
 5745     89/<- %esp 5/r32/ebp
 5746     5d/pop-to-ebp
 5747     c3/return
 5748 
 5749 index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
 5750     # . prologue
 5751     55/push-ebp
 5752     89/<- %ebp 4/r32/esp
 5753     # . save registers
 5754     56/push-esi
 5755     # TODO: bounds-check index
 5756     # esi = arr
 5757     8b/-> *(ebp+8) 6/r32/esi
 5758     # eax = index
 5759     8b/-> *(ebp+0xc) 0/r32/eax
 5760     # eax = *(arr + 12 + index)
 5761     8b/-> *(esi+eax+0xc) 0/r32/eax
 5762 $index:end:
 5763     # . restore registers
 5764     5e/pop-to-esi
 5765     # . epilogue
 5766     89/<- %esp 5/r32/ebp
 5767     5d/pop-to-ebp
 5768     c3/return
 5769 
 5770 #######################################################
 5771 # Compute type sizes
 5772 #######################################################
 5773 
 5774 # Compute the sizes of all user-defined types.
 5775 # We'll need the sizes of their elements, which may be other user-defined
 5776 # types, which we will compute as needed.
 5777 
 5778 # Initially, all user-defined types have their sizes set to -2 (invalid)
 5779 populate-mu-type-sizes:
 5780     # . prologue
 5781     55/push-ebp
 5782     89/<- %ebp 4/r32/esp
 5783     # . save registers
 5784     51/push-ecx
 5785 $populate-mu-type-sizes:total-sizes:
 5786     # var curr/ecx: (handle typeinfo) = *Program->types
 5787     8b/-> *_Program-types 1/r32/ecx
 5788     {
 5789       # if (curr == null) break
 5790       81 7/subop/compare %ecx 0/imm32
 5791       74/jump-if-= break/disp8
 5792       (populate-mu-type-sizes-in-type %ecx)
 5793       # curr = curr->next
 5794       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 5795       eb/jump loop/disp8
 5796     }
 5797 $populate-mu-type-sizes:offsets:
 5798     # var curr/ecx: (handle typeinfo) = *Program->types
 5799     8b/-> *_Program-types 1/r32/ecx
 5800     {
 5801       # if (curr == null) break
 5802       81 7/subop/compare %ecx 0/imm32
 5803       74/jump-if-= break/disp8
 5804       (populate-mu-type-offsets %ecx)
 5805       # curr = curr->next
 5806       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 5807       eb/jump loop/disp8
 5808     }
 5809 $populate-mu-type-sizes:end:
 5810     # . restore registers
 5811     59/pop-to-ecx
 5812     # . epilogue
 5813     89/<- %esp 5/r32/ebp
 5814     5d/pop-to-ebp
 5815     c3/return
 5816 
 5817 # compute sizes of all fields, recursing as necessary
 5818 # sum up all their sizes to arrive at total size
 5819 # fields may be out of order, but that doesn't affect the answer
 5820 populate-mu-type-sizes-in-type:  # T: (handle typeinfo)
 5821     # . prologue
 5822     55/push-ebp
 5823     89/<- %ebp 4/r32/esp
 5824     # . save registers
 5825     50/push-eax
 5826     51/push-ecx
 5827     52/push-edx
 5828     56/push-esi
 5829     57/push-edi
 5830     # esi = T
 5831     8b/-> *(ebp+8) 6/r32/esi
 5832     # if T is already computed, return
 5833     81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-total-size-in-bytes
 5834     7d/jump-if->= $populate-mu-type-sizes-in-type:end/disp8
 5835     # if T is being computed, abort
 5836     81 7/subop/compare *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 5837     74/jump-if-= $populate-mu-type-sizes-in-type:abort/disp8
 5838     # tag T (-2 to -1) to avoid infinite recursion
 5839     c7 0/subop/copy *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 5840     # var total-size/edi: int = 0
 5841     bf/copy-to-edi 0/imm32
 5842     # - for every field, if it's a user-defined type, compute its size
 5843     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 5844     8b/-> *(esi+4) 1/r32/ecx  # Typeinfo-fields
 5845     # var table-size/edx: int = table->write
 5846     8b/-> *ecx 2/r32/edx  # stream-write
 5847     # var curr/ecx: (addr table_row) = table->data
 5848     8d/copy-address *(ecx+0xc) 1/r32/ecx
 5849     # var max/edx: (addr table_row) = table->data + table->write
 5850     8d/copy-address *(ecx+edx) 2/r32/edx
 5851     {
 5852 $populate-mu-type-sizes-in-type:loop:
 5853       # if (curr >= max) break
 5854       39/compare %ecx 2/r32/edx
 5855       73/jump-if-addr>= break/disp8
 5856       # var t/eax: (handle typeinfo-entry) = curr->value
 5857       8b/-> *(ecx+4) 0/r32/eax
 5858       # compute size of t
 5859       (compute-size-of-var *eax)  # Typeinfo-entry-input-var => eax
 5860       # result += eax
 5861       01/add-to %edi 0/r32/eax
 5862       # curr += row-size
 5863       81 0/subop/add %ecx 8/imm32
 5864       #
 5865       eb/jump loop/disp8
 5866     }
 5867     # - save result
 5868     89/<- *(esi+8) 7/r32/edi  # Typeinfo-total-size-in-bytes
 5869 $populate-mu-type-sizes-in-type:end:
 5870     # . restore registers
 5871     5f/pop-to-edi
 5872     5e/pop-to-esi
 5873     5a/pop-to-edx
 5874     59/pop-to-ecx
 5875     58/pop-to-eax
 5876     # . epilogue
 5877     89/<- %esp 5/r32/ebp
 5878     5d/pop-to-ebp
 5879     c3/return
 5880 
 5881 $populate-mu-type-sizes-in-type:abort:
 5882     (write-buffered Stderr "cycle in type definitions\n")
 5883     (flush Stderr)
 5884     # . syscall(exit, 1)
 5885     bb/copy-to-ebx  1/imm32
 5886     b8/copy-to-eax  1/imm32/exit
 5887     cd/syscall  0x80/imm8
 5888     # never gets here
 5889 
 5890 # Analogous to size-of, except we need to compute what size-of can just read
 5891 # off the right data structures.
 5892 compute-size-of-var:  # in: (handle var) -> result/eax: int
 5893     # . prologue
 5894     55/push-ebp
 5895     89/<- %ebp 4/r32/esp
 5896     # eax: (handle var) = in
 5897     8b/-> *(ebp+8) 0/r32/eax
 5898     # eax: type-id = in->type
 5899     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5900     # TODO: support non-atom type
 5901     # TODO: support arrays
 5902     (compute-size-of-type-id *eax)  # Atom-left => eax
 5903 $compute-size-of-var:end:
 5904     # . epilogue
 5905     89/<- %esp 5/r32/ebp
 5906     5d/pop-to-ebp
 5907     c3/return
 5908 
 5909 compute-size-of-type-id:  # t: type-id -> result/eax: int
 5910     # . prologue
 5911     55/push-ebp
 5912     89/<- %ebp 4/r32/esp
 5913     #
 5914     8b/-> *(ebp+8) 0/r32/eax
 5915     # if v is a literal, return 0
 5916     3d/compare-eax-and 0/imm32
 5917     74/jump-if-= $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
 5918     # if v has a user-defined type, compute its size
 5919     # TODO: support non-atom type
 5920     (find-typeinfo %eax)  # => eax
 5921     {
 5922       3d/compare-eax-and 0/imm32
 5923       74/jump-if-= break/disp8
 5924 $compute-size-of-type-id:user-defined:
 5925       (populate-mu-type-sizes %eax)
 5926       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 5927       eb/jump $compute-size-of-type-id:end/disp8
 5928     }
 5929     # otherwise return the word size
 5930     b8/copy-to-eax 4/imm32
 5931 $compute-size-of-type-id:end:
 5932     # . epilogue
 5933     89/<- %esp 5/r32/ebp
 5934     5d/pop-to-ebp
 5935     c3/return
 5936 
 5937 # at this point we have total sizes for all user-defined types
 5938 # compute offsets for each element
 5939 # complication: fields may be out of order
 5940 populate-mu-type-offsets:  # in: (handle typeinfo)
 5941     # . prologue
 5942     55/push-ebp
 5943     89/<- %ebp 4/r32/esp
 5944     # . save registers
 5945     50/push-eax
 5946     51/push-ecx
 5947     52/push-edx
 5948     53/push-ebx
 5949     56/push-esi
 5950     57/push-edi
 5951     # var curr-offset/edi: int = 0
 5952     bf/copy-to-edi 0/imm32
 5953     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 5954     8b/-> *(ebp+8) 1/r32/ecx
 5955     8b/-> *(ecx+4) 1/r32/ecx  # Typeinfo-fields
 5956     # var num-elems/edx: int = table->write / 8
 5957     8b/-> *ecx 2/r32/edx  # stream-write
 5958     c1 5/subop/shift-right-logical  %edx 3/imm8
 5959     # var i/ebx: int = 0
 5960     bb/copy-to-ebx 0/imm32
 5961     {
 5962 $populate-mu-type-offsets:loop:
 5963       39/compare %ebx 2/r32/edx
 5964       7d/jump-if->= break/disp8
 5965       # var v/esi: (handle typeinfo-entry)
 5966       (locate-typeinfo-entry-with-index %ecx %ebx)  # => eax
 5967       89/<- %esi 0/r32/eax
 5968       # v->output-var->offset = curr-offset
 5969       8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5970       89/<- *(eax+0xc) 7/r32/edi  # Var-offset
 5971       # curr-offset += size-of(v->input-var)
 5972       8b/-> *esi 0/r32/eax  # Typeinfo-entry-input-var
 5973       (size-of %eax)  # => eax
 5974       01/add-to %edi 0/r32/eax
 5975       # ++i
 5976       43/increment-ebx
 5977       eb/jump loop/disp8
 5978     }
 5979 $populate-mu-type-offsets:end:
 5980     # . restore registers
 5981     5f/pop-to-edi
 5982     5e/pop-to-esi
 5983     5b/pop-to-ebx
 5984     5a/pop-to-edx
 5985     59/pop-to-ecx
 5986     58/pop-to-eax
 5987     # . epilogue
 5988     89/<- %esp 5/r32/ebp
 5989     5d/pop-to-ebp
 5990     c3/return
 5991 
 5992 locate-typeinfo-entry-with-index:  # table: (handle table string_key (handle typeinfo-entry)), idx: int -> result/eax: (handle typeinfo-entry)
 5993     # . prologue
 5994     55/push-ebp
 5995     89/<- %ebp 4/r32/esp
 5996     # . save registers
 5997     51/push-ecx
 5998     52/push-edx
 5999     53/push-ebx
 6000     56/push-esi
 6001     57/push-edi
 6002     # esi = table
 6003     8b/-> *(ebp+8) 6/r32/esi
 6004     # var curr/ecx: (addr string_key) = table->data
 6005     8d/copy-address *(esi+0xc) 1/r32/ecx
 6006     # var max/edx: (addr byte) = &table->data[table->write]
 6007     8b/-> *esi 2/r32/edx
 6008     8d/copy-address *(ecx+edx) 2/r32/edx
 6009     {
 6010 $locate-typeinfo-entry-with-index:loop:
 6011       39/compare %ecx 2/r32/edx
 6012       73/jump-if-addr>= $locate-typeinfo-entry-with-index:abort/disp8
 6013       # var v/ebx: (handle typeinfo-entry)
 6014       8b/-> *(ecx+4) 3/r32/ebx
 6015       # if (v->index == idx) return v
 6016       8b/-> *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 6017       39/compare *(ebp+0xc) 0/r32/eax
 6018       89/<- %eax 3/r32/ebx
 6019       74/jump-if-= break/disp8
 6020       # curr += 8
 6021       81 0/subop/add %ecx 8/imm32
 6022       eb/jump loop/disp8
 6023     }
 6024 $locate-typeinfo-entry-with-index:end:
 6025     # . restore registers
 6026     5f/pop-to-edi
 6027     5e/pop-to-esi
 6028     5b/pop-to-ebx
 6029     5a/pop-to-edx
 6030     59/pop-to-ecx
 6031     # . epilogue
 6032     89/<- %esp 5/r32/ebp
 6033     5d/pop-to-ebp
 6034     c3/return
 6035 
 6036 $locate-typeinfo-entry-with-index:abort:
 6037     (write-buffered Stderr "overflowing typeinfo-entry->index ")
 6038     (print-int32-buffered Stderr %ecx)
 6039     (write-buffered Stderr "\n")
 6040     (flush Stderr)
 6041     # . syscall(exit, 1)
 6042     bb/copy-to-ebx  1/imm32
 6043     b8/copy-to-eax  1/imm32/exit
 6044     cd/syscall  0x80/imm8
 6045     # never gets here
 6046 
 6047 #######################################################
 6048 # Type-checking
 6049 #######################################################
 6050 
 6051 check-mu-types:
 6052     # . prologue
 6053     55/push-ebp
 6054     89/<- %ebp 4/r32/esp
 6055     #
 6056 $check-mu-types:end:
 6057     # . epilogue
 6058     89/<- %esp 5/r32/ebp
 6059     5d/pop-to-ebp
 6060     c3/return
 6061 
 6062 size-of:  # v: (handle var) -> result/eax: int
 6063     # . prologue
 6064     55/push-ebp
 6065     89/<- %ebp 4/r32/esp
 6066     # . save registers
 6067     51/push-ecx
 6068     # ecx = v->type
 6069     8b/-> *(ebp+8) 1/r32/ecx
 6070     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6071     # TODO: support non-atom type
 6072     # TODO: support arrays
 6073     (size-of-type-id *ecx)  # Atom-left => eax
 6074 $size-of:end:
 6075     # . restore registers
 6076     59/pop-to-ecx
 6077     # . epilogue
 6078     89/<- %esp 5/r32/ebp
 6079     5d/pop-to-ebp
 6080     c3/return
 6081 
 6082 size-of-type-id:  # t: type-id -> result/eax: int
 6083     # . prologue
 6084     55/push-ebp
 6085     89/<- %ebp 4/r32/esp
 6086     #
 6087     8b/-> *(ebp+8) 0/r32/eax
 6088     # if v is a literal, return 0
 6089     3d/compare-eax-and 0/imm32
 6090     74/jump-if-= $size-of-type-id:end/disp8  # eax changes type from type-id to int
 6091     # if v has a user-defined type, return its size
 6092     # TODO: support non-atom type
 6093     (find-typeinfo %eax)  # => eax
 6094     {
 6095       3d/compare-eax-and 0/imm32
 6096       74/jump-if-= break/disp8
 6097 $size-of-type-id:user-defined:
 6098       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6099       eb/jump $size-of-type-id:end/disp8
 6100     }
 6101     # otherwise return the word size
 6102     b8/copy-to-eax 4/imm32
 6103 $size-of-type-id:end:
 6104     # . epilogue
 6105     89/<- %esp 5/r32/ebp
 6106     5d/pop-to-ebp
 6107     c3/return
 6108 
 6109 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
 6110     # . prologue
 6111     55/push-ebp
 6112     89/<- %ebp 4/r32/esp
 6113     # . save registers
 6114     51/push-ecx
 6115     52/push-edx
 6116     # ecx = a
 6117     8b/-> *(ebp+8) 1/r32/ecx
 6118     # edx = b
 6119     8b/-> *(ebp+0xc) 2/r32/edx
 6120     # if (a == b) return true
 6121     8b/-> %ecx 0/r32/eax  # Var-type
 6122     39/compare %edx 0/r32/eax  # Var-type
 6123     b8/copy-to-eax 1/imm32/true
 6124     74/jump-if-= $type-equal?:end/disp8
 6125     # if (a < MAX_TYPE_ID) return false
 6126     81 7/subop/compare %ecx 0x10000/imm32
 6127     b8/copy-to-eax 0/imm32/false
 6128     72/jump-if-addr< $type-equal?:end/disp8
 6129     # if (b < MAX_TYPE_ID) return false
 6130     81 7/subop/compare %edx 0x10000/imm32
 6131     b8/copy-to-eax 0/imm32/false
 6132     72/jump-if-addr< $type-equal?:end/disp8
 6133     # if (!type-equal?(a->left, b->left)) return false
 6134     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
 6135     3d/compare-eax-and 0/imm32/false
 6136     74/jump-if-= $type-equal?:end/disp8
 6137     # return type-equal?(a->right, b->right)
 6138     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
 6139 $type-equal?:end:
 6140     # . restore registers
 6141     5a/pop-to-edx
 6142     59/pop-to-ecx
 6143     # . epilogue
 6144     89/<- %esp 5/r32/ebp
 6145     5d/pop-to-ebp
 6146     c3/return
 6147 
 6148 #######################################################
 6149 # Code-generation
 6150 #######################################################
 6151 
 6152 == data
 6153 
 6154 Curr-block-depth:  # (addr int)
 6155     0/imm32
 6156 Curr-local-stack-offset:  # (addr int)
 6157     0/imm32
 6158 
 6159 == code
 6160 
 6161 emit-subx:  # out: (addr buffered-file)
 6162     # . prologue
 6163     55/push-ebp
 6164     89/<- %ebp 4/r32/esp
 6165     # . save registers
 6166     50/push-eax
 6167     51/push-ecx
 6168     57/push-edi
 6169     # edi = out
 6170     8b/-> *(ebp+8) 7/r32/edi
 6171     # var curr/ecx: (handle function) = *Program->functions
 6172     8b/-> *_Program-functions 1/r32/ecx
 6173     {
 6174       # if (curr == null) break
 6175       81 7/subop/compare %ecx 0/imm32
 6176       0f 84/jump-if-= break/disp32
 6177       (emit-subx-function %edi %ecx)
 6178       # curr = curr->next
 6179       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 6180       e9/jump loop/disp32
 6181     }
 6182 $emit-subx:end:
 6183     # . restore registers
 6184     5f/pop-to-edi
 6185     59/pop-to-ecx
 6186     58/pop-to-eax
 6187     # . epilogue
 6188     89/<- %esp 5/r32/ebp
 6189     5d/pop-to-ebp
 6190     c3/return
 6191 
 6192 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
 6193     # . prologue
 6194     55/push-ebp
 6195     89/<- %ebp 4/r32/esp
 6196     # some preprocessing
 6197     (populate-mu-type-offsets-in-inouts *(ebp+0xc))
 6198     # . save registers
 6199     50/push-eax
 6200     51/push-ecx
 6201     52/push-edx
 6202     57/push-edi
 6203     # edi = out
 6204     8b/-> *(ebp+8) 7/r32/edi
 6205     # ecx = f
 6206     8b/-> *(ebp+0xc) 1/r32/ecx
 6207     # var vars/edx: (stack (addr var) 256)
 6208     81 5/subop/subtract %esp 0x400/imm32
 6209     68/push 0x400/imm32/length
 6210     68/push 0/imm32/top
 6211     89/<- %edx 4/r32/esp
 6212     #
 6213     (write-buffered %edi *ecx)
 6214     (write-buffered %edi ":\n")
 6215     # initialize some global state
 6216     c7 0/subop/copy *Curr-block-depth 1/imm32
 6217     c7 0/subop/copy *Curr-local-stack-offset 0/imm32
 6218     #
 6219     (emit-subx-prologue %edi)
 6220     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
 6221     (emit-subx-epilogue %edi)
 6222     # TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
 6223     # been cleaned up
 6224 $emit-subx-function:end:
 6225     # . reclaim locals
 6226     81 0/subop/add %esp 408/imm32
 6227     # . restore registers
 6228     5f/pop-to-edi
 6229     5a/pop-to-edx
 6230     59/pop-to-ecx
 6231     58/pop-to-eax
 6232     # . epilogue
 6233     89/<- %esp 5/r32/ebp
 6234     5d/pop-to-ebp
 6235     c3/return
 6236 
 6237 populate-mu-type-offsets-in-inouts:  # f: (handle function)
 6238     # . prologue
 6239     55/push-ebp
 6240     89/<- %ebp 4/r32/esp
 6241     # . save registers
 6242     50/push-eax
 6243     51/push-ecx
 6244     52/push-edx
 6245     53/push-ebx
 6246     57/push-edi
 6247     # var next-offset/edx: int = 8
 6248     ba/copy-to-edx 8/imm32
 6249     # var curr/ecx: (handle list var) = f->inouts
 6250     8b/-> *(ebp+8) 1/r32/ecx
 6251     8b/-> *(ecx+8) 1/r32/ecx  # Function-inouts
 6252     {
 6253 $populate-mu-type-offsets-in-inouts:loop:
 6254       81 7/subop/compare %ecx 0/imm32
 6255       74/jump-if-= break/disp8
 6256       # var v/ebx: (handle var) = curr->value
 6257       8b/-> *ecx 3/r32/ebx  # List-value
 6258       # v->offset = next-offset
 6259       89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
 6260       # next-offset += size-of(v)
 6261       (size-of %ebx)  # => eax
 6262       01/add %edx 0/r32/eax
 6263       # curr = curr->next
 6264       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 6265       eb/jump loop/disp8
 6266     }
 6267 $populate-mu-type-offsets-in-inouts:end:
 6268     # . restore registers
 6269     5f/pop-to-edi
 6270     5b/pop-to-ebx
 6271     5a/pop-to-edx
 6272     59/pop-to-ecx
 6273     58/pop-to-eax
 6274     # . epilogue
 6275     89/<- %esp 5/r32/ebp
 6276     5d/pop-to-ebp
 6277     c3/return
 6278 
 6279 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
 6280     # . prologue
 6281     55/push-ebp
 6282     89/<- %ebp 4/r32/esp
 6283     # . save registers
 6284     50/push-eax
 6285     51/push-ecx
 6286     52/push-edx
 6287     53/push-ebx
 6288     56/push-esi
 6289     # esi = stmts
 6290     8b/-> *(ebp+0xc) 6/r32/esi
 6291     # var var-seen?/edx: boolean <- copy false
 6292     ba/copy-to-edx 0/imm32/false
 6293     #
 6294     {
 6295 $emit-subx-stmt-list:loop:
 6296       81 7/subop/compare %esi 0/imm32
 6297       0f 84/jump-if-= break/disp32
 6298       # var curr-stmt/ecx = stmts->value
 6299       8b/-> *esi 1/r32/ecx  # List-value
 6300       {
 6301 $emit-subx-stmt-list:check-for-block:
 6302         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
 6303         75/jump-if-!= break/disp8
 6304 $emit-subx-stmt-list:block:
 6305         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
 6306       }
 6307       {
 6308 $emit-subx-stmt-list:check-for-stmt:
 6309         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
 6310         0f 85/jump-if-!= break/disp32
 6311 $emit-subx-stmt-list:stmt1:
 6312         {
 6313           (is-mu-branch? %ecx)  # => eax
 6314           3d/compare-eax-and 0/imm32/false
 6315           0f 84/jump-if-= break/disp32
 6316 $emit-subx-stmt-list:branch-stmt:
 6317           # if !var-seen? break
 6318           81 7/subop/compare %edx 0/imm32/false
 6319           0f 84/jump-if-= break/disp32
 6320 $emit-subx-stmt-list:branch-stmt-and-var-seen:
 6321 +-- 26 lines: # unconditional loops -----------------------------------------------------------------------------------------------------------------------------------------------------
 6347 +-- 15 lines: # unconditional breaks ----------------------------------------------------------------------------------------------------------------------------------------------------
 6362 +-- 37 lines: # simple conditional branches without a target ----------------------------------------------------------------------------------------------------------------------------
 6399 +-- 19 lines: # conditional branches with an explicit target ----------------------------------------------------------------------------------------------------------------------------
 6418         }
 6419 $emit-subx-stmt-list:1-to-1:
 6420         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
 6421       }
 6422       {
 6423 $emit-subx-stmt-list:check-for-var-def:
 6424         81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
 6425         75/jump-if-!= break/disp8
 6426 $emit-subx-stmt-list:var-def:
 6427         (emit-subx-var-def *(ebp+8) %ecx)
 6428         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
 6429         # var-seen? = true
 6430         ba/copy-to-edx 1/imm32/true
 6431       }
 6432       {
 6433 $emit-subx-stmt-list:check-for-reg-var-def:
 6434         81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
 6435         0f 85/jump-if-!= break/disp32
 6436 $emit-subx-stmt-list:reg-var-def:
 6437         # TODO: ensure that there's exactly one output
 6438         (compute-reg-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10))
 6439         # register variable definition
 6440         (push *(ebp+0x10) %eax)
 6441         # emit the instruction as usual
 6442         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
 6443         # var-seen? = true
 6444         ba/copy-to-edx 1/imm32/true
 6445       }
 6446 $emit-subx-stmt-list:continue:
 6447       # TODO: raise an error on unrecognized Stmt-tag
 6448       8b/-> *(esi+4) 6/r32/esi  # List-next
 6449       e9/jump loop/disp32
 6450     }
 6451 $emit-subx-stmt-list:emit-cleanup:
 6452     (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
 6453 $emit-subx-stmt-list:cleanup:
 6454     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
 6455 $emit-subx-stmt-list:end:
 6456     # . restore registers
 6457     5e/pop-to-esi
 6458     5b/pop-to-ebx
 6459     5a/pop-to-edx
 6460     59/pop-to-ecx
 6461     58/pop-to-eax
 6462     # . epilogue
 6463     89/<- %esp 5/r32/ebp
 6464     5d/pop-to-ebp
 6465     c3/return
 6466 
 6467 compute-reg-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (handle reg-var-def), vars: (addr stack (handle var)) -> result/eax: (handle var)
 6468     # . prologue
 6469     55/push-ebp
 6470     89/<- %ebp 4/r32/esp
 6471     # . save registers
 6472     51/push-ecx
 6473     # ecx = stmt
 6474     8b/-> *(ebp+0xc) 1/r32/ecx
 6475     # var output/ecx: (handle var) = curr-stmt->outputs->value
 6476     8b/-> *(ecx+0xc) 1/r32/ecx  # Regvardef-inouts
 6477     8b/-> *ecx 1/r32/ecx  # List-value
 6478     # v->block-depth = *Curr-block-depth
 6479     8b/-> *Curr-block-depth 0/r32/eax
 6480     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 6481     # var reg/eax: (handle array byte) = output->register
 6482     8b/-> *(ecx+0x10) 0/r32/eax  # Var-register
 6483     # ensure that output is in a register
 6484     3d/compare-eax-and 0/imm32
 6485     0f 84/jump-if-= $compute-reg-and-maybe-emit-spill:abort/disp32
 6486     # if already-spilled-this-block?(reg, vars) return
 6487     (already-spilled-this-block? %ecx *(ebp+0x10))  # => eax
 6488     3d/compare-eax-and 0/imm32/false
 6489     75/jump-if-!= $compute-reg-and-maybe-emit-spill:end/disp8
 6490     # TODO: assert(sizeof(output) == 4)
 6491     # *Curr-local-stack-offset -= 4
 6492     81 5/subop/subtract *Curr-local-stack-offset 4/imm32
 6493     # emit spill
 6494     (emit-indent *(ebp+8) *Curr-block-depth)
 6495     (write-buffered *(ebp+8) "ff 6/subop/push %")
 6496     (write-buffered *(ebp+8) *(ecx+0x10))  # Var-register
 6497     (write-buffered *(ebp+8) Newline)
 6498 $compute-reg-and-maybe-emit-spill:end:
 6499     # return output
 6500     89/<- %eax 1/r32/ecx
 6501     # . restore registers
 6502     59/pop-to-ecx
 6503     # . epilogue
 6504     89/<- %esp 5/r32/ebp
 6505     5d/pop-to-ebp
 6506     c3/return
 6507 
 6508 $compute-reg-and-maybe-emit-spill:abort:
 6509     # error("var '" var->name "' initialized from an instruction must live in a register\n")
 6510     (write-buffered Stderr "var '")
 6511     (write-buffered Stderr *eax)  # Var-name
 6512     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
 6513     (flush Stderr)
 6514     # . syscall(exit, 1)
 6515     bb/copy-to-ebx  1/imm32
 6516     b8/copy-to-eax  1/imm32/exit
 6517     cd/syscall  0x80/imm8
 6518     # never gets here
 6519 
 6520 emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
 6521     # . prologue
 6522     55/push-ebp
 6523     89/<- %ebp 4/r32/esp
 6524     # . save registers
 6525     50/push-eax
 6526     51/push-ecx
 6527     52/push-edx
 6528     # ecx = stmt
 6529     8b/-> *(ebp+0xc) 1/r32/ecx
 6530     # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
 6531     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 6532     8b/-> *edx 2/r32/edx  # Stmt-var-value
 6533     8b/-> *edx 2/r32/edx  # Var-name
 6534     # clean up until target block
 6535     (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
 6536     # emit jump to target block
 6537     (emit-indent *(ebp+8) *Curr-block-depth)
 6538     (write-buffered *(ebp+8) "e9/jump ")
 6539     (write-buffered *(ebp+8) %edx)
 6540     (string-starts-with? *(ecx+4) "break")
 6541     3d/compare-eax-and 0/imm32/false
 6542     {
 6543       74/jump-if-= break/disp8
 6544       (write-buffered *(ebp+8) ":break/disp32\n")
 6545     }
 6546     3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
 6547     {
 6548       75/jump-if-!= break/disp8
 6549       (write-buffered *(ebp+8) ":loop/disp32\n")
 6550     }
 6551 $emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
 6552     # . restore registers
 6553     5a/pop-to-edx
 6554     59/pop-to-ecx
 6555     58/pop-to-eax
 6556     # . epilogue
 6557     89/<- %esp 5/r32/ebp
 6558     5d/pop-to-ebp
 6559     c3/return
 6560 
 6561 is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
 6562     # . prologue
 6563     55/push-ebp
 6564     89/<- %ebp 4/r32/esp
 6565     # . save registers
 6566     51/push-ecx
 6567     # ecx = stmt
 6568     8b/-> *(ebp+8) 1/r32/ecx
 6569     # if (stmt->operation starts with "loop") return true
 6570     (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
 6571     3d/compare-eax-and 0/imm32/false
 6572     75/jump-if-not-equal $is-mu-branch?:end/disp8
 6573     # otherwise return (stmt->operation starts with "break")
 6574     (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
 6575 $is-mu-branch?:end:
 6576     # . restore registers
 6577     59/pop-to-ecx
 6578     # . epilogue
 6579     89/<- %esp 5/r32/ebp
 6580     5d/pop-to-ebp
 6581     c3/return
 6582 
 6583 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
 6584     # . prologue
 6585     55/push-ebp
 6586     89/<- %ebp 4/r32/esp
 6587     # . save registers
 6588     50/push-eax
 6589     # eax = stmt
 6590     8b/-> *(ebp+0xc) 0/r32/eax
 6591     #
 6592     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
 6593     (emit-indent *(ebp+8) *Curr-block-depth)
 6594     (write-buffered *(ebp+8) *eax)
 6595     (write-buffered *(ebp+8) " break/disp32\n")
 6596 $emit-reverse-break:end:
 6597     # . restore registers
 6598     58/pop-to-eax
 6599     # . epilogue
 6600     89/<- %esp 5/r32/ebp
 6601     5d/pop-to-ebp
 6602     c3/return
 6603 
 6604 == data
 6605 
 6606 Reverse-branch:  # (table string string)
 6607   # a table is a stream
 6608   0xa0/imm32/write
 6609   0/imm32/read
 6610   0xa0/imm32/length
 6611   # data
 6612   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
 6613   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
 6614   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
 6615   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
 6616   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
 6617   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
 6618   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
 6619   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
 6620   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
 6621   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
 6622   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
 6623   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
 6624   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
 6625   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
 6626   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
 6627   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
 6628   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
 6629   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
 6630   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
 6631   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
 6632 
 6633 == code
 6634 
 6635 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
 6636     # . prologue
 6637     55/push-ebp
 6638     89/<- %ebp 4/r32/esp
 6639     # . save registers
 6640     50/push-eax
 6641     51/push-ecx
 6642     52/push-edx
 6643     53/push-ebx
 6644     # ecx = vars
 6645     8b/-> *(ebp+0xc) 1/r32/ecx
 6646     # var eax: int = vars->top
 6647     8b/-> *ecx 0/r32/eax
 6648     # var min/ecx: (address (handle var)) = vars->data
 6649     81 0/subop/add %ecx 8/imm32
 6650     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 6651     81 5/subop/subtract %eax 4/imm32
 6652     8d/copy-address *(ecx+eax) 0/r32/eax
 6653     # edx = depth
 6654     8b/-> *(ebp+0x10) 2/r32/edx
 6655     {
 6656 $emit-unconditional-jump-to-depth:loop:
 6657       # if (curr < min) break
 6658       39/compare %eax 1/r32/ecx
 6659       0f 82/jump-if-addr< break/disp32
 6660       # var v/ebx: (handle var) = *curr
 6661       8b/-> *eax 3/r32/ebx
 6662       # if (v->block-depth < until-block-depth) break
 6663       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 6664       0f 8c/jump-if-< break/disp32
 6665       {
 6666 $emit-unconditional-jump-to-depth:check:
 6667         # if v->block-depth != until-block-depth, continue
 6668         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 6669         0f 85/jump-if-!= break/disp32
 6670 $emit-unconditional-jump-to-depth:depth-found:
 6671         # if v is not a literal, continue
 6672         # . var eax: int = size-of(v)
 6673         50/push-eax
 6674         (size-of %ebx)  # => eax
 6675         # . if (eax != 0) continue
 6676         3d/compare-eax-and 0/imm32
 6677         58/pop-to-eax
 6678         #
 6679         0f 85/jump-if-!= break/disp32
 6680 $emit-unconditional-jump-to-depth:label-found:
 6681         # emit unconditional jump, then return
 6682         (emit-indent *(ebp+8) *Curr-block-depth)
 6683         (write-buffered *(ebp+8) "e9/jump ")
 6684         (write-buffered *(ebp+8) *ebx)  # Var-name
 6685         (write-buffered *(ebp+8) ":")
 6686         (write-buffered *(ebp+8) *(ebp+0x14))
 6687         (write-buffered *(ebp+8) "/disp32\n")
 6688         eb/jump $emit-unconditional-jump-to-depth:end/disp8
 6689       }
 6690       # curr -= 4
 6691       2d/subtract-from-eax 4/imm32
 6692       e9/jump loop/disp32
 6693     }
 6694     # TODO: error if no label at 'depth' was found
 6695 $emit-unconditional-jump-to-depth:end:
 6696     # . restore registers
 6697     5b/pop-to-ebx
 6698     5a/pop-to-edx
 6699     59/pop-to-ecx
 6700     58/pop-to-eax
 6701     # . epilogue
 6702     89/<- %esp 5/r32/ebp
 6703     5d/pop-to-ebp
 6704     c3/return
 6705 
 6706 # emit clean-up code for 'vars' until some block depth
 6707 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 6708 emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
 6709     # . prologue
 6710     55/push-ebp
 6711     89/<- %ebp 4/r32/esp
 6712     # . save registers
 6713     50/push-eax
 6714     51/push-ecx
 6715     52/push-edx
 6716     53/push-ebx
 6717     # ecx = vars
 6718     8b/-> *(ebp+0xc) 1/r32/ecx
 6719     # var eax: int = vars->top
 6720     8b/-> *ecx 0/r32/eax
 6721     # var min/ecx: (address (handle var)) = vars->data
 6722     81 0/subop/add %ecx 8/imm32
 6723     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 6724     81 5/subop/subtract %eax 4/imm32
 6725     8d/copy-address *(ecx+eax) 0/r32/eax
 6726     # edx = until-block-depth
 6727     8b/-> *(ebp+0x10) 2/r32/edx
 6728     {
 6729 $emit-cleanup-code-until-depth:loop:
 6730       # if (curr < min) break
 6731       39/compare %eax 1/r32/ecx
 6732       0f 82/jump-if-addr< break/disp32
 6733       # var v/ebx: (handle var) = *curr
 6734       8b/-> *eax 3/r32/ebx
 6735       # if (v->block-depth < until-block-depth) break
 6736       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 6737       0f 8c/jump-if-< break/disp32
 6738       # if v is in a register
 6739       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 6740       {
 6741         0f 84/jump-if-= break/disp32
 6742         50/push-eax
 6743         {
 6744 $emit-cleanup-code-until-depth:check-for-previous-spill:
 6745           (same-register-spilled-before? %ebx *(ebp+0xc) %eax)  # => eax
 6746           3d/compare-eax-and 0/imm32/false
 6747           0f 85/jump-if-!= break/disp32
 6748 $emit-cleanup-code-until-depth:reclaim-var-in-register:
 6749           (emit-indent *(ebp+8) *Curr-block-depth)
 6750           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 6751           (write-buffered *(ebp+8) *(ebx+0x10))
 6752           (write-buffered *(ebp+8) Newline)
 6753         }
 6754         58/pop-to-eax
 6755         eb/jump $emit-cleanup-code-until-depth:continue/disp8
 6756       }
 6757       # otherwise v is on the stack
 6758       {
 6759         75/jump-if-!= break/disp8
 6760 $emit-cleanup-code-until-depth:reclaim-var-on-stack:
 6761         50/push-eax
 6762         (size-of %ebx)  # => eax
 6763         # don't emit code for labels
 6764         3d/compare-eax-and 0/imm32
 6765         74/jump-if-= break/disp8
 6766         #
 6767         (emit-indent *(ebp+8) *Curr-block-depth)
 6768         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 6769         (print-int32-buffered *(ebp+8) %eax)
 6770         (write-buffered *(ebp+8) "/imm32\n")
 6771         58/pop-to-eax
 6772       }
 6773 $emit-cleanup-code-until-depth:continue:
 6774       # curr -= 4
 6775       2d/subtract-from-eax 4/imm32
 6776       e9/jump loop/disp32
 6777     }
 6778 $emit-cleanup-code-until-depth:end:
 6779     # . restore registers
 6780     5b/pop-to-ebx
 6781     5a/pop-to-edx
 6782     59/pop-to-ecx
 6783     58/pop-to-eax
 6784     # . epilogue
 6785     89/<- %esp 5/r32/ebp
 6786     5d/pop-to-ebp
 6787     c3/return
 6788 
 6789 # emit clean-up code for 'vars' until a given label is encountered
 6790 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 6791 emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
 6792     # . prologue
 6793     55/push-ebp
 6794     89/<- %ebp 4/r32/esp
 6795     # . save registers
 6796     50/push-eax
 6797     51/push-ecx
 6798     52/push-edx
 6799     53/push-ebx
 6800     # ecx = vars
 6801     8b/-> *(ebp+0xc) 1/r32/ecx
 6802     # var eax: int = vars->top
 6803     8b/-> *ecx 0/r32/eax
 6804     # var min/ecx: (address (handle var)) = vars->data
 6805     81 0/subop/add %ecx 8/imm32
 6806     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 6807     81 5/subop/subtract %eax 4/imm32
 6808     8d/copy-address *(ecx+eax) 2/r32/edx
 6809     {
 6810 $emit-cleanup-code-until-target:loop:
 6811       # if (curr < min) break
 6812       39/compare %edx 1/r32/ecx
 6813       0f 82/jump-if-addr< break/disp32
 6814       # var v/ebx: (handle var) = *curr
 6815       8b/-> *edx 3/r32/ebx
 6816       # if (v->name == until-block-label) break
 6817       (string-equal? *ebx *(ebp+0x10))  # => eax
 6818       3d/compare-eax-and 0/imm32/false
 6819       0f 85/jump-if-!= break/disp32
 6820       # if v is in a register
 6821       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 6822       {
 6823         74/jump-if-= break/disp8
 6824         50/push-eax
 6825         {
 6826 $emit-cleanup-code-until-target:check-for-previous-spill:
 6827           (same-register-spilled-before? %ebx *(ebp+0xc) %edx)  # => eax
 6828           3d/compare-eax-and 0/imm32/false
 6829           75/jump-if-!= break/disp8
 6830 $emit-cleanup-code-until-target:reclaim-var-in-register:
 6831           (emit-indent *(ebp+8) *Curr-block-depth)
 6832           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 6833           (write-buffered *(ebp+8) *(ebx+0x10))
 6834           (write-buffered *(ebp+8) Newline)
 6835         }
 6836         58/pop-to-eax
 6837         eb/jump $emit-cleanup-code-until-target:continue/disp8
 6838       }
 6839       # otherwise v is on the stack
 6840       {
 6841         75/jump-if-!= break/disp8
 6842 $emit-cleanup-code-until-target:reclaim-var-on-stack:
 6843         (size-of %ebx)  # => eax
 6844         # don't emit code for labels
 6845         3d/compare-eax-and 0/imm32
 6846         74/jump-if-= break/disp8
 6847         #
 6848         (emit-indent *(ebp+8) *Curr-block-depth)
 6849         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 6850         (print-int32-buffered *(ebp+8) %eax)
 6851         (write-buffered *(ebp+8) "/imm32\n")
 6852       }
 6853 $emit-cleanup-code-until-target:continue:
 6854       # curr -= 4
 6855       81 5/subop/subtract %edx 4/imm32
 6856       e9/jump loop/disp32
 6857     }
 6858 $emit-cleanup-code-until-target:end:
 6859     # . restore registers
 6860     5b/pop-to-ebx
 6861     5a/pop-to-edx
 6862     59/pop-to-ecx
 6863     58/pop-to-eax
 6864     # . epilogue
 6865     89/<- %esp 5/r32/ebp
 6866     5d/pop-to-ebp
 6867     c3/return
 6868 
 6869 # is there already a var with the same block-depth and register as 'v' on the 'vars' stack?
 6870 # v is guaranteed not to be within vars
 6871 already-spilled-this-block?:  # v: (handle var), vars: (addr stack (handle var)) -> result/eax: boolean
 6872     # . prologue
 6873     55/push-ebp
 6874     89/<- %ebp 4/r32/esp
 6875     # . save registers
 6876     51/push-ecx
 6877     52/push-edx
 6878     53/push-ebx
 6879     56/push-esi
 6880     57/push-edi
 6881     # ecx = vars
 6882     8b/-> *(ebp+0xc) 1/r32/ecx
 6883     # var eax: int = vars->top
 6884     8b/-> *ecx 0/r32/eax
 6885     # var min/ecx: (address (handle var)) = vars->data
 6886     81 0/subop/add %ecx 8/imm32
 6887     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 6888     81 5/subop/subtract %eax 4/imm32
 6889     8d/copy-address *(ecx+eax) 2/r32/edx
 6890     # var depth/ebx: int = v->block-depth
 6891     8b/-> *(ebp+8) 3/r32/ebx
 6892     8b/-> *(ebx+8) 3/r32/ebx  # Var-block-depth
 6893     # var needle/esi: (handle array byte) = v->register
 6894     8b/-> *(ebp+8) 6/r32/esi
 6895     8b/-> *(esi+0x10) 6/r32/esi  # Var-register
 6896     {
 6897 $already-spilled-this-block?:loop:
 6898       # if (curr < min) break
 6899       39/compare %edx 1/r32/ecx
 6900       0f 82/jump-if-addr< break/disp32
 6901       # var cand/edi: (handle var) = *curr
 6902       8b/-> *edx 7/r32/edi
 6903       # if (cand->block-depth < depth) break
 6904       39/compare *(edi+8) 3/r32/ebx  # Var-block-depth
 6905       0f 8c/jump-if-< break/disp32
 6906       # var cand-reg/edi: (handle array byte) = cand->reg
 6907       8b/-> *(edi+0x10) 7/r32/edi
 6908       # if (cand-reg == null) continue
 6909       {
 6910 $already-spilled-this-block?:check-reg:
 6911         81 7/subop/compare %edi 0/imm32
 6912         74/jump-if-= break/disp8
 6913         # if (cand-reg == needle) return true
 6914         (string-equal? %esi %edi)  # => eax
 6915         3d/compare-eax-and 0/imm32/false
 6916         74/jump-if-= break/disp8
 6917         b8/copy-to-eax 1/imm32/true
 6918         eb/jump $already-spilled-this-block?:end/disp8
 6919       }
 6920 $already-spilled-this-block?:continue:
 6921       # curr -= 4
 6922       81 5/subop/subtract %edx 4/imm32
 6923       e9/jump loop/disp32
 6924     }
 6925     # return false
 6926     b8/copy-to-eax 0/imm32/false
 6927 $already-spilled-this-block?:end:
 6928     # . restore registers
 6929     5f/pop-to-edi
 6930     5e/pop-to-esi
 6931     5b/pop-to-ebx
 6932     5a/pop-to-edx
 6933     59/pop-to-ecx
 6934     # . epilogue
 6935     89/<- %esp 5/r32/ebp
 6936     5d/pop-to-ebp
 6937     c3/return
 6938 
 6939 # is there a var before 'v' with the same block-depth and register on the 'vars' stack?
 6940 # v is guaranteed to be within vars
 6941 # 'start' is provided as an optimization, a pointer within vars
 6942 # *start == v
 6943 same-register-spilled-before?:  # v: (handle var), vars: (addr stack (handle var)), start: (addr (handle var)) -> result/eax: boolean
 6944     # . prologue
 6945     55/push-ebp
 6946     89/<- %ebp 4/r32/esp
 6947     # . save registers
 6948     51/push-ecx
 6949     52/push-edx
 6950     53/push-ebx
 6951     56/push-esi
 6952     57/push-edi
 6953     # ecx = v
 6954     8b/-> *(ebp+8) 1/r32/ecx
 6955     # var reg/edx: (handle array byte) = v->register
 6956     8b/-> *(ecx+0x10) 2/r32/edx  # Var-register
 6957     # var depth/ebx: int = v->block-depth
 6958     8b/-> *(ecx+8) 3/r32/ebx  # Var-block-depth
 6959     # var min/ecx: (address (handle var)) = vars->data
 6960     8b/-> *(ebp+0xc) 1/r32/ecx
 6961     81 0/subop/add %ecx 8/imm32
 6962     # TODO: check that start >= min and start < &vars->data[top]
 6963     # TODO: check that *start == v
 6964     # var curr/esi: (address (handle var)) = start
 6965     8b/-> *(ebp+0x10) 6/r32/esi
 6966     # curr -= 4
 6967     81 5/subop/subtract %esi 4/imm32
 6968     {
 6969 $same-register-spilled-before?:loop:
 6970       # if (curr < min) break
 6971       39/compare %esi 1/r32/ecx
 6972       0f 82/jump-if-addr< break/disp32
 6973       # var x/eax: (handle var) = *curr
 6974       8b/-> *esi 0/r32/eax
 6975       # if (x->block-depth < depth) break
 6976       39/compare *(eax+8) 3/r32/ebx  # Var-block-depth
 6977       0f 8c/jump-if-< break/disp32
 6978       # if (x->register == 0) continue
 6979       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 6980       74/jump-if-= $same-register-spilled-before?:continue/disp8
 6981       # if (x->register == reg) return true
 6982       (string-equal? *(eax+0x10) %edx)  # Var-register => eax
 6983       3d/compare-eax-and 0/imm32/false
 6984       75/jump-if-!= $same-register-spilled-before?:end/disp8
 6985 $same-register-spilled-before?:continue:
 6986       # curr -= 4
 6987       81 5/subop/subtract %esi 4/imm32
 6988       e9/jump loop/disp32
 6989     }
 6990 $same-register-spilled-before?:false:
 6991     b8/copy-to-eax 0/imm32/false
 6992 $same-register-spilled-before?:end:
 6993     # . restore registers
 6994     5f/pop-to-edi
 6995     5e/pop-to-esi
 6996     5b/pop-to-ebx
 6997     5a/pop-to-edx
 6998     59/pop-to-ecx
 6999     # . epilogue
 7000     89/<- %esp 5/r32/ebp
 7001     5d/pop-to-ebp
 7002     c3/return
 7003 
 7004 # clean up global state for 'vars' until some block depth
 7005 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
 7006     # . prologue
 7007     55/push-ebp
 7008     89/<- %ebp 4/r32/esp
 7009     # . save registers
 7010     50/push-eax
 7011     51/push-ecx
 7012     56/push-esi
 7013     # esi = vars
 7014     8b/-> *(ebp+8) 6/r32/esi
 7015     # ecx = until-block-depth
 7016     8b/-> *(ebp+0xc) 1/r32/ecx
 7017     {
 7018 $clean-up-blocks:reclaim-loop:
 7019       # if (vars->top <= 0) break
 7020       81 7/subop/compare *esi 0/imm32  # Stack-top
 7021       7e/jump-if-<= break/disp8
 7022       # var v/eax: (handle var) = top(vars)
 7023       (top %esi)  # => eax
 7024       # if (v->block-depth < until-block-depth) break
 7025       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
 7026       7c/jump-if-< break/disp8
 7027       # if v is on the stack, update Curr-local-stack-offset
 7028       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7029       {
 7030         75/jump-if-!= break/disp8
 7031 $clean-up-blocks:reclaim-var-on-stack:
 7032         (size-of %eax)  # => eax
 7033         01/add *Curr-local-stack-offset 0/r32/eax
 7034       }
 7035       (pop %esi)
 7036       e9/jump loop/disp32
 7037     }
 7038 $clean-up-blocks:end:
 7039     # . restore registers
 7040     5e/pop-to-esi
 7041     59/pop-to-ecx
 7042     58/pop-to-eax
 7043     # . epilogue
 7044     89/<- %esp 5/r32/ebp
 7045     5d/pop-to-ebp
 7046     c3/return
 7047 
 7048 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle stmt)
 7049     # . prologue
 7050     55/push-ebp
 7051     89/<- %ebp 4/r32/esp
 7052     # . save registers
 7053     50/push-eax
 7054     51/push-ecx
 7055     52/push-edx
 7056     # eax = stmt
 7057     8b/-> *(ebp+0xc) 0/r32/eax
 7058     # var v/ecx: (handle var)
 7059     8b/-> *(eax+4) 1/r32/ecx
 7060     # v->block-depth = *Curr-block-depth
 7061     8b/-> *Curr-block-depth 0/r32/eax
 7062     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 7063     # var n/eax: int = size-of(stmt->var)
 7064     (size-of %ecx)  # Vardef-var => eax
 7065     # *Curr-local-stack-offset -= n
 7066     29/subtract-from *Curr-local-stack-offset 0/r32/eax
 7067     # v->offset = *Curr-local-stack-offset
 7068     8b/-> *Curr-local-stack-offset 2/r32/edx
 7069     89/<- *(ecx+0xc) 2/r32/edx  # Var-offset
 7070     # while n > 0
 7071     {
 7072       3d/compare-eax-with 0/imm32
 7073       7e/jump-if-<= break/disp8
 7074       (emit-indent *(ebp+8) *Curr-block-depth)
 7075       (write-buffered *(ebp+8) "68/push 0/imm32\n")
 7076       # n -= 4
 7077       2d/subtract-from-eax 4/imm32
 7078       #
 7079       eb/jump loop/disp8
 7080     }
 7081 $emit-subx-var-def:end:
 7082     # . restore registers
 7083     5a/pop-to-edx
 7084     59/pop-to-ecx
 7085     58/pop-to-eax
 7086     # . epilogue
 7087     89/<- %esp 5/r32/ebp
 7088     5d/pop-to-ebp
 7089     c3/return
 7090 
 7091 emit-subx-stmt:  # out: (addr buffered-file), stmt: (handle stmt), primitives: (handle primitive), functions: (handle function)
 7092     # . prologue
 7093     55/push-ebp
 7094     89/<- %ebp 4/r32/esp
 7095     # . save registers
 7096     50/push-eax
 7097     51/push-ecx
 7098     # - some special-case primitives that don't actually use the 'primitives' data structure
 7099     # ecx = stmt
 7100     8b/-> *(ebp+0xc) 1/r32/ecx
 7101     # array length
 7102     {
 7103       # if (!string-equal?(stmt->operation, "length")) break
 7104       (string-equal? *(ecx+4) "length")  # Stmt1-operation => eax
 7105       3d/compare-eax-and 0/imm32
 7106       0f 84/jump-if-= break/disp32
 7107       (translate-mu-length-stmt *(ebp+8) *(ebp+0xc))
 7108       e9/jump $emit-subx-stmt:end/disp32
 7109     }
 7110     # index into array
 7111     {
 7112       # if (!string-equal?(var->operation, "index")) break
 7113       (string-equal? *(ecx+4) "index")  # Stmt1-operation => eax
 7114       3d/compare-eax-and 0/imm32
 7115       0f 84/jump-if-= break/disp32
 7116       (translate-mu-index-stmt *(ebp+8) *(ebp+0xc))
 7117       e9/jump $emit-subx-stmt:end/disp32
 7118     }
 7119     # compute-offset for index into array
 7120     {
 7121       # if (!string-equal?(var->operation, "compute-offset")) break
 7122       (string-equal? *(ecx+4) "compute-offset")  # Stmt1-operation => eax
 7123       3d/compare-eax-and 0/imm32
 7124       0f 84/jump-if-= break/disp32
 7125       (translate-mu-compute-index-stmt *(ebp+8) *(ebp+0xc))
 7126       e9/jump $emit-subx-stmt:end/disp32
 7127     }
 7128     # get field from record
 7129     {
 7130       # if (!string-equal?(var->operation, "get")) break
 7131       (string-equal? *(ecx+4) "get")  # Stmt1-operation => eax
 7132       3d/compare-eax-and 0/imm32
 7133       0f 84/jump-if-= break/disp32
 7134       (translate-mu-get-stmt *(ebp+8) *(ebp+0xc))
 7135       e9/jump $emit-subx-stmt:end/disp32
 7136     }
 7137     # - if stmt matches a primitive, emit it
 7138     {
 7139 $emit-subx-stmt:check-for-primitive:
 7140       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
 7141       3d/compare-eax-and 0/imm32
 7142       74/jump-if-= break/disp8
 7143 $emit-subx-stmt:primitive:
 7144       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7145       e9/jump $emit-subx-stmt:end/disp32
 7146     }
 7147     # - if stmt matches a function, emit a call to it
 7148     {
 7149 $emit-subx-stmt:check-for-call:
 7150       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
 7151       3d/compare-eax-and 0/imm32
 7152       74/jump-if-= break/disp8
 7153 $emit-subx-stmt:call:
 7154       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7155       e9/jump $emit-subx-stmt:end/disp32
 7156     }
 7157     # otherwise, assume it's a SubX function (TODO: how to type-check?!)
 7158     (emit-hailmary-call *(ebp+8) *(ebp+0xc))
 7159 $emit-subx-stmt:end:
 7160     # . restore registers
 7161     59/pop-to-ecx
 7162     58/pop-to-eax
 7163     # . epilogue
 7164     89/<- %esp 5/r32/ebp
 7165     5d/pop-to-ebp
 7166     c3/return
 7167 
 7168 translate-mu-length-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7169     # . prologue
 7170     55/push-ebp
 7171     89/<- %ebp 4/r32/esp
 7172     # . save registers
 7173     50/push-eax
 7174     51/push-ecx
 7175     # ecx = stmt
 7176     8b/-> *(ebp+0xc) 1/r32/ecx
 7177     #
 7178     (emit-indent *(ebp+8) *Curr-block-depth)
 7179     (write-buffered *(ebp+8) "8b/copy-from *")
 7180     # inouts[0]->register
 7181     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 7182     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7183     (write-buffered *(ebp+8) *(eax+0x10))  # Var-register => eax
 7184     #
 7185     (write-buffered *(ebp+8) " ")
 7186     # outputs[0] "/r32"
 7187     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7188     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7189     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7190     (print-int32-buffered *(ebp+8) *eax)
 7191     (write-buffered *(ebp+8) "/r32\n")
 7192 $translate-mu-length-stmt:end:
 7193     # . restore registers
 7194     59/pop-to-ecx
 7195     58/pop-to-eax
 7196     # . epilogue
 7197     89/<- %esp 5/r32/ebp
 7198     5d/pop-to-ebp
 7199     c3/return
 7200 
 7201 translate-mu-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7202     # . prologue
 7203     55/push-ebp
 7204     89/<- %ebp 4/r32/esp
 7205     # . save registers
 7206     50/push-eax
 7207     51/push-ecx
 7208     52/push-edx
 7209     53/push-ebx
 7210     #
 7211     (emit-indent *(ebp+8) *Curr-block-depth)
 7212     (write-buffered *(ebp+8) "8d/copy-address *(")
 7213     # TODO: ensure inouts[0] is in a register and not dereferenced
 7214 $translate-mu-index-stmt:emit-base:
 7215     # ecx = stmt
 7216     8b/-> *(ebp+0xc) 1/r32/ecx
 7217     # var base/ebx: (handle var) = inouts[0]
 7218     8b/-> *(ecx+8) 3/r32/ebx  # Stmt1-inouts
 7219     8b/-> *ebx 3/r32/ebx  # Stmt-var-value
 7220     # print base->register " + "
 7221     (write-buffered *(ebp+8) *(ebx+0x10))  # Var-register
 7222     #
 7223     (write-buffered *(ebp+8) " + ")
 7224     # var idx/edx: (handle var) = inouts[1]
 7225     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7226     8b/-> *(edx+4) 2/r32/edx  # Stmt-var-next
 7227     8b/-> *edx 2/r32/edx  # Stmt-var-value
 7228     # if inouts[1]->register
 7229     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 7230     {
 7231       0f 84/jump-if-= break/disp32
 7232 $translate-mu-index-stmt:emit-register-index:
 7233       # if inouts[1] is an int
 7234       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 7235       3d/compare-eax-and 0/imm32/false
 7236       {
 7237         0f 84/jump-if-= break/disp32
 7238 $translate-mu-index-stmt:emit-int-register-index:
 7239         # print inouts[1]->register "<<" log2(sizeof(element(inouts[0]->type))) " + 4) "
 7240         # . inouts[1]->register "<<"
 7241         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7242         (write-buffered *(ebp+8) "<<")
 7243         # . log2(sizeof(element(inouts[0]->type)))
 7244         # TODO: ensure size is a power of 2
 7245         (array-element-type-id %ebx)  # => eax
 7246         (size-of-type-id %eax)  # => eax
 7247         (num-shift-rights %eax)  # => eax
 7248         (print-int32-buffered *(ebp+8) %eax)
 7249         e9/jump $translate-mu-index-stmt:emit-register-index-done/disp32
 7250       }
 7251       # if inouts[1]->type is any other atom, abort
 7252       8b/-> *(edx+4) 0/r32/eax  # Var-type
 7253       8b/-> *eax 0/r32/eax  # Tree-left or Atom-value
 7254       3b/compare 0/r32/eax *Max-type-id
 7255       0f 82/jump-if-addr< $translate-mu-index-stmt:error2/disp32
 7256       # if inouts[1] is (offset ...)
 7257       (is-simple-mu-type? %eax 7)  # offset => eax
 7258       3d/compare-eax-and 0/imm32/false
 7259       {
 7260         0f 84/jump-if-= break/disp32
 7261         # print inouts[1]->register "<<" log2(sizeof(element(inouts[0]->type))) " + 4) "
 7262 $translate-mu-index-stmt:emit-offset-register-index:
 7263         # . inouts[1]->register
 7264         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7265       }
 7266 $translate-mu-index-stmt:emit-register-index-done:
 7267       (write-buffered *(ebp+8) " + 4) ")
 7268       e9/jump $translate-mu-index-stmt:emit-output/disp32
 7269     }
 7270     # otherwise if inouts[1] is a literal
 7271     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 7272     3d/compare-eax-and 0/imm32/false
 7273     {
 7274       0f 84/jump-if-= break/disp32
 7275 $translate-mu-index-stmt:emit-literal-index:
 7276       # var idx-value/edx: int = parse-hex-int(inouts[1]->name)
 7277       (parse-hex-int *edx)  # Var-name => eax
 7278       89/<- %edx 0/r32/eax
 7279       # offset = n * sizeof(element(inouts[0]->type))
 7280       (array-element-type-id %ebx)  # => eax
 7281       (size-of-type-id %eax)  # => eax
 7282       f7 4/subop/multiply-into-eax %edx  # clobbers edx
 7283       # offset += 4 for array size
 7284       05/add-to-eax 4/imm32
 7285       # TODO: check edx for overflow
 7286       # print offset
 7287       (print-int32-buffered *(ebp+8) %eax)
 7288       (write-buffered *(ebp+8) ") ")
 7289       e9/jump $translate-mu-index-stmt:emit-output/disp32
 7290     }
 7291     # otherwise abort
 7292     e9/jump $translate-mu-index-stmt:error1/disp32
 7293 $translate-mu-index-stmt:emit-output:
 7294     # outputs[0] "/r32"
 7295     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7296     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7297     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7298     (print-int32-buffered *(ebp+8) *eax)
 7299     (write-buffered *(ebp+8) "/r32\n")
 7300 $translate-mu-index-stmt: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 $translate-mu-index-stmt:error1:
 7312     (write-buffered Stderr "couldn't translate an index instruction. second (index) input must either lie in a register or be a literal\n")
 7313     (flush Stderr)
 7314     # . syscall(exit, 1)
 7315     bb/copy-to-ebx  1/imm32
 7316     b8/copy-to-eax  1/imm32/exit
 7317     cd/syscall  0x80/imm8
 7318     # never gets here
 7319 
 7320 $translate-mu-index-stmt:error2:
 7321     (write-buffered Stderr "couldn't translate an index instruction. second (index) input when in a register must be an int or offset\n")
 7322     (flush Stderr)
 7323     # . syscall(exit, 1)
 7324     bb/copy-to-ebx  1/imm32
 7325     b8/copy-to-eax  1/imm32/exit
 7326     cd/syscall  0x80/imm8
 7327     # never gets here
 7328 
 7329 translate-mu-compute-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7330     # . prologue
 7331     55/push-ebp
 7332     89/<- %ebp 4/r32/esp
 7333     # . save registers
 7334     50/push-eax
 7335     51/push-ecx
 7336     52/push-edx
 7337     53/push-ebx
 7338     #
 7339     (emit-indent *(ebp+8) *Curr-block-depth)
 7340     (write-buffered *(ebp+8) "69/multiply ")
 7341 $translate-mu-compute-index-stmt:emit-elem-size:
 7342     # ecx = stmt
 7343     8b/-> *(ebp+0xc) 1/r32/ecx
 7344     # var first-inout/edx: (handle stmt-var) = stmt->inouts[0]
 7345     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7346     # var base/ebx: (handle var)
 7347     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 7348     # print sizeof(element(base->type))
 7349     (array-element-type-id %ebx)  # => eax
 7350     (size-of-type-id %eax)  # => eax
 7351     (print-int32-buffered *(ebp+8) %eax)
 7352     (write-buffered *(ebp+8) "/imm32")
 7353 $translate-mu-compute-index-stmt:emit-index:
 7354     (emit-subx-var-as-rm32 *(ebp+8) *(edx+4))  # Stmt-var-next
 7355     (write-buffered *(ebp+8) Space)
 7356 $translate-mu-compute-index-stmt:emit-output:
 7357     # outputs[0] "/r32"
 7358     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7359     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7360     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7361     (print-int32-buffered *(ebp+8) *eax)
 7362     (write-buffered *(ebp+8) "/r32\n")
 7363 $translate-mu-compute-index-stmt:end:
 7364     # . restore registers
 7365     5b/pop-to-ebx
 7366     5a/pop-to-edx
 7367     59/pop-to-ecx
 7368     58/pop-to-eax
 7369     # . epilogue
 7370     89/<- %esp 5/r32/ebp
 7371     5d/pop-to-ebp
 7372     c3/return
 7373 
 7374 translate-mu-get-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7375     # . prologue
 7376     55/push-ebp
 7377     89/<- %ebp 4/r32/esp
 7378     # . save registers
 7379     50/push-eax
 7380     51/push-ecx
 7381     52/push-edx
 7382     #
 7383     (emit-indent *(ebp+8) *Curr-block-depth)
 7384     (write-buffered *(ebp+8) "8d/copy-address ")
 7385     # ecx = stmt
 7386     8b/-> *(ebp+0xc) 1/r32/ecx
 7387     # var offset/edx: int = get offset of stmt
 7388     (mu-get-offset %ecx)  # => eax
 7389     89/<- %edx 0/r32/eax
 7390     # var base/eax: (handle var) = stmt->inouts[0]
 7391     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
 7392     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7393     # if base is in a register
 7394     81 7/subop/compare *(eax+0x10) 0/imm32
 7395     {
 7396       0f 84/jump-if-= break/disp32
 7397 $translate-mu-get-stmt:emit-register-input:
 7398       # "*(" inouts[0]->register " + " offset ")"
 7399       (write-buffered *(ebp+8) "*(")
 7400       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 7401       (write-buffered *(ebp+8) " + ")
 7402       (print-int32-buffered *(ebp+8) %edx)
 7403       (write-buffered *(ebp+8) ") ")
 7404       e9/jump $translate-mu-get-stmt:emit-output/disp32
 7405     }
 7406     # otherwise base is on the stack
 7407     {
 7408 $translate-mu-get-stmt:emit-stack-input:
 7409       # "*(ebp + " inouts[0]->offset + offset ")"
 7410       (write-buffered *(ebp+8) "*(ebp+")
 7411       03/add-from *(eax+0xc) 2/r32/edx  # Var-offset
 7412       (print-int32-buffered *(ebp+8) %edx)
 7413       (write-buffered *(ebp+8) ") ")
 7414       eb/jump $translate-mu-get-stmt:emit-output/disp8
 7415     }
 7416 $translate-mu-get-stmt:emit-output:
 7417     # outputs[0] "/r32"
 7418     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7419     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7420     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7421     (print-int32-buffered *(ebp+8) *eax)
 7422     (write-buffered *(ebp+8) "/r32\n")
 7423 $translate-mu-get-stmt:end:
 7424     # . restore registers
 7425     5a/pop-to-edx
 7426     59/pop-to-ecx
 7427     58/pop-to-eax
 7428     # . epilogue
 7429     89/<- %esp 5/r32/ebp
 7430     5d/pop-to-ebp
 7431     c3/return
 7432 
 7433 array-element-type-id:  # v: (handle var) -> result/eax: type-id
 7434     # precondition: n is positive
 7435     # . prologue
 7436     55/push-ebp
 7437     89/<- %ebp 4/r32/esp
 7438     #
 7439     8b/-> *(ebp+8) 0/r32/eax
 7440     8b/-> *(eax+4) 0/r32/eax  # Var-type
 7441     # TODO: ensure type->left is 'addr'
 7442     8b/-> *(eax+4) 0/r32/eax  # Tree-right
 7443     # TODO: ensure that type->right is non-null
 7444     # TODO: ensure that type->right->left is 'array'
 7445     8b/-> *(eax+4) 0/r32/eax  # Tree-right
 7446     # TODO: ensure that type->right->right is non-null
 7447     8b/-> *eax 0/r32/eax  # Tree-left
 7448     8b/-> *eax 0/r32/eax  # Atom-value
 7449 $array-element-type-id:end:
 7450     # . epilogue
 7451     89/<- %esp 5/r32/ebp
 7452     5d/pop-to-ebp
 7453     c3/return
 7454 
 7455 power-of-2?:  # n: int -> result/eax: boolean
 7456     # precondition: n is positive
 7457     # . prologue
 7458     55/push-ebp
 7459     89/<- %ebp 4/r32/esp
 7460     # var tmp/eax: int = n-1
 7461     8b/-> *(ebp+8) 0/r32/eax
 7462     48/decrement-eax
 7463     # var tmp2/eax: int = n & tmp
 7464     0b/and-> *(ebp+8) 0/r32/eax
 7465     # return (tmp2 == 0)
 7466     3d/compare-eax-and 0/imm32
 7467     0f 94/set-if-= %eax
 7468     81 4/subop/and %eax 0xff/imm32
 7469 $power-of-2?:end:
 7470     # . epilogue
 7471     89/<- %esp 5/r32/ebp
 7472     5d/pop-to-ebp
 7473     c3/return
 7474 
 7475 num-shift-rights:  # n: int -> result/eax: int
 7476     # precondition: n is a positive power of 2
 7477     # . prologue
 7478     55/push-ebp
 7479     89/<- %ebp 4/r32/esp
 7480     # . save registers
 7481     51/push-ecx
 7482     # var curr/ecx: int = n
 7483     8b/-> *(ebp+8) 1/r32/ecx
 7484     # result = 0
 7485     b8/copy-to-eax 0/imm32
 7486     {
 7487       # if (curr <= 1) break
 7488       81 7/subop/compare %ecx 1/imm32
 7489       7e/jump-if-<= break/disp8
 7490       40/increment-eax
 7491       c1/shift 5/subop/arithmetic-right %ecx 1/imm8
 7492       eb/jump loop/disp8
 7493     }
 7494 $num-shift-rights:end:
 7495     # . restore registers
 7496     59/pop-to-ecx
 7497     # . epilogue
 7498     89/<- %esp 5/r32/ebp
 7499     5d/pop-to-ebp
 7500     c3/return
 7501 
 7502 mu-get-offset:  # stmt: (handle stmt) -> result/eax: int
 7503     # . prologue
 7504     55/push-ebp
 7505     89/<- %ebp 4/r32/esp
 7506     # var second-inout/eax: (handle stmt-var) = stmt->inouts->next
 7507     8b/-> *(ebp+8) 0/r32/eax
 7508     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 7509     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 7510     # var output-var/eax: (handle var) = second-inout->value
 7511     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7512     # return output-var->offset
 7513     8b/-> *(eax+0xc) 0/r32/eax  # Var-offset
 7514 $emit-get-offset:end:
 7515     # . epilogue
 7516     89/<- %esp 5/r32/ebp
 7517     5d/pop-to-ebp
 7518     c3/return
 7519 
 7520 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
 7521     # . prologue
 7522     55/push-ebp
 7523     89/<- %ebp 4/r32/esp
 7524     # . save registers
 7525     50/push-eax
 7526     51/push-ecx
 7527     56/push-esi
 7528     # esi = block
 7529     8b/-> *(ebp+0xc) 6/r32/esi
 7530     # block->var->block-depth = *Curr-block-depth
 7531     8b/-> *(esi+8) 0/r32/eax  # Block-var
 7532     8b/-> *Curr-block-depth 1/r32/ecx
 7533     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
 7534     # var stmts/eax: (handle list stmt) = block->statements
 7535     8b/-> *(esi+4) 0/r32/eax  # Block-stmts
 7536     #
 7537     {
 7538 $emit-subx-block:check-empty:
 7539       3d/compare-eax-and 0/imm32
 7540       0f 84/jump-if-= break/disp32
 7541       (emit-indent *(ebp+8) *Curr-block-depth)
 7542       (write-buffered *(ebp+8) "{\n")
 7543       # var v/ecx: (handle var)
 7544       8b/-> *(esi+8) 1/r32/ecx  # Block-var
 7545       #
 7546       (write-buffered *(ebp+8) *ecx)  # Var-name
 7547       (write-buffered *(ebp+8) ":loop:\n")
 7548       ff 0/subop/increment *Curr-block-depth
 7549       (push *(ebp+0x10) %ecx)
 7550       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
 7551       (pop *(ebp+0x10))  # => eax
 7552       ff 1/subop/decrement *Curr-block-depth
 7553       (emit-indent *(ebp+8) *Curr-block-depth)
 7554       (write-buffered *(ebp+8) "}\n")
 7555       (write-buffered *(ebp+8) *ecx)  # Var-name
 7556       (write-buffered *(ebp+8) ":break:\n")
 7557     }
 7558 $emit-subx-block:end:
 7559     # . restore registers
 7560     5e/pop-to-esi
 7561     59/pop-to-ecx
 7562     58/pop-to-eax
 7563     # . epilogue
 7564     89/<- %esp 5/r32/ebp
 7565     5d/pop-to-ebp
 7566     c3/return
 7567 
 7568 # Primitives supported
 7569 # For each operation, put variants with hard-coded registers before flexible ones.
 7570 == data
 7571 Primitives:
 7572 # - increment/decrement
 7573 _Primitive-inc-eax:
 7574     # var/eax <- increment => 40/increment-eax
 7575     "increment"/imm32/name
 7576     0/imm32/no-inouts
 7577     Single-int-var-in-eax/imm32/outputs
 7578     "40/increment-eax"/imm32/subx-name
 7579     0/imm32/no-rm32
 7580     0/imm32/no-r32
 7581     0/imm32/no-imm32
 7582     0/imm32/no-disp32
 7583     0/imm32/output-is-write-only
 7584     _Primitive-inc-ecx/imm32/next
 7585 _Primitive-inc-ecx:
 7586     # var/ecx <- increment => 41/increment-ecx
 7587     "increment"/imm32/name
 7588     0/imm32/no-inouts
 7589     Single-int-var-in-ecx/imm32/outputs
 7590     "41/increment-ecx"/imm32/subx-name
 7591     0/imm32/no-rm32
 7592     0/imm32/no-r32
 7593     0/imm32/no-imm32
 7594     0/imm32/no-disp32
 7595     0/imm32/output-is-write-only
 7596     _Primitive-inc-edx/imm32/next
 7597 _Primitive-inc-edx:
 7598     # var/edx <- increment => 42/increment-edx
 7599     "increment"/imm32/name
 7600     0/imm32/no-inouts
 7601     Single-int-var-in-edx/imm32/outputs
 7602     "42/increment-edx"/imm32/subx-name
 7603     0/imm32/no-rm32
 7604     0/imm32/no-r32
 7605     0/imm32/no-imm32
 7606     0/imm32/no-disp32
 7607     0/imm32/output-is-write-only
 7608     _Primitive-inc-ebx/imm32/next
 7609 _Primitive-inc-ebx:
 7610     # var/ebx <- increment => 43/increment-ebx
 7611     "increment"/imm32/name
 7612     0/imm32/no-inouts
 7613     Single-int-var-in-ebx/imm32/outputs
 7614     "43/increment-ebx"/imm32/subx-name
 7615     0/imm32/no-rm32
 7616     0/imm32/no-r32
 7617     0/imm32/no-imm32
 7618     0/imm32/no-disp32
 7619     0/imm32/output-is-write-only
 7620     _Primitive-inc-esi/imm32/next
 7621 _Primitive-inc-esi:
 7622     # var/esi <- increment => 46/increment-esi
 7623     "increment"/imm32/name
 7624     0/imm32/no-inouts
 7625     Single-int-var-in-esi/imm32/outputs
 7626     "46/increment-esi"/imm32/subx-name
 7627     0/imm32/no-rm32
 7628     0/imm32/no-r32
 7629     0/imm32/no-imm32
 7630     0/imm32/no-disp32
 7631     0/imm32/output-is-write-only
 7632     _Primitive-inc-edi/imm32/next
 7633 _Primitive-inc-edi:
 7634     # var/edi <- increment => 47/increment-edi
 7635     "increment"/imm32/name
 7636     0/imm32/no-inouts
 7637     Single-int-var-in-edi/imm32/outputs
 7638     "47/increment-edi"/imm32/subx-name
 7639     0/imm32/no-rm32
 7640     0/imm32/no-r32
 7641     0/imm32/no-imm32
 7642     0/imm32/no-disp32
 7643     0/imm32/output-is-write-only
 7644     _Primitive-dec-eax/imm32/next
 7645 _Primitive-dec-eax:
 7646     # var/eax <- decrement => 48/decrement-eax
 7647     "decrement"/imm32/name
 7648     0/imm32/no-inouts
 7649     Single-int-var-in-eax/imm32/outputs
 7650     "48/decrement-eax"/imm32/subx-name
 7651     0/imm32/no-rm32
 7652     0/imm32/no-r32
 7653     0/imm32/no-imm32
 7654     0/imm32/no-disp32
 7655     0/imm32/output-is-write-only
 7656     _Primitive-dec-ecx/imm32/next
 7657 _Primitive-dec-ecx:
 7658     # var/ecx <- decrement => 49/decrement-ecx
 7659     "decrement"/imm32/name
 7660     0/imm32/no-inouts
 7661     Single-int-var-in-ecx/imm32/outputs
 7662     "49/decrement-ecx"/imm32/subx-name
 7663     0/imm32/no-rm32
 7664     0/imm32/no-r32
 7665     0/imm32/no-imm32
 7666     0/imm32/no-disp32
 7667     0/imm32/output-is-write-only
 7668     _Primitive-dec-edx/imm32/next
 7669 _Primitive-dec-edx:
 7670     # var/edx <- decrement => 4a/decrement-edx
 7671     "decrement"/imm32/name
 7672     0/imm32/no-inouts
 7673     Single-int-var-in-edx/imm32/outputs
 7674     "4a/decrement-edx"/imm32/subx-name
 7675     0/imm32/no-rm32
 7676     0/imm32/no-r32
 7677     0/imm32/no-imm32
 7678     0/imm32/no-disp32
 7679     0/imm32/output-is-write-only
 7680     _Primitive-dec-ebx/imm32/next
 7681 _Primitive-dec-ebx:
 7682     # var/ebx <- decrement => 4b/decrement-ebx
 7683     "decrement"/imm32/name
 7684     0/imm32/no-inouts
 7685     Single-int-var-in-ebx/imm32/outputs
 7686     "4b/decrement-ebx"/imm32/subx-name
 7687     0/imm32/no-rm32
 7688     0/imm32/no-r32
 7689     0/imm32/no-imm32
 7690     0/imm32/no-disp32
 7691     0/imm32/output-is-write-only
 7692     _Primitive-dec-esi/imm32/next
 7693 _Primitive-dec-esi:
 7694     # var/esi <- decrement => 4e/decrement-esi
 7695     "decrement"/imm32/name
 7696     0/imm32/no-inouts
 7697     Single-int-var-in-esi/imm32/outputs
 7698     "4e/decrement-esi"/imm32/subx-name
 7699     0/imm32/no-rm32
 7700     0/imm32/no-r32
 7701     0/imm32/no-imm32
 7702     0/imm32/no-disp32
 7703     0/imm32/output-is-write-only
 7704     _Primitive-dec-edi/imm32/next
 7705 _Primitive-dec-edi:
 7706     # var/edi <- decrement => 4f/decrement-edi
 7707     "decrement"/imm32/name
 7708     0/imm32/no-inouts
 7709     Single-int-var-in-edi/imm32/outputs
 7710     "4f/decrement-edi"/imm32/subx-name
 7711     0/imm32/no-rm32
 7712     0/imm32/no-r32
 7713     0/imm32/no-imm32
 7714     0/imm32/no-disp32
 7715     0/imm32/output-is-write-only
 7716     _Primitive-inc-mem/imm32/next
 7717 _Primitive-inc-mem:
 7718     # increment var => ff 0/subop/increment *(ebp+__)
 7719     "increment"/imm32/name
 7720     Single-int-var-in-mem/imm32/inouts
 7721     0/imm32/no-outputs
 7722     "ff 0/subop/increment"/imm32/subx-name
 7723     1/imm32/rm32-is-first-inout
 7724     0/imm32/no-r32
 7725     0/imm32/no-imm32
 7726     0/imm32/no-disp32
 7727     0/imm32/output-is-write-only
 7728     _Primitive-inc-reg/imm32/next
 7729 _Primitive-inc-reg:
 7730     # var/reg <- increment => ff 0/subop/increment %__
 7731     "increment"/imm32/name
 7732     0/imm32/no-inouts
 7733     Single-int-var-in-some-register/imm32/outputs
 7734     "ff 0/subop/increment"/imm32/subx-name
 7735     3/imm32/rm32-is-first-output
 7736     0/imm32/no-r32
 7737     0/imm32/no-imm32
 7738     0/imm32/no-disp32
 7739     0/imm32/output-is-write-only
 7740     _Primitive-dec-mem/imm32/next
 7741 _Primitive-dec-mem:
 7742     # decrement var => ff 1/subop/decrement *(ebp+__)
 7743     "decrement"/imm32/name
 7744     Single-int-var-in-mem/imm32/inouts
 7745     0/imm32/no-outputs
 7746     "ff 1/subop/decrement"/imm32/subx-name
 7747     1/imm32/rm32-is-first-inout
 7748     0/imm32/no-r32
 7749     0/imm32/no-imm32
 7750     0/imm32/no-disp32
 7751     0/imm32/output-is-write-only
 7752     _Primitive-dec-reg/imm32/next
 7753 _Primitive-dec-reg:
 7754     # var/reg <- decrement => ff 1/subop/decrement %__
 7755     "decrement"/imm32/name
 7756     0/imm32/no-inouts
 7757     Single-int-var-in-some-register/imm32/outputs
 7758     "ff 1/subop/decrement"/imm32/subx-name
 7759     3/imm32/rm32-is-first-output
 7760     0/imm32/no-r32
 7761     0/imm32/no-imm32
 7762     0/imm32/no-disp32
 7763     0/imm32/output-is-write-only
 7764     _Primitive-add-to-eax/imm32/next
 7765 # - add
 7766 _Primitive-add-to-eax:
 7767     # var/eax <- add lit => 05/add-to-eax lit/imm32
 7768     "add"/imm32/name
 7769     Single-lit-var/imm32/inouts
 7770     Single-int-var-in-eax/imm32/outputs
 7771     "05/add-to-eax"/imm32/subx-name
 7772     0/imm32/no-rm32
 7773     0/imm32/no-r32
 7774     1/imm32/imm32-is-first-inout
 7775     0/imm32/no-disp32
 7776     0/imm32/output-is-write-only
 7777     _Primitive-add-reg-to-reg/imm32/next
 7778 _Primitive-add-reg-to-reg:
 7779     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
 7780     "add"/imm32/name
 7781     Single-int-var-in-some-register/imm32/inouts
 7782     Single-int-var-in-some-register/imm32/outputs
 7783     "01/add-to"/imm32/subx-name
 7784     3/imm32/rm32-is-first-output
 7785     1/imm32/r32-is-first-inout
 7786     0/imm32/no-imm32
 7787     0/imm32/no-disp32
 7788     0/imm32/output-is-write-only
 7789     _Primitive-add-reg-to-mem/imm32/next
 7790 _Primitive-add-reg-to-mem:
 7791     # add-to var1 var2/reg => 01/add-to var1 var2/r32
 7792     "add-to"/imm32/name
 7793     Two-args-int-stack-int-reg/imm32/inouts
 7794     0/imm32/outputs
 7795     "01/add-to"/imm32/subx-name
 7796     1/imm32/rm32-is-first-inout
 7797     2/imm32/r32-is-second-inout
 7798     0/imm32/no-imm32
 7799     0/imm32/no-disp32
 7800     0/imm32/output-is-write-only
 7801     _Primitive-add-mem-to-reg/imm32/next
 7802 _Primitive-add-mem-to-reg:
 7803     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
 7804     "add"/imm32/name
 7805     Single-int-var-in-mem/imm32/inouts
 7806     Single-int-var-in-some-register/imm32/outputs
 7807     "03/add"/imm32/subx-name
 7808     1/imm32/rm32-is-first-inout
 7809     3/imm32/r32-is-first-output
 7810     0/imm32/no-imm32
 7811     0/imm32/no-disp32
 7812     0/imm32/output-is-write-only
 7813     _Primitive-add-lit-to-reg/imm32/next
 7814 _Primitive-add-lit-to-reg:
 7815     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
 7816     "add"/imm32/name
 7817     Single-lit-var/imm32/inouts
 7818     Single-int-var-in-some-register/imm32/outputs
 7819     "81 0/subop/add"/imm32/subx-name
 7820     3/imm32/rm32-is-first-output
 7821     0/imm32/no-r32
 7822     1/imm32/imm32-is-first-inout
 7823     0/imm32/no-disp32
 7824     0/imm32/output-is-write-only
 7825     _Primitive-add-lit-to-mem/imm32/next
 7826 _Primitive-add-lit-to-mem:
 7827     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
 7828     "add-to"/imm32/name
 7829     Int-var-and-literal/imm32/inouts
 7830     0/imm32/outputs
 7831     "81 0/subop/add"/imm32/subx-name
 7832     1/imm32/rm32-is-first-inout
 7833     0/imm32/no-r32
 7834     2/imm32/imm32-is-second-inout
 7835     0/imm32/no-disp32
 7836     0/imm32/output-is-write-only
 7837     _Primitive-subtract-from-eax/imm32/next
 7838 # - subtract
 7839 _Primitive-subtract-from-eax:
 7840     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
 7841     "subtract"/imm32/name
 7842     Single-lit-var/imm32/inouts
 7843     Single-int-var-in-eax/imm32/outputs
 7844     "2d/subtract-from-eax"/imm32/subx-name
 7845     0/imm32/no-rm32
 7846     0/imm32/no-r32
 7847     1/imm32/imm32-is-first-inout
 7848     0/imm32/no-disp32
 7849     0/imm32/output-is-write-only
 7850     _Primitive-subtract-reg-from-reg/imm32/next
 7851 _Primitive-subtract-reg-from-reg:
 7852     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
 7853     "subtract"/imm32/name
 7854     Single-int-var-in-some-register/imm32/inouts
 7855     Single-int-var-in-some-register/imm32/outputs
 7856     "29/subtract-from"/imm32/subx-name
 7857     3/imm32/rm32-is-first-output
 7858     1/imm32/r32-is-first-inout
 7859     0/imm32/no-imm32
 7860     0/imm32/no-disp32
 7861     0/imm32/output-is-write-only
 7862     _Primitive-subtract-reg-from-mem/imm32/next
 7863 _Primitive-subtract-reg-from-mem:
 7864     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
 7865     "subtract-from"/imm32/name
 7866     Two-args-int-stack-int-reg/imm32/inouts
 7867     0/imm32/outputs
 7868     "29/subtract-from"/imm32/subx-name
 7869     1/imm32/rm32-is-first-inout
 7870     2/imm32/r32-is-second-inout
 7871     0/imm32/no-imm32
 7872     0/imm32/no-disp32
 7873     0/imm32/output-is-write-only
 7874     _Primitive-subtract-mem-from-reg/imm32/next
 7875 _Primitive-subtract-mem-from-reg:
 7876     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
 7877     "subtract"/imm32/name
 7878     Single-int-var-in-mem/imm32/inouts
 7879     Single-int-var-in-some-register/imm32/outputs
 7880     "2b/subtract"/imm32/subx-name
 7881     1/imm32/rm32-is-first-inout
 7882     3/imm32/r32-is-first-output
 7883     0/imm32/no-imm32
 7884     0/imm32/no-disp32
 7885     0/imm32/output-is-write-only
 7886     _Primitive-subtract-lit-from-reg/imm32/next
 7887 _Primitive-subtract-lit-from-reg:
 7888     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
 7889     "subtract"/imm32/name
 7890     Single-lit-var/imm32/inouts
 7891     Single-int-var-in-some-register/imm32/outputs
 7892     "81 5/subop/subtract"/imm32/subx-name
 7893     3/imm32/rm32-is-first-output
 7894     0/imm32/no-r32
 7895     1/imm32/imm32-is-first-inout
 7896     0/imm32/no-disp32
 7897     0/imm32/output-is-write-only
 7898     _Primitive-subtract-lit-from-mem/imm32/next
 7899 _Primitive-subtract-lit-from-mem:
 7900     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
 7901     "subtract-from"/imm32/name
 7902     Int-var-and-literal/imm32/inouts
 7903     0/imm32/outputs
 7904     "81 5/subop/subtract"/imm32/subx-name
 7905     1/imm32/rm32-is-first-inout
 7906     0/imm32/no-r32
 7907     2/imm32/imm32-is-first-inout
 7908     0/imm32/no-disp32
 7909     0/imm32/output-is-write-only
 7910     _Primitive-and-with-eax/imm32/next
 7911 # - and
 7912 _Primitive-and-with-eax:
 7913     # var/eax <- and lit => 25/and-with-eax lit/imm32
 7914     "and"/imm32/name
 7915     Single-lit-var/imm32/inouts
 7916     Single-int-var-in-eax/imm32/outputs
 7917     "25/and-with-eax"/imm32/subx-name
 7918     0/imm32/no-rm32
 7919     0/imm32/no-r32
 7920     1/imm32/imm32-is-first-inout
 7921     0/imm32/no-disp32
 7922     0/imm32/output-is-write-only
 7923     _Primitive-and-reg-with-reg/imm32/next
 7924 _Primitive-and-reg-with-reg:
 7925     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
 7926     "and"/imm32/name
 7927     Single-int-var-in-some-register/imm32/inouts
 7928     Single-int-var-in-some-register/imm32/outputs
 7929     "21/and-with"/imm32/subx-name
 7930     3/imm32/rm32-is-first-output
 7931     1/imm32/r32-is-first-inout
 7932     0/imm32/no-imm32
 7933     0/imm32/no-disp32
 7934     0/imm32/output-is-write-only
 7935     _Primitive-and-reg-with-mem/imm32/next
 7936 _Primitive-and-reg-with-mem:
 7937     # and-with var1 var2/reg => 21/and-with var1 var2/r32
 7938     "and-with"/imm32/name
 7939     Two-args-int-stack-int-reg/imm32/inouts
 7940     0/imm32/outputs
 7941     "21/and-with"/imm32/subx-name
 7942     1/imm32/rm32-is-first-inout
 7943     2/imm32/r32-is-second-inout
 7944     0/imm32/no-imm32
 7945     0/imm32/no-disp32
 7946     0/imm32/output-is-write-only
 7947     _Primitive-and-mem-with-reg/imm32/next
 7948 _Primitive-and-mem-with-reg:
 7949     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
 7950     "and"/imm32/name
 7951     Single-int-var-in-mem/imm32/inouts
 7952     Single-int-var-in-some-register/imm32/outputs
 7953     "23/and"/imm32/subx-name
 7954     1/imm32/rm32-is-first-inout
 7955     3/imm32/r32-is-first-output
 7956     0/imm32/no-imm32
 7957     0/imm32/no-disp32
 7958     0/imm32/output-is-write-only
 7959     _Primitive-and-lit-with-reg/imm32/next
 7960 _Primitive-and-lit-with-reg:
 7961     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
 7962     "and"/imm32/name
 7963     Single-lit-var/imm32/inouts
 7964     Single-int-var-in-some-register/imm32/outputs
 7965     "81 4/subop/and"/imm32/subx-name
 7966     3/imm32/rm32-is-first-output
 7967     0/imm32/no-r32
 7968     1/imm32/imm32-is-first-inout
 7969     0/imm32/no-disp32
 7970     0/imm32/output-is-write-only
 7971     _Primitive-and-lit-with-mem/imm32/next
 7972 _Primitive-and-lit-with-mem:
 7973     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
 7974     "and-with"/imm32/name
 7975     Int-var-and-literal/imm32/inouts
 7976     0/imm32/outputs
 7977     "81 4/subop/and"/imm32/subx-name
 7978     1/imm32/rm32-is-first-inout
 7979     0/imm32/no-r32
 7980     2/imm32/imm32-is-first-inout
 7981     0/imm32/no-disp32
 7982     0/imm32/output-is-write-only
 7983     _Primitive-or-with-eax/imm32/next
 7984 # - or
 7985 _Primitive-or-with-eax:
 7986     # var/eax <- or lit => 0d/or-with-eax lit/imm32
 7987     "or"/imm32/name
 7988     Single-lit-var/imm32/inouts
 7989     Single-int-var-in-eax/imm32/outputs
 7990     "0d/or-with-eax"/imm32/subx-name
 7991     0/imm32/no-rm32
 7992     0/imm32/no-r32
 7993     1/imm32/imm32-is-first-inout
 7994     0/imm32/no-disp32
 7995     0/imm32/output-is-write-only
 7996     _Primitive-or-reg-with-reg/imm32/next
 7997 _Primitive-or-reg-with-reg:
 7998     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
 7999     "or"/imm32/name
 8000     Single-int-var-in-some-register/imm32/inouts
 8001     Single-int-var-in-some-register/imm32/outputs
 8002     "09/or-with"/imm32/subx-name
 8003     3/imm32/rm32-is-first-output
 8004     1/imm32/r32-is-first-inout
 8005     0/imm32/no-imm32
 8006     0/imm32/no-disp32
 8007     0/imm32/output-is-write-only
 8008     _Primitive-or-reg-with-mem/imm32/next
 8009 _Primitive-or-reg-with-mem:
 8010     # or-with var1 var2/reg => 09/or-with var1 var2/r32
 8011     "or-with"/imm32/name
 8012     Two-args-int-stack-int-reg/imm32/inouts
 8013     0/imm32/outputs
 8014     "09/or-with"/imm32/subx-name
 8015     1/imm32/rm32-is-first-inout
 8016     2/imm32/r32-is-second-inout
 8017     0/imm32/no-imm32
 8018     0/imm32/no-disp32
 8019     0/imm32/output-is-write-only
 8020     _Primitive-or-mem-with-reg/imm32/next
 8021 _Primitive-or-mem-with-reg:
 8022     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
 8023     "or"/imm32/name
 8024     Single-int-var-in-mem/imm32/inouts
 8025     Single-int-var-in-some-register/imm32/outputs
 8026     "0b/or"/imm32/subx-name
 8027     1/imm32/rm32-is-first-inout
 8028     3/imm32/r32-is-first-output
 8029     0/imm32/no-imm32
 8030     0/imm32/no-disp32
 8031     0/imm32/output-is-write-only
 8032     _Primitive-or-lit-with-reg/imm32/next
 8033 _Primitive-or-lit-with-reg:
 8034     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
 8035     "or"/imm32/name
 8036     Single-lit-var/imm32/inouts
 8037     Single-int-var-in-some-register/imm32/outputs
 8038     "81 1/subop/or"/imm32/subx-name
 8039     3/imm32/rm32-is-first-output
 8040     0/imm32/no-r32
 8041     1/imm32/imm32-is-first-inout
 8042     0/imm32/no-disp32
 8043     0/imm32/output-is-write-only
 8044     _Primitive-or-lit-with-mem/imm32/next
 8045 _Primitive-or-lit-with-mem:
 8046     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
 8047     "or-with"/imm32/name
 8048     Int-var-and-literal/imm32/inouts
 8049     0/imm32/outputs
 8050     "81 1/subop/or"/imm32/subx-name
 8051     1/imm32/rm32-is-first-inout
 8052     0/imm32/no-r32
 8053     2/imm32/imm32-is-second-inout
 8054     0/imm32/no-disp32
 8055     0/imm32/output-is-write-only
 8056     _Primitive-xor-with-eax/imm32/next
 8057 # - xor
 8058 _Primitive-xor-with-eax:
 8059     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
 8060     "xor"/imm32/name
 8061     Single-lit-var/imm32/inouts
 8062     Single-int-var-in-eax/imm32/outputs
 8063     "35/xor-with-eax"/imm32/subx-name
 8064     0/imm32/no-rm32
 8065     0/imm32/no-r32
 8066     1/imm32/imm32-is-first-inout
 8067     0/imm32/no-disp32
 8068     0/imm32/output-is-write-only
 8069     _Primitive-xor-reg-with-reg/imm32/next
 8070 _Primitive-xor-reg-with-reg:
 8071     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
 8072     "xor"/imm32/name
 8073     Single-int-var-in-some-register/imm32/inouts
 8074     Single-int-var-in-some-register/imm32/outputs
 8075     "31/xor-with"/imm32/subx-name
 8076     3/imm32/rm32-is-first-output
 8077     1/imm32/r32-is-first-inout
 8078     0/imm32/no-imm32
 8079     0/imm32/no-disp32
 8080     0/imm32/output-is-write-only
 8081     _Primitive-xor-reg-with-mem/imm32/next
 8082 _Primitive-xor-reg-with-mem:
 8083     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
 8084     "xor-with"/imm32/name
 8085     Two-args-int-stack-int-reg/imm32/inouts
 8086     0/imm32/outputs
 8087     "31/xor-with"/imm32/subx-name
 8088     1/imm32/rm32-is-first-inout
 8089     2/imm32/r32-is-second-inout
 8090     0/imm32/no-imm32
 8091     0/imm32/no-disp32
 8092     0/imm32/output-is-write-only
 8093     _Primitive-xor-mem-with-reg/imm32/next
 8094 _Primitive-xor-mem-with-reg:
 8095     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
 8096     "xor"/imm32/name
 8097     Single-int-var-in-mem/imm32/inouts
 8098     Single-int-var-in-some-register/imm32/outputs
 8099     "33/xor"/imm32/subx-name
 8100     1/imm32/rm32-is-first-inout
 8101     3/imm32/r32-is-first-output
 8102     0/imm32/no-imm32
 8103     0/imm32/no-disp32
 8104     0/imm32/output-is-write-only
 8105     _Primitive-xor-lit-with-reg/imm32/next
 8106 _Primitive-xor-lit-with-reg:
 8107     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
 8108     "xor"/imm32/name
 8109     Single-lit-var/imm32/inouts
 8110     Single-int-var-in-some-register/imm32/outputs
 8111     "81 6/subop/xor"/imm32/subx-name
 8112     3/imm32/rm32-is-first-output
 8113     0/imm32/no-r32
 8114     1/imm32/imm32-is-first-inout
 8115     0/imm32/no-disp32
 8116     0/imm32/output-is-write-only
 8117     _Primitive-xor-lit-with-mem/imm32/next
 8118 _Primitive-xor-lit-with-mem:
 8119     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
 8120     "xor-with"/imm32/name
 8121     Int-var-and-literal/imm32/inouts
 8122     0/imm32/outputs
 8123     "81 6/subop/xor"/imm32/subx-name
 8124     1/imm32/rm32-is-first-inout
 8125     0/imm32/no-r32
 8126     2/imm32/imm32-is-first-inout
 8127     0/imm32/no-disp32
 8128     0/imm32/output-is-write-only
 8129     _Primitive-copy-to-eax/imm32/next
 8130 # - copy
 8131 _Primitive-copy-to-eax:
 8132     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
 8133     "copy"/imm32/name
 8134     Single-lit-var/imm32/inouts
 8135     Single-int-var-in-eax/imm32/outputs
 8136     "b8/copy-to-eax"/imm32/subx-name
 8137     0/imm32/no-rm32
 8138     0/imm32/no-r32
 8139     1/imm32/imm32-is-first-inout
 8140     0/imm32/no-disp32
 8141     1/imm32/output-is-write-only
 8142     _Primitive-copy-to-ecx/imm32/next
 8143 _Primitive-copy-to-ecx:
 8144     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
 8145     "copy"/imm32/name
 8146     Single-lit-var/imm32/inouts
 8147     Single-int-var-in-ecx/imm32/outputs
 8148     "b9/copy-to-ecx"/imm32/subx-name
 8149     0/imm32/no-rm32
 8150     0/imm32/no-r32
 8151     1/imm32/imm32-is-first-inout
 8152     0/imm32/no-disp32
 8153     1/imm32/output-is-write-only
 8154     _Primitive-copy-to-edx/imm32/next
 8155 _Primitive-copy-to-edx:
 8156     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
 8157     "copy"/imm32/name
 8158     Single-lit-var/imm32/inouts
 8159     Single-int-var-in-edx/imm32/outputs
 8160     "ba/copy-to-edx"/imm32/subx-name
 8161     0/imm32/no-rm32
 8162     0/imm32/no-r32
 8163     1/imm32/imm32-is-first-inout
 8164     0/imm32/no-disp32
 8165     1/imm32/output-is-write-only
 8166     _Primitive-copy-to-ebx/imm32/next
 8167 _Primitive-copy-to-ebx:
 8168     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
 8169     "copy"/imm32/name
 8170     Single-lit-var/imm32/inouts
 8171     Single-int-var-in-ebx/imm32/outputs
 8172     "bb/copy-to-ebx"/imm32/subx-name
 8173     0/imm32/no-rm32
 8174     0/imm32/no-r32
 8175     1/imm32/imm32-is-first-inout
 8176     0/imm32/no-disp32
 8177     1/imm32/output-is-write-only
 8178     _Primitive-copy-to-esi/imm32/next
 8179 _Primitive-copy-to-esi:
 8180     # var/esi <- copy lit => be/copy-to-esi lit/imm32
 8181     "copy"/imm32/name
 8182     Single-lit-var/imm32/inouts
 8183     Single-int-var-in-esi/imm32/outputs
 8184     "be/copy-to-esi"/imm32/subx-name
 8185     0/imm32/no-rm32
 8186     0/imm32/no-r32
 8187     1/imm32/imm32-is-first-inout
 8188     0/imm32/no-disp32
 8189     1/imm32/output-is-write-only
 8190     _Primitive-copy-to-edi/imm32/next
 8191 _Primitive-copy-to-edi:
 8192     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
 8193     "copy"/imm32/name
 8194     Single-lit-var/imm32/inouts
 8195     Single-int-var-in-edi/imm32/outputs
 8196     "bf/copy-to-edi"/imm32/subx-name
 8197     0/imm32/no-rm32
 8198     0/imm32/no-r32
 8199     1/imm32/imm32-is-first-inout
 8200     0/imm32/no-disp32
 8201     1/imm32/output-is-write-only
 8202     _Primitive-copy-reg-to-reg/imm32/next
 8203 _Primitive-copy-reg-to-reg:
 8204     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
 8205     "copy"/imm32/name
 8206     Single-int-var-in-some-register/imm32/inouts
 8207     Single-int-var-in-some-register/imm32/outputs
 8208     "89/copy-to"/imm32/subx-name
 8209     3/imm32/rm32-is-first-output
 8210     1/imm32/r32-is-first-inout
 8211     0/imm32/no-imm32
 8212     0/imm32/no-disp32
 8213     1/imm32/output-is-write-only
 8214     _Primitive-copy-reg-to-mem/imm32/next
 8215 _Primitive-copy-reg-to-mem:
 8216     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
 8217     "copy-to"/imm32/name
 8218     Two-args-int-stack-int-reg/imm32/inouts
 8219     0/imm32/outputs
 8220     "89/copy-to"/imm32/subx-name
 8221     1/imm32/rm32-is-first-inout
 8222     2/imm32/r32-is-second-inout
 8223     0/imm32/no-imm32
 8224     0/imm32/no-disp32
 8225     1/imm32/output-is-write-only
 8226     _Primitive-copy-mem-to-reg/imm32/next
 8227 _Primitive-copy-mem-to-reg:
 8228     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
 8229     "copy"/imm32/name
 8230     Single-int-var-in-mem/imm32/inouts
 8231     Single-int-var-in-some-register/imm32/outputs
 8232     "8b/copy-from"/imm32/subx-name
 8233     1/imm32/rm32-is-first-inout
 8234     3/imm32/r32-is-first-output
 8235     0/imm32/no-imm32
 8236     0/imm32/no-disp32
 8237     1/imm32/output-is-write-only
 8238     _Primitive-copy-lit-to-reg/imm32/next
 8239 _Primitive-copy-lit-to-reg:
 8240     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
 8241     "copy"/imm32/name
 8242     Single-lit-var/imm32/inouts
 8243     Single-int-var-in-some-register/imm32/outputs
 8244     "c7 0/subop/copy"/imm32/subx-name
 8245     3/imm32/rm32-is-first-output
 8246     0/imm32/no-r32
 8247     1/imm32/imm32-is-first-inout
 8248     0/imm32/no-disp32
 8249     1/imm32/output-is-write-only
 8250     _Primitive-copy-lit-to-mem/imm32/next
 8251 _Primitive-copy-lit-to-mem:
 8252     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
 8253     "copy-to"/imm32/name
 8254     Int-var-and-literal/imm32/inouts
 8255     0/imm32/outputs
 8256     "c7 0/subop/copy"/imm32/subx-name
 8257     1/imm32/rm32-is-first-inout
 8258     0/imm32/no-r32
 8259     2/imm32/imm32-is-first-inout
 8260     0/imm32/no-disp32
 8261     1/imm32/output-is-write-only
 8262     _Primitive-compare-mem-with-reg/imm32/next
 8263 # - compare
 8264 _Primitive-compare-mem-with-reg:
 8265     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
 8266     "compare"/imm32/name
 8267     Two-args-int-stack-int-reg/imm32/inouts
 8268     0/imm32/outputs
 8269     "39/compare->"/imm32/subx-name
 8270     1/imm32/rm32-is-first-inout
 8271     2/imm32/r32-is-second-inout
 8272     0/imm32/no-imm32
 8273     0/imm32/no-disp32
 8274     0/imm32/output-is-write-only
 8275     _Primitive-compare-reg-with-mem/imm32/next
 8276 _Primitive-compare-reg-with-mem:
 8277     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
 8278     "compare"/imm32/name
 8279     Two-args-int-reg-int-stack/imm32/inouts
 8280     0/imm32/outputs
 8281     "3b/compare<-"/imm32/subx-name
 8282     2/imm32/rm32-is-second-inout
 8283     1/imm32/r32-is-first-inout
 8284     0/imm32/no-imm32
 8285     0/imm32/no-disp32
 8286     0/imm32/output-is-write-only
 8287     _Primitive-compare-eax-with-literal/imm32/next
 8288 _Primitive-compare-eax-with-literal:
 8289     # compare var1/eax n => 3d/compare-eax-with n/imm32
 8290     "compare"/imm32/name
 8291     Two-args-int-eax-int-literal/imm32/inouts
 8292     0/imm32/outputs
 8293     "3d/compare-eax-with"/imm32/subx-name
 8294     0/imm32/no-rm32
 8295     0/imm32/no-r32
 8296     2/imm32/imm32-is-second-inout
 8297     0/imm32/no-disp32
 8298     0/imm32/output-is-write-only
 8299     _Primitive-compare-reg-with-literal/imm32/next
 8300 _Primitive-compare-reg-with-literal:
 8301     # compare var1/reg n => 81 7/subop/compare %reg n/imm32
 8302     "compare"/imm32/name
 8303     Int-var-in-register-and-literal/imm32/inouts
 8304     0/imm32/outputs
 8305     "81 7/subop/compare"/imm32/subx-name
 8306     1/imm32/rm32-is-first-inout
 8307     0/imm32/no-r32
 8308     2/imm32/imm32-is-second-inout
 8309     0/imm32/no-disp32
 8310     0/imm32/output-is-write-only
 8311     _Primitive-compare-mem-with-literal/imm32/next
 8312 _Primitive-compare-mem-with-literal:
 8313     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
 8314     "compare"/imm32/name
 8315     Int-var-and-literal/imm32/inouts
 8316     0/imm32/outputs
 8317     "81 7/subop/compare"/imm32/subx-name
 8318     1/imm32/rm32-is-first-inout
 8319     0/imm32/no-r32
 8320     2/imm32/imm32-is-second-inout
 8321     0/imm32/no-disp32
 8322     0/imm32/output-is-write-only
 8323     _Primitive-multiply-reg-by-mem/imm32/next
 8324 # - multiply
 8325 _Primitive-multiply-reg-by-mem:
 8326     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
 8327     "multiply"/imm32/name
 8328     Single-int-var-in-mem/imm32/inouts
 8329     Single-int-var-in-some-register/imm32/outputs
 8330     "0f af/multiply"/imm32/subx-name
 8331     1/imm32/rm32-is-first-inout
 8332     3/imm32/r32-is-first-output
 8333     0/imm32/no-imm32
 8334     0/imm32/no-disp32
 8335     0/imm32/output-is-write-only
 8336     _Primitive-break-if-addr</imm32/next
 8337 # - branches
 8338 _Primitive-break-if-addr<:
 8339     "break-if-addr<"/imm32/name
 8340     0/imm32/inouts
 8341     0/imm32/outputs
 8342     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
 8343     0/imm32/no-rm32
 8344     0/imm32/no-r32
 8345     0/imm32/no-imm32
 8346     0/imm32/no-disp32
 8347     0/imm32/no-output
 8348     _Primitive-break-if-addr>=/imm32/next
 8349 _Primitive-break-if-addr>=:
 8350     "break-if-addr>="/imm32/name
 8351     0/imm32/inouts
 8352     0/imm32/outputs
 8353     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
 8354     0/imm32/no-rm32
 8355     0/imm32/no-r32
 8356     0/imm32/no-imm32
 8357     0/imm32/no-disp32
 8358     0/imm32/no-output
 8359     _Primitive-break-if-=/imm32/next
 8360 _Primitive-break-if-=:
 8361     "break-if-="/imm32/name
 8362     0/imm32/inouts
 8363     0/imm32/outputs
 8364     "0f 84/jump-if-= break/disp32"/imm32/subx-name
 8365     0/imm32/no-rm32
 8366     0/imm32/no-r32
 8367     0/imm32/no-imm32
 8368     0/imm32/no-disp32
 8369     0/imm32/no-output
 8370     _Primitive-break-if-!=/imm32/next
 8371 _Primitive-break-if-!=:
 8372     "break-if-!="/imm32/name
 8373     0/imm32/inouts
 8374     0/imm32/outputs
 8375     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
 8376     0/imm32/no-rm32
 8377     0/imm32/no-r32
 8378     0/imm32/no-imm32
 8379     0/imm32/no-disp32
 8380     0/imm32/no-output
 8381     _Primitive-break-if-addr<=/imm32/next
 8382 _Primitive-break-if-addr<=:
 8383     "break-if-addr<="/imm32/name
 8384     0/imm32/inouts
 8385     0/imm32/outputs
 8386     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
 8387     0/imm32/no-rm32
 8388     0/imm32/no-r32
 8389     0/imm32/no-imm32
 8390     0/imm32/no-disp32
 8391     0/imm32/no-output
 8392     _Primitive-break-if-addr>/imm32/next
 8393 _Primitive-break-if-addr>:
 8394     "break-if-addr>"/imm32/name
 8395     0/imm32/inouts
 8396     0/imm32/outputs
 8397     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
 8398     0/imm32/no-rm32
 8399     0/imm32/no-r32
 8400     0/imm32/no-imm32
 8401     0/imm32/no-disp32
 8402     0/imm32/no-output
 8403     _Primitive-break-if-</imm32/next
 8404 _Primitive-break-if-<:
 8405     "break-if-<"/imm32/name
 8406     0/imm32/inouts
 8407     0/imm32/outputs
 8408     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
 8409     0/imm32/no-rm32
 8410     0/imm32/no-r32
 8411     0/imm32/no-imm32
 8412     0/imm32/no-disp32
 8413     0/imm32/no-output
 8414     _Primitive-break-if->=/imm32/next
 8415 _Primitive-break-if->=:
 8416     "break-if->="/imm32/name
 8417     0/imm32/inouts
 8418     0/imm32/outputs
 8419     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
 8420     0/imm32/no-rm32
 8421     0/imm32/no-r32
 8422     0/imm32/no-imm32
 8423     0/imm32/no-disp32
 8424     0/imm32/no-output
 8425     _Primitive-break-if-<=/imm32/next
 8426 _Primitive-break-if-<=:
 8427     "break-if-<="/imm32/name
 8428     0/imm32/inouts
 8429     0/imm32/outputs
 8430     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
 8431     0/imm32/no-rm32
 8432     0/imm32/no-r32
 8433     0/imm32/no-imm32
 8434     0/imm32/no-disp32
 8435     0/imm32/no-output
 8436     _Primitive-break-if->/imm32/next
 8437 _Primitive-break-if->:
 8438     "break-if->"/imm32/name
 8439     0/imm32/inouts
 8440     0/imm32/outputs
 8441     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
 8442     0/imm32/no-rm32
 8443     0/imm32/no-r32
 8444     0/imm32/no-imm32
 8445     0/imm32/no-disp32
 8446     0/imm32/no-output
 8447     _Primitive-break/imm32/next
 8448 _Primitive-break:
 8449     "break"/imm32/name
 8450     0/imm32/inouts
 8451     0/imm32/outputs
 8452     "e9/jump break/disp32"/imm32/subx-name
 8453     0/imm32/no-rm32
 8454     0/imm32/no-r32
 8455     0/imm32/no-imm32
 8456     0/imm32/no-disp32
 8457     0/imm32/no-output
 8458     _Primitive-loop-if-addr</imm32/next
 8459 _Primitive-loop-if-addr<:
 8460     "loop-if-addr<"/imm32/name
 8461     0/imm32/inouts
 8462     0/imm32/outputs
 8463     "0f 82/jump-if-addr< loop/disp32"/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/no-output
 8469     _Primitive-loop-if-addr>=/imm32/next
 8470 _Primitive-loop-if-addr>=:
 8471     "loop-if-addr>="/imm32/name
 8472     0/imm32/inouts
 8473     0/imm32/outputs
 8474     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
 8475     0/imm32/no-rm32
 8476     0/imm32/no-r32
 8477     0/imm32/no-imm32
 8478     0/imm32/no-disp32
 8479     0/imm32/no-output
 8480     _Primitive-loop-if-=/imm32/next
 8481 _Primitive-loop-if-=:
 8482     "loop-if-="/imm32/name
 8483     0/imm32/inouts
 8484     0/imm32/outputs
 8485     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
 8486     0/imm32/no-rm32
 8487     0/imm32/no-r32
 8488     0/imm32/no-imm32
 8489     0/imm32/no-disp32
 8490     0/imm32/no-output
 8491     _Primitive-loop-if-!=/imm32/next
 8492 _Primitive-loop-if-!=:
 8493     "loop-if-!="/imm32/name
 8494     0/imm32/inouts
 8495     0/imm32/outputs
 8496     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
 8497     0/imm32/no-rm32
 8498     0/imm32/no-r32
 8499     0/imm32/no-imm32
 8500     0/imm32/no-disp32
 8501     0/imm32/no-output
 8502     _Primitive-loop-if-addr<=/imm32/next
 8503 _Primitive-loop-if-addr<=:
 8504     "loop-if-addr<="/imm32/name
 8505     0/imm32/inouts
 8506     0/imm32/outputs
 8507     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
 8508     0/imm32/no-rm32
 8509     0/imm32/no-r32
 8510     0/imm32/no-imm32
 8511     0/imm32/no-disp32
 8512     0/imm32/no-output
 8513     _Primitive-loop-if-addr>/imm32/next
 8514 _Primitive-loop-if-addr>:
 8515     "loop-if-addr>"/imm32/name
 8516     0/imm32/inouts
 8517     0/imm32/outputs
 8518     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
 8519     0/imm32/no-rm32
 8520     0/imm32/no-r32
 8521     0/imm32/no-imm32
 8522     0/imm32/no-disp32
 8523     0/imm32/no-output
 8524     _Primitive-loop-if-</imm32/next
 8525 _Primitive-loop-if-<:
 8526     "loop-if-<"/imm32/name
 8527     0/imm32/inouts
 8528     0/imm32/outputs
 8529     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
 8530     0/imm32/no-rm32
 8531     0/imm32/no-r32
 8532     0/imm32/no-imm32
 8533     0/imm32/no-disp32
 8534     0/imm32/no-output
 8535     _Primitive-loop-if->=/imm32/next
 8536 _Primitive-loop-if->=:
 8537     "loop-if->="/imm32/name
 8538     0/imm32/inouts
 8539     0/imm32/outputs
 8540     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
 8541     0/imm32/no-rm32
 8542     0/imm32/no-r32
 8543     0/imm32/no-imm32
 8544     0/imm32/no-disp32
 8545     0/imm32/no-output
 8546     _Primitive-loop-if-<=/imm32/next
 8547 _Primitive-loop-if-<=:
 8548     "loop-if-<="/imm32/name
 8549     0/imm32/inouts
 8550     0/imm32/outputs
 8551     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
 8552     0/imm32/no-rm32
 8553     0/imm32/no-r32
 8554     0/imm32/no-imm32
 8555     0/imm32/no-disp32
 8556     0/imm32/no-output
 8557     _Primitive-loop-if->/imm32/next
 8558 _Primitive-loop-if->:
 8559     "loop-if->"/imm32/name
 8560     0/imm32/inouts
 8561     0/imm32/outputs
 8562     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
 8563     0/imm32/no-rm32
 8564     0/imm32/no-r32
 8565     0/imm32/no-imm32
 8566     0/imm32/no-disp32
 8567     0/imm32/no-output
 8568     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
 8569 _Primitive-loop:
 8570     "loop"/imm32/name
 8571     0/imm32/inouts
 8572     0/imm32/outputs
 8573     "e9/jump loop/disp32"/imm32/subx-name
 8574     0/imm32/no-rm32
 8575     0/imm32/no-r32
 8576     0/imm32/no-imm32
 8577     0/imm32/no-disp32
 8578     0/imm32/no-output
 8579     _Primitive-break-if-addr<-named/imm32/next
 8580 # - branches to named blocks
 8581 _Primitive-break-if-addr<-named:
 8582     "break-if-addr<"/imm32/name
 8583     Single-lit-var/imm32/inouts
 8584     0/imm32/outputs
 8585     "0f 82/jump-if-addr<"/imm32/subx-name
 8586     0/imm32/no-rm32
 8587     0/imm32/no-r32
 8588     0/imm32/no-imm32
 8589     1/imm32/disp32-is-first-inout
 8590     0/imm32/no-output
 8591     _Primitive-break-if-addr>=-named/imm32/next
 8592 _Primitive-break-if-addr>=-named:
 8593     "break-if-addr>="/imm32/name
 8594     Single-lit-var/imm32/inouts
 8595     0/imm32/outputs
 8596     "0f 83/jump-if-addr>="/imm32/subx-name
 8597     0/imm32/no-rm32
 8598     0/imm32/no-r32
 8599     0/imm32/no-imm32
 8600     1/imm32/disp32-is-first-inout
 8601     0/imm32/no-output
 8602     _Primitive-break-if-=-named/imm32/next
 8603 _Primitive-break-if-=-named:
 8604     "break-if-="/imm32/name
 8605     Single-lit-var/imm32/inouts
 8606     0/imm32/outputs
 8607     "0f 84/jump-if-="/imm32/subx-name
 8608     0/imm32/no-rm32
 8609     0/imm32/no-r32
 8610     0/imm32/no-imm32
 8611     1/imm32/disp32-is-first-inout
 8612     0/imm32/no-output
 8613     _Primitive-break-if-!=-named/imm32/next
 8614 _Primitive-break-if-!=-named:
 8615     "break-if-!="/imm32/name
 8616     Single-lit-var/imm32/inouts
 8617     0/imm32/outputs
 8618     "0f 85/jump-if-!="/imm32/subx-name
 8619     0/imm32/no-rm32
 8620     0/imm32/no-r32
 8621     0/imm32/no-imm32
 8622     1/imm32/disp32-is-first-inout
 8623     0/imm32/no-output
 8624     _Primitive-break-if-addr<=-named/imm32/next
 8625 _Primitive-break-if-addr<=-named:
 8626     "break-if-addr<="/imm32/name
 8627     Single-lit-var/imm32/inouts
 8628     0/imm32/outputs
 8629     "0f 86/jump-if-addr<="/imm32/subx-name
 8630     0/imm32/no-rm32
 8631     0/imm32/no-r32
 8632     0/imm32/no-imm32
 8633     1/imm32/disp32-is-first-inout
 8634     0/imm32/no-output
 8635     _Primitive-break-if-addr>-named/imm32/next
 8636 _Primitive-break-if-addr>-named:
 8637     "break-if-addr>"/imm32/name
 8638     Single-lit-var/imm32/inouts
 8639     0/imm32/outputs
 8640     "0f 87/jump-if-addr>"/imm32/subx-name
 8641     0/imm32/no-rm32
 8642     0/imm32/no-r32
 8643     0/imm32/no-imm32
 8644     1/imm32/disp32-is-first-inout
 8645     0/imm32/no-output
 8646     _Primitive-break-if-<-named/imm32/next
 8647 _Primitive-break-if-<-named:
 8648     "break-if-<"/imm32/name
 8649     Single-lit-var/imm32/inouts
 8650     0/imm32/outputs
 8651     "0f 8c/jump-if-<"/imm32/subx-name
 8652     0/imm32/no-rm32
 8653     0/imm32/no-r32
 8654     0/imm32/no-imm32
 8655     1/imm32/disp32-is-first-inout
 8656     0/imm32/no-output
 8657     _Primitive-break-if->=-named/imm32/next
 8658 _Primitive-break-if->=-named:
 8659     "break-if->="/imm32/name
 8660     Single-lit-var/imm32/inouts
 8661     0/imm32/outputs
 8662     "0f 8d/jump-if->="/imm32/subx-name
 8663     0/imm32/no-rm32
 8664     0/imm32/no-r32
 8665     0/imm32/no-imm32
 8666     1/imm32/disp32-is-first-inout
 8667     0/imm32/no-output
 8668     _Primitive-break-if-<=-named/imm32/next
 8669 _Primitive-break-if-<=-named:
 8670     "break-if-<="/imm32/name
 8671     Single-lit-var/imm32/inouts
 8672     0/imm32/outputs
 8673     "0f 8e/jump-if-<="/imm32/subx-name
 8674     0/imm32/no-rm32
 8675     0/imm32/no-r32
 8676     0/imm32/no-imm32
 8677     1/imm32/disp32-is-first-inout
 8678     0/imm32/no-output
 8679     _Primitive-break-if->-named/imm32/next
 8680 _Primitive-break-if->-named:
 8681     "break-if->"/imm32/name
 8682     Single-lit-var/imm32/inouts
 8683     0/imm32/outputs
 8684     "0f 8f/jump-if->"/imm32/subx-name
 8685     0/imm32/no-rm32
 8686     0/imm32/no-r32
 8687     0/imm32/no-imm32
 8688     1/imm32/disp32-is-first-inout
 8689     0/imm32/no-output
 8690     _Primitive-break-named/imm32/next
 8691 _Primitive-break-named:
 8692     "break"/imm32/name
 8693     Single-lit-var/imm32/inouts
 8694     0/imm32/outputs
 8695     "e9/jump"/imm32/subx-name
 8696     0/imm32/no-rm32
 8697     0/imm32/no-r32
 8698     0/imm32/no-imm32
 8699     1/imm32/disp32-is-first-inout
 8700     0/imm32/no-output
 8701     _Primitive-loop-if-addr<-named/imm32/next
 8702 _Primitive-loop-if-addr<-named:
 8703     "loop-if-addr<"/imm32/name
 8704     Single-lit-var/imm32/inouts
 8705     0/imm32/outputs
 8706     "0f 82/jump-if-addr<"/imm32/subx-name
 8707     0/imm32/no-rm32
 8708     0/imm32/no-r32
 8709     0/imm32/no-imm32
 8710     1/imm32/disp32-is-first-inout
 8711     0/imm32/no-output
 8712     _Primitive-loop-if-addr>=-named/imm32/next
 8713 _Primitive-loop-if-addr>=-named:
 8714     "loop-if-addr>="/imm32/name
 8715     Single-lit-var/imm32/inouts
 8716     0/imm32/outputs
 8717     "0f 83/jump-if-addr>="/imm32/subx-name
 8718     0/imm32/no-rm32
 8719     0/imm32/no-r32
 8720     0/imm32/no-imm32
 8721     1/imm32/disp32-is-first-inout
 8722     0/imm32/no-output
 8723     _Primitive-loop-if-=-named/imm32/next
 8724 _Primitive-loop-if-=-named:
 8725     "loop-if-="/imm32/name
 8726     Single-lit-var/imm32/inouts
 8727     0/imm32/outputs
 8728     "0f 84/jump-if-="/imm32/subx-name
 8729     0/imm32/no-rm32
 8730     0/imm32/no-r32
 8731     0/imm32/no-imm32
 8732     1/imm32/disp32-is-first-inout
 8733     0/imm32/no-output
 8734     _Primitive-loop-if-!=-named/imm32/next
 8735 _Primitive-loop-if-!=-named:
 8736     "loop-if-!="/imm32/name
 8737     Single-lit-var/imm32/inouts
 8738     0/imm32/outputs
 8739     "0f 85/jump-if-!="/imm32/subx-name
 8740     0/imm32/no-rm32
 8741     0/imm32/no-r32
 8742     0/imm32/no-imm32
 8743     1/imm32/disp32-is-first-inout
 8744     0/imm32/no-output
 8745     _Primitive-loop-if-addr<=-named/imm32/next
 8746 _Primitive-loop-if-addr<=-named:
 8747     "loop-if-addr<="/imm32/name
 8748     Single-lit-var/imm32/inouts
 8749     0/imm32/outputs
 8750     "0f 86/jump-if-addr<="/imm32/subx-name
 8751     0/imm32/no-rm32
 8752     0/imm32/no-r32
 8753     0/imm32/no-imm32
 8754     1/imm32/disp32-is-first-inout
 8755     0/imm32/no-output
 8756     _Primitive-loop-if-addr>-named/imm32/next
 8757 _Primitive-loop-if-addr>-named:
 8758     "loop-if-addr>"/imm32/name
 8759     Single-lit-var/imm32/inouts
 8760     0/imm32/outputs
 8761     "0f 87/jump-if-addr>"/imm32/subx-name
 8762     0/imm32/no-rm32
 8763     0/imm32/no-r32
 8764     0/imm32/no-imm32
 8765     1/imm32/disp32-is-first-inout
 8766     0/imm32/no-output
 8767     _Primitive-loop-if-<-named/imm32/next
 8768 _Primitive-loop-if-<-named:
 8769     "loop-if-<"/imm32/name
 8770     Single-lit-var/imm32/inouts
 8771     0/imm32/outputs
 8772     "0f 8c/jump-if-<"/imm32/subx-name
 8773     0/imm32/no-rm32
 8774     0/imm32/no-r32
 8775     0/imm32/no-imm32
 8776     1/imm32/disp32-is-first-inout
 8777     0/imm32/no-output
 8778     _Primitive-loop-if->=-named/imm32/next
 8779 _Primitive-loop-if->=-named:
 8780     "loop-if->="/imm32/name
 8781     Single-lit-var/imm32/inouts
 8782     0/imm32/outputs
 8783     "0f 8d/jump-if->="/imm32/subx-name
 8784     0/imm32/no-rm32
 8785     0/imm32/no-r32
 8786     0/imm32/no-imm32
 8787     1/imm32/disp32-is-first-inout
 8788     0/imm32/no-output
 8789     _Primitive-loop-if-<=-named/imm32/next
 8790 _Primitive-loop-if-<=-named:
 8791     "loop-if-<="/imm32/name
 8792     Single-lit-var/imm32/inouts
 8793     0/imm32/outputs
 8794     "0f 8e/jump-if-<="/imm32/subx-name
 8795     0/imm32/no-rm32
 8796     0/imm32/no-r32
 8797     0/imm32/no-imm32
 8798     1/imm32/disp32-is-first-inout
 8799     0/imm32/no-output
 8800     _Primitive-loop-if->-named/imm32/next
 8801 _Primitive-loop-if->-named:
 8802     "loop-if->"/imm32/name
 8803     Single-lit-var/imm32/inouts
 8804     0/imm32/outputs
 8805     "0f 8f/jump-if->"/imm32/subx-name
 8806     0/imm32/no-rm32
 8807     0/imm32/no-r32
 8808     0/imm32/no-imm32
 8809     1/imm32/disp32-is-first-inout
 8810     0/imm32/no-output
 8811     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
 8812 _Primitive-loop-named:
 8813     "loop"/imm32/name
 8814     Single-lit-var/imm32/inouts
 8815     0/imm32/outputs
 8816     "e9/jump"/imm32/subx-name
 8817     0/imm32/no-rm32
 8818     0/imm32/no-r32
 8819     0/imm32/no-imm32
 8820     1/imm32/disp32-is-first-inout
 8821     0/imm32/no-output
 8822     0/imm32/next
 8823 
 8824 Single-int-var-in-mem:
 8825     Int-var-in-mem/imm32
 8826     0/imm32/next
 8827 
 8828 Int-var-in-mem:
 8829     "arg1"/imm32/name
 8830     Type-int/imm32
 8831     1/imm32/some-block-depth
 8832     1/imm32/some-stack-offset
 8833     0/imm32/no-register
 8834 
 8835 Two-args-int-stack-int-reg:
 8836     Int-var-in-mem/imm32
 8837     Single-int-var-in-some-register/imm32/next
 8838 
 8839 Two-args-int-reg-int-stack:
 8840     Int-var-in-some-register/imm32
 8841     Single-int-var-in-mem/imm32/next
 8842 
 8843 Two-args-int-eax-int-literal:
 8844     Int-var-in-eax/imm32
 8845     Single-lit-var/imm32/next
 8846 
 8847 Int-var-and-literal:
 8848     Int-var-in-mem/imm32
 8849     Single-lit-var/imm32/next
 8850 
 8851 Int-var-in-register-and-literal:
 8852     Int-var-in-some-register/imm32
 8853     Single-lit-var/imm32/next
 8854 
 8855 Single-int-var-in-some-register:
 8856     Int-var-in-some-register/imm32
 8857     0/imm32/next
 8858 
 8859 Int-var-in-some-register:
 8860     "arg1"/imm32/name
 8861     Type-int/imm32
 8862     1/imm32/some-block-depth
 8863     0/imm32/no-stack-offset
 8864     Any-register/imm32
 8865 
 8866 Single-int-var-in-eax:
 8867     Int-var-in-eax/imm32
 8868     0/imm32/next
 8869 
 8870 Int-var-in-eax:
 8871     "arg1"/imm32/name
 8872     Type-int/imm32
 8873     1/imm32/some-block-depth
 8874     0/imm32/no-stack-offset
 8875     "eax"/imm32/register
 8876 
 8877 Single-int-var-in-ecx:
 8878     Int-var-in-ecx/imm32
 8879     0/imm32/next
 8880 
 8881 Int-var-in-ecx:
 8882     "arg1"/imm32/name
 8883     Type-int/imm32
 8884     1/imm32/some-block-depth
 8885     0/imm32/no-stack-offset
 8886     "ecx"/imm32/register
 8887 
 8888 Single-int-var-in-edx:
 8889     Int-var-in-edx/imm32
 8890     0/imm32/next
 8891 
 8892 Int-var-in-edx:
 8893     "arg1"/imm32/name
 8894     Type-int/imm32
 8895     1/imm32/some-block-depth
 8896     0/imm32/no-stack-offset
 8897     "edx"/imm32/register
 8898 
 8899 Single-int-var-in-ebx:
 8900     Int-var-in-ebx/imm32
 8901     0/imm32/next
 8902 
 8903 Int-var-in-ebx:
 8904     "arg1"/imm32/name
 8905     Type-int/imm32
 8906     1/imm32/some-block-depth
 8907     0/imm32/no-stack-offset
 8908     "ebx"/imm32/register
 8909 
 8910 Single-int-var-in-esi:
 8911     Int-var-in-esi/imm32
 8912     0/imm32/next
 8913 
 8914 Int-var-in-esi:
 8915     "arg1"/imm32/name
 8916     Type-int/imm32
 8917     1/imm32/some-block-depth
 8918     0/imm32/no-stack-offset
 8919     "esi"/imm32/register
 8920 
 8921 Single-int-var-in-edi:
 8922     Int-var-in-edi/imm32
 8923     0/imm32/next
 8924 
 8925 Int-var-in-edi:
 8926     "arg1"/imm32/name
 8927     Type-int/imm32
 8928     1/imm32/some-block-depth
 8929     0/imm32/no-stack-offset
 8930     "edi"/imm32/register
 8931 
 8932 Single-lit-var:
 8933     Lit-var/imm32
 8934     0/imm32/next
 8935 
 8936 Lit-var:
 8937     "literal"/imm32/name
 8938     Type-literal/imm32
 8939     1/imm32/some-block-depth
 8940     0/imm32/no-stack-offset
 8941     0/imm32/no-register
 8942 
 8943 Type-int:
 8944     1/imm32/left/int
 8945     0/imm32/right/null
 8946 
 8947 Type-literal:
 8948     0/imm32/left/literal
 8949     0/imm32/right/null
 8950 
 8951 == code
 8952 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle stmt), primitive: (handle function)
 8953     # . prologue
 8954     55/push-ebp
 8955     89/<- %ebp 4/r32/esp
 8956     # . save registers
 8957     50/push-eax
 8958     51/push-ecx
 8959     # ecx = primitive
 8960     8b/-> *(ebp+0x10) 1/r32/ecx
 8961     # emit primitive name
 8962     (emit-indent *(ebp+8) *Curr-block-depth)
 8963     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
 8964     # emit rm32 if necessary
 8965     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
 8966     # emit r32 if necessary
 8967     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
 8968     # emit imm32 if necessary
 8969     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
 8970     # emit disp32 if necessary
 8971     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
 8972     (write-buffered *(ebp+8) Newline)
 8973 $emit-subx-primitive:end:
 8974     # . restore registers
 8975     59/pop-to-ecx
 8976     58/pop-to-eax
 8977     # . epilogue
 8978     89/<- %esp 5/r32/ebp
 8979     5d/pop-to-ebp
 8980     c3/return
 8981 
 8982 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 8983     # . prologue
 8984     55/push-ebp
 8985     89/<- %ebp 4/r32/esp
 8986     # . save registers
 8987     50/push-eax
 8988     # if (l == 0) return
 8989     81 7/subop/compare *(ebp+0xc) 0/imm32
 8990     74/jump-if-= $emit-subx-rm32:end/disp8
 8991     # var v/eax: (handle var)
 8992     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 8993     (emit-subx-var-as-rm32 *(ebp+8) %eax)
 8994 $emit-subx-rm32:end:
 8995     # . restore registers
 8996     58/pop-to-eax
 8997     # . epilogue
 8998     89/<- %esp 5/r32/ebp
 8999     5d/pop-to-ebp
 9000     c3/return
 9001 
 9002 get-stmt-operand-from-arg-location:  # stmt: (handle stmt), l: arg-location -> var/eax: (handle stmt-var)
 9003     # . prologue
 9004     55/push-ebp
 9005     89/<- %ebp 4/r32/esp
 9006     # . save registers
 9007     51/push-ecx
 9008     # eax = l
 9009     8b/-> *(ebp+0xc) 0/r32/eax
 9010     # ecx = stmt
 9011     8b/-> *(ebp+8) 1/r32/ecx
 9012     # if (l == 1) return stmt->inouts
 9013     {
 9014       3d/compare-eax-and 1/imm32
 9015       75/jump-if-!= break/disp8
 9016 $get-stmt-operand-from-arg-location:1:
 9017       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9018       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9019     }
 9020     # if (l == 2) return stmt->inouts->next
 9021     {
 9022       3d/compare-eax-and 2/imm32
 9023       75/jump-if-!= break/disp8
 9024 $get-stmt-operand-from-arg-location:2:
 9025       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9026       8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 9027       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9028     }
 9029     # if (l == 3) return stmt->outputs
 9030     {
 9031       3d/compare-eax-and 3/imm32
 9032       75/jump-if-!= break/disp8
 9033 $get-stmt-operand-from-arg-location:3:
 9034       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 9035       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9036     }
 9037     # abort
 9038     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
 9039 $get-stmt-operand-from-arg-location:end:
 9040     # . restore registers
 9041     59/pop-to-ecx
 9042     # . epilogue
 9043     89/<- %esp 5/r32/ebp
 9044     5d/pop-to-ebp
 9045     c3/return
 9046 
 9047 $get-stmt-operand-from-arg-location:abort:
 9048     # error("invalid arg-location " eax)
 9049     (write-buffered Stderr "invalid arg-location ")
 9050     (print-int32-buffered Stderr %eax)
 9051     (write-buffered Stderr Newline)
 9052     (flush Stderr)
 9053     # . syscall(exit, 1)
 9054     bb/copy-to-ebx  1/imm32
 9055     b8/copy-to-eax  1/imm32/exit
 9056     cd/syscall  0x80/imm8
 9057     # never gets here
 9058 
 9059 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9060     # . prologue
 9061     55/push-ebp
 9062     89/<- %ebp 4/r32/esp
 9063     # . save registers
 9064     50/push-eax
 9065     51/push-ecx
 9066     # if (location == 0) return
 9067     81 7/subop/compare *(ebp+0xc) 0/imm32
 9068     0f 84/jump-if-= $emit-subx-r32:end/disp32
 9069     # var v/eax: (handle var)
 9070     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9071     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9072     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
 9073     (write-buffered *(ebp+8) Space)
 9074     (print-int32-buffered *(ebp+8) *eax)
 9075     (write-buffered *(ebp+8) "/r32")
 9076 $emit-subx-r32:end:
 9077     # . restore registers
 9078     59/pop-to-ecx
 9079     58/pop-to-eax
 9080     # . epilogue
 9081     89/<- %esp 5/r32/ebp
 9082     5d/pop-to-ebp
 9083     c3/return
 9084 
 9085 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9086     # . prologue
 9087     55/push-ebp
 9088     89/<- %ebp 4/r32/esp
 9089     # . save registers
 9090     50/push-eax
 9091     51/push-ecx
 9092     # if (location == 0) return
 9093     81 7/subop/compare *(ebp+0xc) 0/imm32
 9094     74/jump-if-= $emit-subx-imm32:end/disp8
 9095     # var v/eax: (handle var)
 9096     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9097     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9098     (write-buffered *(ebp+8) Space)
 9099     (write-buffered *(ebp+8) *eax)  # Var-name
 9100     (write-buffered *(ebp+8) "/imm32")
 9101 $emit-subx-imm32:end:
 9102     # . restore registers
 9103     59/pop-to-ecx
 9104     58/pop-to-eax
 9105     # . epilogue
 9106     89/<- %esp 5/r32/ebp
 9107     5d/pop-to-ebp
 9108     c3/return
 9109 
 9110 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9111     # . prologue
 9112     55/push-ebp
 9113     89/<- %ebp 4/r32/esp
 9114     # . save registers
 9115     50/push-eax
 9116     51/push-ecx
 9117     # if (location == 0) return
 9118     81 7/subop/compare *(ebp+0xc) 0/imm32
 9119     0f 84/jump-if-= $emit-subx-disp32:end/disp32
 9120     # var v/eax: (handle var)
 9121     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9122     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9123     (write-buffered *(ebp+8) Space)
 9124     (write-buffered *(ebp+8) *eax)  # Var-name
 9125     # hack: if instruction operation starts with "break", emit ":break"
 9126     # var name/ecx: (addr array byte) = stmt->operation
 9127     8b/-> *(ebp+0x10) 0/r32/eax
 9128     8b/-> *(eax+4) 1/r32/ecx
 9129     {
 9130       (string-starts-with? %ecx "break")  # => eax
 9131       3d/compare-eax-and 0/imm32/false
 9132       74/jump-if-= break/disp8
 9133       (write-buffered *(ebp+8) ":break")
 9134     }
 9135     # hack: if instruction operation starts with "loop", emit ":loop"
 9136     {
 9137       (string-starts-with? %ecx "loop")  # => eax
 9138       3d/compare-eax-and 0/imm32/false
 9139       74/jump-if-= break/disp8
 9140       (write-buffered *(ebp+8) ":loop")
 9141     }
 9142     (write-buffered *(ebp+8) "/disp32")
 9143 $emit-subx-disp32:end:
 9144     # . restore registers
 9145     59/pop-to-ecx
 9146     58/pop-to-eax
 9147     # . epilogue
 9148     89/<- %esp 5/r32/ebp
 9149     5d/pop-to-ebp
 9150     c3/return
 9151 
 9152 emit-subx-call:  # out: (addr buffered-file), stmt: (handle stmt), callee: (handle function)
 9153     # . prologue
 9154     55/push-ebp
 9155     89/<- %ebp 4/r32/esp
 9156     # . save registers
 9157     51/push-ecx
 9158     #
 9159     (emit-indent *(ebp+8) *Curr-block-depth)
 9160     (write-buffered *(ebp+8) "(")
 9161     # - emit function name
 9162     8b/-> *(ebp+0x10) 1/r32/ecx
 9163     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
 9164     # - emit arguments
 9165     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9166     8b/-> *(ebp+0xc) 1/r32/ecx
 9167     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9168     {
 9169       # if (curr == null) break
 9170       81 7/subop/compare %ecx 0/imm32
 9171       74/jump-if-= break/disp8
 9172       #
 9173       (emit-subx-call-operand *(ebp+8) %ecx)
 9174       # curr = curr->next
 9175       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9176       eb/jump loop/disp8
 9177     }
 9178     #
 9179     (write-buffered *(ebp+8) ")\n")
 9180 $emit-subx-call:end:
 9181     # . restore registers
 9182     59/pop-to-ecx
 9183     # . epilogue
 9184     89/<- %esp 5/r32/ebp
 9185     5d/pop-to-ebp
 9186     c3/return
 9187 
 9188 # like a function call, except we have no idea what function it is
 9189 # we hope it's defined in SubX and that the types are ok
 9190 emit-hailmary-call:  # out: (addr buffered-file), stmt: (handle stmt)
 9191     # . prologue
 9192     55/push-ebp
 9193     89/<- %ebp 4/r32/esp
 9194     # . save registers
 9195     51/push-ecx
 9196     #
 9197     (emit-indent *(ebp+8) *Curr-block-depth)
 9198     (write-buffered *(ebp+8) "(")
 9199     # ecx = stmt
 9200     8b/-> *(ebp+0xc) 1/r32/ecx
 9201     # - emit function name
 9202     (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
 9203     # - emit arguments
 9204     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9205     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9206     {
 9207       # if (curr == null) break
 9208       81 7/subop/compare %ecx 0/imm32
 9209       74/jump-if-= break/disp8
 9210       #
 9211       (emit-subx-call-operand *(ebp+8) %ecx)
 9212       # curr = curr->next
 9213       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9214       eb/jump loop/disp8
 9215     }
 9216     #
 9217     (write-buffered *(ebp+8) ")\n")
 9218 $emit-hailmary-call:end:
 9219     # . restore registers
 9220     59/pop-to-ecx
 9221     # . epilogue
 9222     89/<- %esp 5/r32/ebp
 9223     5d/pop-to-ebp
 9224     c3/return
 9225 
 9226 emit-subx-call-operand:  # out: (addr buffered-file), s: (handle stmt-var)
 9227     # shares code with emit-subx-var-as-rm32
 9228     # . prologue
 9229     55/push-ebp
 9230     89/<- %ebp 4/r32/esp
 9231     # . save registers
 9232     50/push-eax
 9233     51/push-ecx
 9234     56/push-esi
 9235     # ecx = s
 9236     8b/-> *(ebp+0xc) 1/r32/ecx
 9237     # var operand/esi: (handle var) = s->value
 9238     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9239     # if (operand->register && s->is-deref?) emit "*__"
 9240     {
 9241 $emit-subx-call-operand:check-for-register-indirect:
 9242       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9243       74/jump-if-= break/disp8
 9244       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9245       74/jump-if-= break/disp8
 9246 $emit-subx-call-operand:register-indirect:
 9247       (write-buffered *(ebp+8) " *")
 9248       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9249       e9/jump $emit-subx-call-operand:end/disp32
 9250     }
 9251     # if (operand->register && !s->is-deref?) emit "%__"
 9252     {
 9253 $emit-subx-call-operand:check-for-register-direct:
 9254       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9255       74/jump-if-= break/disp8
 9256       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9257       75/jump-if-!= break/disp8
 9258 $emit-subx-call-operand:register-direct:
 9259       (write-buffered *(ebp+8) " %")
 9260       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9261       e9/jump $emit-subx-call-operand:end/disp32
 9262     }
 9263     # else if (operand->stack-offset) emit "*(ebp+__)"
 9264     {
 9265       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
 9266       74/jump-if-= break/disp8
 9267 $emit-subx-call-operand:stack:
 9268       (write-buffered *(ebp+8) Space)
 9269       (write-buffered *(ebp+8) "*(ebp+")
 9270       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
 9271       (write-buffered *(ebp+8) ")")
 9272       e9/jump $emit-subx-call-operand:end/disp32
 9273     }
 9274     # else if (operand->type == literal) emit "__"
 9275     {
 9276       8b/-> *(esi+4) 0/r32/eax  # Var-type
 9277       81 7/subop/compare *eax 0/imm32  # Tree-left
 9278       75/jump-if-!= break/disp8
 9279 $emit-subx-call-operand:literal:
 9280       (write-buffered *(ebp+8) Space)
 9281       (write-buffered *(ebp+8) *esi)
 9282     }
 9283 $emit-subx-call-operand:end:
 9284     # . restore registers
 9285     5e/pop-to-esi
 9286     59/pop-to-ecx
 9287     58/pop-to-eax
 9288     # . epilogue
 9289     89/<- %esp 5/r32/ebp
 9290     5d/pop-to-ebp
 9291     c3/return
 9292 
 9293 emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (handle stmt-var)
 9294     # . prologue
 9295     55/push-ebp
 9296     89/<- %ebp 4/r32/esp
 9297     # . save registers
 9298     50/push-eax
 9299     51/push-ecx
 9300     56/push-esi
 9301     # ecx = s
 9302     8b/-> *(ebp+0xc) 1/r32/ecx
 9303     # var operand/esi: (handle var) = s->value
 9304     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9305     # if (operand->register && s->is-deref?) emit "*__"
 9306     {
 9307 $emit-subx-var-as-rm32:check-for-register-indirect:
 9308       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9309       74/jump-if-= break/disp8
 9310       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9311       74/jump-if-= break/disp8
 9312 $emit-subx-var-as-rm32:register-indirect:
 9313       (write-buffered *(ebp+8) " *")
 9314       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9315     }
 9316     # if (operand->register && !s->is-deref?) emit "%__"
 9317     {
 9318 $emit-subx-var-as-rm32:check-for-register-direct:
 9319       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9320       74/jump-if-= break/disp8
 9321       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9322       75/jump-if-!= break/disp8
 9323 $emit-subx-var-as-rm32:register-direct:
 9324       (write-buffered *(ebp+8) " %")
 9325       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9326     }
 9327     # else if (operand->stack-offset) emit "*(ebp+__)"
 9328     {
 9329       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
 9330       74/jump-if-= break/disp8
 9331 $emit-subx-var-as-rm32:stack:
 9332       (write-buffered *(ebp+8) Space)
 9333       (write-buffered *(ebp+8) "*(ebp+")
 9334       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
 9335       (write-buffered *(ebp+8) ")")
 9336     }
 9337 $emit-subx-var-as-rm32:end:
 9338     # . restore registers
 9339     5e/pop-to-esi
 9340     59/pop-to-ecx
 9341     58/pop-to-eax
 9342     # . epilogue
 9343     89/<- %esp 5/r32/ebp
 9344     5d/pop-to-ebp
 9345     c3/return
 9346 
 9347 find-matching-function:  # functions: (addr function), stmt: (handle stmt) -> result/eax: (handle function)
 9348     # . prologue
 9349     55/push-ebp
 9350     89/<- %ebp 4/r32/esp
 9351     # . save registers
 9352     51/push-ecx
 9353     # var curr/ecx: (handle function) = functions
 9354     8b/-> *(ebp+8) 1/r32/ecx
 9355     {
 9356       # if (curr == null) break
 9357       81 7/subop/compare %ecx 0/imm32
 9358       74/jump-if-= break/disp8
 9359       # if match(stmt, curr) return curr
 9360       {
 9361         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
 9362         3d/compare-eax-and 0/imm32/false
 9363         74/jump-if-= break/disp8
 9364         89/<- %eax 1/r32/ecx
 9365         eb/jump $find-matching-function:end/disp8
 9366       }
 9367       # curr = curr->next
 9368       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 9369       eb/jump loop/disp8
 9370     }
 9371     # return null
 9372     b8/copy-to-eax 0/imm32
 9373 $find-matching-function:end:
 9374     # . restore registers
 9375     59/pop-to-ecx
 9376     # . epilogue
 9377     89/<- %esp 5/r32/ebp
 9378     5d/pop-to-ebp
 9379     c3/return
 9380 
 9381 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle stmt) -> result/eax: (handle primitive)
 9382     # . prologue
 9383     55/push-ebp
 9384     89/<- %ebp 4/r32/esp
 9385     # . save registers
 9386     51/push-ecx
 9387     # var curr/ecx: (handle primitive) = primitives
 9388     8b/-> *(ebp+8) 1/r32/ecx
 9389     {
 9390 $find-matching-primitive:loop:
 9391       # if (curr == null) break
 9392       81 7/subop/compare %ecx 0/imm32
 9393       0f 84/jump-if-= break/disp32
 9394 #?       (write-buffered Stderr "prim: ")
 9395 #?       (write-buffered Stderr *ecx)  # Primitive-name
 9396 #?       (write-buffered Stderr " => ")
 9397 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
 9398 #?       (write-buffered Stderr Newline)
 9399 #?       (flush Stderr)
 9400       # if match(curr, stmt) return curr
 9401       {
 9402         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
 9403         3d/compare-eax-and 0/imm32/false
 9404         74/jump-if-= break/disp8
 9405         89/<- %eax 1/r32/ecx
 9406         eb/jump $find-matching-primitive:end/disp8
 9407       }
 9408 $find-matching-primitive:next-primitive:
 9409       # curr = curr->next
 9410       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
 9411       e9/jump loop/disp32
 9412     }
 9413     # return null
 9414     b8/copy-to-eax 0/imm32
 9415 $find-matching-primitive:end:
 9416     # . restore registers
 9417     59/pop-to-ecx
 9418     # . epilogue
 9419     89/<- %esp 5/r32/ebp
 9420     5d/pop-to-ebp
 9421     c3/return
 9422 
 9423 mu-stmt-matches-function?:  # stmt: (handle stmt), function: (handle function) -> result/eax: boolean
 9424     # . prologue
 9425     55/push-ebp
 9426     89/<- %ebp 4/r32/esp
 9427     # . save registers
 9428     51/push-ecx
 9429     # return function->name == stmt->operation
 9430     8b/-> *(ebp+8) 1/r32/ecx
 9431     8b/-> *(ebp+0xc) 0/r32/eax
 9432     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
 9433 $mu-stmt-matches-function?:end:
 9434     # . restore registers
 9435     59/pop-to-ecx
 9436     # . epilogue
 9437     89/<- %esp 5/r32/ebp
 9438     5d/pop-to-ebp
 9439     c3/return
 9440 
 9441 mu-stmt-matches-primitive?:  # stmt: (handle stmt), primitive: (handle primitive) -> result/eax: boolean
 9442     # A mu stmt matches a primitive if the name matches, all the inout vars
 9443     # match, and all the output vars match.
 9444     # Vars match if types match and registers match.
 9445     # In addition, a stmt output matches a primitive's output if types match
 9446     # and the primitive has a wildcard register.
 9447     # . prologue
 9448     55/push-ebp
 9449     89/<- %ebp 4/r32/esp
 9450     # . save registers
 9451     51/push-ecx
 9452     52/push-edx
 9453     53/push-ebx
 9454     56/push-esi
 9455     57/push-edi
 9456     # ecx = stmt
 9457     8b/-> *(ebp+8) 1/r32/ecx
 9458     # edx = primitive
 9459     8b/-> *(ebp+0xc) 2/r32/edx
 9460     {
 9461 $mu-stmt-matches-primitive?:check-name:
 9462       # if (primitive->name != stmt->operation) return false
 9463       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
 9464       3d/compare-eax-and 0/imm32/false
 9465       75/jump-if-!= break/disp8
 9466       b8/copy-to-eax 0/imm32
 9467       e9/jump $mu-stmt-matches-primitive?:end/disp32
 9468     }
 9469 $mu-stmt-matches-primitive?:check-inouts:
 9470     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
 9471     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
 9472     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
 9473     {
 9474       # if (curr == 0 && curr2 == 0) move on to check outputs
 9475       {
 9476         81 7/subop/compare %esi 0/imm32
 9477         75/jump-if-!= break/disp8
 9478 $mu-stmt-matches-primitive?:stmt-inout-is-null:
 9479         {
 9480           81 7/subop/compare %edi 0/imm32
 9481           75/jump-if-!= break/disp8
 9482           #
 9483           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
 9484         }
 9485         # return false
 9486         b8/copy-to-eax 0/imm32/false
 9487         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9488       }
 9489       # if (curr2 == 0) return false
 9490       {
 9491         81 7/subop/compare %edi 0/imm32
 9492         75/jump-if-!= break/disp8
 9493 $mu-stmt-matches-primitive?:prim-inout-is-null:
 9494         b8/copy-to-eax 0/imm32/false
 9495         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9496       }
 9497       # if (curr != curr2) return false
 9498       {
 9499         (operand-matches-primitive? %esi *edi)  # List-value => eax
 9500         3d/compare-eax-and 0/imm32/false
 9501         75/jump-if-!= break/disp8
 9502         b8/copy-to-eax 0/imm32/false
 9503         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9504       }
 9505       # curr=curr->next
 9506       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
 9507       # curr2=curr2->next
 9508       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
 9509       eb/jump loop/disp8
 9510     }
 9511 $mu-stmt-matches-primitive?:check-outputs:
 9512     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
 9513     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
 9514     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
 9515     {
 9516       # if (curr == 0) return (curr2 == 0)
 9517       {
 9518 $mu-stmt-matches-primitive?:check-output:
 9519         81 7/subop/compare %esi 0/imm32
 9520         75/jump-if-!= break/disp8
 9521         {
 9522           81 7/subop/compare %edi 0/imm32
 9523           75/jump-if-!= break/disp8
 9524           # return true
 9525           b8/copy-to-eax 1/imm32
 9526           e9/jump $mu-stmt-matches-primitive?:end/disp32
 9527         }
 9528         # return false
 9529         b8/copy-to-eax 0/imm32
 9530         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9531       }
 9532       # if (curr2 == 0) return false
 9533       {
 9534         81 7/subop/compare %edi 0/imm32
 9535         75/jump-if-!= break/disp8
 9536         b8/copy-to-eax 0/imm32
 9537         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9538       }
 9539       # if (curr != curr2) return false
 9540       {
 9541         (operand-matches-primitive? %esi *edi)  # List-value => eax
 9542         3d/compare-eax-and 0/imm32/false
 9543         75/jump-if-!= break/disp8
 9544         b8/copy-to-eax 0/imm32
 9545         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9546       }
 9547       # curr=curr->next
 9548       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
 9549       # curr2=curr2->next
 9550       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
 9551       eb/jump loop/disp8
 9552     }
 9553 $mu-stmt-matches-primitive?:return-true:
 9554     b8/copy-to-eax 1/imm32
 9555 $mu-stmt-matches-primitive?:end:
 9556     # . restore registers
 9557     5f/pop-to-edi
 9558     5e/pop-to-esi
 9559     5b/pop-to-ebx
 9560     5a/pop-to-edx
 9561     59/pop-to-ecx
 9562     # . epilogue
 9563     89/<- %esp 5/r32/ebp
 9564     5d/pop-to-ebp
 9565     c3/return
 9566 
 9567 operand-matches-primitive?:  # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
 9568     # . prologue
 9569     55/push-ebp
 9570     89/<- %ebp 4/r32/esp
 9571     # . save registers
 9572     51/push-ecx
 9573     56/push-esi
 9574     57/push-edi
 9575     # ecx = s
 9576     8b/-> *(ebp+8) 1/r32/ecx
 9577     # var var/esi : (handle var) = s->value
 9578     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9579     # edi = prim-var
 9580     8b/-> *(ebp+0xc) 7/r32/edi
 9581 $operand-matches-primitive?:check-type:
 9582     # if (var->type != prim-var->type) return false
 9583     (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
 9584     3d/compare-eax-and 0/imm32/false
 9585     b8/copy-to-eax 0/imm32/false
 9586     0f 84/jump-if-= $operand-matches-primitive?:end/disp32
 9587     {
 9588 $operand-matches-primitive?:check-register:
 9589       # if prim-var is in memory and var is in register but dereference, match
 9590       {
 9591         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
 9592         0f 85/jump-if-!= break/disp32
 9593         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9594         74/jump-if-= break/disp8
 9595         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9596         74/jump-if-= break/disp8
 9597         e9/jump $operand-matches-primitive?:return-true/disp32
 9598       }
 9599       # if prim-var is in register and var is in register but dereference, no match
 9600       {
 9601         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
 9602         0f 84/jump-if-= break/disp32
 9603         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9604         0f 84/jump-if-= break/disp32
 9605         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9606         74/jump-if-= break/disp8
 9607         e9/jump $operand-matches-primitive?:return-false/disp32
 9608       }
 9609       # return false if var->register doesn't match prim-var->register
 9610       {
 9611         # if register addresses are equal, it's a match
 9612         8b/-> *(esi+0x10) 0/r32/eax  # Var-register
 9613         39/compare *(edi+0x10) 0/r32/eax  # Var-register
 9614         74/jump-if-= break/disp8
 9615         # if either address is 0, return false
 9616         3d/compare-eax-and 0/imm32
 9617         74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
 9618         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
 9619         74/jump-if-=  $operand-matches-primitive?:return-false/disp8
 9620         # if prim-var->register is wildcard, it's a match
 9621         (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
 9622         3d/compare-eax-and 0/imm32/false
 9623         75/jump-if-!= break/disp8
 9624         # if string contents aren't equal, return false
 9625         (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
 9626         3d/compare-eax-and 0/imm32/false
 9627         74/jump-if-= $operand-matches-primitive?:return-false/disp8
 9628       }
 9629     }
 9630 $operand-matches-primitive?:return-true:
 9631     b8/copy-to-eax 1/imm32/true
 9632     eb/jump $operand-matches-primitive?:end/disp8
 9633 $operand-matches-primitive?:return-false:
 9634     b8/copy-to-eax 0/imm32/false
 9635 $operand-matches-primitive?:end:
 9636     # . restore registers
 9637     5f/pop-to-edi
 9638     5e/pop-to-esi
 9639     59/pop-to-ecx
 9640     # . epilogue
 9641     89/<- %esp 5/r32/ebp
 9642     5d/pop-to-ebp
 9643     c3/return
 9644 
 9645 subx-type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
 9646     # . prologue
 9647     55/push-ebp
 9648     89/<- %ebp 4/r32/esp
 9649     # . save registers
 9650     51/push-ecx
 9651     # var alit/ecx: boolean = is-literal-type?(a)
 9652     (is-simple-mu-type? *(ebp+8) 0)  # => eax
 9653     89/<- %ecx 0/r32/eax
 9654     # var blit/eax: boolean = is-literal-type?(b)
 9655     (is-simple-mu-type? *(ebp+0xc) 0)  # => eax
 9656     # return alit == blit
 9657     39/compare %eax 1/r32/ecx
 9658     0f 94/set-if-= %eax
 9659     81 4/subop/and %eax 0xff/imm32
 9660 $subx-type-equal?:end:
 9661     # . restore registers
 9662     59/pop-to-ecx
 9663     # . epilogue
 9664     89/<- %esp 5/r32/ebp
 9665     5d/pop-to-ebp
 9666     c3/return
 9667 
 9668 is-simple-mu-type?:  # a: (handle tree type-id), n: type-id -> result/eax: boolean
 9669     # . prologue
 9670     55/push-ebp
 9671     89/<- %ebp 4/r32/esp
 9672     # . save registers
 9673     51/push-ecx
 9674     # ecx = n
 9675     8b/-> *(ebp+0xc) 1/r32/ecx
 9676     # return (a->value == n)
 9677     8b/-> *(ebp+8) 0/r32/eax
 9678     39/compare *eax 1/r32/ecx  # Atom-type
 9679     0f 94/set-if-= %eax
 9680     81 4/subop/and %eax 0xff/imm32
 9681 $is-simple-mu-type?:end:
 9682     # . restore registers
 9683     59/pop-to-ecx
 9684     # . epilogue
 9685     89/<- %esp 5/r32/ebp
 9686     5d/pop-to-ebp
 9687     c3/return
 9688 
 9689 test-emit-subx-stmt-primitive:
 9690     # Primitive operation on a variable on the stack.
 9691     #   increment foo
 9692     # =>
 9693     #   ff 0/subop/increment *(ebp-8)
 9694     #
 9695     # There's a variable on the var stack as follows:
 9696     #   name: 'foo'
 9697     #   type: int
 9698     #   stack-offset: -8
 9699     #
 9700     # There's a primitive with this info:
 9701     #   name: 'increment'
 9702     #   inouts: int/mem
 9703     #   value: 'ff 0/subop/increment'
 9704     #
 9705     # There's nothing in functions.
 9706     #
 9707     # . prologue
 9708     55/push-ebp
 9709     89/<- %ebp 4/r32/esp
 9710     # setup
 9711     (clear-stream _test-output-stream)
 9712     (clear-stream $_test-output-buffered-file->buffer)
 9713     # var type/ecx: (handle tree type-id) = int
 9714     68/push 0/imm32/right/null
 9715     68/push 1/imm32/left/int
 9716     89/<- %ecx 4/r32/esp
 9717     # var var-foo/ecx: var
 9718     68/push 0/imm32/no-register
 9719     68/push -8/imm32/stack-offset
 9720     68/push 1/imm32/block-depth
 9721     51/push-ecx
 9722     68/push "foo"/imm32
 9723     89/<- %ecx 4/r32/esp
 9724     # var operand/ebx: (handle stmt-var)
 9725     68/push 0/imm32/is-deref:false
 9726     68/push 0/imm32/next
 9727     51/push-ecx/var-foo
 9728     89/<- %ebx 4/r32/esp
 9729     # var stmt/esi: statement
 9730     68/push 0/imm32/next
 9731     68/push 0/imm32/outputs
 9732     53/push-ebx/operands
 9733     68/push "increment"/imm32/operation
 9734     68/push 1/imm32
 9735     89/<- %esi 4/r32/esp
 9736     # var primitives/ebx: primitive
 9737     68/push 0/imm32/next
 9738     68/push 0/imm32/output-is-write-only
 9739     68/push 0/imm32/no-disp32
 9740     68/push 0/imm32/no-imm32
 9741     68/push 0/imm32/no-r32
 9742     68/push 1/imm32/rm32-is-first-inout
 9743     68/push "ff 0/subop/increment"/imm32/subx-name
 9744     68/push 0/imm32/outputs
 9745     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
 9746     68/push "increment"/imm32/name
 9747     89/<- %ebx 4/r32/esp
 9748     # convert
 9749     c7 0/subop/copy *Curr-block-depth 0/imm32
 9750     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
 9751     (flush _test-output-buffered-file)
 9752 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 9758     # check output
 9759     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
 9760     # . epilogue
 9761     89/<- %esp 5/r32/ebp
 9762     5d/pop-to-ebp
 9763     c3/return
 9764 
 9765 test-emit-subx-stmt-primitive-register:
 9766     # Primitive operation on a variable in a register.
 9767     #   foo <- increment
 9768     # =>
 9769     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
 9770     #
 9771     # There's a variable on the var stack as follows:
 9772     #   name: 'foo'
 9773     #   type: int
 9774     #   register: 'eax'
 9775     #
 9776     # There's a primitive with this info:
 9777     #   name: 'increment'
 9778     #   out: int/reg
 9779     #   value: 'ff 0/subop/increment'
 9780     #
 9781     # There's nothing in functions.
 9782     #
 9783     # . prologue
 9784     55/push-ebp
 9785     89/<- %ebp 4/r32/esp
 9786     # setup
 9787     (clear-stream _test-output-stream)
 9788     (clear-stream $_test-output-buffered-file->buffer)
 9789     # var type/ecx: (handle tree type-id) = int
 9790     68/push 0/imm32/right/null
 9791     68/push 1/imm32/left/int
 9792     89/<- %ecx 4/r32/esp
 9793     # var var-foo/ecx: var in eax
 9794     68/push "eax"/imm32/register
 9795     68/push 0/imm32/no-stack-offset
 9796     68/push 1/imm32/block-depth
 9797     51/push-ecx
 9798     68/push "foo"/imm32
 9799     89/<- %ecx 4/r32/esp
 9800     # var operand/ebx: (handle stmt-var)
 9801     68/push 0/imm32/is-deref:false
 9802     68/push 0/imm32/next
 9803     51/push-ecx/var-foo
 9804     89/<- %ebx 4/r32/esp
 9805     # var stmt/esi: statement
 9806     68/push 0/imm32/next
 9807     53/push-ebx/outputs
 9808     68/push 0/imm32/inouts
 9809     68/push "increment"/imm32/operation
 9810     68/push 1/imm32
 9811     89/<- %esi 4/r32/esp
 9812     # var formal-var/ebx: var in any register
 9813     68/push Any-register/imm32
 9814     68/push 0/imm32/no-stack-offset
 9815     68/push 1/imm32/block-depth
 9816     ff 6/subop/push *(ecx+4)  # Var-type
 9817     68/push "dummy"/imm32
 9818     89/<- %ebx 4/r32/esp
 9819     # var operand/ebx: (handle stmt-var)
 9820     68/push 0/imm32/is-deref:false
 9821     68/push 0/imm32/next
 9822     53/push-ebx/formal-var
 9823     89/<- %ebx 4/r32/esp
 9824     # var primitives/ebx: primitive
 9825     68/push 0/imm32/next
 9826     68/push 0/imm32/output-is-write-only
 9827     68/push 0/imm32/no-disp32
 9828     68/push 0/imm32/no-imm32
 9829     68/push 0/imm32/no-r32
 9830     68/push 3/imm32/rm32-in-first-output
 9831     68/push "ff 0/subop/increment"/imm32/subx-name
 9832     53/push-ebx/outputs
 9833     68/push 0/imm32/inouts
 9834     68/push "increment"/imm32/name
 9835     89/<- %ebx 4/r32/esp
 9836     # convert
 9837     c7 0/subop/copy *Curr-block-depth 0/imm32
 9838     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
 9839     (flush _test-output-buffered-file)
 9840 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 9846     # check output
 9847     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
 9848     # . epilogue
 9849     89/<- %esp 5/r32/ebp
 9850     5d/pop-to-ebp
 9851     c3/return
 9852 
 9853 test-emit-subx-stmt-select-primitive:
 9854     # Select the right primitive between overloads.
 9855     #   foo <- increment
 9856     # =>
 9857     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
 9858     #
 9859     # There's a variable on the var stack as follows:
 9860     #   name: 'foo'
 9861     #   type: int
 9862     #   register: 'eax'
 9863     #
 9864     # There's two primitives, as follows:
 9865     #   - name: 'increment'
 9866     #     out: int/reg
 9867     #     value: 'ff 0/subop/increment'
 9868     #   - name: 'increment'
 9869     #     inout: int/mem
 9870     #     value: 'ff 0/subop/increment'
 9871     #
 9872     # There's nothing in functions.
 9873     #
 9874     # . prologue
 9875     55/push-ebp
 9876     89/<- %ebp 4/r32/esp
 9877     # setup
 9878     (clear-stream _test-output-stream)
 9879     (clear-stream $_test-output-buffered-file->buffer)
 9880     # var type/ecx: (handle tree type-id) = int
 9881     68/push 0/imm32/right/null
 9882     68/push 1/imm32/left/int
 9883     89/<- %ecx 4/r32/esp
 9884     # var var-foo/ecx: var in eax
 9885     68/push "eax"/imm32/register
 9886     68/push 0/imm32/no-stack-offset
 9887     68/push 1/imm32/block-depth
 9888     51/push-ecx
 9889     68/push "foo"/imm32
 9890     89/<- %ecx 4/r32/esp
 9891     # var real-outputs/edi: (handle stmt-var)
 9892     68/push 0/imm32/is-deref:false
 9893     68/push 0/imm32/next
 9894     51/push-ecx/var-foo
 9895     89/<- %edi 4/r32/esp
 9896     # var stmt/esi: statement
 9897     68/push 0/imm32/next
 9898     57/push-edi/outputs
 9899     68/push 0/imm32/inouts
 9900     68/push "increment"/imm32/operation
 9901     68/push 1/imm32
 9902     89/<- %esi 4/r32/esp
 9903     # var formal-var/ebx: var in any register
 9904     68/push Any-register/imm32
 9905     68/push 0/imm32/no-stack-offset
 9906     68/push 1/imm32/block-depth
 9907     ff 6/subop/push *(ecx+4)  # Var-type
 9908     68/push "dummy"/imm32
 9909     89/<- %ebx 4/r32/esp
 9910     # var formal-outputs/ebx: (handle stmt-var)
 9911     68/push 0/imm32/is-deref:false
 9912     68/push 0/imm32/next
 9913     53/push-ebx/formal-var
 9914     89/<- %ebx 4/r32/esp
 9915     # var primitive1/ebx: primitive
 9916     68/push 0/imm32/next
 9917     68/push 0/imm32/output-is-write-only
 9918     68/push 0/imm32/no-disp32
 9919     68/push 0/imm32/no-imm32
 9920     68/push 0/imm32/no-r32
 9921     68/push 3/imm32/rm32-in-first-output
 9922     68/push "ff 0/subop/increment"/imm32/subx-name
 9923     53/push-ebx/outputs/formal-outputs
 9924     68/push 0/imm32/inouts
 9925     68/push "increment"/imm32/name
 9926     89/<- %ebx 4/r32/esp
 9927     # var primitives/ebx: primitive
 9928     53/push-ebx/next
 9929     68/push 0/imm32/output-is-write-only
 9930     68/push 0/imm32/no-disp32
 9931     68/push 0/imm32/no-imm32
 9932     68/push 0/imm32/no-r32
 9933     68/push 1/imm32/rm32-is-first-inout
 9934     68/push "ff 0/subop/increment"/imm32/subx-name
 9935     68/push 0/imm32/outputs
 9936     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
 9937     68/push "increment"/imm32/name
 9938     89/<- %ebx 4/r32/esp
 9939     # convert
 9940     c7 0/subop/copy *Curr-block-depth 0/imm32
 9941     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
 9942     (flush _test-output-buffered-file)
 9943 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 9949     # check output
 9950     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
 9951     # . epilogue
 9952     89/<- %esp 5/r32/ebp
 9953     5d/pop-to-ebp
 9954     c3/return
 9955 
 9956 test-emit-subx-stmt-select-primitive-2:
 9957     # Select the right primitive between overloads.
 9958     #   foo <- increment
 9959     # =>
 9960     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
 9961     #
 9962     # There's a variable on the var stack as follows:
 9963     #   name: 'foo'
 9964     #   type: int
 9965     #   register: 'eax'
 9966     #
 9967     # There's two primitives, as follows:
 9968     #   - name: 'increment'
 9969     #     out: int/reg
 9970     #     value: 'ff 0/subop/increment'
 9971     #   - name: 'increment'
 9972     #     inout: int/mem
 9973     #     value: 'ff 0/subop/increment'
 9974     #
 9975     # There's nothing in functions.
 9976     #
 9977     # . prologue
 9978     55/push-ebp
 9979     89/<- %ebp 4/r32/esp
 9980     # setup
 9981     (clear-stream _test-output-stream)
 9982     (clear-stream $_test-output-buffered-file->buffer)
 9983     # var type/ecx: (handle tree type-id) = int
 9984     68/push 0/imm32/right/null
 9985     68/push 1/imm32/left/int
 9986     89/<- %ecx 4/r32/esp
 9987     # var var-foo/ecx: var in eax
 9988     68/push "eax"/imm32/register
 9989     68/push 0/imm32/no-stack-offset
 9990     68/push 1/imm32/block-depth
 9991     51/push-ecx
 9992     68/push "foo"/imm32
 9993     89/<- %ecx 4/r32/esp
 9994     # var inouts/edi: (handle stmt-var)
 9995     68/push 0/imm32/is-deref:false
 9996     68/push 0/imm32/next
 9997     51/push-ecx/var-foo
 9998     89/<- %edi 4/r32/esp
 9999     # var stmt/esi: statement
10000     68/push 0/imm32/next
10001     68/push 0/imm32/outputs
10002     57/push-edi/inouts
10003     68/push "increment"/imm32/operation
10004     68/push 1/imm32
10005     89/<- %esi 4/r32/esp
10006     # var formal-var/ebx: var in any register
10007     68/push Any-register/imm32
10008     68/push 0/imm32/no-stack-offset
10009     68/push 1/imm32/block-depth
10010     ff 6/subop/push *(ecx+4)  # Var-type
10011     68/push "dummy"/imm32
10012     89/<- %ebx 4/r32/esp
10013     # var operand/ebx: (handle stmt-var)
10014     68/push 0/imm32/is-deref:false
10015     68/push 0/imm32/next
10016     53/push-ebx/formal-var
10017     89/<- %ebx 4/r32/esp
10018     # var primitive1/ebx: primitive
10019     68/push 0/imm32/next
10020     68/push 0/imm32/output-is-write-only
10021     68/push 0/imm32/no-disp32
10022     68/push 0/imm32/no-imm32
10023     68/push 0/imm32/no-r32
10024     68/push 3/imm32/rm32-in-first-output
10025     68/push "ff 0/subop/increment"/imm32/subx-name
10026     53/push-ebx/outputs/formal-outputs
10027     68/push 0/imm32/inouts
10028     68/push "increment"/imm32/name
10029     89/<- %ebx 4/r32/esp
10030     # var primitives/ebx: primitive
10031     53/push-ebx/next
10032     68/push 0/imm32/output-is-write-only
10033     68/push 0/imm32/no-disp32
10034     68/push 0/imm32/no-imm32
10035     68/push 0/imm32/no-r32
10036     68/push 1/imm32/rm32-is-first-inout
10037     68/push "ff 0/subop/increment"/imm32/subx-name
10038     68/push 0/imm32/outputs
10039     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10040     68/push "increment"/imm32/name
10041     89/<- %ebx 4/r32/esp
10042     # convert
10043     c7 0/subop/copy *Curr-block-depth 0/imm32
10044     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10045     (flush _test-output-buffered-file)
10046 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10052     # check output
10053     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
10054     # . epilogue
10055     89/<- %esp 5/r32/ebp
10056     5d/pop-to-ebp
10057     c3/return
10058 
10059 test-increment-register:
10060     # Select the right register between overloads.
10061     #   foo <- increment
10062     # =>
10063     #   50/increment-eax
10064     #
10065     # There's a variable on the var stack as follows:
10066     #   name: 'foo'
10067     #   type: int
10068     #   register: 'eax'
10069     #
10070     # Primitives are the global definitions.
10071     #
10072     # There are no functions defined.
10073     #
10074     # . prologue
10075     55/push-ebp
10076     89/<- %ebp 4/r32/esp
10077     # setup
10078     (clear-stream _test-output-stream)
10079     (clear-stream $_test-output-buffered-file->buffer)
10080     # var type/ecx: (handle tree type-id) = int
10081     68/push 0/imm32/right/null
10082     68/push 1/imm32/left/int
10083     89/<- %ecx 4/r32/esp
10084     # var var-foo/ecx: var in eax
10085     68/push "eax"/imm32/register
10086     68/push 0/imm32/no-stack-offset
10087     68/push 1/imm32/block-depth
10088     51/push-ecx
10089     68/push "foo"/imm32
10090     89/<- %ecx 4/r32/esp
10091     # var real-outputs/edi: (handle stmt-var)
10092     68/push 0/imm32/is-deref:false
10093     68/push 0/imm32/next
10094     51/push-ecx/var-foo
10095     89/<- %edi 4/r32/esp
10096     # var stmt/esi: statement
10097     68/push 0/imm32/next
10098     57/push-edi/outputs
10099     68/push 0/imm32/inouts
10100     68/push "increment"/imm32/operation
10101     68/push 1/imm32/regular-stmt
10102     89/<- %esi 4/r32/esp
10103     # convert
10104     c7 0/subop/copy *Curr-block-depth 0/imm32
10105     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10106     (flush _test-output-buffered-file)
10107 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10113     # check output
10114     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
10115     # . epilogue
10116     89/<- %esp 5/r32/ebp
10117     5d/pop-to-ebp
10118     c3/return
10119 
10120 test-increment-var:
10121     # Select the right primitive between overloads.
10122     #   foo <- increment
10123     # =>
10124     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10125     #
10126     # There's a variable on the var stack as follows:
10127     #   name: 'foo'
10128     #   type: int
10129     #   register: 'eax'
10130     #
10131     # Primitives are the global definitions.
10132     #
10133     # There are no functions defined.
10134     #
10135     # . prologue
10136     55/push-ebp
10137     89/<- %ebp 4/r32/esp
10138     # setup
10139     (clear-stream _test-output-stream)
10140     (clear-stream $_test-output-buffered-file->buffer)
10141     # var type/ecx: (handle tree type-id) = int
10142     68/push 0/imm32/right/null
10143     68/push 1/imm32/left/int
10144     89/<- %ecx 4/r32/esp
10145     # var var-foo/ecx: var in eax
10146     68/push "eax"/imm32/register
10147     68/push 0/imm32/no-stack-offset
10148     68/push 1/imm32/block-depth
10149     51/push-ecx
10150     68/push "foo"/imm32
10151     89/<- %ecx 4/r32/esp
10152     # var inouts/edi: (handle stmt-var)
10153     68/push 0/imm32/is-deref:false
10154     68/push 0/imm32/next
10155     51/push-ecx/var-foo
10156     89/<- %edi 4/r32/esp
10157     # var stmt/esi: statement
10158     68/push 0/imm32/next
10159     57/push-edi/outputs
10160     68/push 0/imm32/inouts
10161     68/push "increment"/imm32/operation
10162     68/push 1/imm32
10163     89/<- %esi 4/r32/esp
10164     # convert
10165     c7 0/subop/copy *Curr-block-depth 0/imm32
10166     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10167     (flush _test-output-buffered-file)
10168 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10174     # check output
10175     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
10176     # . epilogue
10177     89/<- %esp 5/r32/ebp
10178     5d/pop-to-ebp
10179     c3/return
10180 
10181 test-add-reg-to-reg:
10182     #   var1/reg <- add var2/reg
10183     # =>
10184     #   01/add %var1 var2
10185     #
10186     # . prologue
10187     55/push-ebp
10188     89/<- %ebp 4/r32/esp
10189     # setup
10190     (clear-stream _test-output-stream)
10191     (clear-stream $_test-output-buffered-file->buffer)
10192     # var type/ecx: (handle tree type-id) = int
10193     68/push 0/imm32/right/null
10194     68/push 1/imm32/left/int
10195     89/<- %ecx 4/r32/esp
10196     # var var-var1/ecx: var in eax
10197     68/push "eax"/imm32/register
10198     68/push 0/imm32/no-stack-offset
10199     68/push 1/imm32/block-depth
10200     51/push-ecx
10201     68/push "var1"/imm32
10202     89/<- %ecx 4/r32/esp
10203     # var var-var2/edx: var in ecx
10204     68/push "ecx"/imm32/register
10205     68/push 0/imm32/no-stack-offset
10206     68/push 1/imm32/block-depth
10207     ff 6/subop/push *(ecx+4)  # Var-type
10208     68/push "var2"/imm32
10209     89/<- %edx 4/r32/esp
10210     # var inouts/esi: (handle stmt-var) = [var2]
10211     68/push 0/imm32/is-deref:false
10212     68/push 0/imm32/next
10213     52/push-edx/var-var2
10214     89/<- %esi 4/r32/esp
10215     # var outputs/edi: (handle stmt-var) = [var1, var2]
10216     68/push 0/imm32/is-deref:false
10217     68/push 0/imm32/next
10218     51/push-ecx/var-var1
10219     89/<- %edi 4/r32/esp
10220     # var stmt/esi: statement
10221     68/push 0/imm32/next
10222     57/push-edi/outputs
10223     56/push-esi/inouts
10224     68/push "add"/imm32/operation
10225     68/push 1/imm32
10226     89/<- %esi 4/r32/esp
10227     # convert
10228     c7 0/subop/copy *Curr-block-depth 0/imm32
10229     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10230     (flush _test-output-buffered-file)
10231 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10237     # check output
10238     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
10239     # . epilogue
10240     89/<- %esp 5/r32/ebp
10241     5d/pop-to-ebp
10242     c3/return
10243 
10244 test-add-reg-to-mem:
10245     #   add-to var1 var2/reg
10246     # =>
10247     #   01/add *(ebp+__) var2
10248     #
10249     # . prologue
10250     55/push-ebp
10251     89/<- %ebp 4/r32/esp
10252     # setup
10253     (clear-stream _test-output-stream)
10254     (clear-stream $_test-output-buffered-file->buffer)
10255     # var type/ecx: (handle tree type-id) = int
10256     68/push 0/imm32/right/null
10257     68/push 1/imm32/left/int
10258     89/<- %ecx 4/r32/esp
10259     # var var-var1/ecx: var
10260     68/push 0/imm32/no-register
10261     68/push 8/imm32/stack-offset
10262     68/push 1/imm32/block-depth
10263     51/push-ecx
10264     68/push "var1"/imm32
10265     89/<- %ecx 4/r32/esp
10266     # var var-var2/edx: var in ecx
10267     68/push "ecx"/imm32/register
10268     68/push 0/imm32/no-stack-offset
10269     68/push 1/imm32/block-depth
10270     ff 6/subop/push *(ecx+4)  # Var-type
10271     68/push "var2"/imm32
10272     89/<- %edx 4/r32/esp
10273     # var inouts/esi: (handle stmt-var) = [var2]
10274     68/push 0/imm32/is-deref:false
10275     68/push 0/imm32/next
10276     52/push-edx/var-var2
10277     89/<- %esi 4/r32/esp
10278     # var inouts = (handle stmt-var) = [var1, var2]
10279     56/push-esi/next
10280     51/push-ecx/var-var1
10281     89/<- %esi 4/r32/esp
10282     # var stmt/esi: statement
10283     68/push 0/imm32/next
10284     68/push 0/imm32/outputs
10285     56/push-esi/inouts
10286     68/push "add-to"/imm32/operation
10287     68/push 1/imm32
10288     89/<- %esi 4/r32/esp
10289     # convert
10290     c7 0/subop/copy *Curr-block-depth 0/imm32
10291     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10292     (flush _test-output-buffered-file)
10293 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10299     # check output
10300     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
10301     # . epilogue
10302     89/<- %esp 5/r32/ebp
10303     5d/pop-to-ebp
10304     c3/return
10305 
10306 test-add-mem-to-reg:
10307     #   var1/reg <- add var2
10308     # =>
10309     #   03/add *(ebp+__) var1
10310     #
10311     # . prologue
10312     55/push-ebp
10313     89/<- %ebp 4/r32/esp
10314     # setup
10315     (clear-stream _test-output-stream)
10316     (clear-stream $_test-output-buffered-file->buffer)
10317     # var type/ecx: (handle tree type-id) = int
10318     68/push 0/imm32/right/null
10319     68/push 1/imm32/left/int
10320     89/<- %ecx 4/r32/esp
10321     # var var-var1/ecx: var in eax
10322     68/push "eax"/imm32/register
10323     68/push 0/imm32/no-stack-offset
10324     68/push 1/imm32/block-depth
10325     51/push-ecx
10326     68/push "var1"/imm32
10327     89/<- %ecx 4/r32/esp
10328     # var var-var2/edx: var
10329     68/push 0/imm32/no-register
10330     68/push 8/imm32/stack-offset
10331     68/push 1/imm32/block-depth
10332     ff 6/subop/push *(ecx+4)  # Var-type
10333     68/push "var2"/imm32
10334     89/<- %edx 4/r32/esp
10335     # var inouts/esi: (handle stmt-var) = [var2]
10336     68/push 0/imm32/is-deref:false
10337     68/push 0/imm32/next
10338     52/push-edx/var-var2
10339     89/<- %esi 4/r32/esp
10340     # var outputs/edi = (handle stmt-var) = [var1]
10341     68/push 0/imm32/is-deref:false
10342     68/push 0/imm32/next
10343     51/push-ecx/var-var1
10344     89/<- %edi 4/r32/esp
10345     # var stmt/esi: statement
10346     68/push 0/imm32/next
10347     57/push-edi/outputs
10348     56/push-esi/inouts
10349     68/push "add"/imm32/operation
10350     68/push 1/imm32
10351     89/<- %esi 4/r32/esp
10352     # convert
10353     c7 0/subop/copy *Curr-block-depth 0/imm32
10354     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10355     (flush _test-output-buffered-file)
10356 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10362     # check output
10363     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
10364     # . epilogue
10365     89/<- %esp 5/r32/ebp
10366     5d/pop-to-ebp
10367     c3/return
10368 
10369 test-add-literal-to-eax:
10370     #   var1/eax <- add 0x34
10371     # =>
10372     #   05/add-to-eax 0x34/imm32
10373     #
10374     # . prologue
10375     55/push-ebp
10376     89/<- %ebp 4/r32/esp
10377     # setup
10378     (clear-stream _test-output-stream)
10379     (clear-stream $_test-output-buffered-file->buffer)
10380     # var type/ecx: (handle tree type-id) = int
10381     68/push 0/imm32/right/null
10382     68/push 1/imm32/left/int
10383     89/<- %ecx 4/r32/esp
10384     # var var-var1/ecx: var in eax
10385     68/push "eax"/imm32/register
10386     68/push 0/imm32/no-stack-offset
10387     68/push 1/imm32/block-depth
10388     51/push-ecx
10389     68/push "var1"/imm32
10390     89/<- %ecx 4/r32/esp
10391     # var type/edx: (handle tree type-id) = literal
10392     68/push 0/imm32/right/null
10393     68/push 0/imm32/left/literal
10394     89/<- %edx 4/r32/esp
10395     # var var-var2/edx: var literal
10396     68/push 0/imm32/no-register
10397     68/push 0/imm32/no-stack-offset
10398     68/push 1/imm32/block-depth
10399     52/push-edx
10400     68/push "0x34"/imm32
10401     89/<- %edx 4/r32/esp
10402     # var inouts/esi: (handle stmt-var) = [var2]
10403     68/push 0/imm32/is-deref:false
10404     68/push 0/imm32/next
10405     52/push-edx/var-var2
10406     89/<- %esi 4/r32/esp
10407     # var outputs/edi: (handle stmt-var) = [var1]
10408     68/push 0/imm32/is-deref:false
10409     68/push 0/imm32/next
10410     51/push-ecx/var-var1
10411     89/<- %edi 4/r32/esp
10412     # var stmt/esi: statement
10413     68/push 0/imm32/next
10414     57/push-edi/outputs
10415     56/push-esi/inouts
10416     68/push "add"/imm32/operation
10417     68/push 1/imm32
10418     89/<- %esi 4/r32/esp
10419     # convert
10420     c7 0/subop/copy *Curr-block-depth 0/imm32
10421     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10422     (flush _test-output-buffered-file)
10423 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10429     # check output
10430     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
10431     # . epilogue
10432     89/<- %esp 5/r32/ebp
10433     5d/pop-to-ebp
10434     c3/return
10435 
10436 test-add-literal-to-reg:
10437     #   var1/ecx <- add 0x34
10438     # =>
10439     #   81 0/subop/add %ecx 0x34/imm32
10440     #
10441     # . prologue
10442     55/push-ebp
10443     89/<- %ebp 4/r32/esp
10444     # setup
10445     (clear-stream _test-output-stream)
10446     (clear-stream $_test-output-buffered-file->buffer)
10447     # var type/ecx: (handle tree type-id) = int
10448     68/push 0/imm32/right/null
10449     68/push 1/imm32/left/int
10450     89/<- %ecx 4/r32/esp
10451     # var var-var1/ecx: var in ecx
10452     68/push "ecx"/imm32/register
10453     68/push 0/imm32/no-stack-offset
10454     68/push 1/imm32/block-depth
10455     51/push-ecx
10456     68/push "var1"/imm32
10457     89/<- %ecx 4/r32/esp
10458     # var type/edx: (handle tree type-id) = literal
10459     68/push 0/imm32/right/null
10460     68/push 0/imm32/left/literal
10461     89/<- %edx 4/r32/esp
10462     # var var-var2/edx: var literal
10463     68/push 0/imm32/no-register
10464     68/push 0/imm32/no-stack-offset
10465     68/push 1/imm32/block-depth
10466     52/push-edx
10467     68/push "0x34"/imm32
10468     89/<- %edx 4/r32/esp
10469     # var inouts/esi: (handle stmt-var) = [var2]
10470     68/push 0/imm32/is-deref:false
10471     68/push 0/imm32/next
10472     52/push-edx/var-var2
10473     89/<- %esi 4/r32/esp
10474     # var outputs/edi: (handle stmt-var) = [var1]
10475     68/push 0/imm32/is-deref:false
10476     68/push 0/imm32/next
10477     51/push-ecx/var-var1
10478     89/<- %edi 4/r32/esp
10479     # var stmt/esi: statement
10480     68/push 0/imm32/next
10481     57/push-edi/outputs
10482     56/push-esi/inouts
10483     68/push "add"/imm32/operation
10484     68/push 1/imm32
10485     89/<- %esi 4/r32/esp
10486     # convert
10487     c7 0/subop/copy *Curr-block-depth 0/imm32
10488     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10489     (flush _test-output-buffered-file)
10490 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10496     # check output
10497     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
10498     # . epilogue
10499     89/<- %esp 5/r32/ebp
10500     5d/pop-to-ebp
10501     c3/return
10502 
10503 test-add-literal-to-mem:
10504     #   add-to var1, 0x34
10505     # =>
10506     #   81 0/subop/add %eax 0x34/imm32
10507     #
10508     # . prologue
10509     55/push-ebp
10510     89/<- %ebp 4/r32/esp
10511     # setup
10512     (clear-stream _test-output-stream)
10513     (clear-stream $_test-output-buffered-file->buffer)
10514     # var type/ecx: (handle tree type-id) = int
10515     68/push 0/imm32/right/null
10516     68/push 1/imm32/left/int
10517     89/<- %ecx 4/r32/esp
10518     # var var-var1/ecx: var
10519     68/push 0/imm32/no-register
10520     68/push 8/imm32/stack-offset
10521     68/push 1/imm32/block-depth
10522     51/push-ecx
10523     68/push "var1"/imm32
10524     89/<- %ecx 4/r32/esp
10525     # var type/edx: (handle tree type-id) = literal
10526     68/push 0/imm32/right/null
10527     68/push 0/imm32/left/literal
10528     89/<- %edx 4/r32/esp
10529     # var var-var2/edx: var literal
10530     68/push 0/imm32/no-register
10531     68/push 0/imm32/no-stack-offset
10532     68/push 1/imm32/block-depth
10533     52/push-edx
10534     68/push "0x34"/imm32
10535     89/<- %edx 4/r32/esp
10536     # var inouts/esi: (handle stmt-var) = [var2]
10537     68/push 0/imm32/is-deref:false
10538     68/push 0/imm32/next
10539     52/push-edx/var-var2
10540     89/<- %esi 4/r32/esp
10541     # var inouts = (handle stmt-var) = [var1, var2]
10542     68/push 0/imm32/is-deref:false
10543     56/push-esi/next
10544     51/push-ecx/var-var1
10545     89/<- %esi 4/r32/esp
10546     # var stmt/esi: statement
10547     68/push 0/imm32/next
10548     68/push 0/imm32/outputs
10549     56/push-esi/inouts
10550     68/push "add-to"/imm32/operation
10551     68/push 1/imm32
10552     89/<- %esi 4/r32/esp
10553     # convert
10554     c7 0/subop/copy *Curr-block-depth 0/imm32
10555     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10556     (flush _test-output-buffered-file)
10557 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10563     # check output
10564     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
10565     # . epilogue
10566     89/<- %esp 5/r32/ebp
10567     5d/pop-to-ebp
10568     c3/return
10569 
10570 test-compare-mem-with-reg:
10571     #   compare var1, var2/eax
10572     # =>
10573     #   39/compare *(ebp+___) 0/r32/eax
10574     #
10575     # . prologue
10576     55/push-ebp
10577     89/<- %ebp 4/r32/esp
10578     # setup
10579     (clear-stream _test-output-stream)
10580     (clear-stream $_test-output-buffered-file->buffer)
10581     # var type/ecx: (handle tree type-id) = int
10582     68/push 0/imm32/right/null
10583     68/push 1/imm32/left/int
10584     89/<- %ecx 4/r32/esp
10585     # var var-var2/ecx: var in eax
10586     68/push "eax"/imm32/register
10587     68/push 0/imm32/no-stack-offset
10588     68/push 1/imm32/block-depth
10589     51/push-ecx
10590     68/push "var2"/imm32
10591     89/<- %ecx 4/r32/esp
10592     # var var-var1/edx: var
10593     68/push 0/imm32/no-register
10594     68/push 8/imm32/stack-offset
10595     68/push 1/imm32/block-depth
10596     ff 6/subop/push *(ecx+4)  # Var-type
10597     68/push "var1"/imm32
10598     89/<- %edx 4/r32/esp
10599     # var inouts/esi: (handle stmt-var) = [var2]
10600     68/push 0/imm32/is-deref:false
10601     68/push 0/imm32/next
10602     51/push-ecx/var-var2
10603     89/<- %esi 4/r32/esp
10604     # inouts = [var1, var2]
10605     68/push 0/imm32/is-deref:false
10606     56/push-esi
10607     52/push-edx/var-var1
10608     89/<- %esi 4/r32/esp
10609     # var stmt/esi: statement
10610     68/push 0/imm32/next
10611     68/push 0/imm32/outputs
10612     56/push-esi/inouts
10613     68/push "compare"/imm32/operation
10614     68/push 1/imm32
10615     89/<- %esi 4/r32/esp
10616     # convert
10617     c7 0/subop/copy *Curr-block-depth 0/imm32
10618     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10619     (flush _test-output-buffered-file)
10620 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10626     # check output
10627     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
10628     # . epilogue
10629     89/<- %esp 5/r32/ebp
10630     5d/pop-to-ebp
10631     c3/return
10632 
10633 test-compare-reg-with-mem:
10634     #   compare var1/eax, var2
10635     # =>
10636     #   3b/compare *(ebp+___) 0/r32/eax
10637     #
10638     # . prologue
10639     55/push-ebp
10640     89/<- %ebp 4/r32/esp
10641     # setup
10642     (clear-stream _test-output-stream)
10643     (clear-stream $_test-output-buffered-file->buffer)
10644     # var type/ecx: (handle tree type-id) = int
10645     68/push 0/imm32/right/null
10646     68/push 1/imm32/left/int
10647     89/<- %ecx 4/r32/esp
10648     # var var-var1/ecx: var in eax
10649     68/push "eax"/imm32/register
10650     68/push 0/imm32/no-stack-offset
10651     68/push 1/imm32/block-depth
10652     51/push-ecx
10653     68/push "var1"/imm32
10654     89/<- %ecx 4/r32/esp
10655     # var var-var2/edx: var
10656     68/push 0/imm32/no-register
10657     68/push 8/imm32/stack-offset
10658     68/push 1/imm32/block-depth
10659     ff 6/subop/push *(ecx+4)  # Var-type
10660     68/push "var2"/imm32
10661     89/<- %edx 4/r32/esp
10662     # var inouts/esi: (handle stmt-var) = [var2]
10663     68/push 0/imm32/is-deref:false
10664     68/push 0/imm32/next
10665     52/push-edx/var-var2
10666     89/<- %esi 4/r32/esp
10667     # inouts = [var1, var2]
10668     68/push 0/imm32/is-deref:false
10669     56/push-esi
10670     51/push-ecx/var-var1
10671     89/<- %esi 4/r32/esp
10672     # var stmt/esi: statement
10673     68/push 0/imm32/next
10674     68/push 0/imm32/outputs
10675     56/push-esi/inouts
10676     68/push "compare"/imm32/operation
10677     68/push 1/imm32
10678     89/<- %esi 4/r32/esp
10679     # convert
10680     c7 0/subop/copy *Curr-block-depth 0/imm32
10681     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10682     (flush _test-output-buffered-file)
10683 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10689     # check output
10690     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
10691     # . epilogue
10692     89/<- %esp 5/r32/ebp
10693     5d/pop-to-ebp
10694     c3/return
10695 
10696 test-compare-mem-with-literal:
10697     #   compare var1, 0x34
10698     # =>
10699     #   81 7/subop/compare *(ebp+___) 0x34/imm32
10700     #
10701     # . prologue
10702     55/push-ebp
10703     89/<- %ebp 4/r32/esp
10704     # setup
10705     (clear-stream _test-output-stream)
10706     (clear-stream $_test-output-buffered-file->buffer)
10707     # var type/ecx: (handle tree type-id) = int
10708     68/push 0/imm32/right/null
10709     68/push 1/imm32/left/int
10710     89/<- %ecx 4/r32/esp
10711     # var var-var1/ecx: var
10712     68/push 0/imm32/no-register
10713     68/push 8/imm32/stack-offset
10714     68/push 1/imm32/block-depth
10715     51/push-ecx
10716     68/push "var1"/imm32
10717     89/<- %ecx 4/r32/esp
10718     # var type/edx: (handle tree type-id) = literal
10719     68/push 0/imm32/right/null
10720     68/push 0/imm32/left/literal
10721     89/<- %edx 4/r32/esp
10722     # var var-var2/edx: var literal
10723     68/push 0/imm32/no-register
10724     68/push 0/imm32/no-stack-offset
10725     68/push 1/imm32/block-depth
10726     52/push-edx
10727     68/push "0x34"/imm32
10728     89/<- %edx 4/r32/esp
10729     # var inouts/esi: (handle stmt-var) = [var2]
10730     68/push 0/imm32/is-deref:false
10731     68/push 0/imm32/next
10732     52/push-edx/var-var2
10733     89/<- %esi 4/r32/esp
10734     # inouts = [var1, var2]
10735     68/push 0/imm32/is-deref:false
10736     56/push-esi/next
10737     51/push-ecx/var-var1
10738     89/<- %esi 4/r32/esp
10739     # var stmt/esi: statement
10740     68/push 0/imm32/next
10741     68/push 0/imm32/outputs
10742     56/push-esi/inouts
10743     68/push "compare"/imm32/operation
10744     68/push 1/imm32
10745     89/<- %esi 4/r32/esp
10746     # convert
10747     c7 0/subop/copy *Curr-block-depth 0/imm32
10748     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10749     (flush _test-output-buffered-file)
10750 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10756     # check output
10757     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
10758     # . epilogue
10759     89/<- %esp 5/r32/ebp
10760     5d/pop-to-ebp
10761     c3/return
10762 
10763 test-compare-eax-with-literal:
10764     #   compare var1/eax 0x34
10765     # =>
10766     #   3d/compare-eax-with 0x34/imm32
10767     #
10768     # . prologue
10769     55/push-ebp
10770     89/<- %ebp 4/r32/esp
10771     # setup
10772     (clear-stream _test-output-stream)
10773     (clear-stream $_test-output-buffered-file->buffer)
10774     # var type/ecx: (handle tree type-id) = int
10775     68/push 0/imm32/right/null
10776     68/push 1/imm32/left/int
10777     89/<- %ecx 4/r32/esp
10778     # var var-var1/ecx: var in eax
10779     68/push "eax"/imm32/register
10780     68/push 0/imm32/no-stack-offset
10781     68/push 1/imm32/block-depth
10782     51/push-ecx
10783     68/push "var1"/imm32
10784     89/<- %ecx 4/r32/esp
10785     # var type/edx: (handle tree type-id) = literal
10786     68/push 0/imm32/right/null
10787     68/push 0/imm32/left/literal
10788     89/<- %edx 4/r32/esp
10789     # var var-var2/edx: var literal
10790     68/push 0/imm32/no-register
10791     68/push 0/imm32/no-stack-offset
10792     68/push 1/imm32/block-depth
10793     52/push-edx
10794     68/push "0x34"/imm32
10795     89/<- %edx 4/r32/esp
10796     # var inouts/esi: (handle stmt-var) = [var2]
10797     68/push 0/imm32/is-deref:false
10798     68/push 0/imm32/next
10799     52/push-edx/var-var2
10800     89/<- %esi 4/r32/esp
10801     # inouts = [var1, var2]
10802     68/push 0/imm32/is-deref:false
10803     56/push-esi/next
10804     51/push-ecx/var-var1
10805     89/<- %esi 4/r32/esp
10806     # var stmt/esi: statement
10807     68/push 0/imm32/next
10808     68/push 0/imm32/outputs
10809     56/push-esi/inouts
10810     68/push "compare"/imm32/operation
10811     68/push 1/imm32/regular-stmt
10812     89/<- %esi 4/r32/esp
10813     # convert
10814     c7 0/subop/copy *Curr-block-depth 0/imm32
10815     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10816     (flush _test-output-buffered-file)
10817 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10823     # check output
10824     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
10825     # . epilogue
10826     89/<- %esp 5/r32/ebp
10827     5d/pop-to-ebp
10828     c3/return
10829 
10830 test-compare-reg-with-literal:
10831     #   compare var1/ecx 0x34
10832     # =>
10833     #   81 7/subop/compare %ecx 0x34/imm32
10834     #
10835     # . prologue
10836     55/push-ebp
10837     89/<- %ebp 4/r32/esp
10838     # setup
10839     (clear-stream _test-output-stream)
10840     (clear-stream $_test-output-buffered-file->buffer)
10841     # var type/ecx: (handle tree type-id) = int
10842     68/push 0/imm32/right/null
10843     68/push 1/imm32/left/int
10844     89/<- %ecx 4/r32/esp
10845     # var var-var1/ecx: var in ecx
10846     68/push "ecx"/imm32/register
10847     68/push 0/imm32/no-stack-offset
10848     68/push 1/imm32/block-depth
10849     51/push-ecx
10850     68/push "var1"/imm32
10851     89/<- %ecx 4/r32/esp
10852     # var type/edx: (handle tree type-id) = literal
10853     68/push 0/imm32/right/null
10854     68/push 0/imm32/left/literal
10855     89/<- %edx 4/r32/esp
10856     # var var-var2/edx: var literal
10857     68/push 0/imm32/no-register
10858     68/push 0/imm32/no-stack-offset
10859     68/push 1/imm32/block-depth
10860     52/push-edx
10861     68/push "0x34"/imm32
10862     89/<- %edx 4/r32/esp
10863     # var inouts/esi: (handle stmt-var) = [var2]
10864     68/push 0/imm32/is-deref:false
10865     68/push 0/imm32/next
10866     52/push-edx/var-var2
10867     89/<- %esi 4/r32/esp
10868     # inouts = [var1, var2]
10869     68/push 0/imm32/is-deref:false
10870     56/push-esi/next
10871     51/push-ecx/var-var1
10872     89/<- %esi 4/r32/esp
10873     # var stmt/esi: statement
10874     68/push 0/imm32/next
10875     68/push 0/imm32/outputs
10876     56/push-esi/inouts
10877     68/push "compare"/imm32/operation
10878     68/push 1/imm32/regular-stmt
10879     89/<- %esi 4/r32/esp
10880     # convert
10881     c7 0/subop/copy *Curr-block-depth 0/imm32
10882     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10883     (flush _test-output-buffered-file)
10884 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10890     # check output
10891     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
10892     # . epilogue
10893     89/<- %esp 5/r32/ebp
10894     5d/pop-to-ebp
10895     c3/return
10896 
10897 test-emit-subx-stmt-function-call:
10898     # Call a function on a variable on the stack.
10899     #   f foo
10900     # =>
10901     #   (f2 *(ebp-8))
10902     # (Changing the function name supports overloading in general, but here it
10903     # just serves to help disambiguate things.)
10904     #
10905     # There's a variable on the var stack as follows:
10906     #   name: 'foo'
10907     #   type: int
10908     #   stack-offset: -8
10909     #
10910     # There's nothing in primitives.
10911     #
10912     # There's a function with this info:
10913     #   name: 'f'
10914     #   inout: int/mem
10915     #   value: 'f2'
10916     #
10917     # . prologue
10918     55/push-ebp
10919     89/<- %ebp 4/r32/esp
10920     # setup
10921     (clear-stream _test-output-stream)
10922     (clear-stream $_test-output-buffered-file->buffer)
10923     # var type/ecx: (handle tree type-id) = int
10924     68/push 0/imm32/right/null
10925     68/push 1/imm32/left/int
10926     89/<- %ecx 4/r32/esp
10927     # var var-foo/ecx: var
10928     68/push 0/imm32/no-register
10929     68/push -8/imm32/stack-offset
10930     68/push 0/imm32/block-depth
10931     51/push-ecx
10932     68/push "foo"/imm32
10933     89/<- %ecx 4/r32/esp
10934     # var inouts/esi: (handle stmt-var)
10935     68/push 0/imm32/is-deref:false
10936     68/push 0/imm32/next
10937     51/push-ecx/var-foo
10938     89/<- %esi 4/r32/esp
10939     # var stmt/esi: statement
10940     68/push 0/imm32/next
10941     68/push 0/imm32/outputs
10942     56/push-esi/inouts
10943     68/push "f"/imm32/operation
10944     68/push 1/imm32
10945     89/<- %esi 4/r32/esp
10946     # var functions/ebx: function
10947     68/push 0/imm32/next
10948     68/push 0/imm32/body
10949     68/push 0/imm32/outputs
10950     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
10951     68/push "f2"/imm32/subx-name
10952     68/push "f"/imm32/name
10953     89/<- %ebx 4/r32/esp
10954     # convert
10955     c7 0/subop/copy *Curr-block-depth 0/imm32
10956     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
10957     (flush _test-output-buffered-file)
10958 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10964     # check output
10965     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
10966     # . epilogue
10967     89/<- %esp 5/r32/ebp
10968     5d/pop-to-ebp
10969     c3/return
10970 
10971 test-emit-subx-stmt-function-call-with-literal-arg:
10972     # Call a function on a literal.
10973     #   f 34
10974     # =>
10975     #   (f2 34)
10976     #
10977     # . prologue
10978     55/push-ebp
10979     89/<- %ebp 4/r32/esp
10980     # setup
10981     (clear-stream _test-output-stream)
10982     (clear-stream $_test-output-buffered-file->buffer)
10983     # var type/ecx: (handle tree type-id) = literal
10984     68/push 0/imm32/right/null
10985     68/push 0/imm32/left/literal
10986     89/<- %ecx 4/r32/esp
10987     # var var-foo/ecx: var literal
10988     68/push 0/imm32/no-register
10989     68/push 0/imm32/no-stack-offset
10990     68/push 0/imm32/block-depth
10991     51/push-ecx
10992     68/push "34"/imm32
10993     89/<- %ecx 4/r32/esp
10994     # var inouts/esi: (handle stmt-var)
10995     68/push 0/imm32/is-deref:false
10996     68/push 0/imm32/next
10997     51/push-ecx/var-foo
10998     89/<- %esi 4/r32/esp
10999     # var stmt/esi: statement
11000     68/push 0/imm32/next
11001     68/push 0/imm32/outputs
11002     56/push-esi/inouts
11003     68/push "f"/imm32/operation
11004     68/push 1/imm32
11005     89/<- %esi 4/r32/esp
11006     # var functions/ebx: function
11007     68/push 0/imm32/next
11008     68/push 0/imm32/body
11009     68/push 0/imm32/outputs
11010     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
11011     68/push "f2"/imm32/subx-name
11012     68/push "f"/imm32/name
11013     89/<- %ebx 4/r32/esp
11014     # convert
11015     c7 0/subop/copy *Curr-block-depth 0/imm32
11016     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
11017     (flush _test-output-buffered-file)
11018 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11024     # check output
11025     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
11026     # . epilogue
11027     89/<- %esp 5/r32/ebp
11028     5d/pop-to-ebp
11029     c3/return
11030 
11031 emit-indent:  # out: (addr buffered-file), n: int
11032     # . prologue
11033     55/push-ebp
11034     89/<- %ebp 4/r32/esp
11035     # . save registers
11036     50/push-eax
11037     # var i/eax: int = n
11038     8b/-> *(ebp+0xc) 0/r32/eax
11039     {
11040       # if (i <= 0) break
11041       3d/compare-eax-with 0/imm32
11042       7e/jump-if-<= break/disp8
11043       (write-buffered *(ebp+8) "  ")
11044       48/decrement-eax
11045       eb/jump loop/disp8
11046     }
11047 $emit-indent:end:
11048     # . restore registers
11049     58/pop-to-eax
11050     # . epilogue
11051     89/<- %esp 5/r32/ebp
11052     5d/pop-to-ebp
11053     c3/return
11054 
11055 emit-subx-prologue:  # out: (addr buffered-file)
11056     # . prologue
11057     55/push-ebp
11058     89/<- %ebp 4/r32/esp
11059     #
11060     (write-buffered *(ebp+8) "  # . prologue\n")
11061     (write-buffered *(ebp+8) "  55/push-ebp\n")
11062     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
11063 $emit-subx-prologue:end:
11064     # . epilogue
11065     89/<- %esp 5/r32/ebp
11066     5d/pop-to-ebp
11067     c3/return
11068 
11069 emit-subx-epilogue:  # out: (addr buffered-file)
11070     # . prologue
11071     55/push-ebp
11072     89/<- %ebp 4/r32/esp
11073     #
11074     (write-buffered *(ebp+8) "  # . epilogue\n")
11075     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
11076     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
11077     (write-buffered *(ebp+8) "  c3/return\n")
11078 $emit-subx-epilogue:end:
11079     # . epilogue
11080     89/<- %esp 5/r32/ebp
11081     5d/pop-to-ebp
11082     c3/return