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-with-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-with-literal-arg/0")
  777     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg/1")
  778     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg/2")
  779     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg/3")
  780     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg/4")
  781     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg/5")
  782     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-with-literal-arg/6")
  783     (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-with-literal-arg/7")
  784     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg/8")
  785     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg/9")
  786     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg/10")
  787     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg/11")
  788     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg/12")
  789     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg/13")
  790     # . epilogue
  791     89/<- %esp 5/r32/ebp
  792     5d/pop-to-ebp
  793     c3/return
  794 
  795 test-convert-function-with-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-with-literal-arg-2/0")
  820     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg-2/1")
  821     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg-2/2")
  822     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg-2/3")
  823     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg-2/4")
  824     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg-2/5")
  825     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-with-literal-arg-2/6")
  826     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-with-literal-arg-2/7")
  827     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg-2/8")
  828     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg-2/9")
  829     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg-2/10")
  830     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg-2/11")
  831     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg-2/12")
  832     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-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-with-compound-type-in-mem:
  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: (addr int)\n")
  953     (write _test-input-stream "  copy-to x, 0\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-with-compound-type-in-mem/0")
  966     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/1")
  967     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-compound-type-in-mem/2")
  968     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/3")
  969     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/4")
  970     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-compound-type-in-mem/5")
  971     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-compound-type-in-mem/6")
  972     (check-next-stream-line-equal _test-output-stream "    c7 0/subop/copy *(ebp+0xfffffffc) 0/imm32"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/7")
  973     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/8")
  974     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/9")
  975     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/10")
  976     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/11")
  977     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/12")
  978     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-compound-type-in-mem/13")
  979     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-compound-type-in-mem/14")
  980     # . epilogue
  981     89/<- %esp 5/r32/ebp
  982     5d/pop-to-ebp
  983     c3/return
  984 
  985 test-convert-function-with-local-var-in-reg:
  986     # . prologue
  987     55/push-ebp
  988     89/<- %ebp 4/r32/esp
  989     # setup
  990     (clear-stream _test-input-stream)
  991     (clear-stream $_test-input-buffered-file->buffer)
  992     (clear-stream _test-output-stream)
  993     (clear-stream $_test-output-buffered-file->buffer)
  994     #
  995     (write _test-input-stream "fn foo {\n")
  996     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
  997     (write _test-input-stream "  x <- increment\n")
  998     (write _test-input-stream "}\n")
  999     # convert
 1000     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1001     (flush _test-output-buffered-file)
 1002 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1008     # check output
 1009     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
 1010     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
 1011     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
 1012     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
 1013     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
 1014     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
 1015     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
 1016     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
 1017     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
 1018     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
 1019     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
 1020     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
 1021     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
 1022     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
 1023     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
 1024     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
 1025     # . epilogue
 1026     89/<- %esp 5/r32/ebp
 1027     5d/pop-to-ebp
 1028     c3/return
 1029 
 1030 test-convert-function-with-second-local-var-in-same-reg:
 1031     # . prologue
 1032     55/push-ebp
 1033     89/<- %ebp 4/r32/esp
 1034     # setup
 1035     (clear-stream _test-input-stream)
 1036     (clear-stream $_test-input-buffered-file->buffer)
 1037     (clear-stream _test-output-stream)
 1038     (clear-stream $_test-output-buffered-file->buffer)
 1039     #
 1040     (write _test-input-stream "fn foo {\n")
 1041     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1042     (write _test-input-stream "  var y/ecx: int <- copy 4\n")
 1043     (write _test-input-stream "  y <- increment\n")
 1044     (write _test-input-stream "}\n")
 1045     # convert
 1046     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1047     (flush _test-output-buffered-file)
 1048 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1054     # check output
 1055     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-second-local-var-in-same-reg/0")
 1056     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-second-local-var-in-same-reg/1")
 1057     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-second-local-var-in-same-reg/2")
 1058     (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")
 1059     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-second-local-var-in-same-reg/4")
 1060     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-second-local-var-in-same-reg/5")
 1061     (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")
 1062     (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")
 1063     (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")
 1064     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-second-local-var-in-same-reg/9")
 1065     (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")
 1066     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-second-local-var-in-same-reg/11")
 1067     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-second-local-var-in-same-reg/12")
 1068     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-second-local-var-in-same-reg/13")
 1069     (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")
 1070     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-second-local-var-in-same-reg/15")
 1071     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-second-local-var-in-same-reg/16")
 1072     # . epilogue
 1073     89/<- %esp 5/r32/ebp
 1074     5d/pop-to-ebp
 1075     c3/return
 1076 
 1077 test-convert-function-with-local-var-dereferenced:
 1078     # . prologue
 1079     55/push-ebp
 1080     89/<- %ebp 4/r32/esp
 1081     # setup
 1082     (clear-stream _test-input-stream)
 1083     (clear-stream $_test-input-buffered-file->buffer)
 1084     (clear-stream _test-output-stream)
 1085     (clear-stream $_test-output-buffered-file->buffer)
 1086     #
 1087     (write _test-input-stream "fn foo {\n")
 1088     (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
 1089     (write _test-input-stream "  increment *x\n")
 1090     (write _test-input-stream "}\n")
 1091     # convert
 1092     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1093     (flush _test-output-buffered-file)
 1094 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1100     # check output
 1101     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
 1102     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
 1103     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
 1104     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
 1105     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
 1106     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
 1107     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
 1108     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
 1109     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
 1110     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
 1111     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
 1112     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
 1113     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
 1114     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
 1115     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
 1116     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
 1117     # . epilogue
 1118     89/<- %esp 5/r32/ebp
 1119     5d/pop-to-ebp
 1120     c3/return
 1121 
 1122 test-convert-compare-register-with-literal:
 1123     # . prologue
 1124     55/push-ebp
 1125     89/<- %ebp 4/r32/esp
 1126     # setup
 1127     (clear-stream _test-input-stream)
 1128     (clear-stream $_test-input-buffered-file->buffer)
 1129     (clear-stream _test-output-stream)
 1130     (clear-stream $_test-output-buffered-file->buffer)
 1131     #
 1132     (write _test-input-stream "fn foo {\n")
 1133     (write _test-input-stream "  var x/ecx: int <- copy 0\n")
 1134     (write _test-input-stream "  compare x, 0\n")
 1135     (write _test-input-stream "}\n")
 1136     # convert
 1137     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1138     (flush _test-output-buffered-file)
 1139 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1145     # check output
 1146     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
 1147     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
 1148     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
 1149     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
 1150     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
 1151     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
 1152     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 1153     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
 1154     (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
 1155     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 1156     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
 1157     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
 1158     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
 1159     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
 1160     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
 1161     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
 1162     # . epilogue
 1163     89/<- %esp 5/r32/ebp
 1164     5d/pop-to-ebp
 1165     c3/return
 1166 
 1167 test-convert-function-with-local-var-in-block:
 1168     # . prologue
 1169     55/push-ebp
 1170     89/<- %ebp 4/r32/esp
 1171     # setup
 1172     (clear-stream _test-input-stream)
 1173     (clear-stream $_test-input-buffered-file->buffer)
 1174     (clear-stream _test-output-stream)
 1175     (clear-stream $_test-output-buffered-file->buffer)
 1176     #
 1177     (write _test-input-stream "fn foo {\n")
 1178     (write _test-input-stream "  {\n")
 1179     (write _test-input-stream "    var x: int\n")
 1180     (write _test-input-stream "    increment x\n")
 1181     (write _test-input-stream "  }\n")
 1182     (write _test-input-stream "}\n")
 1183     # convert
 1184     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1185     (flush _test-output-buffered-file)
 1186 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1192     # check output
 1193     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
 1194     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
 1195     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
 1196     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
 1197     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
 1198     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
 1199     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
 1200     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
 1201     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
 1202     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
 1203     (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")
 1204     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
 1205     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
 1206     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
 1207     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
 1208     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
 1209     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
 1210     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
 1211     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
 1212     # . epilogue
 1213     89/<- %esp 5/r32/ebp
 1214     5d/pop-to-ebp
 1215     c3/return
 1216 
 1217 test-convert-function-with-local-var-in-named-block:
 1218     # . prologue
 1219     55/push-ebp
 1220     89/<- %ebp 4/r32/esp
 1221     # setup
 1222     (clear-stream _test-input-stream)
 1223     (clear-stream $_test-input-buffered-file->buffer)
 1224     (clear-stream _test-output-stream)
 1225     (clear-stream $_test-output-buffered-file->buffer)
 1226     #
 1227     (write _test-input-stream "fn foo {\n")
 1228     (write _test-input-stream "  $bar: {\n")
 1229     (write _test-input-stream "    var x: int\n")
 1230     (write _test-input-stream "    increment x\n")
 1231     (write _test-input-stream "  }\n")
 1232     (write _test-input-stream "}\n")
 1233     # convert
 1234     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1235     (flush _test-output-buffered-file)
 1236 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1242     # check output
 1243     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
 1244     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
 1245     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
 1246     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
 1247     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
 1248     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
 1249     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
 1250     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
 1251     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
 1252     (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")
 1253     (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")
 1254     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
 1255     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
 1256     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
 1257     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
 1258     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
 1259     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
 1260     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
 1261     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
 1262     # . epilogue
 1263     89/<- %esp 5/r32/ebp
 1264     5d/pop-to-ebp
 1265     c3/return
 1266 
 1267 test-always-shadow-outermost-reg-vars-in-function:
 1268     # . prologue
 1269     55/push-ebp
 1270     89/<- %ebp 4/r32/esp
 1271     # setup
 1272     (clear-stream _test-input-stream)
 1273     (clear-stream $_test-input-buffered-file->buffer)
 1274     (clear-stream _test-output-stream)
 1275     (clear-stream $_test-output-buffered-file->buffer)
 1276     #
 1277     (write _test-input-stream "fn foo {\n")
 1278     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1279     (write _test-input-stream "}\n")
 1280     # convert
 1281     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1282     (flush _test-output-buffered-file)
 1283 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1289     # check output
 1290     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-always-shadow-outermost-reg-vars-in-function/0")
 1291     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-always-shadow-outermost-reg-vars-in-function/1")
 1292     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-always-shadow-outermost-reg-vars-in-function/2")
 1293     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-always-shadow-outermost-reg-vars-in-function/3")
 1294     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-always-shadow-outermost-reg-vars-in-function/4")
 1295     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-always-shadow-outermost-reg-vars-in-function/5")
 1296     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 1297     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-always-shadow-outermost-reg-vars-in-function/8")
 1298     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 1299     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-always-shadow-outermost-reg-vars-in-function/12")
 1300     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-always-shadow-outermost-reg-vars-in-function/13")
 1301     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-always-shadow-outermost-reg-vars-in-function/14")
 1302     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-always-shadow-outermost-reg-vars-in-function/15")
 1303     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-always-shadow-outermost-reg-vars-in-function/16")
 1304     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-always-shadow-outermost-reg-vars-in-function/17")
 1305     # . epilogue
 1306     89/<- %esp 5/r32/ebp
 1307     5d/pop-to-ebp
 1308     c3/return
 1309 
 1310 _pending-test-clobber-dead-local:
 1311     # . prologue
 1312     55/push-ebp
 1313     89/<- %ebp 4/r32/esp
 1314     # setup
 1315     (clear-stream _test-input-stream)
 1316     (clear-stream $_test-input-buffered-file->buffer)
 1317     (clear-stream _test-output-stream)
 1318     (clear-stream $_test-output-buffered-file->buffer)
 1319     #
 1320     (write _test-input-stream "fn foo {\n")
 1321     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1322     (write _test-input-stream "  {\n")
 1323     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1324     (write _test-input-stream "  }\n")
 1325     (write _test-input-stream "}\n")
 1326     # convert
 1327     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1328     (flush _test-output-buffered-file)
 1329 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1335     # check output
 1336     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-clobber-dead-local/0")
 1337     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-clobber-dead-local/1")
 1338     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-clobber-dead-local/2")
 1339     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-clobber-dead-local/3")
 1340     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-clobber-dead-local/4")
 1341     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-clobber-dead-local/5")
 1342     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-clobber-dead-local/6")
 1343     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-clobber-dead-local/7")
 1344     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-clobber-dead-local/8")
 1345     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-clobber-dead-local/9")
 1346     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-clobber-dead-local/10")  # no push/pop here
 1347     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-clobber-dead-local/11")
 1348     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-clobber-dead-local/12")
 1349     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-clobber-dead-local/13")
 1350     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-clobber-dead-local/14")
 1351     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-clobber-dead-local/15")
 1352     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-clobber-dead-local/16")
 1353     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-clobber-dead-local/17")
 1354     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-clobber-dead-local/18")
 1355     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-clobber-dead-local/19")
 1356     # . epilogue
 1357     89/<- %esp 5/r32/ebp
 1358     5d/pop-to-ebp
 1359     c3/return
 1360 
 1361 test-shadow-live-local:
 1362     # . prologue
 1363     55/push-ebp
 1364     89/<- %ebp 4/r32/esp
 1365     # setup
 1366     (clear-stream _test-input-stream)
 1367     (clear-stream $_test-input-buffered-file->buffer)
 1368     (clear-stream _test-output-stream)
 1369     (clear-stream $_test-output-buffered-file->buffer)
 1370     #
 1371     (write _test-input-stream "fn foo {\n")
 1372     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1373     (write _test-input-stream "  {\n")
 1374     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1375     (write _test-input-stream "  }\n")
 1376     (write _test-input-stream "  x <- increment\n")
 1377     (write _test-input-stream "}\n")
 1378     # convert
 1379     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1380     (flush _test-output-buffered-file)
 1381 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1387     # check output
 1388     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-local/0")
 1389     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-local/1")
 1390     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-local/2")
 1391     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-local/3")
 1392     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-local/4")
 1393     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-local/5")
 1394     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-live-local/6")
 1395     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-live-local/7")
 1396     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-local/8")
 1397     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-local/9")
 1398     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-local/10")
 1399     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-local/11")
 1400     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-local/12")
 1401     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-local/13")
 1402     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-local/14")
 1403     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-local/15")
 1404     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-live-local/16")
 1405     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-local/17")
 1406     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-local/18")
 1407     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-local/19")
 1408     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-local/20")
 1409     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-local/21")
 1410     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-local/21")
 1411     # . epilogue
 1412     89/<- %esp 5/r32/ebp
 1413     5d/pop-to-ebp
 1414     c3/return
 1415 
 1416 test-shadow-live-output:
 1417     # . prologue
 1418     55/push-ebp
 1419     89/<- %ebp 4/r32/esp
 1420     # setup
 1421     (clear-stream _test-input-stream)
 1422     (clear-stream $_test-input-buffered-file->buffer)
 1423     (clear-stream _test-output-stream)
 1424     (clear-stream $_test-output-buffered-file->buffer)
 1425     #
 1426     (write _test-input-stream "fn foo -> x/ecx: int {\n")
 1427     (write _test-input-stream "  x <- copy 3\n")
 1428     (write _test-input-stream "  {\n")
 1429     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1430     (write _test-input-stream "  }\n")
 1431     (write _test-input-stream "  x <- increment\n")
 1432     (write _test-input-stream "}\n")
 1433     # convert
 1434     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1435     (flush _test-output-buffered-file)
 1436 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1442     # check output
 1443     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-output/0")
 1444     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-output/1")
 1445     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-output/2")
 1446     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-output/3")
 1447     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-output/4")
 1448     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-output/5")
 1449     (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
 1450     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-output/8")
 1451     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-output/9")
 1452     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-output/10")
 1453     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-output/11")
 1454     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-output/12")
 1455     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-output/13")
 1456     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-output/14")
 1457     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-output/15")
 1458     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-output/17")
 1459     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-output/18")
 1460     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-output/19")
 1461     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-output/20")
 1462     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-output/21")
 1463     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-output/21")
 1464     # . epilogue
 1465     89/<- %esp 5/r32/ebp
 1466     5d/pop-to-ebp
 1467     c3/return
 1468 
 1469 _pending-test-local-clobbered-by-output:
 1470     # also doesn't spill
 1471     # . prologue
 1472     55/push-ebp
 1473     89/<- %ebp 4/r32/esp
 1474     # setup
 1475     (clear-stream _test-input-stream)
 1476     (clear-stream $_test-input-buffered-file->buffer)
 1477     (clear-stream _test-output-stream)
 1478     (clear-stream $_test-output-buffered-file->buffer)
 1479     #
 1480     (write _test-input-stream "fn foo -> x/ecx: int {\n")
 1481     (write _test-input-stream "  var y/ecx: int <- copy 4\n")
 1482     (write _test-input-stream "  x <- copy y\n")
 1483     (write _test-input-stream "}\n")
 1484     # convert
 1485     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1486     (flush _test-output-buffered-file)
 1487 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1493     # check output
 1494     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-local-clobbered-by-output/0")
 1495     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-local-clobbered-by-output/1")
 1496     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-local-clobbered-by-output/2")
 1497     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-local-clobbered-by-output/3")
 1498     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-local-clobbered-by-output/4")
 1499     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-local-clobbered-by-output/5")
 1500     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-local-clobbered-by-output/6")
 1501     (check-next-stream-line-equal _test-output-stream "    89/copy-to %ecx 0x00000001/r32"  "F - test-local-clobbered-by-output/7")
 1502     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-local-clobbered-by-output/8")
 1503     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-local-clobbered-by-output/9")
 1504     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-local-clobbered-by-output/10")
 1505     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-local-clobbered-by-output/11")
 1506     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-local-clobbered-by-output/12")
 1507     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-local-clobbered-by-output/13")
 1508     # . epilogue
 1509     89/<- %esp 5/r32/ebp
 1510     5d/pop-to-ebp
 1511     c3/return
 1512 
 1513 test-convert-function-with-branches-in-block:
 1514     # . prologue
 1515     55/push-ebp
 1516     89/<- %ebp 4/r32/esp
 1517     # setup
 1518     (clear-stream _test-input-stream)
 1519     (clear-stream $_test-input-buffered-file->buffer)
 1520     (clear-stream _test-output-stream)
 1521     (clear-stream $_test-output-buffered-file->buffer)
 1522     #
 1523     (write _test-input-stream "fn foo x: int {\n")
 1524     (write _test-input-stream "  {\n")
 1525     (write _test-input-stream "    break-if->=\n")
 1526     (write _test-input-stream "    loop-if-addr<\n")
 1527     (write _test-input-stream "    increment x\n")
 1528     (write _test-input-stream "    loop\n")
 1529     (write _test-input-stream "  }\n")
 1530     (write _test-input-stream "}\n")
 1531     # convert
 1532     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1533     (flush _test-output-buffered-file)
 1534 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1540     # check output
 1541     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
 1542     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
 1543     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
 1544     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
 1545     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
 1546     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
 1547     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
 1548     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
 1549     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
 1550     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
 1551     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
 1552     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
 1553     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
 1554     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
 1555     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
 1556     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
 1557     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
 1558     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
 1559     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
 1560     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
 1561     # . epilogue
 1562     89/<- %esp 5/r32/ebp
 1563     5d/pop-to-ebp
 1564     c3/return
 1565 
 1566 test-convert-function-with-branches-in-named-block:
 1567     # . prologue
 1568     55/push-ebp
 1569     89/<- %ebp 4/r32/esp
 1570     # setup
 1571     (clear-stream _test-input-stream)
 1572     (clear-stream $_test-input-buffered-file->buffer)
 1573     (clear-stream _test-output-stream)
 1574     (clear-stream $_test-output-buffered-file->buffer)
 1575     #
 1576     (write _test-input-stream "fn foo x: int {\n")
 1577     (write _test-input-stream "  $bar: {\n")
 1578     (write _test-input-stream "    break-if->= $bar\n")
 1579     (write _test-input-stream "    loop-if-addr< $bar\n")
 1580     (write _test-input-stream "    increment x\n")
 1581     (write _test-input-stream "    loop\n")
 1582     (write _test-input-stream "  }\n")
 1583     (write _test-input-stream "}\n")
 1584     # convert
 1585     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1586     (flush _test-output-buffered-file)
 1587 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1593     # check output
 1594     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
 1595     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
 1596     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
 1597     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
 1598     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
 1599     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
 1600     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
 1601     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
 1602     (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")
 1603     (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")
 1604     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
 1605     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
 1606     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
 1607     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
 1608     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
 1609     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
 1610     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
 1611     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
 1612     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
 1613     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
 1614     # . epilogue
 1615     89/<- %esp 5/r32/ebp
 1616     5d/pop-to-ebp
 1617     c3/return
 1618 
 1619 test-convert-function-with-var-in-nested-block:
 1620     # . prologue
 1621     55/push-ebp
 1622     89/<- %ebp 4/r32/esp
 1623     # setup
 1624     (clear-stream _test-input-stream)
 1625     (clear-stream $_test-input-buffered-file->buffer)
 1626     (clear-stream _test-output-stream)
 1627     (clear-stream $_test-output-buffered-file->buffer)
 1628     #
 1629     (write _test-input-stream "fn foo x: int {\n")
 1630     (write _test-input-stream "  {\n")
 1631     (write _test-input-stream "    {\n")
 1632     (write _test-input-stream "      var x: int\n")
 1633     (write _test-input-stream "      increment x\n")
 1634     (write _test-input-stream "    }\n")
 1635     (write _test-input-stream "  }\n")
 1636     (write _test-input-stream "}\n")
 1637     # convert
 1638     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1639     (flush _test-output-buffered-file)
 1640 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1646     # check output
 1647     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
 1648     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
 1649     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
 1650     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
 1651     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
 1652     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
 1653     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
 1654     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
 1655     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
 1656     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
 1657     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
 1658     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
 1659     (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")
 1660     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
 1661     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
 1662     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
 1663     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
 1664     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
 1665     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
 1666     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
 1667     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
 1668     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
 1669     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
 1670     # . epilogue
 1671     89/<- %esp 5/r32/ebp
 1672     5d/pop-to-ebp
 1673     c3/return
 1674 
 1675 test-convert-function-with-multiple-vars-in-nested-blocks:
 1676     # . prologue
 1677     55/push-ebp
 1678     89/<- %ebp 4/r32/esp
 1679     # setup
 1680     (clear-stream _test-input-stream)
 1681     (clear-stream $_test-input-buffered-file->buffer)
 1682     (clear-stream _test-output-stream)
 1683     (clear-stream $_test-output-buffered-file->buffer)
 1684     #
 1685     (write _test-input-stream "fn foo x: int {\n")
 1686     (write _test-input-stream "  {\n")
 1687     (write _test-input-stream "    var x/eax: int <- copy 0\n")
 1688     (write _test-input-stream "    {\n")
 1689     (write _test-input-stream "      var y: int\n")
 1690     (write _test-input-stream "      x <- add y\n")
 1691     (write _test-input-stream "    }\n")
 1692     (write _test-input-stream "  }\n")
 1693     (write _test-input-stream "}\n")
 1694     # convert
 1695     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1696     (flush _test-output-buffered-file)
 1697 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1703     # check output
 1704     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
 1705     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
 1706     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
 1707     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
 1708     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
 1709     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
 1710     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
 1711     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
 1712     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
 1713     (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")
 1714     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
 1715     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
 1716     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
 1717     (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")
 1718     (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")
 1719     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
 1720     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
 1721     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
 1722     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
 1723     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
 1724     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
 1725     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
 1726     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
 1727     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
 1728     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
 1729     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
 1730     # . epilogue
 1731     89/<- %esp 5/r32/ebp
 1732     5d/pop-to-ebp
 1733     c3/return
 1734 
 1735 test-convert-function-with-branches-and-local-vars:
 1736     # A conditional 'break' after a 'var' in a block is converted into a
 1737     # nested block that performs all necessary cleanup before jumping. This
 1738     # results in some ugly code duplication.
 1739     # . prologue
 1740     55/push-ebp
 1741     89/<- %ebp 4/r32/esp
 1742     # setup
 1743     (clear-stream _test-input-stream)
 1744     (clear-stream $_test-input-buffered-file->buffer)
 1745     (clear-stream _test-output-stream)
 1746     (clear-stream $_test-output-buffered-file->buffer)
 1747     #
 1748     (write _test-input-stream "fn foo {\n")
 1749     (write _test-input-stream "  {\n")
 1750     (write _test-input-stream "    var x: int\n")
 1751     (write _test-input-stream "    break-if->=\n")
 1752     (write _test-input-stream "    increment x\n")
 1753     (write _test-input-stream "  }\n")
 1754     (write _test-input-stream "}\n")
 1755     # convert
 1756     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1757     (flush _test-output-buffered-file)
 1758 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1764     # check output
 1765     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
 1766     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
 1767     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
 1768     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
 1769     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
 1770     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
 1771     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
 1772     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
 1773     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
 1774     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
 1775     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
 1776     (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")
 1777     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
 1778     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
 1779     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
 1780     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/15")
 1781     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
 1782     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
 1783     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
 1784     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
 1785     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
 1786     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
 1787     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
 1788     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
 1789     # . epilogue
 1790     89/<- %esp 5/r32/ebp
 1791     5d/pop-to-ebp
 1792     c3/return
 1793 
 1794 test-convert-function-with-conditional-loops-and-local-vars:
 1795     # A conditional 'loop' after a 'var' in a block is converted into a nested
 1796     # block that performs all necessary cleanup before jumping. This results
 1797     # in some ugly code duplication.
 1798     # . prologue
 1799     55/push-ebp
 1800     89/<- %ebp 4/r32/esp
 1801     # setup
 1802     (clear-stream _test-input-stream)
 1803     (clear-stream $_test-input-buffered-file->buffer)
 1804     (clear-stream _test-output-stream)
 1805     (clear-stream $_test-output-buffered-file->buffer)
 1806     #
 1807     (write _test-input-stream "fn foo {\n")
 1808     (write _test-input-stream "  {\n")
 1809     (write _test-input-stream "    var x: int\n")
 1810     (write _test-input-stream "    loop-if->=\n")
 1811     (write _test-input-stream "    increment x\n")
 1812     (write _test-input-stream "  }\n")
 1813     (write _test-input-stream "}\n")
 1814     # convert
 1815     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1816     (flush _test-output-buffered-file)
 1817 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1823     # check output
 1824     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
 1825     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
 1826     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
 1827     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
 1828     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
 1829     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
 1830     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
 1831     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
 1832     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
 1833     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
 1834     (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")
 1835     (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")
 1836     (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")
 1837     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
 1838     (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")
 1839     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/15")
 1840     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
 1841     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
 1842     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
 1843     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
 1844     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
 1845     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
 1846     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
 1847     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
 1848     # . epilogue
 1849     89/<- %esp 5/r32/ebp
 1850     5d/pop-to-ebp
 1851     c3/return
 1852 
 1853 test-convert-function-with-unconditional-loops-and-local-vars:
 1854     # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
 1855     # regular block cleanup. Any instructions after 'loop' are dead and
 1856     # therefore skipped.
 1857     # . prologue
 1858     55/push-ebp
 1859     89/<- %ebp 4/r32/esp
 1860     # setup
 1861     (clear-stream _test-input-stream)
 1862     (clear-stream $_test-input-buffered-file->buffer)
 1863     (clear-stream _test-output-stream)
 1864     (clear-stream $_test-output-buffered-file->buffer)
 1865     #
 1866     (write _test-input-stream "fn foo {\n")
 1867     (write _test-input-stream "  {\n")
 1868     (write _test-input-stream "    var x: int\n")
 1869     (write _test-input-stream "    loop\n")
 1870     (write _test-input-stream "    increment x\n")
 1871     (write _test-input-stream "  }\n")
 1872     (write _test-input-stream "}\n")
 1873     # convert
 1874     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1875     (flush _test-output-buffered-file)
 1876 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1882     # check output
 1883     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
 1884     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
 1885     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
 1886     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
 1887     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
 1888     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
 1889     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
 1890     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
 1891     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
 1892     (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")
 1893     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
 1894     # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
 1895     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
 1896     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
 1897     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
 1898     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
 1899     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
 1900     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
 1901     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
 1902     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
 1903     # . epilogue
 1904     89/<- %esp 5/r32/ebp
 1905     5d/pop-to-ebp
 1906     c3/return
 1907 
 1908 test-convert-function-with-branches-and-loops-and-local-vars:
 1909     # . prologue
 1910     55/push-ebp
 1911     89/<- %ebp 4/r32/esp
 1912     # setup
 1913     (clear-stream _test-input-stream)
 1914     (clear-stream $_test-input-buffered-file->buffer)
 1915     (clear-stream _test-output-stream)
 1916     (clear-stream $_test-output-buffered-file->buffer)
 1917     #
 1918     (write _test-input-stream "fn foo {\n")
 1919     (write _test-input-stream "  {\n")
 1920     (write _test-input-stream "    var x: int\n")
 1921     (write _test-input-stream "    break-if->=\n")
 1922     (write _test-input-stream "    increment x\n")
 1923     (write _test-input-stream "    loop\n")
 1924     (write _test-input-stream "  }\n")
 1925     (write _test-input-stream "}\n")
 1926     # convert
 1927     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1928     (flush _test-output-buffered-file)
 1929 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1935     # check output
 1936     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
 1937     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
 1938     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
 1939     (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")
 1940     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
 1941     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
 1942     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
 1943     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
 1944     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
 1945     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
 1946     (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")
 1947     (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")
 1948     (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")
 1949     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
 1950     (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")
 1951     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/15")
 1952     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
 1953     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
 1954     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
 1955     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
 1956     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
 1957     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
 1958     (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")
 1959     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
 1960     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
 1961     # . epilogue
 1962     89/<- %esp 5/r32/ebp
 1963     5d/pop-to-ebp
 1964     c3/return
 1965 
 1966 test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
 1967     # . prologue
 1968     55/push-ebp
 1969     89/<- %ebp 4/r32/esp
 1970     # setup
 1971     (clear-stream _test-input-stream)
 1972     (clear-stream $_test-input-buffered-file->buffer)
 1973     (clear-stream _test-output-stream)
 1974     (clear-stream $_test-output-buffered-file->buffer)
 1975     #
 1976     (write _test-input-stream "fn foo {\n")
 1977     (write _test-input-stream "  a: {\n")
 1978     (write _test-input-stream "    var x: int\n")
 1979     (write _test-input-stream "    {\n")
 1980     (write _test-input-stream "      var y: int\n")
 1981     (write _test-input-stream "      break-if->= a\n")
 1982     (write _test-input-stream "      increment x\n")
 1983     (write _test-input-stream "      loop\n")
 1984     (write _test-input-stream "    }\n")
 1985     (write _test-input-stream "  }\n")
 1986     (write _test-input-stream "}\n")
 1987     # convert
 1988     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1989     (flush _test-output-buffered-file)
 1990 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 1996     # check output
 1997     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
 1998     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
 1999     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
 2000     (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")
 2001     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
 2002     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
 2003     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
 2004     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
 2005     (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")
 2006     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
 2007     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
 2008     (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")
 2009     (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
 2010     (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")
 2011     (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")
 2012     (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")
 2013     (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")
 2014     (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
 2015     (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")
 2016     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/19")
 2017     (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")
 2018     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
 2019     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
 2020     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/23")
 2021     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
 2022     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
 2023     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
 2024     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
 2025     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
 2026     (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")
 2027     (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")
 2028     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
 2029     # . epilogue
 2030     89/<- %esp 5/r32/ebp
 2031     5d/pop-to-ebp
 2032     c3/return
 2033 
 2034 test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
 2035     # . prologue
 2036     55/push-ebp
 2037     89/<- %ebp 4/r32/esp
 2038     # setup
 2039     (clear-stream _test-input-stream)
 2040     (clear-stream $_test-input-buffered-file->buffer)
 2041     (clear-stream _test-output-stream)
 2042     (clear-stream $_test-output-buffered-file->buffer)
 2043     #
 2044     (write _test-input-stream "fn foo {\n")
 2045     (write _test-input-stream "  a: {\n")
 2046     (write _test-input-stream "    var x: int\n")
 2047     (write _test-input-stream "    {\n")
 2048     (write _test-input-stream "      var y: int\n")
 2049     (write _test-input-stream "      break a\n")
 2050     (write _test-input-stream "      increment x\n")
 2051     (write _test-input-stream "    }\n")
 2052     (write _test-input-stream "  }\n")
 2053     (write _test-input-stream "}\n")
 2054     # convert
 2055     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2056     (flush _test-output-buffered-file)
 2057 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2063     # check output
 2064     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
 2065     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
 2066     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
 2067     (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")
 2068     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
 2069     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
 2070     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
 2071     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
 2072     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
 2073     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
 2074     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
 2075     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
 2076     (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")
 2077     (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")
 2078     (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")
 2079     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
 2080     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
 2081     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17")
 2082     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
 2083     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
 2084     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
 2085     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
 2086     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
 2087     (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")
 2088     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
 2089     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
 2090     # . epilogue
 2091     89/<- %esp 5/r32/ebp
 2092     5d/pop-to-ebp
 2093     c3/return
 2094 
 2095 test-convert-function-with-unconditional-break-and-local-vars:
 2096     # . prologue
 2097     55/push-ebp
 2098     89/<- %ebp 4/r32/esp
 2099     # setup
 2100     (clear-stream _test-input-stream)
 2101     (clear-stream $_test-input-buffered-file->buffer)
 2102     (clear-stream _test-output-stream)
 2103     (clear-stream $_test-output-buffered-file->buffer)
 2104     #
 2105     (write _test-input-stream "fn foo {\n")
 2106     (write _test-input-stream "  {\n")
 2107     (write _test-input-stream "    var x: int\n")
 2108     (write _test-input-stream "    {\n")
 2109     (write _test-input-stream "      var y: int\n")
 2110     (write _test-input-stream "      break\n")
 2111     (write _test-input-stream "      increment x\n")
 2112     (write _test-input-stream "    }\n")
 2113     (write _test-input-stream "  }\n")
 2114     (write _test-input-stream "}\n")
 2115     # convert
 2116     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2117     (flush _test-output-buffered-file)
 2118 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2124     # check output
 2125     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
 2126     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
 2127     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
 2128     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
 2129     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
 2130     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
 2131     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
 2132     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
 2133     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
 2134     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
 2135     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
 2136     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
 2137     (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")
 2138     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
 2139     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
 2140     (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")
 2141     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
 2142     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
 2143     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
 2144     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
 2145     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
 2146     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
 2147     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
 2148     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
 2149     # . epilogue
 2150     89/<- %esp 5/r32/ebp
 2151     5d/pop-to-ebp
 2152     c3/return
 2153 
 2154 test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
 2155     # . prologue
 2156     55/push-ebp
 2157     89/<- %ebp 4/r32/esp
 2158     # setup
 2159     (clear-stream _test-input-stream)
 2160     (clear-stream $_test-input-buffered-file->buffer)
 2161     (clear-stream _test-output-stream)
 2162     (clear-stream $_test-output-buffered-file->buffer)
 2163     #
 2164     (write _test-input-stream "fn foo {\n")
 2165     (write _test-input-stream "  a: {\n")
 2166     (write _test-input-stream "    var x: int\n")
 2167     (write _test-input-stream "    {\n")
 2168     (write _test-input-stream "      var y: int\n")
 2169     (write _test-input-stream "      loop a\n")
 2170     (write _test-input-stream "      increment x\n")
 2171     (write _test-input-stream "    }\n")
 2172     (write _test-input-stream "  }\n")
 2173     (write _test-input-stream "}\n")
 2174     # convert
 2175     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2176     (flush _test-output-buffered-file)
 2177 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2183     # check output
 2184     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
 2185     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
 2186     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
 2187     (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")
 2188     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
 2189     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
 2190     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
 2191     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
 2192     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
 2193     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
 2194     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
 2195     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
 2196     (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")
 2197     (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")
 2198     (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")
 2199     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
 2200     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
 2201     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17")
 2202     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
 2203     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
 2204     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
 2205     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
 2206     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
 2207     (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")
 2208     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
 2209     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
 2210     # . epilogue
 2211     89/<- %esp 5/r32/ebp
 2212     5d/pop-to-ebp
 2213     c3/return
 2214 
 2215 test-convert-function-with-local-array-var-in-mem:
 2216     # . prologue
 2217     55/push-ebp
 2218     89/<- %ebp 4/r32/esp
 2219     # setup
 2220     (clear-stream _test-input-stream)
 2221     (clear-stream $_test-input-buffered-file->buffer)
 2222     (clear-stream _test-output-stream)
 2223     (clear-stream $_test-output-buffered-file->buffer)
 2224     #
 2225     (write _test-input-stream "fn foo {\n")
 2226     (write _test-input-stream "  var x: (array int 3)\n")
 2227     (write _test-input-stream "}\n")
 2228     # convert
 2229     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2230     (flush _test-output-buffered-file)
 2231 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2237     # check output
 2238     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-array-var-in-mem/0")
 2239     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-array-var-in-mem/1")
 2240     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-array-var-in-mem/2")
 2241     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-array-var-in-mem/3")
 2242     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-array-var-in-mem/4")
 2243     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-array-var-in-mem/5")
 2244     # define x
 2245     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-function-with-local-array-var-in-mem/7")
 2246     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-function-with-local-array-var-in-mem/8")
 2247     # reclaim x
 2248     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-function-with-local-array-var-in-mem/9")
 2249     #
 2250     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-array-var-in-mem/10")
 2251     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-array-var-in-mem/11")
 2252     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-array-var-in-mem/12")
 2253     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-array-var-in-mem/13")
 2254     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-array-var-in-mem/14")
 2255     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-array-var-in-mem/15")
 2256     # . epilogue
 2257     89/<- %esp 5/r32/ebp
 2258     5d/pop-to-ebp
 2259     c3/return
 2260 
 2261 test-convert-length-of-array:
 2262     # . prologue
 2263     55/push-ebp
 2264     89/<- %ebp 4/r32/esp
 2265     # setup
 2266     (clear-stream _test-input-stream)
 2267     (clear-stream $_test-input-buffered-file->buffer)
 2268     (clear-stream _test-output-stream)
 2269     (clear-stream $_test-output-buffered-file->buffer)
 2270     #
 2271     (write _test-input-stream "fn foo a: (addr array int) {\n")
 2272     (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
 2273     (write _test-input-stream "  var c/eax: int <- length b\n")
 2274     (write _test-input-stream "}\n")
 2275     # convert
 2276     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2277     (flush _test-output-buffered-file)
 2278 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2284     # check output
 2285     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
 2286     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
 2287     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
 2288     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
 2289     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
 2290     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
 2291     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
 2292     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
 2293     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
 2294     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
 2295     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
 2296     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
 2297     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
 2298     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
 2299     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
 2300     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
 2301     # . epilogue
 2302     89/<- %esp 5/r32/ebp
 2303     5d/pop-to-ebp
 2304     c3/return
 2305 
 2306 test-convert-length-of-array-on-stack:
 2307     # . prologue
 2308     55/push-ebp
 2309     89/<- %ebp 4/r32/esp
 2310     # setup
 2311     (clear-stream _test-input-stream)
 2312     (clear-stream $_test-input-buffered-file->buffer)
 2313     (clear-stream _test-output-stream)
 2314     (clear-stream $_test-output-buffered-file->buffer)
 2315     #
 2316     (write _test-input-stream "fn foo {\n")
 2317     (write _test-input-stream "  var a: (array int 3)\n")
 2318     (write _test-input-stream "  var b/eax: int <- length a\n")
 2319     (write _test-input-stream "}\n")
 2320     # convert
 2321     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2322     (flush _test-output-buffered-file)
 2323 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2329     # check output
 2330     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-on-stack/0")
 2331     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-on-stack/1")
 2332     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-on-stack/2")
 2333     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-on-stack/3")
 2334     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-on-stack/4")
 2335     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-on-stack/5")
 2336     # define x
 2337     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-length-of-array-on-stack/6")
 2338     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-length-of-array-on-stack/7")
 2339     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-on-stack/8")
 2340     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0xfffffff0) 0x00000000/r32"  "F - test-convert-length-of-array-on-stack/9")
 2341     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-on-stack/10")
 2342     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-length-of-array-on-stack/11")
 2343     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-on-stack/12")
 2344     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-on-stack/13")
 2345     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-on-stack/14")
 2346     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-on-stack/15")
 2347     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-on-stack/16")
 2348     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-on-stack/17")
 2349     # . epilogue
 2350     89/<- %esp 5/r32/ebp
 2351     5d/pop-to-ebp
 2352     c3/return
 2353 
 2354 test-convert-index-into-array:
 2355     # . prologue
 2356     55/push-ebp
 2357     89/<- %ebp 4/r32/esp
 2358     # setup
 2359     (clear-stream _test-input-stream)
 2360     (clear-stream $_test-input-buffered-file->buffer)
 2361     (clear-stream _test-output-stream)
 2362     (clear-stream $_test-output-buffered-file->buffer)
 2363     #
 2364     (write _test-input-stream "fn foo {\n")
 2365     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2366     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2367     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2368     (write _test-input-stream "}\n")
 2369     # convert
 2370     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2371     (flush _test-output-buffered-file)
 2372 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2378     # check output
 2379     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array/0")
 2380     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array/1")
 2381     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array/2")
 2382     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array/3")
 2383     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array/4")
 2384     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array/5")
 2385     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array/6")
 2386     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array/7")
 2387     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array/8")
 2388     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array/9")
 2389     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000002 + 4) 0x00000000/r32"  "F - test-convert-index-into-array/11")
 2390     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array/13")
 2391     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array/14")
 2392     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array/15")
 2393     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array/16")
 2394     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array/17")
 2395     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array/18")
 2396     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array/19")
 2397     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array/20")
 2398     # . epilogue
 2399     89/<- %esp 5/r32/ebp
 2400     5d/pop-to-ebp
 2401     c3/return
 2402 
 2403 test-convert-index-into-array-with-literal:
 2404     # . prologue
 2405     55/push-ebp
 2406     89/<- %ebp 4/r32/esp
 2407     # setup
 2408     (clear-stream _test-input-stream)
 2409     (clear-stream $_test-input-buffered-file->buffer)
 2410     (clear-stream _test-output-stream)
 2411     (clear-stream $_test-output-buffered-file->buffer)
 2412     #
 2413     (write _test-input-stream "fn foo {\n")
 2414     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2415     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
 2416     (write _test-input-stream "}\n")
 2417     # convert
 2418     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2419     (flush _test-output-buffered-file)
 2420 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2426     # check output
 2427     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-with-literal/0")
 2428     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-with-literal/1")
 2429     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-with-literal/2")
 2430     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-with-literal/3")
 2431     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-with-literal/4")
 2432     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-with-literal/5")
 2433     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-with-literal/6")
 2434     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-with-literal/7")
 2435                                                                                  # 2 * 4 bytes/elem + 4 bytes for length = offset 12
 2436     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x0000000c) 0x00000000/r32"  "F - test-convert-index-into-array-with-literal/8")
 2437     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-with-literal/9")
 2438     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-with-literal/10")
 2439     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-with-literal/11")
 2440     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-with-literal/12")
 2441     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-with-literal/13")
 2442     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-with-literal/14")
 2443     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-with-literal/15")
 2444     # . epilogue
 2445     89/<- %esp 5/r32/ebp
 2446     5d/pop-to-ebp
 2447     c3/return
 2448 
 2449 test-convert-index-into-array-on-stack:
 2450     # . prologue
 2451     55/push-ebp
 2452     89/<- %ebp 4/r32/esp
 2453     # setup
 2454     (clear-stream _test-input-stream)
 2455     (clear-stream $_test-input-buffered-file->buffer)
 2456     (clear-stream _test-output-stream)
 2457     (clear-stream $_test-output-buffered-file->buffer)
 2458     #
 2459     (write _test-input-stream "fn foo {\n")
 2460     (write _test-input-stream "  var arr: (array int 3)\n")
 2461     (write _test-input-stream "  var idx/eax: int <- copy 2\n")
 2462     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2463     (write _test-input-stream "}\n")
 2464     # convert
 2465     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2466     (flush _test-output-buffered-file)
 2467 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2473     # check output
 2474     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack/0")
 2475     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack/1")
 2476     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack/2")
 2477     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack/3")
 2478     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack/4")
 2479     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack/5")
 2480     # var arr
 2481     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack/6")
 2482     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack/7")
 2483     # var idx
 2484     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack/8")
 2485     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 2/imm32"                  "F - test-convert-index-into-array-on-stack/9")
 2486     # var x is at (ebp-0x10) + idx<<2 + 4 = ebp + idx<<2 - 0xc
 2487     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + eax<<0x00000002 + 0xfffffff4) 0x00000000/r32"  "F - test-convert-index-into-array-on-stack/10")
 2488     # reclaim idx
 2489     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack/11")
 2490     # reclaim arr
 2491     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack/12")
 2492     #
 2493     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack/13")
 2494     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack/14")
 2495     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack/15")
 2496     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack/16")
 2497     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack/17")
 2498     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack/18")
 2499     # . epilogue
 2500     89/<- %esp 5/r32/ebp
 2501     5d/pop-to-ebp
 2502     c3/return
 2503 
 2504 test-convert-index-into-array-on-stack-with-literal:
 2505     # . prologue
 2506     55/push-ebp
 2507     89/<- %ebp 4/r32/esp
 2508     # setup
 2509     (clear-stream _test-input-stream)
 2510     (clear-stream $_test-input-buffered-file->buffer)
 2511     (clear-stream _test-output-stream)
 2512     (clear-stream $_test-output-buffered-file->buffer)
 2513     #
 2514     (write _test-input-stream "fn foo {\n")
 2515     (write _test-input-stream "  var arr: (array int 3)\n")
 2516     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
 2517     (write _test-input-stream "}\n")
 2518     # convert
 2519     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2520     (flush _test-output-buffered-file)
 2521 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2527     # check output
 2528     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack-with-literal/0")
 2529     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack-with-literal/1")
 2530     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack-with-literal/2")
 2531     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack-with-literal/3")
 2532     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack-with-literal/4")
 2533     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack-with-literal/5")
 2534     # var arr
 2535     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack-with-literal/6")
 2536     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack-with-literal/7")
 2537     # var x
 2538     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack-with-literal/8")
 2539     # x is at (ebp-0x10) + 4 + 2*4 = ebp-4
 2540     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + 0xfffffffc) 0x00000000/r32"  "F - test-convert-index-into-array-on-stack-with-literal/9")
 2541     # reclaim x
 2542     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack-with-literal/10")
 2543     # reclaim arr
 2544     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack-with-literal/11")
 2545     #
 2546     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack-with-literal/12")
 2547     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack-with-literal/13")
 2548     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack-with-literal/14")
 2549     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack-with-literal/15")
 2550     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack-with-literal/16")
 2551     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack-with-literal/17")
 2552     # . epilogue
 2553     89/<- %esp 5/r32/ebp
 2554     5d/pop-to-ebp
 2555     c3/return
 2556 
 2557 test-convert-index-into-array-using-offset:
 2558     # . prologue
 2559     55/push-ebp
 2560     89/<- %ebp 4/r32/esp
 2561     # setup
 2562     (clear-stream _test-input-stream)
 2563     (clear-stream $_test-input-buffered-file->buffer)
 2564     (clear-stream _test-output-stream)
 2565     (clear-stream $_test-output-buffered-file->buffer)
 2566     #
 2567     (write _test-input-stream "fn foo {\n")
 2568     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2569     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2570     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2571     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2572     (write _test-input-stream "}\n")
 2573     # convert
 2574     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2575     (flush _test-output-buffered-file)
 2576 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2582     # check output
 2583     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset/0")
 2584     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset/1")
 2585     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset/2")
 2586     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset/3")
 2587     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset/4")
 2588     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset/5")
 2589     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset/6")
 2590     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset/7")
 2591     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset/8")
 2592     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-using-offset/9")
 2593     (check-next-stream-line-equal _test-output-stream "    69/multiply 0x00000004/imm32 %ecx 0x00000001/r32"  "F - test-convert-index-into-array-using-offset/10")
 2594     (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")
 2595     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset/12")
 2596     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset/13")
 2597     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset/14")
 2598     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset/15")
 2599     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset/16")
 2600     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset/17")
 2601     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset/18")
 2602     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset/19")
 2603     # . epilogue
 2604     89/<- %esp 5/r32/ebp
 2605     5d/pop-to-ebp
 2606     c3/return
 2607 
 2608 test-convert-index-into-array-using-offset-on-stack:
 2609     # . prologue
 2610     55/push-ebp
 2611     89/<- %ebp 4/r32/esp
 2612     # setup
 2613     (clear-stream _test-input-stream)
 2614     (clear-stream $_test-input-buffered-file->buffer)
 2615     (clear-stream _test-output-stream)
 2616     (clear-stream $_test-output-buffered-file->buffer)
 2617     #
 2618     (write _test-input-stream "fn foo {\n")
 2619     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2620     (write _test-input-stream "  var idx: int\n")
 2621     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2622     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2623     (write _test-input-stream "}\n")
 2624     # convert
 2625     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2626     (flush _test-output-buffered-file)
 2627 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2633     # check output
 2634     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset-on-stack/0")
 2635     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset-on-stack/1")
 2636     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset-on-stack/2")
 2637     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset-on-stack/3")
 2638     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset-on-stack/4")
 2639     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset-on-stack/5")
 2640     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset-on-stack/6")
 2641     (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")
 2642     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-using-offset-on-stack/8")
 2643     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset-on-stack/9")
 2644     (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")
 2645     (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")
 2646     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset-on-stack/12")
 2647     (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")
 2648     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset-on-stack/14")
 2649     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset-on-stack/15")
 2650     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset-on-stack/16")
 2651     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset-on-stack/17")
 2652     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset-on-stack/18")
 2653     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset-on-stack/19")
 2654     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset-on-stack/20")
 2655     # . epilogue
 2656     89/<- %esp 5/r32/ebp
 2657     5d/pop-to-ebp
 2658     c3/return
 2659 
 2660 test-convert-function-and-type-definition:
 2661     # . prologue
 2662     55/push-ebp
 2663     89/<- %ebp 4/r32/esp
 2664     # setup
 2665     (clear-stream _test-input-stream)
 2666     (clear-stream $_test-input-buffered-file->buffer)
 2667     (clear-stream _test-output-stream)
 2668     (clear-stream $_test-output-buffered-file->buffer)
 2669     #
 2670     (write _test-input-stream "fn foo a: (addr t) {\n")
 2671     (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
 2672     (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
 2673     (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
 2674     (write _test-input-stream "}\n")
 2675     (write _test-input-stream "type t {\n")
 2676     (write _test-input-stream "  x: int\n")
 2677     (write _test-input-stream "  y: int\n")
 2678     (write _test-input-stream "}\n")
 2679     # convert
 2680     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2681     (flush _test-output-buffered-file)
 2682 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2688     # check output
 2689     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
 2690     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
 2691     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
 2692     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
 2693     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
 2694     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
 2695     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
 2696     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
 2697     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
 2698     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
 2699     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
 2700     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
 2701     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
 2702     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
 2703     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
 2704     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
 2705     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
 2706     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
 2707     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
 2708     # . epilogue
 2709     89/<- %esp 5/r32/ebp
 2710     5d/pop-to-ebp
 2711     c3/return
 2712 
 2713 test-convert-function-with-local-var-with-user-defined-type:
 2714     # . prologue
 2715     55/push-ebp
 2716     89/<- %ebp 4/r32/esp
 2717     # setup
 2718     (clear-stream _test-input-stream)
 2719     (clear-stream $_test-input-buffered-file->buffer)
 2720     (clear-stream _test-output-stream)
 2721     (clear-stream $_test-output-buffered-file->buffer)
 2722     #
 2723     (write _test-input-stream "fn foo {\n")
 2724     (write _test-input-stream "  var a: t\n")
 2725     (write _test-input-stream "}\n")
 2726     (write _test-input-stream "type t {\n")
 2727     (write _test-input-stream "  x: int\n")
 2728     (write _test-input-stream "  y: int\n")
 2729     (write _test-input-stream "}\n")
 2730     # convert
 2731     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2732     (flush _test-output-buffered-file)
 2733 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2739     # check output
 2740     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
 2741     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
 2742     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
 2743     (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")
 2744     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
 2745     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
 2746     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
 2747     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
 2748     (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")
 2749     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
 2750     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
 2751     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
 2752     (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")
 2753     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
 2754     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
 2755     # . epilogue
 2756     89/<- %esp 5/r32/ebp
 2757     5d/pop-to-ebp
 2758     c3/return
 2759 
 2760 test-convert-get-of-type-on-stack:
 2761     # . prologue
 2762     55/push-ebp
 2763     89/<- %ebp 4/r32/esp
 2764     # setup
 2765     (clear-stream _test-input-stream)
 2766     (clear-stream $_test-input-buffered-file->buffer)
 2767     (clear-stream _test-output-stream)
 2768     (clear-stream $_test-output-buffered-file->buffer)
 2769     #
 2770     (write _test-input-stream "fn foo {\n")
 2771     (write _test-input-stream "  var a: t\n")
 2772     (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
 2773     (write _test-input-stream "}\n")
 2774     (write _test-input-stream "type t {\n")
 2775     (write _test-input-stream "  x: int\n")
 2776     (write _test-input-stream "  y: int\n")
 2777     (write _test-input-stream "}\n")
 2778     # convert
 2779     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2780     (flush _test-output-buffered-file)
 2781 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2787     # check output
 2788     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-of-type-on-stack/0")
 2789     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-of-type-on-stack/1")
 2790     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-of-type-on-stack/2")
 2791     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-of-type-on-stack/3")
 2792     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-of-type-on-stack/4")
 2793     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-of-type-on-stack/5")
 2794     # var a
 2795     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/6")
 2796     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/7")
 2797     # var c
 2798     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-of-type-on-stack/8")
 2799     # get
 2800     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000001/r32"  "F - test-convert-get-of-type-on-stack/9")
 2801     # reclaim c
 2802     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-of-type-on-stack/10")
 2803     # reclaim a
 2804     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-get-of-type-on-stack/11")
 2805     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-of-type-on-stack/12")
 2806     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-of-type-on-stack/13")
 2807     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-of-type-on-stack/14")
 2808     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-of-type-on-stack/15")
 2809     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-of-type-on-stack/16")
 2810     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-of-type-on-stack/17")
 2811     # . epilogue
 2812     89/<- %esp 5/r32/ebp
 2813     5d/pop-to-ebp
 2814     c3/return
 2815 
 2816 test-convert-array-of-user-defined-types:
 2817     # . prologue
 2818     55/push-ebp
 2819     89/<- %ebp 4/r32/esp
 2820     # setup
 2821     (clear-stream _test-input-stream)
 2822     (clear-stream $_test-input-buffered-file->buffer)
 2823     (clear-stream _test-output-stream)
 2824     (clear-stream $_test-output-buffered-file->buffer)
 2825     #
 2826     (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
 2827     (write _test-input-stream "  x: int\n")
 2828     (write _test-input-stream "  y: int\n")
 2829     (write _test-input-stream "}\n")
 2830     (write _test-input-stream "fn foo {\n")
 2831     (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
 2832     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2833     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2834     (write _test-input-stream "}\n")
 2835     # convert
 2836     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2837     (flush _test-output-buffered-file)
 2838 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2844     # check output
 2845     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-array-of-user-defined-types/0")
 2846     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-array-of-user-defined-types/1")
 2847     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-array-of-user-defined-types/2")
 2848     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-array-of-user-defined-types/3")
 2849     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-array-of-user-defined-types/4")
 2850     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-array-of-user-defined-types/5")
 2851     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-array-of-user-defined-types/6")
 2852     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-array-of-user-defined-types/7")
 2853     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-array-of-user-defined-types/8")
 2854     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-array-of-user-defined-types/9")
 2855     (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")
 2856     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-array-of-user-defined-types/13")
 2857     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-array-of-user-defined-types/14")
 2858     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-array-of-user-defined-types/15")
 2859     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-array-of-user-defined-types/16")
 2860     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-array-of-user-defined-types/17")
 2861     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-array-of-user-defined-types/18")
 2862     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-array-of-user-defined-types/19")
 2863     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-array-of-user-defined-types/20")
 2864     # . epilogue
 2865     89/<- %esp 5/r32/ebp
 2866     5d/pop-to-ebp
 2867     c3/return
 2868 
 2869 #######################################################
 2870 # Parsing
 2871 #######################################################
 2872 
 2873 parse-mu:  # in: (addr buffered-file)
 2874     # pseudocode
 2875     #   var curr-function: (addr (handle function)) = Program->functions
 2876     #   var curr-type: (addr (handle typeinfo)) = Program->types
 2877     #   var line: (stream byte 512)
 2878     #   var word-slice: slice
 2879     #   while true                                  # line loop
 2880     #     clear-stream(line)
 2881     #     read-line-buffered(in, line)
 2882     #     if (line->write == 0) break               # end of file
 2883     #     word-slice = next-mu-token(line)
 2884     #     if slice-empty?(word-slice)               # end of line
 2885     #       continue
 2886     #     else if slice-starts-with?(word-slice, "#")  # comment
 2887     #       continue                                # end of line
 2888     #     else if slice-equal?(word-slice, "fn")
 2889     #       var new-function: (handle function) = allocate(function)
 2890     #       var vars: (stack (addr var) 256)
 2891     #       populate-mu-function-header(in, new-function, vars)
 2892     #       populate-mu-function-body(in, new-function, vars)
 2893     #       assert(vars->top == 0)
 2894     #       *curr-function = new-function
 2895     #       curr-function = &new-function->next
 2896     #     else if slice-equal?(word-slice, "type")
 2897     #       word-slice = next-mu-token(line)
 2898     #       type-id = pos-or-insert-slice(Type-id, word-slice)
 2899     #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
 2900     #       assert(next-word(line) == "{")
 2901     #       populate-mu-type(in, new-type)
 2902     #     else
 2903     #       abort()
 2904     #
 2905     # . prologue
 2906     55/push-ebp
 2907     89/<- %ebp 4/r32/esp
 2908     # . save registers
 2909     50/push-eax
 2910     51/push-ecx
 2911     52/push-edx
 2912     53/push-ebx
 2913     56/push-esi
 2914     57/push-edi
 2915     # var line/ecx: (stream byte 512)
 2916     81 5/subop/subtract %esp 0x200/imm32
 2917     68/push 0x200/imm32/length
 2918     68/push 0/imm32/read
 2919     68/push 0/imm32/write
 2920     89/<- %ecx 4/r32/esp
 2921     # var word-slice/edx: slice
 2922     68/push 0/imm32/end
 2923     68/push 0/imm32/start
 2924     89/<- %edx 4/r32/esp
 2925     # var curr-function/edi: (addr (handle function))
 2926     bf/copy-to-edi _Program-functions/imm32
 2927     # var curr-type/esi: (addr (handle typeinfo))
 2928     be/copy-to-esi _Program-types/imm32
 2929     # var vars/ebx: (stack (addr var) 256)
 2930     81 5/subop/subtract %esp 0x400/imm32
 2931     68/push 0x400/imm32/length
 2932     68/push 0/imm32/top
 2933     89/<- %ebx 4/r32/esp
 2934     {
 2935 $parse-mu:line-loop:
 2936       (clear-stream %ecx)
 2937       (read-line-buffered *(ebp+8) %ecx)
 2938       # if (line->write == 0) break
 2939       81 7/subop/compare *ecx 0/imm32
 2940       0f 84/jump-if-= break/disp32
 2941 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
 2947       (next-mu-token %ecx %edx)
 2948       # if slice-empty?(word-slice) continue
 2949       (slice-empty? %edx)
 2950       3d/compare-eax-and 0/imm32/false
 2951       0f 85/jump-if-!= loop/disp32
 2952       # if (*word-slice->start == "#") continue
 2953       # . eax = *word-slice->start
 2954       8b/-> *edx 0/r32/eax
 2955       8a/copy-byte *eax 0/r32/AL
 2956       81 4/subop/and %eax 0xff/imm32
 2957       # . if (eax == '#') continue
 2958       3d/compare-eax-and 0x23/imm32/hash
 2959       0f 84/jump-if-= loop/disp32
 2960       # if (slice-equal?(word-slice, "fn")) parse a function
 2961       {
 2962 $parse-mu:fn:
 2963         (slice-equal? %edx "fn")
 2964         3d/compare-eax-and 0/imm32/false
 2965         0f 84/jump-if-= break/disp32
 2966         # var new-function/eax: (handle function) = populate-mu-function(line, in, vars)
 2967         (allocate Heap *Function-size)  # => eax
 2968         (zero-out %eax *Function-size)
 2969         (clear-stack %ebx)
 2970         (populate-mu-function-header %ecx %eax %ebx)
 2971         (populate-mu-function-body *(ebp+8) %eax %ebx)
 2972         # *curr-function = new-function
 2973         89/<- *edi 0/r32/eax
 2974         # curr-function = &new-function->next
 2975         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
 2976         e9/jump $parse-mu:line-loop/disp32
 2977       }
 2978       # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
 2979       {
 2980 $parse-mu:type:
 2981         (slice-equal? %edx "type")
 2982         3d/compare-eax-and 0/imm32
 2983         0f 84/jump-if-= break/disp32
 2984         (next-mu-token %ecx %edx)
 2985         # var type-id/eax: int
 2986         (pos-or-insert-slice Type-id %edx)  # => eax
 2987         # var new-type/eax: (handle typeinfo)
 2988         (find-or-create-typeinfo %eax)  # => eax
 2989         # TODO: ensure that 'line' has nothing else but '{'
 2990         (populate-mu-type *(ebp+8) %eax)  # => eax
 2991         e9/jump $parse-mu:line-loop/disp32
 2992       }
 2993       # otherwise abort
 2994       e9/jump $parse-mu:error1/disp32
 2995     } # end line loop
 2996 $parse-mu:end:
 2997     # . reclaim locals
 2998     81 0/subop/add %esp 0x630/imm32
 2999     # . restore registers
 3000     5f/pop-to-edi
 3001     5e/pop-to-esi
 3002     5b/pop-to-ebx
 3003     5a/pop-to-edx
 3004     59/pop-to-ecx
 3005     58/pop-to-eax
 3006     # . epilogue
 3007     89/<- %esp 5/r32/ebp
 3008     5d/pop-to-ebp
 3009     c3/return
 3010 
 3011 $parse-mu:error1:
 3012     # error("unexpected top-level command: " word-slice "\n")
 3013     (write-buffered Stderr "unexpected top-level command: ")
 3014     (write-slice-buffered Stderr %edx)
 3015     (write-buffered Stderr "\n")
 3016     (flush Stderr)
 3017     # . syscall(exit, 1)
 3018     bb/copy-to-ebx  1/imm32
 3019     b8/copy-to-eax  1/imm32/exit
 3020     cd/syscall  0x80/imm8
 3021     # never gets here
 3022 
 3023 $parse-mu:error2:
 3024     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
 3025     (print-int32-buffered Stderr *ebx)
 3026     (write-buffered Stderr " vars not reclaimed after fn '")
 3027     (write-slice-buffered Stderr *eax)  # Function-name
 3028     (write-buffered Stderr "'\n")
 3029     (flush Stderr)
 3030     # . syscall(exit, 1)
 3031     bb/copy-to-ebx  1/imm32
 3032     b8/copy-to-eax  1/imm32/exit
 3033     cd/syscall  0x80/imm8
 3034     # never gets here
 3035 
 3036 # scenarios considered:
 3037 # ✗ fn foo  # no block
 3038 # ✓ fn foo {
 3039 # ✗ fn foo { {
 3040 # ✗ fn foo { }
 3041 # ✗ fn foo { } {
 3042 # ✗ fn foo x {
 3043 # ✗ fn foo x: {
 3044 # ✓ fn foo x: int {
 3045 # ✓ fn foo x: int {
 3046 # ✓ fn foo x: int -> y/eax: int {
 3047 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
 3048     # pseudocode:
 3049     #   var name: slice
 3050     #   next-mu-token(first-line, name)
 3051     #   assert(name not in '{' '}' '->')
 3052     #   out->name = slice-to-string(name)
 3053     #   ## inouts
 3054     #   while true
 3055     #     ## name
 3056     #     name = next-mu-token(first-line)
 3057     #     if (name == '{') goto done
 3058     #     if (name == '->') break
 3059     #     assert(name != '}')
 3060     #     var v: (handle var) = parse-var-with-type(name, first-line)
 3061     #     assert(v->register == null)
 3062     #     # v->block-depth is implicitly 0
 3063     #     out->inouts = append(out->inouts, v)
 3064     #     push(vars, v)
 3065     #   ## outputs
 3066     #   while true
 3067     #     ## name
 3068     #     name = next-mu-token(first-line)
 3069     #     assert(name not in '{' '}' '->')
 3070     #     var v: (handle var) = parse-var-with-type(name, first-line)
 3071     #     assert(v->register != null)
 3072     #     out->outputs = append(out->outputs, v)
 3073     #   done:
 3074     #
 3075     # . prologue
 3076     55/push-ebp
 3077     89/<- %ebp 4/r32/esp
 3078     # . save registers
 3079     50/push-eax
 3080     51/push-ecx
 3081     52/push-edx
 3082     53/push-ebx
 3083     57/push-edi
 3084     # edi = out
 3085     8b/-> *(ebp+0xc) 7/r32/edi
 3086     # var word-slice/ecx: slice
 3087     68/push 0/imm32/end
 3088     68/push 0/imm32/start
 3089     89/<- %ecx 4/r32/esp
 3090     # read function name
 3091     (next-mu-token *(ebp+8) %ecx)
 3092     # error checking
 3093     # TODO: error if name starts with 'break' or 'loop'
 3094     # if (word-slice == '{') abort
 3095     (slice-equal? %ecx "{")   # => eax
 3096     3d/compare-eax-and 0/imm32/false
 3097     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3098     # if (word-slice == '->') abort
 3099     (slice-equal? %ecx "->")   # => eax
 3100     3d/compare-eax-and 0/imm32/false
 3101     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3102     # if (word-slice == '}') abort
 3103     (slice-equal? %ecx "}")   # => eax
 3104     3d/compare-eax-and 0/imm32/false
 3105     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3106     # save function name
 3107     (slice-to-string Heap %ecx)  # => eax
 3108     89/<- *edi 0/r32/eax  # Function-name
 3109     # initialize default subx-name as well
 3110     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
 3111     # save function inouts
 3112     {
 3113 $populate-mu-function-header:check-for-inout:
 3114       (next-mu-token *(ebp+8) %ecx)
 3115       # if (word-slice == '{') goto done
 3116       (slice-equal? %ecx "{")   # => eax
 3117       3d/compare-eax-and 0/imm32/false
 3118       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
 3119       # if (word-slice == '->') break
 3120       (slice-equal? %ecx "->")   # => eax
 3121       3d/compare-eax-and 0/imm32/false
 3122       0f 85/jump-if-!= break/disp32
 3123       # if (word-slice == '}') abort
 3124       (slice-equal? %ecx "}")   # => eax
 3125       3d/compare-eax-and 0/imm32/false
 3126       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3127       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
 3128       (parse-var-with-type %ecx *(ebp+8))  # => eax
 3129       89/<- %ebx 0/r32/eax
 3130       # assert(v->register == null)
 3131       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 3132       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
 3133       # v->block-depth is implicitly 0
 3134       #
 3135       # out->inouts = append(out->inouts, v)
 3136       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
 3137       89/<- *(edi+8) 0/r32/eax  # Function-inouts
 3138       # push(vars, v)
 3139       (push *(ebp+0x10) %ebx)
 3140       #
 3141       e9/jump loop/disp32
 3142     }
 3143     # save function outputs
 3144     {
 3145 $populate-mu-function-header:check-for-out:
 3146       (next-mu-token *(ebp+8) %ecx)
 3147       # if (word-slice == '{') break
 3148       (slice-equal? %ecx "{")   # => eax
 3149       3d/compare-eax-and 0/imm32/false
 3150       0f 85/jump-if-!= break/disp32
 3151       # if (word-slice == '->') abort
 3152       (slice-equal? %ecx "->")   # => eax
 3153       3d/compare-eax-and 0/imm32/false
 3154       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3155       # if (word-slice == '}') abort
 3156       (slice-equal? %ecx "}")   # => eax
 3157       3d/compare-eax-and 0/imm32/false
 3158       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3159       #
 3160       (parse-var-with-type %ecx *(ebp+8))  # => eax
 3161       89/<- %ebx 0/r32/eax
 3162       # assert(var->register != null)
 3163       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 3164       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
 3165       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
 3166       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
 3167       e9/jump loop/disp32
 3168     }
 3169 $populate-mu-function-header:done:
 3170     (check-no-tokens-left *(ebp+8))
 3171 $populate-mu-function-header:end:
 3172     # . reclaim locals
 3173     81 0/subop/add %esp 8/imm32
 3174     # . restore registers
 3175     5f/pop-to-edi
 3176     5b/pop-to-ebx
 3177     5a/pop-to-edx
 3178     59/pop-to-ecx
 3179     58/pop-to-eax
 3180     # . epilogue
 3181     89/<- %esp 5/r32/ebp
 3182     5d/pop-to-ebp
 3183     c3/return
 3184 
 3185 $populate-mu-function-header:error1:
 3186     # error("function header not in form 'fn <name> {'")
 3187     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
 3188     (flush Stderr)
 3189     (rewind-stream *(ebp+8))
 3190     (write-stream 2 *(ebp+8))
 3191     (write-buffered Stderr "'\n")
 3192     (flush Stderr)
 3193     # . syscall(exit, 1)
 3194     bb/copy-to-ebx  1/imm32
 3195     b8/copy-to-eax  1/imm32/exit
 3196     cd/syscall  0x80/imm8
 3197     # never gets here
 3198 
 3199 $populate-mu-function-header:error2:
 3200     # error("function input '" var "' cannot be in a register")
 3201     (write-buffered Stderr "function input '")
 3202     (write-buffered Stderr *ebx)  # Var-name
 3203     (write-buffered Stderr "' cannot be in a register")
 3204     (flush Stderr)
 3205     # . syscall(exit, 1)
 3206     bb/copy-to-ebx  1/imm32
 3207     b8/copy-to-eax  1/imm32/exit
 3208     cd/syscall  0x80/imm8
 3209     # never gets here
 3210 
 3211 $populate-mu-function-header:error3:
 3212     # error("function input '" var "' must be in a register")
 3213     (write-buffered Stderr "function input '")
 3214     (write-buffered Stderr *eax)  # Var-name
 3215     (write-buffered Stderr " must be in a register'")
 3216     (flush Stderr)
 3217     (rewind-stream *(ebp+8))
 3218     (write-stream 2 *(ebp+8))
 3219     (write-buffered Stderr "'\n")
 3220     (flush Stderr)
 3221     # . syscall(exit, 1)
 3222     bb/copy-to-ebx  1/imm32
 3223     b8/copy-to-eax  1/imm32/exit
 3224     cd/syscall  0x80/imm8
 3225     # never gets here
 3226 
 3227 test-function-header-with-arg:
 3228     # . prologue
 3229     55/push-ebp
 3230     89/<- %ebp 4/r32/esp
 3231     # setup
 3232     (clear-stream _test-input-stream)
 3233     (write _test-input-stream "foo n: int {\n")
 3234     # var result/ecx: function
 3235     2b/subtract-> *Function-size 4/r32/esp
 3236     89/<- %ecx 4/r32/esp
 3237     (zero-out %ecx *Function-size)
 3238     # var vars/ebx: (stack (addr var) 16)
 3239     81 5/subop/subtract %esp 0x10/imm32
 3240     68/push 0x10/imm32/length
 3241     68/push 0/imm32/top
 3242     89/<- %ebx 4/r32/esp
 3243     # convert
 3244     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3245     # check result
 3246     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
 3247     # edx: (handle list var) = result->inouts
 3248     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3249     # ebx: (handle var) = result->inouts->value
 3250     8b/-> *edx 3/r32/ebx  # List-value
 3251     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
 3252     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3253     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
 3254     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
 3255     # . epilogue
 3256     89/<- %esp 5/r32/ebp
 3257     5d/pop-to-ebp
 3258     c3/return
 3259 
 3260 test-function-header-with-multiple-args:
 3261     # . prologue
 3262     55/push-ebp
 3263     89/<- %ebp 4/r32/esp
 3264     # setup
 3265     (clear-stream _test-input-stream)
 3266     (write _test-input-stream "foo a: int, b: int c: int {\n")
 3267     # result/ecx: (handle function)
 3268     2b/subtract-> *Function-size 4/r32/esp
 3269     89/<- %ecx 4/r32/esp
 3270     (zero-out %ecx *Function-size)
 3271     # var vars/ebx: (stack (addr var) 16)
 3272     81 5/subop/subtract %esp 0x10/imm32
 3273     68/push 0x10/imm32/length
 3274     68/push 0/imm32/top
 3275     89/<- %ebx 4/r32/esp
 3276     # convert
 3277     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3278     # check result
 3279     (check-strings-equal *ecx "foo")  # Function-name
 3280     # edx: (handle list var) = result->inouts
 3281     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3282 $test-function-header-with-multiple-args:inout0:
 3283     # ebx: (handle var) = result->inouts->value
 3284     8b/-> *edx 3/r32/ebx  # List-value
 3285     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
 3286     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3287     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
 3288     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
 3289     # edx = result->inouts->next
 3290     8b/-> *(edx+4) 2/r32/edx  # List-next
 3291 $test-function-header-with-multiple-args:inout1:
 3292     # ebx = result->inouts->next->value
 3293     8b/-> *edx 3/r32/ebx  # List-value
 3294     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
 3295     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3296     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
 3297     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
 3298     # edx = result->inouts->next->next
 3299     8b/-> *(edx+4) 2/r32/edx  # List-next
 3300 $test-function-header-with-multiple-args:inout2:
 3301     # ebx = result->inouts->next->next->value
 3302     8b/-> *edx 3/r32/ebx  # List-value
 3303     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
 3304     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3305     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
 3306     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
 3307     # . epilogue
 3308     89/<- %esp 5/r32/ebp
 3309     5d/pop-to-ebp
 3310     c3/return
 3311 
 3312 test-function-with-multiple-args-and-outputs:
 3313     # . prologue
 3314     55/push-ebp
 3315     89/<- %ebp 4/r32/esp
 3316     # setup
 3317     (clear-stream _test-input-stream)
 3318     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
 3319     # result/ecx: (handle function)
 3320     2b/subtract-> *Function-size 4/r32/esp
 3321     89/<- %ecx 4/r32/esp
 3322     (zero-out %ecx *Function-size)
 3323     # var vars/ebx: (stack (addr var) 16)
 3324     81 5/subop/subtract %esp 0x10/imm32
 3325     68/push 0x10/imm32/length
 3326     68/push 0/imm32/top
 3327     89/<- %ebx 4/r32/esp
 3328     # convert
 3329     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3330     # check result
 3331     (check-strings-equal *ecx "foo")  # Function-name
 3332     # edx: (handle list var) = result->inouts
 3333     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3334     # ebx: (handle var) = result->inouts->value
 3335     8b/-> *edx 3/r32/ebx  # List-value
 3336     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
 3337     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3338     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
 3339     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
 3340     # edx = result->inouts->next
 3341     8b/-> *(edx+4) 2/r32/edx  # List-next
 3342     # ebx = result->inouts->next->value
 3343     8b/-> *edx 3/r32/ebx  # List-value
 3344     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
 3345     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3346     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
 3347     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
 3348     # edx = result->inouts->next->next
 3349     8b/-> *(edx+4) 2/r32/edx  # List-next
 3350     # ebx = result->inouts->next->next->value
 3351     8b/-> *edx 3/r32/ebx  # List-value
 3352     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
 3353     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3354     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
 3355     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
 3356     # edx: (handle list var) = result->outputs
 3357     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
 3358     # ebx: (handle var) = result->outputs->value
 3359     8b/-> *edx 3/r32/ebx  # List-value
 3360     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
 3361     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3362     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3363     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
 3364     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
 3365     # edx = result->outputs->next
 3366     8b/-> *(edx+4) 2/r32/edx  # List-next
 3367     # ebx = result->outputs->next->value
 3368     8b/-> *edx 3/r32/ebx  # List-value
 3369     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
 3370     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3371     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3372     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
 3373     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
 3374     # . epilogue
 3375     89/<- %esp 5/r32/ebp
 3376     5d/pop-to-ebp
 3377     c3/return
 3378 
 3379 # format for variables with types
 3380 #   x: int
 3381 #   x: int,
 3382 #   x/eax: int
 3383 #   x/eax: int,
 3384 # ignores at most one trailing comma
 3385 # WARNING: modifies name
 3386 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
 3387     # pseudocode:
 3388     #   var s: slice
 3389     #   if (!slice-ends-with(name, ":"))
 3390     #     abort
 3391     #   --name->end to skip ':'
 3392     #   next-token-from-slice(name->start, name->end, '/', s)
 3393     #   result = new-var-from-slice(s)
 3394     #   ## register
 3395     #   next-token-from-slice(s->end, name->end, '/', s)
 3396     #   if (!slice-empty?(s))
 3397     #     v->register = slice-to-string(s)
 3398     #   ## type
 3399     #   var type: (handle tree type-id) = parse-type(first-line)
 3400     #   v->type = type
 3401     #   return v
 3402     #
 3403     # . prologue
 3404     55/push-ebp
 3405     89/<- %ebp 4/r32/esp
 3406     # . save registers
 3407     51/push-ecx
 3408     52/push-edx
 3409     53/push-ebx
 3410     56/push-esi
 3411     57/push-edi
 3412     # esi = name
 3413     8b/-> *(ebp+8) 6/r32/esi
 3414     # if (!slice-ends-with?(name, ":")) abort
 3415     8b/-> *(esi+4) 1/r32/ecx  # Slice-end
 3416     49/decrement-ecx
 3417     8a/copy-byte *ecx 1/r32/CL
 3418     81 4/subop/and %ecx 0xff/imm32
 3419     81 7/subop/compare %ecx 0x3a/imm32/colon
 3420     0f 85/jump-if-!= $parse-var-with-type:abort/disp32
 3421     # --name->end to skip ':'
 3422     ff 1/subop/decrement *(esi+4)
 3423     # var s/ecx: slice
 3424     68/push 0/imm32/end
 3425     68/push 0/imm32/start
 3426     89/<- %ecx 4/r32/esp
 3427 $parse-var-with-type:parse-name:
 3428     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
 3429 $parse-var-with-type:create-var:
 3430     # edi = new-var-from-slice(s)
 3431     (new-var-from-slice Heap %ecx)  # => eax
 3432     89/<- %edi 0/r32/eax
 3433     # save v->register
 3434 $parse-var-with-type:save-register:
 3435     # s = next-token(...)
 3436     (next-token-from-slice *(ecx+4) *(esi+4) 0x2f %ecx)  # s->end, name->end, '/'
 3437     # if (!slice-empty?(s)) v->register = slice-to-string(s)
 3438     {
 3439 $parse-var-with-type:write-register:
 3440       (slice-empty? %ecx)  # => eax
 3441       3d/compare-eax-and 0/imm32/false
 3442       75/jump-if-!= break/disp8
 3443       (slice-to-string Heap %ecx)
 3444       89/<- *(edi+0x10) 0/r32/eax  # Var-register
 3445     }
 3446 $parse-var-with-type:save-type:
 3447     (parse-type Heap *(ebp+0xc))  # => eax
 3448 #?     (write-buffered Stderr "saving to var ")
 3449 #?     (print-int32-buffered Stderr %edi)
 3450 #?     (write-buffered Stderr Newline)
 3451 #?     (flush Stderr)
 3452     89/<- *(edi+4) 0/r32/eax  # Var-type
 3453 $parse-var-with-type:end:
 3454     # return result
 3455     89/<- %eax 7/r32/edi
 3456     # . reclaim locals
 3457     81 0/subop/add %esp 8/imm32
 3458     # . restore registers
 3459     5f/pop-to-edi
 3460     5e/pop-to-esi
 3461     5b/pop-to-ebx
 3462     5a/pop-to-edx
 3463     59/pop-to-ecx
 3464     # . epilogue
 3465     89/<- %esp 5/r32/ebp
 3466     5d/pop-to-ebp
 3467     c3/return
 3468 
 3469 $parse-var-with-type:abort:
 3470     # error("var should have form 'name: type' in '" line "'\n")
 3471     (write-buffered Stderr "var should have form 'name: type' in '")
 3472     (flush Stderr)
 3473     (rewind-stream *(ebp+0xc))
 3474     (write-stream 2 *(ebp+0xc))
 3475     (write-buffered Stderr "'\n")
 3476     (flush Stderr)
 3477     # . syscall(exit, 1)
 3478     bb/copy-to-ebx  1/imm32
 3479     b8/copy-to-eax  1/imm32/exit
 3480     cd/syscall  0x80/imm8
 3481     # never gets here
 3482 
 3483 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3484     # pseudocode:
 3485     #   var s: slice = next-mu-token(in)
 3486     #   assert s != ""
 3487     #   assert s != "->"
 3488     #   assert s != "{"
 3489     #   assert s != "}"
 3490     #   if s == ")"
 3491     #     return 0
 3492     #   result = allocate(Tree)
 3493     #   zero-out(result, *Tree-size)
 3494     #   if s != "("
 3495     #     result->left = pos-or-insert-slice(Type-id, s)
 3496     #     return
 3497     #   result->left = parse-type(ad, in)
 3498     #   result->right = parse-type-tree(ad, in)
 3499     #
 3500     # . prologue
 3501     55/push-ebp
 3502     89/<- %ebp 4/r32/esp
 3503     # . save registers
 3504     51/push-ecx
 3505     52/push-edx
 3506     # var s/ecx: slice
 3507     68/push 0/imm32
 3508     68/push 0/imm32
 3509     89/<- %ecx 4/r32/esp
 3510     # s = next-mu-token(in)
 3511     (next-mu-token *(ebp+0xc) %ecx)
 3512 #?     (write-buffered Stderr "tok: ")
 3513 #?     (write-slice-buffered Stderr %ecx)
 3514 #?     (write-buffered Stderr "$\n")
 3515 #?     (flush Stderr)
 3516     # assert s != ""
 3517     (slice-equal? %ecx "")
 3518     3d/compare-eax-and 0/imm32/false
 3519     0f 85/jump-if-!= $parse-type:abort/disp32
 3520     # assert s != "{"
 3521     (slice-equal? %ecx "{")
 3522     3d/compare-eax-and 0/imm32/false
 3523     0f 85/jump-if-!= $parse-type:abort/disp32
 3524     # assert s != "}"
 3525     (slice-equal? %ecx "}")
 3526     3d/compare-eax-and 0/imm32/false
 3527     0f 85/jump-if-!= $parse-type:abort/disp32
 3528     # assert s != "->"
 3529     (slice-equal? %ecx "->")
 3530     3d/compare-eax-and 0/imm32/false
 3531     0f 85/jump-if-!= $parse-type:abort/disp32
 3532     # if (s == ")") return 0
 3533     (slice-equal? %ecx ")")
 3534     3d/compare-eax-and 0/imm32/false
 3535     b8/copy-to-eax 0/imm32
 3536     0f 85/jump-if-!= $parse-type:end/disp32
 3537     # var result/edx: (handle tree type-id)
 3538     (allocate *(ebp+8) *Tree-size)  # => eax
 3539     (zero-out %eax *Tree-size)
 3540     89/<- %edx 0/r32/eax
 3541     {
 3542       # if (s != "(") break
 3543       (slice-equal? %ecx "(")
 3544       3d/compare-eax-and 0/imm32/false
 3545       75/jump-if-!= break/disp8
 3546       # EGREGIOUS HACK for static array sizes: if s is a number, parse it
 3547       {
 3548         (is-hex-int? %ecx)  # => eax
 3549         3d/compare-eax-and 0/imm32/false
 3550         74/jump-if-= break/disp8
 3551         (parse-hex-int-from-slice %ecx)  # => eax
 3552         89/<- *edx 0/r32/eax  # Tree-left
 3553         e9/jump $parse-type:return-edx/disp32
 3554       }
 3555       # result->left = pos-or-insert-slice(Type-id, s)
 3556       (pos-or-insert-slice Type-id %ecx)  # => eax
 3557 #?       (write-buffered Stderr "=> {")
 3558 #?       (print-int32-buffered Stderr %eax)
 3559 #?       (write-buffered Stderr ", 0}\n")
 3560 #?       (flush Stderr)
 3561       89/<- *edx 0/r32/eax  # Tree-left
 3562       e9/jump $parse-type:return-edx/disp32
 3563     }
 3564     # otherwise s == "("
 3565     # result->left = parse-type(ad, in)
 3566     (parse-type *(ebp+8) *(ebp+0xc))
 3567 #?     (write-buffered Stderr "=> {")
 3568 #?     (print-int32-buffered Stderr %eax)
 3569     89/<- *edx 0/r32/eax  # Tree-left
 3570     # result->right = parse-type-tree(ad, in)
 3571     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3572 #?     (write-buffered Stderr Space)
 3573 #?     (print-int32-buffered Stderr %eax)
 3574 #?     (write-buffered Stderr "}\n")
 3575 #?     (flush Stderr)
 3576     89/<- *(edx+4) 0/r32/eax  # Tree-right
 3577 $parse-type:return-edx:
 3578     89/<- %eax 2/r32/edx
 3579 $parse-type:end:
 3580     # . reclaim locals
 3581     81 0/subop/add %esp 8/imm32
 3582     # . restore registers
 3583     5a/pop-to-edx
 3584     59/pop-to-ecx
 3585     # . epilogue
 3586     89/<- %esp 5/r32/ebp
 3587     5d/pop-to-ebp
 3588     c3/return
 3589 
 3590 $parse-type:abort:
 3591     # error("unexpected token when parsing type: '" s "'\n")
 3592     (write-buffered Stderr "unexpected token when parsing type: '")
 3593     (write-slice-buffered Stderr %ecx)
 3594     (write-buffered Stderr "'\n")
 3595     (flush Stderr)
 3596     # . syscall(exit, 1)
 3597     bb/copy-to-ebx  1/imm32
 3598     b8/copy-to-eax  1/imm32/exit
 3599     cd/syscall  0x80/imm8
 3600     # never gets here
 3601 
 3602 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3603     # pseudocode:
 3604     #   var tmp: (handle tree type-id) = parse-type(ad, in)
 3605     #   if tmp == 0
 3606     #     return 0
 3607     #   result = allocate(Tree)
 3608     #   zero-out(result, *Tree-size)
 3609     #   result->left = tmp
 3610     #   result->right = parse-type-tree(ad, in)
 3611     #
 3612     # . prologue
 3613     55/push-ebp
 3614     89/<- %ebp 4/r32/esp
 3615     # . save registers
 3616     51/push-ecx
 3617     52/push-edx
 3618     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
 3619     (parse-type *(ebp+8) *(ebp+0xc))
 3620     # if (tmp == 0) return tmp
 3621     3d/compare-eax-and 0/imm32
 3622     74/jump-if-= $parse-type-tree:end/disp8
 3623     # var tmp2/ecx = tmp
 3624     89/<- %ecx 0/r32/eax
 3625     # var result/edx: (handle tree type-id)
 3626     (allocate *(ebp+8) *Tree-size)  # => eax
 3627     (zero-out %eax *Tree-size)
 3628     89/<- %edx 0/r32/eax
 3629     # result->left = tmp2
 3630     89/<- *edx 1/r32/ecx  # Tree-left
 3631     # result->right = parse-type-tree(ad, in)
 3632     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3633     89/<- *(edx+4) 0/r32/eax  # Tree-right
 3634 $parse-type-tree:return-edx:
 3635     89/<- %eax 2/r32/edx
 3636 $parse-type-tree:end:
 3637     # . restore registers
 3638     5a/pop-to-edx
 3639     59/pop-to-ecx
 3640     # . epilogue
 3641     89/<- %esp 5/r32/ebp
 3642     5d/pop-to-ebp
 3643     c3/return
 3644 
 3645 next-mu-token:  # in: (addr stream byte), out: (addr slice)
 3646     # pseudocode:
 3647     # start:
 3648     #   skip-chars-matching-whitespace(in)
 3649     #   if in->read >= in->write              # end of in
 3650     #     out = {0, 0}
 3651     #     return
 3652     #   out->start = &in->data[in->read]
 3653     #   var curr-byte/eax: byte = in->data[in->read]
 3654     #   if curr->byte == ','                  # comment token
 3655     #     ++in->read
 3656     #     goto start
 3657     #   if curr-byte == '#'                   # comment
 3658     #     goto done                             # treat as eof
 3659     #   if curr-byte == '"'                   # string literal
 3660     #     skip-string(in)
 3661     #     goto done                           # no metadata
 3662     #   if curr-byte == '('
 3663     #     ++in->read
 3664     #     goto done
 3665     #   if curr-byte == ')'
 3666     #     ++in->read
 3667     #     goto done
 3668     #   # read a word
 3669     #   while true
 3670     #     if in->read >= in->write
 3671     #       break
 3672     #     curr-byte = in->data[in->read]
 3673     #     if curr-byte == ' '
 3674     #       break
 3675     #     if curr-byte == '\r'
 3676     #       break
 3677     #     if curr-byte == '\n'
 3678     #       break
 3679     #     if curr-byte == '('
 3680     #       break
 3681     #     if curr-byte == ')'
 3682     #       break
 3683     #     if curr-byte == ','
 3684     #       break
 3685     #     ++in->read
 3686     # done:
 3687     #   out->end = &in->data[in->read]
 3688     #
 3689     # . prologue
 3690     55/push-ebp
 3691     89/<- %ebp 4/r32/esp
 3692     # . save registers
 3693     50/push-eax
 3694     51/push-ecx
 3695     56/push-esi
 3696     57/push-edi
 3697     # esi = in
 3698     8b/-> *(ebp+8) 6/r32/esi
 3699     # edi = out
 3700     8b/-> *(ebp+0xc) 7/r32/edi
 3701 $next-mu-token:start:
 3702     (skip-chars-matching-whitespace %esi)
 3703 $next-mu-token:check0:
 3704     # if (in->read >= in->write) return out = {0, 0}
 3705     # . ecx = in->read
 3706     8b/-> *(esi+4) 1/r32/ecx
 3707     # . if (ecx >= in->write) return out = {0, 0}
 3708     3b/compare 1/r32/ecx *esi
 3709     c7 0/subop/copy *edi 0/imm32
 3710     c7 0/subop/copy *(edi+4) 0/imm32
 3711     0f 8d/jump-if->= $next-mu-token:end/disp32
 3712     # out->start = &in->data[in->read]
 3713     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3714     89/<- *edi 0/r32/eax
 3715     # var curr-byte/eax: byte = in->data[in->read]
 3716     31/xor %eax 0/r32/eax
 3717     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3718     {
 3719 $next-mu-token:check-for-comma:
 3720       # if (curr-byte != ',') break
 3721       3d/compare-eax-and 0x2c/imm32/comma
 3722       75/jump-if-!= break/disp8
 3723       # ++in->read
 3724       ff 0/subop/increment *(esi+4)
 3725       # restart
 3726       e9/jump $next-mu-token:start/disp32
 3727     }
 3728     {
 3729 $next-mu-token:check-for-comment:
 3730       # if (curr-byte != '#') break
 3731       3d/compare-eax-and 0x23/imm32/pound
 3732       75/jump-if-!= break/disp8
 3733       # return eof
 3734       e9/jump $next-mu-token:done/disp32
 3735     }
 3736     {
 3737 $next-mu-token:check-for-string-literal:
 3738       # if (curr-byte != '"') break
 3739       3d/compare-eax-and 0x22/imm32/dquote
 3740       75/jump-if-!= break/disp8
 3741       (skip-string %esi)
 3742       # return
 3743       e9/jump $next-mu-token:done/disp32
 3744     }
 3745     {
 3746 $next-mu-token:check-for-open-paren:
 3747       # if (curr-byte != '(') break
 3748       3d/compare-eax-and 0x28/imm32/open-paren
 3749       75/jump-if-!= break/disp8
 3750       # ++in->read
 3751       ff 0/subop/increment *(esi+4)
 3752       # return
 3753       e9/jump $next-mu-token:done/disp32
 3754     }
 3755     {
 3756 $next-mu-token:check-for-close-paren:
 3757       # if (curr-byte != ')') break
 3758       3d/compare-eax-and 0x29/imm32/close-paren
 3759       75/jump-if-!= break/disp8
 3760       # ++in->read
 3761       ff 0/subop/increment *(esi+4)
 3762       # return
 3763       e9/jump $next-mu-token:done/disp32
 3764     }
 3765     {
 3766 $next-mu-token:regular-word-without-metadata:
 3767       # if (in->read >= in->write) break
 3768       # . ecx = in->read
 3769       8b/-> *(esi+4) 1/r32/ecx
 3770       # . if (ecx >= in->write) break
 3771       3b/compare *esi 1/r32/ecx
 3772       7d/jump-if->= break/disp8
 3773       # var c/eax: byte = in->data[in->read]
 3774       31/xor %eax 0/r32/eax
 3775       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3776       # if (c == ' ') break
 3777       3d/compare-eax-and 0x20/imm32/space
 3778       74/jump-if-= break/disp8
 3779       # if (c == '\r') break
 3780       3d/compare-eax-and 0xd/imm32/carriage-return
 3781       74/jump-if-= break/disp8
 3782       # if (c == '\n') break
 3783       3d/compare-eax-and 0xa/imm32/newline
 3784       74/jump-if-= break/disp8
 3785       # if (c == '(') break
 3786       3d/compare-eax-and 0x28/imm32/open-paren
 3787       0f 84/jump-if-= break/disp32
 3788       # if (c == ')') break
 3789       3d/compare-eax-and 0x29/imm32/close-paren
 3790       0f 84/jump-if-= break/disp32
 3791       # if (c == ',') break
 3792       3d/compare-eax-and 0x2c/imm32/comma
 3793       0f 84/jump-if-= break/disp32
 3794       # ++in->read
 3795       ff 0/subop/increment *(esi+4)
 3796       #
 3797       e9/jump loop/disp32
 3798     }
 3799 $next-mu-token:done:
 3800     # out->end = &in->data[in->read]
 3801     8b/-> *(esi+4) 1/r32/ecx
 3802     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3803     89/<- *(edi+4) 0/r32/eax
 3804 $next-mu-token:end:
 3805     # . restore registers
 3806     5f/pop-to-edi
 3807     5e/pop-to-esi
 3808     59/pop-to-ecx
 3809     58/pop-to-eax
 3810     # . epilogue
 3811     89/<- %esp 5/r32/ebp
 3812     5d/pop-to-ebp
 3813     c3/return
 3814 
 3815 pos-or-insert-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 3816     # . prologue
 3817     55/push-ebp
 3818     89/<- %ebp 4/r32/esp
 3819     # if (pos-slice(arr, s) != -1) return it
 3820     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 3821     3d/compare-eax-and -1/imm32
 3822     75/jump-if-!= $pos-or-insert-slice:end/disp8
 3823 $pos-or-insert-slice:insert:
 3824     (slice-to-string Heap *(ebp+0xc))  # => eax
 3825     (write-int *(ebp+8) %eax)
 3826     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 3827 $pos-or-insert-slice:end:
 3828     # . epilogue
 3829     89/<- %esp 5/r32/ebp
 3830     5d/pop-to-ebp
 3831     c3/return
 3832 
 3833 # return the index in an array of strings matching 's', -1 if not found
 3834 # index is denominated in elements, not bytes
 3835 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 3836     # . prologue
 3837     55/push-ebp
 3838     89/<- %ebp 4/r32/esp
 3839     # . save registers
 3840     51/push-ecx
 3841     52/push-edx
 3842     53/push-ebx
 3843     56/push-esi
 3844 #?     (write-buffered Stderr "pos-slice: ")
 3845 #?     (write-slice-buffered Stderr *(ebp+0xc))
 3846 #?     (write-buffered Stderr "\n")
 3847 #?     (flush Stderr)
 3848     # esi = arr
 3849     8b/-> *(ebp+8) 6/r32/esi
 3850     # var index/ecx: int = 0
 3851     b9/copy-to-ecx 0/imm32
 3852     # var curr/edx: (addr (addr array byte)) = arr->data
 3853     8d/copy-address *(esi+0xc) 2/r32/edx
 3854     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
 3855     8b/-> *esi 3/r32/ebx
 3856     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
 3857     {
 3858 #?       (write-buffered Stderr "  ")
 3859 #?       (print-int32-buffered Stderr %ecx)
 3860 #?       (write-buffered Stderr "\n")
 3861 #?       (flush Stderr)
 3862       # if (curr >= max) return -1
 3863       39/compare %edx 3/r32/ebx
 3864       b8/copy-to-eax -1/imm32
 3865       73/jump-if-addr>= $pos-slice:end/disp8
 3866       # if (slice-equal?(s, *curr)) break
 3867       (slice-equal? *(ebp+0xc) *edx)  # => eax
 3868       3d/compare-eax-and 0/imm32/false
 3869       75/jump-if-!= break/disp8
 3870       # ++index
 3871       41/increment-ecx
 3872       # curr += 4
 3873       81 0/subop/add %edx 4/imm32
 3874       #
 3875       eb/jump loop/disp8
 3876     }
 3877     # return index
 3878     89/<- %eax 1/r32/ecx
 3879 $pos-slice:end:
 3880 #?     (write-buffered Stderr "=> ")
 3881 #?     (print-int32-buffered Stderr %eax)
 3882 #?     (write-buffered Stderr "\n")
 3883     # . restore registers
 3884     5e/pop-to-esi
 3885     5b/pop-to-ebx
 3886     5a/pop-to-edx
 3887     59/pop-to-ecx
 3888     # . epilogue
 3889     89/<- %esp 5/r32/ebp
 3890     5d/pop-to-ebp
 3891     c3/return
 3892 
 3893 test-parse-var-with-type:
 3894     # . prologue
 3895     55/push-ebp
 3896     89/<- %ebp 4/r32/esp
 3897     # (eax..ecx) = "x:"
 3898     b8/copy-to-eax "x:"/imm32
 3899     8b/-> *eax 1/r32/ecx
 3900     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3901     05/add-to-eax 4/imm32
 3902     # var slice/ecx: slice = {eax, ecx}
 3903     51/push-ecx
 3904     50/push-eax
 3905     89/<- %ecx 4/r32/esp
 3906     # _test-input-stream contains "int"
 3907     (clear-stream _test-input-stream)
 3908     (write _test-input-stream "int")
 3909     #
 3910     (parse-var-with-type %ecx _test-input-stream)
 3911     8b/-> *eax 2/r32/edx  # Var-name
 3912     (check-strings-equal %edx "x" "F - test-var-with-type/name")
 3913     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3914     (check-ints-equal *edx 1 "F - test-var-with-type/type")
 3915     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
 3916     # . epilogue
 3917     89/<- %esp 5/r32/ebp
 3918     5d/pop-to-ebp
 3919     c3/return
 3920 
 3921 test-parse-var-with-type-and-register:
 3922     # . prologue
 3923     55/push-ebp
 3924     89/<- %ebp 4/r32/esp
 3925     # (eax..ecx) = "x/eax:"
 3926     b8/copy-to-eax "x/eax:"/imm32
 3927     8b/-> *eax 1/r32/ecx
 3928     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3929     05/add-to-eax 4/imm32
 3930     # var slice/ecx: slice = {eax, ecx}
 3931     51/push-ecx
 3932     50/push-eax
 3933     89/<- %ecx 4/r32/esp
 3934     # _test-input-stream contains "int"
 3935     (clear-stream _test-input-stream)
 3936     (write _test-input-stream "int")
 3937     #
 3938     (parse-var-with-type %ecx _test-input-stream)
 3939     8b/-> *eax 2/r32/edx  # Var-name
 3940     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
 3941     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3942     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
 3943     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3944     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
 3945     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
 3946     # . epilogue
 3947     89/<- %esp 5/r32/ebp
 3948     5d/pop-to-ebp
 3949     c3/return
 3950 
 3951 test-parse-var-with-trailing-characters:
 3952     # . prologue
 3953     55/push-ebp
 3954     89/<- %ebp 4/r32/esp
 3955     # (eax..ecx) = "x:"
 3956     b8/copy-to-eax "x:"/imm32
 3957     8b/-> *eax 1/r32/ecx
 3958     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3959     05/add-to-eax 4/imm32
 3960     # var slice/ecx: slice = {eax, ecx}
 3961     51/push-ecx
 3962     50/push-eax
 3963     89/<- %ecx 4/r32/esp
 3964     # _test-input-stream contains "int,"
 3965     (clear-stream _test-input-stream)
 3966     (write _test-input-stream "int,")
 3967     #
 3968     (parse-var-with-type %ecx _test-input-stream)
 3969     8b/-> *eax 2/r32/edx  # Var-name
 3970     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
 3971     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3972     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
 3973     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3974     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
 3975     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
 3976     # . epilogue
 3977     89/<- %esp 5/r32/ebp
 3978     5d/pop-to-ebp
 3979     c3/return
 3980 
 3981 test-parse-var-with-register-and-trailing-characters:
 3982     # . prologue
 3983     55/push-ebp
 3984     89/<- %ebp 4/r32/esp
 3985     # (eax..ecx) = "x/eax:"
 3986     b8/copy-to-eax "x/eax:"/imm32
 3987     8b/-> *eax 1/r32/ecx
 3988     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3989     05/add-to-eax 4/imm32
 3990     # var slice/ecx: slice = {eax, ecx}
 3991     51/push-ecx
 3992     50/push-eax
 3993     89/<- %ecx 4/r32/esp
 3994     # _test-input-stream contains "int,"
 3995     (clear-stream _test-input-stream)
 3996     (write _test-input-stream "int,")
 3997     #
 3998     (parse-var-with-type %ecx _test-input-stream)
 3999     8b/-> *eax 2/r32/edx  # Var-name
 4000     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
 4001     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4002     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
 4003     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4004     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
 4005     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
 4006     # . epilogue
 4007     89/<- %esp 5/r32/ebp
 4008     5d/pop-to-ebp
 4009     c3/return
 4010 
 4011 test-parse-var-with-compound-type:
 4012     # . prologue
 4013     55/push-ebp
 4014     89/<- %ebp 4/r32/esp
 4015     # (eax..ecx) = "x:"
 4016     b8/copy-to-eax "x:"/imm32
 4017     8b/-> *eax 1/r32/ecx
 4018     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4019     05/add-to-eax 4/imm32
 4020     # var slice/ecx: slice = {eax, ecx}
 4021     51/push-ecx
 4022     50/push-eax
 4023     89/<- %ecx 4/r32/esp
 4024     # _test-input-stream contains "(addr int)"
 4025     (clear-stream _test-input-stream)
 4026     (write _test-input-stream "(addr int)")
 4027     #
 4028     (parse-var-with-type %ecx _test-input-stream)
 4029     8b/-> *eax 2/r32/edx  # Var-name
 4030     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
 4031     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4032     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
 4033     # var type/edx: (handle tree type-id) = var->type
 4034     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4035     # type->left == atom(addr)
 4036     8b/-> *edx 0/r32/eax  # Atom-value
 4037     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
 4038     # type->right->left == atom(int)
 4039     8b/-> *(edx+4) 2/r32/edx  # Tree-right
 4040     8b/-> *edx 0/r32/eax  # Tree-left
 4041     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
 4042     # type->right->right == null
 4043     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
 4044     # . epilogue
 4045     89/<- %esp 5/r32/ebp
 4046     5d/pop-to-ebp
 4047     c3/return
 4048 
 4049 # identifier starts with a letter or '$' or '_'
 4050 # no constraints at the moment on later letters
 4051 # all we really want to do so far is exclude '{', '}' and '->'
 4052 is-identifier?:  # in: (addr slice) -> result/eax: boolean
 4053     # . prologue
 4054     55/push-ebp
 4055     89/<- %ebp 4/r32/esp
 4056     # if (slice-empty?(in)) return false
 4057     (slice-empty? *(ebp+8))  # => eax
 4058     3d/compare-eax-and 0/imm32/false
 4059     75/jump-if-!= $is-identifier?:false/disp8
 4060     # var c/eax: byte = *in->start
 4061     8b/-> *(ebp+8) 0/r32/eax
 4062     8b/-> *eax 0/r32/eax
 4063     8a/copy-byte *eax 0/r32/AL
 4064     81 4/subop/and %eax 0xff/imm32
 4065     # if (c == '$') return true
 4066     3d/compare-eax-and 0x24/imm32/$
 4067     74/jump-if-= $is-identifier?:true/disp8
 4068     # if (c == '_') return true
 4069     3d/compare-eax-and 0x5f/imm32/_
 4070     74/jump-if-= $is-identifier?:true/disp8
 4071     # drop case
 4072     25/and-eax-with 0x5f/imm32
 4073     # if (c < 'A') return false
 4074     3d/compare-eax-and 0x41/imm32/A
 4075     7c/jump-if-< $is-identifier?:false/disp8
 4076     # if (c > 'Z') return false
 4077     3d/compare-eax-and 0x5a/imm32/Z
 4078     7f/jump-if-> $is-identifier?:false/disp8
 4079     # otherwise return true
 4080 $is-identifier?:true:
 4081     b8/copy-to-eax 1/imm32/true
 4082     eb/jump $is-identifier?:end/disp8
 4083 $is-identifier?:false:
 4084     b8/copy-to-eax 0/imm32/false
 4085 $is-identifier?:end:
 4086     # . epilogue
 4087     89/<- %esp 5/r32/ebp
 4088     5d/pop-to-ebp
 4089     c3/return
 4090 
 4091 test-is-identifier-dollar:
 4092     # . prologue
 4093     55/push-ebp
 4094     89/<- %ebp 4/r32/esp
 4095     # (eax..ecx) = "$a"
 4096     b8/copy-to-eax "$a"/imm32
 4097     8b/-> *eax 1/r32/ecx
 4098     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4099     05/add-to-eax 4/imm32
 4100     # var slice/ecx: slice = {eax, ecx}
 4101     51/push-ecx
 4102     50/push-eax
 4103     89/<- %ecx 4/r32/esp
 4104     #
 4105     (is-identifier? %ecx)
 4106     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
 4107     # . epilogue
 4108     89/<- %esp 5/r32/ebp
 4109     5d/pop-to-ebp
 4110     c3/return
 4111 
 4112 test-is-identifier-underscore:
 4113     # . prologue
 4114     55/push-ebp
 4115     89/<- %ebp 4/r32/esp
 4116     # (eax..ecx) = "_a"
 4117     b8/copy-to-eax "_a"/imm32
 4118     8b/-> *eax 1/r32/ecx
 4119     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4120     05/add-to-eax 4/imm32
 4121     # var slice/ecx: slice = {eax, ecx}
 4122     51/push-ecx
 4123     50/push-eax
 4124     89/<- %ecx 4/r32/esp
 4125     #
 4126     (is-identifier? %ecx)
 4127     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
 4128     # . epilogue
 4129     89/<- %esp 5/r32/ebp
 4130     5d/pop-to-ebp
 4131     c3/return
 4132 
 4133 test-is-identifier-a:
 4134     # . prologue
 4135     55/push-ebp
 4136     89/<- %ebp 4/r32/esp
 4137     # (eax..ecx) = "a$"
 4138     b8/copy-to-eax "a$"/imm32
 4139     8b/-> *eax 1/r32/ecx
 4140     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4141     05/add-to-eax 4/imm32
 4142     # var slice/ecx: slice = {eax, ecx}
 4143     51/push-ecx
 4144     50/push-eax
 4145     89/<- %ecx 4/r32/esp
 4146     #
 4147     (is-identifier? %ecx)
 4148     (check-ints-equal %eax 1 "F - test-is-identifier-a")
 4149     # . epilogue
 4150     89/<- %esp 5/r32/ebp
 4151     5d/pop-to-ebp
 4152     c3/return
 4153 
 4154 test-is-identifier-z:
 4155     # . prologue
 4156     55/push-ebp
 4157     89/<- %ebp 4/r32/esp
 4158     # (eax..ecx) = "z$"
 4159     b8/copy-to-eax "z$"/imm32
 4160     8b/-> *eax 1/r32/ecx
 4161     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4162     05/add-to-eax 4/imm32
 4163     # var slice/ecx: slice = {eax, ecx}
 4164     51/push-ecx
 4165     50/push-eax
 4166     89/<- %ecx 4/r32/esp
 4167     #
 4168     (is-identifier? %ecx)
 4169     (check-ints-equal %eax 1 "F - test-is-identifier-z")
 4170     # . epilogue
 4171     89/<- %esp 5/r32/ebp
 4172     5d/pop-to-ebp
 4173     c3/return
 4174 
 4175 test-is-identifier-A:
 4176     # . prologue
 4177     55/push-ebp
 4178     89/<- %ebp 4/r32/esp
 4179     # (eax..ecx) = "A$"
 4180     b8/copy-to-eax "A$"/imm32
 4181     8b/-> *eax 1/r32/ecx
 4182     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4183     05/add-to-eax 4/imm32
 4184     # var slice/ecx: slice = {eax, ecx}
 4185     51/push-ecx
 4186     50/push-eax
 4187     89/<- %ecx 4/r32/esp
 4188     #
 4189     (is-identifier? %ecx)
 4190     (check-ints-equal %eax 1 "F - test-is-identifier-A")
 4191     # . epilogue
 4192     89/<- %esp 5/r32/ebp
 4193     5d/pop-to-ebp
 4194     c3/return
 4195 
 4196 test-is-identifier-Z:
 4197     # . prologue
 4198     55/push-ebp
 4199     89/<- %ebp 4/r32/esp
 4200     # (eax..ecx) = "Z$"
 4201     b8/copy-to-eax "Z$"/imm32
 4202     8b/-> *eax 1/r32/ecx
 4203     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4204     05/add-to-eax 4/imm32
 4205     # var slice/ecx: slice = {eax, ecx}
 4206     51/push-ecx
 4207     50/push-eax
 4208     89/<- %ecx 4/r32/esp
 4209     #
 4210     (is-identifier? %ecx)
 4211     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
 4212     # . epilogue
 4213     89/<- %esp 5/r32/ebp
 4214     5d/pop-to-ebp
 4215     c3/return
 4216 
 4217 test-is-identifier-@:
 4218     # character before 'A' is invalid
 4219     # . prologue
 4220     55/push-ebp
 4221     89/<- %ebp 4/r32/esp
 4222     # (eax..ecx) = "@a"
 4223     b8/copy-to-eax "@a"/imm32
 4224     8b/-> *eax 1/r32/ecx
 4225     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4226     05/add-to-eax 4/imm32
 4227     # var slice/ecx: slice = {eax, ecx}
 4228     51/push-ecx
 4229     50/push-eax
 4230     89/<- %ecx 4/r32/esp
 4231     #
 4232     (is-identifier? %ecx)
 4233     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4234     # . epilogue
 4235     89/<- %esp 5/r32/ebp
 4236     5d/pop-to-ebp
 4237     c3/return
 4238 
 4239 test-is-identifier-square-bracket:
 4240     # character after 'Z' is invalid
 4241     # . prologue
 4242     55/push-ebp
 4243     89/<- %ebp 4/r32/esp
 4244     # (eax..ecx) = "[a"
 4245     b8/copy-to-eax "[a"/imm32
 4246     8b/-> *eax 1/r32/ecx
 4247     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4248     05/add-to-eax 4/imm32
 4249     # var slice/ecx: slice = {eax, ecx}
 4250     51/push-ecx
 4251     50/push-eax
 4252     89/<- %ecx 4/r32/esp
 4253     #
 4254     (is-identifier? %ecx)
 4255     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4256     # . epilogue
 4257     89/<- %esp 5/r32/ebp
 4258     5d/pop-to-ebp
 4259     c3/return
 4260 
 4261 test-is-identifier-backtick:
 4262     # character before 'a' is invalid
 4263     # . prologue
 4264     55/push-ebp
 4265     89/<- %ebp 4/r32/esp
 4266     # (eax..ecx) = "`a"
 4267     b8/copy-to-eax "`a"/imm32
 4268     8b/-> *eax 1/r32/ecx
 4269     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4270     05/add-to-eax 4/imm32
 4271     # var slice/ecx: slice = {eax, ecx}
 4272     51/push-ecx
 4273     50/push-eax
 4274     89/<- %ecx 4/r32/esp
 4275     #
 4276     (is-identifier? %ecx)
 4277     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
 4278     # . epilogue
 4279     89/<- %esp 5/r32/ebp
 4280     5d/pop-to-ebp
 4281     c3/return
 4282 
 4283 test-is-identifier-curly-brace-open:
 4284     # character after 'z' is invalid; also used for blocks
 4285     # . prologue
 4286     55/push-ebp
 4287     89/<- %ebp 4/r32/esp
 4288     # (eax..ecx) = "{a"
 4289     b8/copy-to-eax "{a"/imm32
 4290     8b/-> *eax 1/r32/ecx
 4291     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4292     05/add-to-eax 4/imm32
 4293     # var slice/ecx: slice = {eax, ecx}
 4294     51/push-ecx
 4295     50/push-eax
 4296     89/<- %ecx 4/r32/esp
 4297     #
 4298     (is-identifier? %ecx)
 4299     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
 4300     # . epilogue
 4301     89/<- %esp 5/r32/ebp
 4302     5d/pop-to-ebp
 4303     c3/return
 4304 
 4305 test-is-identifier-curly-brace-close:
 4306     # . prologue
 4307     55/push-ebp
 4308     89/<- %ebp 4/r32/esp
 4309     # (eax..ecx) = "}a"
 4310     b8/copy-to-eax "}a"/imm32
 4311     8b/-> *eax 1/r32/ecx
 4312     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4313     05/add-to-eax 4/imm32
 4314     # var slice/ecx: slice = {eax, ecx}
 4315     51/push-ecx
 4316     50/push-eax
 4317     89/<- %ecx 4/r32/esp
 4318     #
 4319     (is-identifier? %ecx)
 4320     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
 4321     # . epilogue
 4322     89/<- %esp 5/r32/ebp
 4323     5d/pop-to-ebp
 4324     c3/return
 4325 
 4326 test-is-identifier-hyphen:
 4327     # disallow leading '-' since '->' has special meaning
 4328     # . prologue
 4329     55/push-ebp
 4330     89/<- %ebp 4/r32/esp
 4331     # (eax..ecx) = "-a"
 4332     b8/copy-to-eax "-a"/imm32
 4333     8b/-> *eax 1/r32/ecx
 4334     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4335     05/add-to-eax 4/imm32
 4336     # var slice/ecx: slice = {eax, ecx}
 4337     51/push-ecx
 4338     50/push-eax
 4339     89/<- %ecx 4/r32/esp
 4340     #
 4341     (is-identifier? %ecx)
 4342     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
 4343     # . epilogue
 4344     89/<- %esp 5/r32/ebp
 4345     5d/pop-to-ebp
 4346     c3/return
 4347 
 4348 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
 4349     # . prologue
 4350     55/push-ebp
 4351     89/<- %ebp 4/r32/esp
 4352     # . save registers
 4353     50/push-eax
 4354     56/push-esi
 4355     57/push-edi
 4356     # esi = in
 4357     8b/-> *(ebp+8) 6/r32/esi
 4358     # edi = out
 4359     8b/-> *(ebp+0xc) 7/r32/edi
 4360     # var eax: (handle block) = parse-mu-block(in, vars, fn)
 4361     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
 4362     # out->body = eax
 4363     89/<- *(edi+0x10) 0/r32/eax  # Function-body
 4364 $populate-mu-function-body:end:
 4365     # . restore registers
 4366     5f/pop-to-edi
 4367     5e/pop-to-esi
 4368     58/pop-to-eax
 4369     # . epilogue
 4370     89/<- %esp 5/r32/ebp
 4371     5d/pop-to-ebp
 4372     c3/return
 4373 
 4374 # parses a block, assuming that the leading '{' has already been read by the caller
 4375 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
 4376     # pseudocode:
 4377     #   var line: (stream byte 512)
 4378     #   var word-slice: slice
 4379     #   result/eax = allocate(Heap, Stmt-size)
 4380     #   result->tag = 0/block
 4381     #   result->name = some unique name
 4382     #   while true                                  # line loop
 4383     #     clear-stream(line)
 4384     #     read-line-buffered(in, line)
 4385     #     if (line->write == 0) break               # end of file
 4386     #     word-slice = next-mu-token(line)
 4387     #     if slice-empty?(word-slice)               # end of line
 4388     #       continue
 4389     #     else if slice-starts-with?(word-slice, "#")
 4390     #       continue
 4391     #     else if slice-equal?(word-slice, "{")
 4392     #       assert(no-tokens-in(line))
 4393     #       block = parse-mu-block(in, vars, fn)
 4394     #       append-to-block(result, block)
 4395     #     else if slice-equal?(word-slice, "}")
 4396     #       break
 4397     #     else if slice-ends-with?(word-slice, ":")
 4398     #       # TODO: error-check the rest of 'line'
 4399     #       --word-slice->end to skip ':'
 4400     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
 4401     #       append-to-block(result, named-block)
 4402     #     else if slice-equal?(word-slice, "var")
 4403     #       var-def = parse-mu-var-def(line, vars)
 4404     #       append-to-block(result, var-def)
 4405     #     else
 4406     #       stmt = parse-mu-stmt(line, vars, fn)
 4407     #       append-to-block(result, stmt)
 4408     #   return result
 4409     #
 4410     # . prologue
 4411     55/push-ebp
 4412     89/<- %ebp 4/r32/esp
 4413     # . save registers
 4414     51/push-ecx
 4415     52/push-edx
 4416     53/push-ebx
 4417     57/push-edi
 4418     # var line/ecx: (stream byte 512)
 4419     81 5/subop/subtract %esp 0x200/imm32
 4420     68/push 0x200/imm32/length
 4421     68/push 0/imm32/read
 4422     68/push 0/imm32/write
 4423     89/<- %ecx 4/r32/esp
 4424     # var word-slice/edx: slice
 4425     68/push 0/imm32/end
 4426     68/push 0/imm32/start
 4427     89/<- %edx 4/r32/esp
 4428     # edi = result
 4429     (allocate Heap *Stmt-size)  # => eax
 4430     (zero-out %eax *Stmt-size)
 4431     89/<- %edi 0/r32/eax
 4432     # set result->tag
 4433     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
 4434     # set result->var
 4435     (new-block-name *(ebp+0x10))  # => eax
 4436     89/<- *(edi+8) 0/r32/eax  # Block-var
 4437     # push result->var to vars
 4438     (push *(ebp+0xc) %eax)
 4439     {
 4440 $parse-mu-block:line-loop:
 4441       # line = read-line-buffered(in)
 4442       (clear-stream %ecx)
 4443       (read-line-buffered *(ebp+8) %ecx)
 4444 #?       (write-buffered Stderr "line: ")
 4445 #?       (write-stream-data Stderr %ecx)
 4446 #?       (write-buffered Stderr Newline)
 4447 #?       (flush Stderr)
 4448       # if (line->write == 0) break
 4449       81 7/subop/compare *ecx 0/imm32
 4450       0f 84/jump-if-= break/disp32
 4451       # word-slice = next-mu-token(line)
 4452       (next-mu-token %ecx %edx)
 4453 #?       (write-buffered Stderr "word: ")
 4454 #?       (write-slice-buffered Stderr %edx)
 4455 #?       (write-buffered Stderr Newline)
 4456 #?       (flush Stderr)
 4457       # if slice-empty?(word-slice) continue
 4458       (slice-empty? %edx)
 4459       3d/compare-eax-and 0/imm32/false
 4460       0f 85/jump-if-!= loop/disp32
 4461       # if (slice-starts-with?(word-slice, '#') continue
 4462       # . eax = *word-slice->start
 4463       8b/-> *edx 0/r32/eax
 4464       8a/copy-byte *eax 0/r32/AL
 4465       81 4/subop/and %eax 0xff/imm32
 4466       # . if (eax == '#') continue
 4467       3d/compare-eax-and 0x23/imm32/hash
 4468       0f 84/jump-if-= loop/disp32
 4469       # if slice-equal?(word-slice, "{")
 4470       {
 4471 $parse-mu-block:check-for-block:
 4472         (slice-equal? %edx "{")
 4473         3d/compare-eax-and 0/imm32/false
 4474         74/jump-if-= break/disp8
 4475         (check-no-tokens-left %ecx)
 4476         # parse new block and append
 4477         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4478         (append-to-block Heap %edi %eax)
 4479         e9/jump $parse-mu-block:line-loop/disp32
 4480       }
 4481       # if slice-equal?(word-slice, "}") break
 4482 $parse-mu-block:check-for-end:
 4483       (slice-equal? %edx "}")
 4484       3d/compare-eax-and 0/imm32/false
 4485       0f 85/jump-if-!= break/disp32
 4486       # if slice-ends-with?(word-slice, ":") parse named block and append
 4487       {
 4488 $parse-mu-block:check-for-named-block:
 4489         # . eax = *(word-slice->end-1)
 4490         8b/-> *(edx+4) 0/r32/eax
 4491         48/decrement-eax
 4492         8a/copy-byte *eax 0/r32/AL
 4493         81 4/subop/and %eax 0xff/imm32
 4494         # . if (eax != ':') break
 4495         3d/compare-eax-and 0x3a/imm32/colon
 4496         0f 85/jump-if-!= break/disp32
 4497         # TODO: error-check the rest of 'line'
 4498         #
 4499         # skip ':'
 4500         ff 1/subop/decrement *(edx+4)  # Slice-end
 4501         #
 4502         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4503         (append-to-block Heap %edi %eax)
 4504         e9/jump $parse-mu-block:line-loop/disp32
 4505       }
 4506       # if slice-equal?(word-slice, "var")
 4507       {
 4508 $parse-mu-block:check-for-var:
 4509         (slice-equal? %edx "var")
 4510         3d/compare-eax-and 0/imm32/false
 4511         74/jump-if-= break/disp8
 4512         #
 4513         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
 4514         (append-to-block Heap %edi %eax)
 4515         e9/jump $parse-mu-block:line-loop/disp32
 4516       }
 4517 $parse-mu-block:regular-stmt:
 4518       # otherwise
 4519       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4520       (append-to-block Heap %edi %eax)
 4521       e9/jump loop/disp32
 4522     } # end line loop
 4523     #
 4524     (pop *(ebp+0xc))  # => eax
 4525     # return result
 4526     89/<- %eax 7/r32/edi
 4527 $parse-mu-block:end:
 4528     # . reclaim locals
 4529     81 0/subop/add %esp 0x214/imm32
 4530     # . restore registers
 4531     5f/pop-to-edi
 4532     5b/pop-to-ebx
 4533     5a/pop-to-edx
 4534     59/pop-to-ecx
 4535     # . epilogue
 4536     89/<- %esp 5/r32/ebp
 4537     5d/pop-to-ebp
 4538     c3/return
 4539 
 4540 $parse-mu-block:abort:
 4541     # error("'{' or '}' should be on its own line, but got '")
 4542     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4543     (rewind-stream %ecx)
 4544     (write-stream 2 %ecx)
 4545     (write-buffered Stderr "'\n")
 4546     (flush Stderr)
 4547     # . syscall(exit, 1)
 4548     bb/copy-to-ebx  1/imm32
 4549     b8/copy-to-eax  1/imm32/exit
 4550     cd/syscall  0x80/imm8
 4551     # never gets here
 4552 
 4553 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
 4554     # . prologue
 4555     55/push-ebp
 4556     89/<- %ebp 4/r32/esp
 4557     # . save registers
 4558     51/push-ecx
 4559     52/push-edx
 4560     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
 4561     8b/-> *(ebp+8) 0/r32/eax
 4562     8b/-> *eax 0/r32/eax  # Function-name
 4563     8b/-> *eax 0/r32/eax  # String-length
 4564     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
 4565     89/<- %ecx 0/r32/eax
 4566     # var name/edx: (stream byte n)
 4567     29/subtract %esp 1/r32/ecx
 4568     ff 6/subop/push %ecx
 4569     68/push 0/imm32/read
 4570     68/push 0/imm32/write
 4571     89/<- %edx 4/r32/esp
 4572     (clear-stream %edx)
 4573     # eax = fn->name
 4574     8b/-> *(ebp+8) 0/r32/eax
 4575     8b/-> *eax 0/r32/eax  # Function-name
 4576     # construct result using Next-block-index (and increment it)
 4577     (write %edx "$")
 4578     (write %edx %eax)
 4579     (write %edx ":")
 4580     (print-int32 %edx *Next-block-index)
 4581     ff 0/subop/increment *Next-block-index
 4582     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
 4583     # . eax = name->write
 4584     8b/-> *edx 0/r32/eax
 4585     # . edx = name->data
 4586     8d/copy-address *(edx+0xc) 2/r32/edx
 4587     # . eax = name->write + name->data
 4588     01/add %eax 2/r32/edx
 4589     # . push {edx, eax}
 4590     ff 6/subop/push %eax
 4591     ff 6/subop/push %edx
 4592     89/<- %eax 4/r32/esp
 4593     # result->var = new literal(s)
 4594     (new-literal Heap %eax)  # => eax
 4595 $new-block-name:end:
 4596     # . reclaim locals
 4597     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
 4598     81 0/subop/add %ecx 8/imm32  # slice
 4599     01/add %esp 1/r32/ecx
 4600     # . restore registers
 4601     5a/pop-to-edx
 4602     59/pop-to-ecx
 4603     # . epilogue
 4604     89/<- %esp 5/r32/ebp
 4605     5d/pop-to-ebp
 4606     c3/return
 4607 
 4608 == data
 4609 
 4610 # Global state added to each var record when parsing a function
 4611 Next-block-index:  # (addr int)
 4612     1/imm32
 4613 
 4614 == code
 4615 
 4616 check-no-tokens-left:  # line: (addr stream byte)
 4617     # . prologue
 4618     55/push-ebp
 4619     89/<- %ebp 4/r32/esp
 4620     # . save registers
 4621     50/push-eax
 4622     51/push-ecx
 4623     # var s/ecx: slice
 4624     68/push 0/imm32/end
 4625     68/push 0/imm32/start
 4626     89/<- %ecx 4/r32/esp
 4627     #
 4628     (next-mu-token *(ebp+8) %ecx)
 4629     # if slice-empty?(s) return
 4630     (slice-empty? %ecx)
 4631     3d/compare-eax-and 0/imm32/false
 4632     75/jump-if-!= $check-no-tokens-left:end/disp8
 4633     # if (slice-starts-with?(s, '#') return
 4634     # . eax = *s->start
 4635     8b/-> *edx 0/r32/eax
 4636     8a/copy-byte *eax 0/r32/AL
 4637     81 4/subop/and %eax 0xff/imm32
 4638     # . if (eax == '#') continue
 4639     3d/compare-eax-and 0x23/imm32/hash
 4640     74/jump-if-= $check-no-tokens-left:end/disp8
 4641     # abort
 4642     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4643     (rewind-stream %ecx)
 4644     (write-stream 2 %ecx)
 4645     (write-buffered Stderr "'\n")
 4646     (flush Stderr)
 4647     # . syscall(exit, 1)
 4648     bb/copy-to-ebx  1/imm32
 4649     b8/copy-to-eax  1/imm32/exit
 4650     cd/syscall  0x80/imm8
 4651     # never gets here
 4652 $check-no-tokens-left:end:
 4653     # . reclaim locals
 4654     81 0/subop/add %esp 8/imm32
 4655     # . restore registers
 4656     59/pop-to-ecx
 4657     58/pop-to-eax
 4658     # . epilogue
 4659     89/<- %esp 5/r32/ebp
 4660     5d/pop-to-ebp
 4661     c3/return
 4662 
 4663 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4664     # pseudocode:
 4665     #   var v: (handle var) = new-literal(name)
 4666     #   push(vars, v)
 4667     #   result = parse-mu-block(in, vars, fn)
 4668     #   pop(vars)
 4669     #   result->name = s
 4670     #   return result
 4671     #
 4672     # . prologue
 4673     55/push-ebp
 4674     89/<- %ebp 4/r32/esp
 4675     # . save registers
 4676     51/push-ecx
 4677     # var v/ecx: (handle var)
 4678     (new-literal Heap *(ebp+8))  # => eax
 4679     89/<- %ecx 0/r32/eax
 4680     # push(vars, v)
 4681     (push *(ebp+0x10) %ecx)
 4682     # eax = result
 4683     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
 4684     # pop the var
 4685     50/push-eax
 4686     (pop *(ebp+0x10))  # => eax
 4687     58/pop-to-eax
 4688     # result->tag = named-block
 4689     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
 4690     # result->var = v
 4691     89/<- *(eax+8) 1/r32/ecx  # Block-var
 4692 $parse-mu-named-block:end:
 4693     # . restore registers
 4694     59/pop-to-ecx
 4695     # . epilogue
 4696     89/<- %esp 5/r32/ebp
 4697     5d/pop-to-ebp
 4698     c3/return
 4699 
 4700 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
 4701     # . prologue
 4702     55/push-ebp
 4703     89/<- %ebp 4/r32/esp
 4704     # . save registers
 4705     51/push-ecx
 4706     52/push-edx
 4707     # var word-slice/ecx: slice
 4708     68/push 0/imm32/end
 4709     68/push 0/imm32/start
 4710     89/<- %ecx 4/r32/esp
 4711     # var v/edx: (handle var) = parse-var-with-type(line)
 4712     (next-mu-token *(ebp+8) %ecx)
 4713     (parse-var-with-type %ecx *(ebp+8))  # => eax
 4714     89/<- %edx 0/r32/eax
 4715     #
 4716     (push *(ebp+0xc) %edx)
 4717     # either v has no register and there's no more to this line
 4718     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
 4719     3d/compare-eax-and 0/imm32
 4720     {
 4721       75/jump-if-!= break/disp8
 4722       # TODO: ensure that there's nothing else on this line
 4723       (new-var-def Heap %edx)  # => eax
 4724       eb/jump $parse-mu-var-def:end/disp8
 4725     }
 4726     # or v has a register and there's more to this line
 4727     {
 4728       74/jump-if-= break/disp8
 4729       # ensure that the next word is '<-'
 4730       (next-mu-token *(ebp+8) %ecx)
 4731       (slice-equal? %ecx "<-")  # => eax
 4732       3d/compare-eax-and 0/imm32/false
 4733       74/jump-if-= $parse-mu-var-def:abort/disp8
 4734       #
 4735       (new-reg-var-def Heap %edx)  # => eax
 4736       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
 4737     }
 4738 $parse-mu-var-def:end:
 4739     # . reclaim locals
 4740     81 0/subop/add %esp 8/imm32
 4741     # . restore registers
 4742     5a/pop-to-edx
 4743     59/pop-to-ecx
 4744     # . epilogue
 4745     89/<- %esp 5/r32/ebp
 4746     5d/pop-to-ebp
 4747     c3/return
 4748 
 4749 $parse-mu-var-def:abort:
 4750     (rewind-stream *(ebp+8))
 4751     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
 4752     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
 4753     (flush Stderr)
 4754     (write-stream 2 *(ebp+8))
 4755     (write-buffered Stderr "'\n")
 4756     (flush Stderr)
 4757     # . syscall(exit, 1)
 4758     bb/copy-to-ebx  1/imm32
 4759     b8/copy-to-eax  1/imm32/exit
 4760     cd/syscall  0x80/imm8
 4761     # never gets here
 4762 
 4763 test-parse-mu-var-def:
 4764     # 'var n: int'
 4765     # . prologue
 4766     55/push-ebp
 4767     89/<- %ebp 4/r32/esp
 4768     # setup
 4769     (clear-stream _test-input-stream)
 4770     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
 4771     # var vars/ecx: (stack (addr var) 4)
 4772     81 5/subop/subtract %esp 0x10/imm32
 4773     68/push 0x10/imm32/length
 4774     68/push 0/imm32/top
 4775     89/<- %ecx 4/r32/esp
 4776     (clear-stack %ecx)
 4777     # convert
 4778     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 4779     # check result
 4780     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
 4781     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
 4782     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
 4783     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
 4784     # ensure type is int
 4785     8b/-> *(eax+4) 0/r32/eax  # Var-type
 4786     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
 4787     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
 4788     # . epilogue
 4789     89/<- %esp 5/r32/ebp
 4790     5d/pop-to-ebp
 4791     c3/return
 4792 
 4793 test-parse-mu-reg-var-def:
 4794     # 'var n/eax: int <- copy 0'
 4795     # . prologue
 4796     55/push-ebp
 4797     89/<- %ebp 4/r32/esp
 4798     # setup
 4799     (clear-stream _test-input-stream)
 4800     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
 4801     # var vars/ecx: (stack (addr var) 4)
 4802     81 5/subop/subtract %esp 0x10/imm32
 4803     68/push 0x10/imm32/length
 4804     68/push 0/imm32/top
 4805     89/<- %ecx 4/r32/esp
 4806     (clear-stack %ecx)
 4807     # convert
 4808     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 4809     # check result
 4810     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
 4811     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
 4812     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
 4813     8b/-> *eax 0/r32/eax  # Stmt-var-value
 4814     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
 4815     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
 4816     # ensure type is int
 4817     8b/-> *(eax+4) 0/r32/eax  # Var-type
 4818     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
 4819     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
 4820     # . epilogue
 4821     89/<- %esp 5/r32/ebp
 4822     5d/pop-to-ebp
 4823     c3/return
 4824 
 4825 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4826     # pseudocode:
 4827     #   var name: slice
 4828     #   result = allocate(Heap, Stmt-size)
 4829     #   if stmt-has-outputs?(line)
 4830     #     while true
 4831     #       name = next-mu-token(line)
 4832     #       if (name == '<-') break
 4833     #       assert(is-identifier?(name))
 4834     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
 4835     #       result->outputs = append(result->outputs, v)
 4836     #   add-operation-and-inputs-to-stmt(result, line, vars)
 4837     #
 4838     # . prologue
 4839     55/push-ebp
 4840     89/<- %ebp 4/r32/esp
 4841     # . save registers
 4842     51/push-ecx
 4843     52/push-edx
 4844     57/push-edi
 4845     # var name/ecx: slice
 4846     68/push 0/imm32/end
 4847     68/push 0/imm32/start
 4848     89/<- %ecx 4/r32/esp
 4849     # var is-deref?/edx: boolean = false
 4850     ba/copy-to-edx 0/imm32/false
 4851     # result/edi: (handle stmt)
 4852     (allocate Heap *Stmt-size)  # => eax
 4853     (zero-out %eax *Stmt-size)
 4854     89/<- %edi 0/r32/eax
 4855     # result->tag = 1/stmt
 4856     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
 4857     {
 4858       (stmt-has-outputs? *(ebp+8))
 4859       3d/compare-eax-and 0/imm32/false
 4860       0f 84/jump-if-= break/disp32
 4861       {
 4862 $parse-mu-stmt:read-outputs:
 4863         # name = next-mu-token(line)
 4864         (next-mu-token *(ebp+8) %ecx)
 4865         # if slice-empty?(word-slice) break
 4866         (slice-empty? %ecx)  # => eax
 4867         3d/compare-eax-and 0/imm32/false
 4868         0f 85/jump-if-!= break/disp32
 4869         # if (name == "<-") break
 4870         (slice-equal? %ecx "<-")  # => eax
 4871         3d/compare-eax-and 0/imm32/false
 4872         0f 85/jump-if-!= break/disp32
 4873         # is-deref? = false
 4874         ba/copy-to-edx 0/imm32/false
 4875         # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 4876         8b/-> *ecx 0/r32/eax  # Slice-start
 4877         8a/copy-byte *eax 0/r32/AL
 4878         81 4/subop/and %eax 0xff/imm32
 4879         3d/compare-eax-and 0x2a/imm32/asterisk
 4880         {
 4881           75/jump-if-!= break/disp8
 4882           ff 0/subop/increment *ecx
 4883           ba/copy-to-edx 1/imm32/true
 4884         }
 4885         # assert(is-identifier?(name))
 4886         (is-identifier? %ecx)  # => eax
 4887         3d/compare-eax-and 0/imm32/false
 4888         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
 4889         # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
 4890         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4891         (append-stmt-var Heap %eax *(edi+0xc) %edx)  # Stmt1-outputs => eax
 4892         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
 4893         e9/jump loop/disp32
 4894       }
 4895     }
 4896     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
 4897 $parse-mu-stmt:end:
 4898     # return result
 4899     89/<- %eax 7/r32/edi
 4900     # . reclaim locals
 4901     81 0/subop/add %esp 8/imm32
 4902     # . restore registers
 4903     5f/pop-to-edi
 4904     5a/pop-to-edx
 4905     59/pop-to-ecx
 4906     # . epilogue
 4907     89/<- %esp 5/r32/ebp
 4908     5d/pop-to-ebp
 4909     c3/return
 4910 
 4911 $parse-mu-stmt:abort:
 4912     # error("invalid identifier '" name "'\n")
 4913     (write-buffered Stderr "invalid identifier '")
 4914     (write-slice-buffered Stderr %ecx)
 4915     (write-buffered Stderr "'\n")
 4916     (flush Stderr)
 4917     # . syscall(exit, 1)
 4918     bb/copy-to-ebx  1/imm32
 4919     b8/copy-to-eax  1/imm32/exit
 4920     cd/syscall  0x80/imm8
 4921     # never gets here
 4922 
 4923 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
 4924     # pseudocode:
 4925     #   stmt->name = slice-to-string(next-mu-token(line))
 4926     #   while true
 4927     #     name = next-mu-token(line)
 4928     #     v = lookup-var-or-literal(name)
 4929     #     stmt->inouts = append(stmt->inouts, v)
 4930     #
 4931     # . prologue
 4932     55/push-ebp
 4933     89/<- %ebp 4/r32/esp
 4934     # . save registers
 4935     50/push-eax
 4936     51/push-ecx
 4937     52/push-edx
 4938     53/push-ebx
 4939     57/push-edi
 4940     # edi = stmt
 4941     8b/-> *(ebp+8) 7/r32/edi
 4942     # var name/ecx: slice
 4943     68/push 0/imm32/end
 4944     68/push 0/imm32/start
 4945     89/<- %ecx 4/r32/esp
 4946     # var is-deref?/edx: boolean = false
 4947     ba/copy-to-edx 0/imm32/false
 4948 $add-operation-and-inputs-to-stmt:read-operation:
 4949     (next-mu-token *(ebp+0xc) %ecx)
 4950     (slice-to-string Heap %ecx)  # => eax
 4951     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
 4952     # var is-get?/ebx: boolean = (name == "get")
 4953     (slice-equal? %ecx "get")  # => eax
 4954     89/<- %ebx 0/r32/eax
 4955     {
 4956 $add-operation-and-inputs-to-stmt:read-inouts:
 4957       # name = next-mu-token(line)
 4958       (next-mu-token *(ebp+0xc) %ecx)
 4959       # if slice-empty?(word-slice) break
 4960       (slice-empty? %ecx)  # => eax
 4961       3d/compare-eax-and 0/imm32/false
 4962       0f 85/jump-if-!= break/disp32
 4963       # if (name == "<-") abort
 4964       (slice-equal? %ecx "<-")
 4965       3d/compare-eax-and 0/imm32/false
 4966       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
 4967       # if (is-get? && second operand) lookup or create offset
 4968       {
 4969         81 7/subop/compare %ebx 0/imm32/false
 4970         74/jump-if-= break/disp8
 4971         81 7/subop/compare *(edi+8) 0/imm32  # Stmt1-inouts or Regvardef-inouts
 4972         74/jump-if-= break/disp8
 4973         (lookup-or-create-constant *(edi+8) %ecx)  # Stmt1-inouts => eax
 4974 #?         (write-buffered Stderr "creating new output var ")
 4975 #?         (print-int32-buffered Stderr %eax)
 4976 #?         (write-buffered Stderr " for field called ")
 4977 #?         (write-slice-buffered Stderr %ecx)
 4978 #?         (write-buffered Stderr Newline)
 4979 #?         (flush Stderr)
 4980         e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
 4981       }
 4982       # is-deref? = false
 4983       ba/copy-to-edx 0/imm32/false
 4984       # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 4985       8b/-> *ecx 0/r32/eax  # Slice-start
 4986       8a/copy-byte *eax 0/r32/AL
 4987       81 4/subop/and %eax 0xff/imm32
 4988       3d/compare-eax-and 0x2a/imm32/asterisk
 4989       {
 4990         75/jump-if-!= break/disp8
 4991 $add-operation-and-inputs-to-stmt:inout-is-deref:
 4992         ff 0/subop/increment *ecx
 4993         ba/copy-to-edx 1/imm32/true
 4994       }
 4995       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
 4996 $add-operation-and-inputs-to-stmt:save-var:
 4997       (append-stmt-var Heap %eax *(edi+8) %edx)  # Stmt1-inouts or Regvardef-inouts => eax
 4998       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 4999       e9/jump loop/disp32
 5000     }
 5001 $add-operation-and-inputs-to-stmt:end:
 5002     # . reclaim locals
 5003     81 0/subop/add %esp 8/imm32
 5004     # . restore registers
 5005     5f/pop-to-edi
 5006     5b/pop-to-ebx
 5007     5a/pop-to-edx
 5008     59/pop-to-ecx
 5009     58/pop-to-eax
 5010     # . epilogue
 5011     89/<- %esp 5/r32/ebp
 5012     5d/pop-to-ebp
 5013     c3/return
 5014 
 5015 $add-operation-and-inputs-to-stmt:abort:
 5016     # error("invalid statement '" line "'\n")
 5017     (rewind-stream *(ebp+8))
 5018     (write-buffered Stderr "invalid identifier '")
 5019     (flush Stderr)
 5020     (write-stream 2 *(ebp+8))
 5021     (write-buffered Stderr "'\n")
 5022     (flush Stderr)
 5023     # . syscall(exit, 1)
 5024     bb/copy-to-ebx  1/imm32
 5025     b8/copy-to-eax  1/imm32/exit
 5026     cd/syscall  0x80/imm8
 5027     # never gets here
 5028 
 5029 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
 5030     # . prologue
 5031     55/push-ebp
 5032     89/<- %ebp 4/r32/esp
 5033     # . save registers
 5034     51/push-ecx
 5035     # var word-slice/ecx: slice
 5036     68/push 0/imm32/end
 5037     68/push 0/imm32/start
 5038     89/<- %ecx 4/r32/esp
 5039     # result = false
 5040     b8/copy-to-eax 0/imm32/false
 5041     (rewind-stream *(ebp+8))
 5042     {
 5043       (next-mu-token *(ebp+8) %ecx)
 5044       # if slice-empty?(word-slice) break
 5045       (slice-empty? %ecx)
 5046       3d/compare-eax-and 0/imm32/false
 5047       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 5048       0f 85/jump-if-!= break/disp32
 5049       # if slice-starts-with?(word-slice, '#') break
 5050       # . eax = *word-slice->start
 5051       8b/-> *ecx 0/r32/eax
 5052       8a/copy-byte *eax 0/r32/AL
 5053       81 4/subop/and %eax 0xff/imm32
 5054       # . if (eax == '#') break
 5055       3d/compare-eax-and 0x23/imm32/hash
 5056       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 5057       0f 84/jump-if-= break/disp32
 5058       # if slice-equal?(word-slice, '<-') return true
 5059       (slice-equal? %ecx "<-")
 5060       3d/compare-eax-and 0/imm32/false
 5061       74/jump-if-= loop/disp8
 5062       b8/copy-to-eax 1/imm32/true
 5063     }
 5064 $stmt-has-outputs:end:
 5065     (rewind-stream *(ebp+8))
 5066     # . reclaim locals
 5067     81 0/subop/add %esp 8/imm32
 5068     # . restore registers
 5069     59/pop-to-ecx
 5070     # . epilogue
 5071     89/<- %esp 5/r32/ebp
 5072     5d/pop-to-ebp
 5073     c3/return
 5074 
 5075 # if 'name' starts with a digit, create a new literal var for it
 5076 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
 5077 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5078     # . prologue
 5079     55/push-ebp
 5080     89/<- %ebp 4/r32/esp
 5081     # . save registers
 5082     51/push-ecx
 5083     56/push-esi
 5084     # esi = name
 5085     8b/-> *(ebp+8) 6/r32/esi
 5086     # if slice-empty?(name) abort
 5087     (slice-empty? %esi)  # => eax
 5088     3d/compare-eax-and 0/imm32/false
 5089     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
 5090     # var c/ecx: byte = *name->start
 5091     8b/-> *esi 1/r32/ecx
 5092     8a/copy-byte *ecx 1/r32/CL
 5093     81 4/subop/and %ecx 0xff/imm32
 5094     # if is-decimal-digit?(c) return new var(name)
 5095     {
 5096       (is-decimal-digit? %ecx)  # => eax
 5097       3d/compare-eax-and 0/imm32/false
 5098       74/jump-if-= break/disp8
 5099       (new-literal-integer Heap %esi)  # => eax
 5100       eb/jump $lookup-var-or-literal:end/disp8
 5101     }
 5102     # else if (c == '"') return new var(name)
 5103     {
 5104       81 7/subop/compare %ecx 0x22/imm32/dquote
 5105       75/jump-if-!= break/disp8
 5106       (new-literal Heap %esi)  # => eax
 5107       eb/jump $lookup-var-or-literal:end/disp8
 5108     }
 5109     # otherwise return lookup-var(name, vars)
 5110     {
 5111       (lookup-var %esi *(ebp+0xc))  # => eax
 5112     }
 5113 $lookup-var-or-literal:end:
 5114     # . restore registers
 5115     5e/pop-to-esi
 5116     59/pop-to-ecx
 5117     # . epilogue
 5118     89/<- %esp 5/r32/ebp
 5119     5d/pop-to-ebp
 5120     c3/return
 5121 
 5122 $lookup-var-or-literal:abort:
 5123     (write-buffered Stderr "empty variable!")
 5124     (flush Stderr)
 5125     # . syscall(exit, 1)
 5126     bb/copy-to-ebx  1/imm32
 5127     b8/copy-to-eax  1/imm32/exit
 5128     cd/syscall  0x80/imm8
 5129     # never gets here
 5130 
 5131 # return first 'name' from the top (back) of 'vars' and abort if not found
 5132 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5133     # . prologue
 5134     55/push-ebp
 5135     89/<- %ebp 4/r32/esp
 5136     # var target/eax: (handle array byte) = slice-to-string(name)
 5137     (slice-to-string Heap *(ebp+8))  # => eax
 5138     #
 5139     (lookup-var-helper %eax *(ebp+0xc))  # => eax
 5140     # if (result == 0) abort
 5141     3d/compare-eax-and 0/imm32
 5142     74/jump-if-= $lookup-var:abort/disp8
 5143 $lookup-var:end:
 5144     # . epilogue
 5145     89/<- %esp 5/r32/ebp
 5146     5d/pop-to-ebp
 5147     c3/return
 5148 
 5149 $lookup-var:abort:
 5150     (write-buffered Stderr "unknown variable '")
 5151     (write-slice-buffered Stderr *(ebp+8))
 5152     (write-buffered Stderr "'\n")
 5153     (flush Stderr)
 5154     # . syscall(exit, 1)
 5155     bb/copy-to-ebx  1/imm32
 5156     b8/copy-to-eax  1/imm32/exit
 5157     cd/syscall  0x80/imm8
 5158     # never gets here
 5159 
 5160 # return first 'name' from the top (back) of 'vars', and 0/null if not found
 5161 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5162     # pseudocode:
 5163     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
 5164     #   var min = vars->data
 5165     #   while curr >= min
 5166     #     var v: (handle var) = *curr
 5167     #     if v->name == name
 5168     #       return v
 5169     #   return 0
 5170     #
 5171     # . prologue
 5172     55/push-ebp
 5173     89/<- %ebp 4/r32/esp
 5174     # . save registers
 5175     52/push-edx
 5176     53/push-ebx
 5177     56/push-esi
 5178     # esi = vars
 5179     8b/-> *(ebp+0xc) 6/r32/esi
 5180     # ebx = vars->top
 5181     8b/-> *esi 3/r32/ebx
 5182     # if (vars->top > vars->length) abort
 5183     3b/compare 0/r32/eax *(esi+4)
 5184     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
 5185     # var min/edx: (addr handle var) = vars->data
 5186     8d/copy-address *(esi+8) 2/r32/edx
 5187     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
 5188     81 5/subop/subtract %ebx 4/imm32
 5189     8d/copy-address *(esi+ebx+8) 3/r32/ebx
 5190     {
 5191       # if (curr < min) return 0
 5192       39/compare %ebx 2/r32/edx
 5193       b8/copy-to-eax 0/imm32
 5194       0f 82/jump-if-addr< break/disp32
 5195       # var v/eax: (handle var) = *curr
 5196       8b/-> *ebx 0/r32/eax
 5197       # if (v->name == name) return v
 5198       (string-equal? *eax *(ebp+8))  # Var-name
 5199       3d/compare-eax-and 0/imm32/false
 5200       8b/-> *ebx 0/r32/eax
 5201       75/jump-if-!= break/disp8
 5202       # curr -= 4
 5203       81 5/subop/subtract %ebx 4/imm32
 5204       e9/jump loop/disp32
 5205     }
 5206 $lookup-var-helper:end:
 5207     # . restore registers
 5208     5e/pop-to-esi
 5209     5b/pop-to-ebx
 5210     5a/pop-to-edx
 5211     # . epilogue
 5212     89/<- %esp 5/r32/ebp
 5213     5d/pop-to-ebp
 5214     c3/return
 5215 
 5216 $lookup-var-helper:error1:
 5217     (write-buffered Stderr "malformed stack when looking up '")
 5218     (write-slice-buffered Stderr *(ebp+8))
 5219     (write-buffered Stderr "'\n")
 5220     (flush Stderr)
 5221     # . syscall(exit, 1)
 5222     bb/copy-to-ebx  1/imm32
 5223     b8/copy-to-eax  1/imm32/exit
 5224     cd/syscall  0x80/imm8
 5225     # never gets here
 5226 
 5227 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
 5228 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
 5229     # . prologue
 5230     55/push-ebp
 5231     89/<- %ebp 4/r32/esp
 5232     # . save registers
 5233     51/push-ecx
 5234     # var target/ecx: (handle array byte) = slice-to-string(name)
 5235     (slice-to-string Heap *(ebp+8))  # => eax
 5236     89/<- %ecx 0/r32/eax
 5237     #
 5238     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
 5239     {
 5240       # if (result != 0) return
 5241       3d/compare-eax-and 0/imm32
 5242       75/jump-if-!= break/disp8
 5243       # if name is one of fn's outputs, return it
 5244       {
 5245         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
 5246         3d/compare-eax-and 0/imm32
 5247         # otherwise abort
 5248         0f 84/jump-if-!= $lookup-var:abort/disp32
 5249       }
 5250     }
 5251 $lookup-or-define-var:end:
 5252     # . restore registers
 5253     59/pop-to-ecx
 5254     # . epilogue
 5255     89/<- %esp 5/r32/ebp
 5256     5d/pop-to-ebp
 5257     c3/return
 5258 
 5259 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
 5260     # . prologue
 5261     55/push-ebp
 5262     89/<- %ebp 4/r32/esp
 5263     # . save registers
 5264     51/push-ecx
 5265     # var curr/ecx: (handle list var) = fn->outputs
 5266     8b/-> *(ebp+8) 1/r32/ecx
 5267     8b/-> *(ecx+0xc) 1/r32/ecx
 5268     # while curr != null
 5269     {
 5270       81 7/subop/compare %ecx 0/imm32
 5271       74/jump-if-= break/disp8
 5272       # var v: (handle var) = *curr
 5273       8b/-> *ecx 0/r32/eax  # List-value
 5274       # if (curr->name == name) return curr
 5275       50/push-eax
 5276       (string-equal? *eax *(ebp+0xc))
 5277       3d/compare-eax-and 0/imm32/false
 5278       58/pop-to-eax
 5279       75/jump-if-!= $find-in-function-outputs:end/disp8
 5280       # curr = curr->next
 5281       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 5282       eb/jump loop/disp8
 5283     }
 5284     b8/copy-to-eax 0/imm32
 5285 $find-in-function-outputs:end:
 5286     # . restore registers
 5287     59/pop-to-ecx
 5288     # . epilogue
 5289     89/<- %esp 5/r32/ebp
 5290     5d/pop-to-ebp
 5291     c3/return
 5292 
 5293 test-parse-mu-stmt:
 5294     # . prologue
 5295     55/push-ebp
 5296     89/<- %ebp 4/r32/esp
 5297     # setup
 5298     (clear-stream _test-input-stream)
 5299     (write _test-input-stream "increment n\n")
 5300     # var vars/ecx: (stack (addr var) 4)
 5301     81 5/subop/subtract %esp 0x10/imm32
 5302     68/push 0x10/imm32/length
 5303     68/push 0/imm32/top
 5304     89/<- %ecx 4/r32/esp
 5305     (clear-stack %ecx)
 5306     # var v/edx: var
 5307     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5308     89/<- %edx 4/r32/esp
 5309     (zero-out %edx 0x14)  # Var-size
 5310     # v->name = "n"
 5311     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5312     #
 5313     (push %ecx %edx)
 5314     # convert
 5315     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5316     # check result
 5317     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
 5318     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
 5319     # edx: (handle list var) = result->inouts
 5320     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5321     # ebx: (handle var) = result->inouts->value
 5322     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5323     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
 5324     # . epilogue
 5325     89/<- %esp 5/r32/ebp
 5326     5d/pop-to-ebp
 5327     c3/return
 5328 
 5329 test-parse-mu-stmt-with-comma:
 5330     # . prologue
 5331     55/push-ebp
 5332     89/<- %ebp 4/r32/esp
 5333     # setup
 5334     (clear-stream _test-input-stream)
 5335     (write _test-input-stream "copy-to n, 3\n")
 5336     # var vars/ecx: (stack (addr var) 4)
 5337     81 5/subop/subtract %esp 0x10/imm32
 5338     68/push 0x10/imm32/length
 5339     68/push 0/imm32/top
 5340     89/<- %ecx 4/r32/esp
 5341     (clear-stack %ecx)
 5342     # var v/edx: var
 5343     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5344     89/<- %edx 4/r32/esp
 5345     (zero-out %edx 0x14)  # Var-size
 5346     # v->name = "n"
 5347     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5348     #
 5349     (push %ecx %edx)
 5350     # convert
 5351     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5352     # check result
 5353     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
 5354     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
 5355     # edx: (handle list var) = result->inouts
 5356     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5357     # ebx: (handle var) = result->inouts->value
 5358     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5359     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
 5360     # . epilogue
 5361     89/<- %esp 5/r32/ebp
 5362     5d/pop-to-ebp
 5363     c3/return
 5364 
 5365 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)
 5366     # . prologue
 5367     55/push-ebp
 5368     89/<- %ebp 4/r32/esp
 5369     # . save registers
 5370     51/push-ecx
 5371     #
 5372     (allocate *(ebp+8) *Function-size)  # => eax
 5373     8b/-> *(ebp+0xc) 1/r32/ecx
 5374     89/<- *eax 1/r32/ecx  # Function-name
 5375     8b/-> *(ebp+0x10) 1/r32/ecx
 5376     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
 5377     8b/-> *(ebp+0x14) 1/r32/ecx
 5378     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
 5379     8b/-> *(ebp+0x18) 1/r32/ecx
 5380     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
 5381     8b/-> *(ebp+0x1c) 1/r32/ecx
 5382     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
 5383     8b/-> *(ebp+0x20) 1/r32/ecx
 5384     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
 5385 $new-function:end:
 5386     # . restore registers
 5387     59/pop-to-ecx
 5388     # . epilogue
 5389     89/<- %esp 5/r32/ebp
 5390     5d/pop-to-ebp
 5391     c3/return
 5392 
 5393 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte) -> result/eax: (handle var)
 5394     # . prologue
 5395     55/push-ebp
 5396     89/<- %ebp 4/r32/esp
 5397     # . save registers
 5398     51/push-ecx
 5399     #
 5400     (allocate *(ebp+8) *Var-size)  # => eax
 5401     (zero-out %eax *Var-size)
 5402     8b/-> *(ebp+0xc) 1/r32/ecx
 5403     89/<- *eax 1/r32/ecx  # Var-name
 5404 $new-var:end:
 5405     # . restore registers
 5406     59/pop-to-ecx
 5407     # . epilogue
 5408     89/<- %esp 5/r32/ebp
 5409     5d/pop-to-ebp
 5410     c3/return
 5411 
 5412 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5413     # . prologue
 5414     55/push-ebp
 5415     89/<- %ebp 4/r32/esp
 5416     # . save registers
 5417     51/push-ecx
 5418     # if (!is-hex-int?(name)) abort
 5419     (is-hex-int? *(ebp+0xc))  # => eax
 5420     3d/compare-eax-and 0/imm32/false
 5421     0f 84/jump-if-= $new-literal-integer:abort/disp32
 5422     # var type/ecx: (handle tree type-id) = new type()
 5423     (allocate *(ebp+8) *Tree-size)  # => eax
 5424     (zero-out %eax *Tree-size)  # default type is 'literal'
 5425     89/<- %ecx 0/r32/eax
 5426     # result = new var(s)
 5427     (new-var-from-slice *(ebp+8) *(ebp+0xc))  # => eax
 5428     89/<- *(eax+4) 1/r32/ecx
 5429 $new-literal-integer:end:
 5430     # . restore registers
 5431     59/pop-to-ecx
 5432     # . epilogue
 5433     89/<- %esp 5/r32/ebp
 5434     5d/pop-to-ebp
 5435     c3/return
 5436 
 5437 $new-literal-integer:abort:
 5438     (write-buffered Stderr "variable cannot begin with a digit '")
 5439     (write-slice-buffered Stderr *(ebp+0xc))
 5440     (write-buffered Stderr "'\n")
 5441     (flush Stderr)
 5442     # . syscall(exit, 1)
 5443     bb/copy-to-ebx  1/imm32
 5444     b8/copy-to-eax  1/imm32/exit
 5445     cd/syscall  0x80/imm8
 5446     # never gets here
 5447 
 5448 new-literal:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5449     # . prologue
 5450     55/push-ebp
 5451     89/<- %ebp 4/r32/esp
 5452     # . save registers
 5453     51/push-ecx
 5454     52/push-edx
 5455     # var s/ecx: (addr array byte)
 5456     (slice-to-string Heap *(ebp+0xc))  # => eax
 5457     89/<- %ecx 0/r32/eax
 5458     # type = new type()
 5459     (allocate *(ebp+8) *Tree-size)  # => eax
 5460     (zero-out %eax *Tree-size)  # default type is 'literal'
 5461     89/<- %edx 0/r32/eax
 5462     # eax = result
 5463     (new-var *(ebp+8) %ecx)  # => eax
 5464     # result->type = type
 5465     89/<- *(eax+4) 2/r32/edx  # Var-type
 5466 $new-literal:end:
 5467     # . restore registers
 5468     5a/pop-to-edx
 5469     59/pop-to-ecx
 5470     # . epilogue
 5471     89/<- %esp 5/r32/ebp
 5472     5d/pop-to-ebp
 5473     c3/return
 5474 
 5475 new-var-from-slice:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5476     # . prologue
 5477     55/push-ebp
 5478     89/<- %ebp 4/r32/esp
 5479     # . save registers
 5480     51/push-ecx
 5481     # result = new-var(slice-to-string(name))
 5482     (slice-to-string Heap *(ebp+0xc))  # => eax
 5483     (new-var *(ebp+8) %eax)
 5484 $new-var-from-slice:end:
 5485     # . restore registers
 5486     59/pop-to-ecx
 5487     # . epilogue
 5488     89/<- %esp 5/r32/ebp
 5489     5d/pop-to-ebp
 5490     c3/return
 5491 
 5492 new-block:  # ad: (addr allocation-descriptor), data: (handle list stmt) -> result/eax: (handle stmt)
 5493     # . prologue
 5494     55/push-ebp
 5495     89/<- %ebp 4/r32/esp
 5496     # . save registers
 5497     51/push-ecx
 5498     #
 5499     (allocate *(ebp+8) *Stmt-size)  # => eax
 5500     (zero-out %eax *Stmt-size)
 5501     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
 5502     8b/-> *(ebp+0xc) 1/r32/ecx
 5503     89/<- *(eax+4) 1/r32/ecx  # Block-stmts
 5504 $new-block:end:
 5505     # . restore registers
 5506     59/pop-to-ecx
 5507     # . epilogue
 5508     89/<- %esp 5/r32/ebp
 5509     5d/pop-to-ebp
 5510     c3/return
 5511 
 5512 new-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5513     # . prologue
 5514     55/push-ebp
 5515     89/<- %ebp 4/r32/esp
 5516     # . save registers
 5517     51/push-ecx
 5518     #
 5519     (allocate *(ebp+8) *Stmt-size)  # => eax
 5520     (zero-out %eax *Stmt-size)
 5521     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
 5522     # result->var = var
 5523     8b/-> *(ebp+0xc) 1/r32/ecx
 5524     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
 5525 $new-var-def:end:
 5526     # . restore registers
 5527     59/pop-to-ecx
 5528     # . epilogue
 5529     89/<- %esp 5/r32/ebp
 5530     5d/pop-to-ebp
 5531     c3/return
 5532 
 5533 new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5534     # . prologue
 5535     55/push-ebp
 5536     89/<- %ebp 4/r32/esp
 5537     # . save registers
 5538     51/push-ecx
 5539     57/push-edi
 5540     # ecx = var
 5541     8b/-> *(ebp+0xc) 1/r32/ecx
 5542     # edi = result
 5543     (allocate *(ebp+8) *Stmt-size)  # => eax
 5544     89/<- %edi 0/r32/eax
 5545     (zero-out %edi *Stmt-size)
 5546     # set tag
 5547     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
 5548     # set output
 5549     (append-stmt-var Heap %ecx *(edi+0xc) 0)  # Regvardef-outputs => eax
 5550     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
 5551 $new-reg-var-def:end:
 5552     89/<- %eax 7/r32/edi
 5553     # . restore registers
 5554     5f/pop-to-edi
 5555     59/pop-to-ecx
 5556     # . epilogue
 5557     89/<- %esp 5/r32/ebp
 5558     5d/pop-to-ebp
 5559     c3/return
 5560 
 5561 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
 5562     # . prologue
 5563     55/push-ebp
 5564     89/<- %ebp 4/r32/esp
 5565     # . save registers
 5566     51/push-ecx
 5567     #
 5568     (allocate *(ebp+8) *List-size)  # => eax
 5569     (zero-out %eax *List-size)
 5570     8b/-> *(ebp+0xc) 1/r32/ecx
 5571     89/<- *eax 1/r32/ecx  # List-value
 5572     # if (list == null) return result
 5573     81 7/subop/compare *(ebp+0x10) 0/imm32
 5574     74/jump-if-= $append-list:end/disp8
 5575     # otherwise append
 5576     # var curr/ecx = list
 5577     8b/-> *(ebp+0x10) 1/r32/ecx
 5578     # while (curr->next != null) curr = curr->next
 5579     {
 5580       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5581       74/jump-if-= break/disp8
 5582       # curr = curr->next
 5583       8b/-> *(ecx+4) 1/r32/ecx
 5584       eb/jump loop/disp8
 5585     }
 5586     # curr->next = result
 5587     89/<- *(ecx+4) 0/r32/eax
 5588     # return list
 5589     8b/-> *(ebp+0x10) 0/r32/eax
 5590 $append-list:end:
 5591     # . restore registers
 5592     59/pop-to-ecx
 5593     # . epilogue
 5594     89/<- %esp 5/r32/ebp
 5595     5d/pop-to-ebp
 5596     c3/return
 5597 
 5598 append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
 5599     # . prologue
 5600     55/push-ebp
 5601     89/<- %ebp 4/r32/esp
 5602     # . save registers
 5603     51/push-ecx
 5604     #
 5605     (allocate *(ebp+8) *Stmt-var-size)  # => eax
 5606     (zero-out %eax *Stmt-var-size)
 5607     8b/-> *(ebp+0xc) 1/r32/ecx
 5608     89/<- *eax 1/r32/ecx  # Stmt-var-value
 5609     8b/-> *(ebp+0x14) 1/r32/ecx
 5610     89/<- *(eax+8) 1/r32/ecx  # Stmt-var-is-deref
 5611     # if (list == null) return result
 5612     81 7/subop/compare *(ebp+0x10) 0/imm32
 5613     74/jump-if-= $append-stmt-var:end/disp8
 5614     # otherwise append
 5615     # var curr/ecx: (handle stmt-var) = vars
 5616     8b/-> *(ebp+0x10) 1/r32/ecx
 5617     # while (curr->next != null) curr = curr->next
 5618     {
 5619       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5620       74/jump-if-= break/disp8
 5621       # curr = curr->next
 5622       8b/-> *(ecx+4) 1/r32/ecx
 5623       eb/jump loop/disp8
 5624     }
 5625     # curr->next = result
 5626     89/<- *(ecx+4) 0/r32/eax
 5627     # return vars
 5628     8b/-> *(ebp+0x10) 0/r32/eax
 5629 $append-stmt-var:end:
 5630     # . restore registers
 5631     59/pop-to-ecx
 5632     # . epilogue
 5633     89/<- %esp 5/r32/ebp
 5634     5d/pop-to-ebp
 5635     c3/return
 5636 
 5637 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
 5638     # . prologue
 5639     55/push-ebp
 5640     89/<- %ebp 4/r32/esp
 5641     # . save registers
 5642     56/push-esi
 5643     # esi = block
 5644     8b/-> *(ebp+0xc) 6/r32/esi
 5645     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-stmts
 5646     89/<- *(esi+4) 0/r32/eax  # Block-stmts
 5647 $append-to-block:end:
 5648     # . restore registers
 5649     5e/pop-to-esi
 5650     # . epilogue
 5651     89/<- %esp 5/r32/ebp
 5652     5d/pop-to-ebp
 5653     c3/return
 5654 
 5655 ## Parsing types
 5656 # We need to create metadata on user-defined types, and we need to use this
 5657 # metadata as we parse instructions.
 5658 # However, we also want to allow types to be used before their definitions.
 5659 # This means we can't ever assume any type data structures exist.
 5660 
 5661 lookup-or-create-constant:  # container: (handle stmt-var), field-name: (addr slice) -> result/eax: (handle var)
 5662     # . prologue
 5663     55/push-ebp
 5664     89/<- %ebp 4/r32/esp
 5665     # . save registers
 5666     56/push-esi
 5667     # var container-type/esi: type-id
 5668     (container-type *(ebp+8))  # => eax
 5669 #?     (write-buffered Stderr "lookup-or-create-constant: container type-id: ")
 5670 #?     (print-int32-buffered Stderr %eax)
 5671 #?     (write-buffered Stderr Newline)
 5672 #?     (flush Stderr)
 5673     89/<- %esi 0/r32/eax
 5674     # var typeinfo/eax: (addr typeinfo)
 5675     (find-or-create-typeinfo %esi)  # => eax
 5676 #?     (write-buffered Stderr "lookup-or-create-constant: typeinfo: ")
 5677 #?     (print-int32-buffered Stderr %eax)
 5678 #?     (write-buffered Stderr Newline)
 5679 #?     (flush Stderr)
 5680     # result = find-or-create-typeinfo-output-var(typeinfo, field-name)
 5681     (find-or-create-typeinfo-output-var %eax *(ebp+0xc))  # => eax
 5682 $lookup-or-create-constant:end:
 5683     # . restore registers
 5684     5e/pop-to-esi
 5685     # . epilogue
 5686     89/<- %esp 5/r32/ebp
 5687     5d/pop-to-ebp
 5688     c3/return
 5689 
 5690 # if addr var:
 5691 #   container->var->type->right->left->value
 5692 # otherwise
 5693 #   container->var->type->value
 5694 container-type:  # container: (handle stmt-var) -> result/eax: type-id
 5695     # . prologue
 5696     55/push-ebp
 5697     89/<- %ebp 4/r32/esp
 5698     #
 5699     8b/-> *(ebp+8) 0/r32/eax
 5700     8b/-> *eax 0/r32/eax  # Stmt-var-value
 5701     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5702     {
 5703       81 7/subop/compare *(eax+4) 0/imm32
 5704       74/jump-if-= break/disp8
 5705       8b/-> *(eax+4) 0/r32/eax  # Tree-right
 5706       8b/-> *eax 0/r32/eax  # Tree-left
 5707     }
 5708     8b/-> *eax 0/r32/eax  # Atom-value
 5709 $container-type:end:
 5710     # . epilogue
 5711     89/<- %esp 5/r32/ebp
 5712     5d/pop-to-ebp
 5713     c3/return
 5714 
 5715 find-or-create-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5716     # . prologue
 5717     55/push-ebp
 5718     89/<- %ebp 4/r32/esp
 5719     # . save registers
 5720     51/push-ecx
 5721     # eax = find-typeinfo(t)
 5722     (find-typeinfo *(ebp+8))  # => eax
 5723     {
 5724       # if (curr != 0) break
 5725       3d/compare-eax-and 0/imm32
 5726       75/jump-if-!= break/disp8
 5727 $find-or-create-typeinfo:create:
 5728       (allocate Heap *Typeinfo-size)  # => eax
 5729       (zero-out %eax *Typeinfo-size)
 5730       # result->id = t
 5731       8b/-> *(ebp+8) 1/r32/ecx
 5732       89/<- *eax 1/r32/ecx  # Typeinfo-id
 5733       # result->fields = new table
 5734       # . ecx = new table
 5735       50/push-eax
 5736       (new-stream Heap 0x40 *Typeinfo-fields-row-size)  # => eax
 5737       89/<- %ecx 0/r32/eax
 5738       58/pop-to-eax
 5739       # . result->fields = ecx
 5740       89/<- *(eax+4) 1/r32/ecx  # Typeinfo-fields
 5741       # result->next = Program->types
 5742       8b/-> *_Program-types 1/r32/ecx
 5743       89/<- *(eax+0xc) 1/r32/ecx  # Typeinfo-next
 5744       # Program->types = result
 5745       89/<- *_Program-types 0/r32/eax
 5746     }
 5747 $find-or-create-typeinfo:end:
 5748     # . restore registers
 5749     59/pop-to-ecx
 5750     # . epilogue
 5751     89/<- %esp 5/r32/ebp
 5752     5d/pop-to-ebp
 5753     c3/return
 5754 
 5755 find-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5756     # . prologue
 5757     55/push-ebp
 5758     89/<- %ebp 4/r32/esp
 5759     # . save registers
 5760     51/push-ecx
 5761     # ecx = t
 5762     8b/-> *(ebp+8) 1/r32/ecx
 5763     # var curr/eax: (handle typeinfo) = Program->types
 5764     8b/-> *_Program-types 0/r32/eax
 5765     {
 5766       # if (curr == 0) break
 5767       3d/compare-eax-and 0/imm32
 5768       74/jump-if-= break/disp8
 5769       # if (curr->id == t) return curr
 5770       39/compare *eax 1/r32/ecx  # Typeinfo-id
 5771       0f 84/jump-if-= $find-or-create-typeinfo:end/disp32
 5772       # curr = curr->next
 5773       8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-next
 5774       #
 5775       eb/jump loop/disp8
 5776     }
 5777 $find-typeinfo:end:
 5778     # . restore registers
 5779     59/pop-to-ecx
 5780     # . epilogue
 5781     89/<- %esp 5/r32/ebp
 5782     5d/pop-to-ebp
 5783     c3/return
 5784 
 5785 find-or-create-typeinfo-output-var:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle var)
 5786     # . prologue
 5787     55/push-ebp
 5788     89/<- %ebp 4/r32/esp
 5789     # . save registers
 5790     51/push-ecx
 5791     56/push-esi
 5792     # esi = find-or-create-typeinfo-fields(T, f)
 5793     (find-or-create-typeinfo-fields *(ebp+8) *(ebp+0xc))  # => eax
 5794     89/<- %esi 0/r32/eax
 5795     # if output var doesn't exist, create it
 5796     {
 5797       81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-entry-output-var
 5798       75/jump-if-!= break/disp8
 5799       # var type/eax: (handle tree type-id) = new var("dummy name", constant type, -1 offset)
 5800       (allocate Heap *Tree-size)  # => eax
 5801       c7 0/subop/copy *eax 6/imm32/constant  # Atom-value
 5802       c7 0/subop/copy *(eax+4) 0/imm32  # Tree-right
 5803       89/<- %ecx 0/r32/eax
 5804       # eax = result
 5805       (new-var Heap "field")  # => eax
 5806       # result->type = type
 5807       89/<- *(eax+4) 1/r32/ecx  # Var-type
 5808       # result->offset isn't filled out yet
 5809       c7 0/subop/copy *(eax+0xc) -1/imm32/uninitialized  # Var-offset
 5810       # save result as output var
 5811       89/<- *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5812     }
 5813     # return the output var
 5814     8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5815 $find-or-create-typeinfo-output-var:end:
 5816     # . restore registers
 5817     5e/pop-to-esi
 5818     59/pop-to-ecx
 5819     # . epilogue
 5820     89/<- %esp 5/r32/ebp
 5821     5d/pop-to-ebp
 5822     c3/return
 5823 
 5824 find-or-create-typeinfo-fields:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle typeinfo-entry)
 5825     # . prologue
 5826     55/push-ebp
 5827     89/<- %ebp 4/r32/esp
 5828     # . save registers
 5829     56/push-esi
 5830     # esi = T->fields
 5831     8b/-> *(ebp+8) 6/r32/esi
 5832     8b/-> *(esi+4) 6/r32/esi  # Typeinfo-fields
 5833     # esi = get-or-insert(T->fields, f)
 5834     (leaky-get-or-insert-slice %esi *(ebp+0xc) *Typeinfo-fields-row-size)  # => eax
 5835     89/<- %esi 0/r32/eax
 5836     # if typeinfo-entry doesn't exist, allocate it
 5837     {
 5838       81 7/subop/compare *esi 0/imm32  # output var
 5839       75/jump-if-!= break/disp8
 5840       (allocate Heap *Typeinfo-entry-size)  # => eax
 5841       (zero-out %eax *Typeinfo-entry-size)
 5842       89/<- *esi 0/r32/eax
 5843     }
 5844     # eax = T->fields[f]->entry
 5845     8b/-> *esi 0/r32/eax
 5846 $find-or-create-typeinfo-fields:end:
 5847     # . restore registers
 5848     5e/pop-to-esi
 5849     # . epilogue
 5850     89/<- %esp 5/r32/ebp
 5851     5d/pop-to-ebp
 5852     c3/return
 5853 
 5854 populate-mu-type:  # in: (addr stream byte), t: (handle typeinfo)
 5855     # pseudocode:
 5856     #   var line: (stream byte 512)
 5857     #   curr-index = 0
 5858     #   while true
 5859     #     clear-stream(line)
 5860     #     read-line-buffered(in, line)
 5861     #     if line->write == 0
 5862     #       abort
 5863     #     word-slice = next-mu-token(line)
 5864     #     if slice-empty?(word-slice)               # end of line
 5865     #       continue
 5866     #     if slice-equal?(word-slice, "}")
 5867     #       break
 5868     #     var v: (handle var) = parse-var-with-type(word-slice, line)
 5869     #     var r: (handle typeinfo-fields) = find-or-create-typeinfo-fields(t, word-slice/v->name)
 5870     #     TODO: ensure that r->first is null
 5871     #     r->index = curr-index
 5872     #     curr-index++
 5873     #     r->input-var = v
 5874     #     if r->output-var == 0
 5875     #       r->output-var = new literal
 5876     #     TODO: ensure nothing else in line
 5877     # t->total-size-in-bytes = -2 (not yet initialized)
 5878     #
 5879     # . prologue
 5880     55/push-ebp
 5881     89/<- %ebp 4/r32/esp
 5882     # var curr-index: int at *(ebp-4)
 5883     68/push 0/imm32
 5884     # . save registers
 5885     50/push-eax
 5886     51/push-ecx
 5887     52/push-edx
 5888     53/push-ebx
 5889     56/push-esi
 5890     57/push-edi
 5891     # edi = t
 5892     8b/-> *(ebp+0xc) 7/r32/edi
 5893     # var line/ecx: (stream byte 512)
 5894     81 5/subop/subtract %esp 0x200/imm32
 5895     68/push 0x200/imm32/length
 5896     68/push 0/imm32/read
 5897     68/push 0/imm32/write
 5898     89/<- %ecx 4/r32/esp
 5899     # var word-slice/edx: slice
 5900     68/push 0/imm32/end
 5901     68/push 0/imm32/start
 5902     89/<- %edx 4/r32/esp
 5903     {
 5904 $populate-mu-type:line-loop:
 5905       (clear-stream %ecx)
 5906       (read-line-buffered *(ebp+8) %ecx)
 5907       # if (line->write == 0) abort
 5908       81 7/subop/compare *ecx 0/imm32
 5909       0f 84/jump-if-= $populate-mu-type:abort/disp32
 5910 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
 5916       (next-mu-token %ecx %edx)
 5917       # if slice-empty?(word-slice) continue
 5918       (slice-empty? %edx)  # => eax
 5919       3d/compare-eax-and 0/imm32
 5920       0f 85/jump-if-!= loop/disp32
 5921       # if slice-equal?(word-slice, "}") break
 5922       (slice-equal? %edx "}")
 5923       3d/compare-eax-and 0/imm32
 5924       0f 85/jump-if-!= break/disp32
 5925 $populate-mu-type:parse-element:
 5926       # var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
 5927       (parse-var-with-type %edx %ecx)  # => eax
 5928       89/<- %esi 0/r32/eax
 5929 $populate-mu-type:create-typeinfo-fields:
 5930       # var r/ebx: (handle typeinfo-entry)
 5931       (find-or-create-typeinfo-fields %edi %edx)  # => eax
 5932       89/<- %ebx 0/r32/eax
 5933 #?       (write-buffered Stderr "var ")
 5934 #?       (write-buffered Stderr *esi)  # Var-name
 5935 #?       (write-buffered Stderr " is at index ")
 5936 #?       (print-int32-buffered Stderr *(ebp-4))
 5937 #?       (write-buffered Stderr Newline)
 5938 #?       (flush Stderr)
 5939       # r->index = curr-index
 5940       8b/-> *(ebp-4) 0/r32/eax
 5941       89/<- *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 5942       # ++curr-index
 5943       ff 0/subop/increment *(ebp-4)
 5944 $populate-mu-type:set-input-type:
 5945       # r->input-var = v
 5946       89/<- *ebx 6/r32/esi  # Typeinfo-entry-input-var
 5947       {
 5948 $populate-mu-type:create-output-type:
 5949         # if (r->output-var == 0) create a new var with some placeholder data
 5950         81 7/subop/compare *(ebx+8) 0/imm32  # Typeinfo-entry-output-var
 5951         75/jump-if-!= break/disp8
 5952         (new-literal Heap %edx)  # => eax
 5953         89/<- *(ebx+8) 0/r32/eax  # Typeinfo-entry-output-var
 5954       }
 5955       e9/jump loop/disp32
 5956     }
 5957 $populate-mu-type:invalidate-total-size-in-bytes:
 5958     # Offsets and total size may not be accurate here since we may not yet
 5959     # have encountered the element types.
 5960     # We'll recompute them separately after parsing the entire program.
 5961     c7 0/subop/copy *(edi+8) -2/imm32/uninitialized  # Typeinfo-total-size-in-bytes
 5962 $populate-mu-type:end:
 5963     # . reclaim locals
 5964     81 0/subop/add %esp 0x214/imm32
 5965     # . restore registers
 5966     5f/pop-to-edi
 5967     5e/pop-to-esi
 5968     5b/pop-to-ebx
 5969     5a/pop-to-edx
 5970     59/pop-to-ecx
 5971     58/pop-to-eax
 5972     # reclaim curr-index
 5973     81 0/subop/add %esp 4/imm32
 5974     # . epilogue
 5975     89/<- %esp 5/r32/ebp
 5976     5d/pop-to-ebp
 5977     c3/return
 5978 
 5979 $populate-mu-type:abort:
 5980     # error("unexpected top-level command: " word-slice "\n")
 5981     (write-buffered Stderr "incomplete type definition '")
 5982     (type-name *edi)  # Typeinfo-id => eax
 5983     (write-buffered Stderr %eax)
 5984     (write-buffered Stderr "\n")
 5985     (flush Stderr)
 5986     # . syscall(exit, 1)
 5987     bb/copy-to-ebx  1/imm32
 5988     b8/copy-to-eax  1/imm32/exit
 5989     cd/syscall  0x80/imm8
 5990     # never gets here
 5991 
 5992 type-name:  # index: int -> result/eax: (addr array byte)
 5993     # . prologue
 5994     55/push-ebp
 5995     89/<- %ebp 4/r32/esp
 5996     #
 5997     (index Type-id *(ebp+8))
 5998 $type-name:end:
 5999     # . epilogue
 6000     89/<- %esp 5/r32/ebp
 6001     5d/pop-to-ebp
 6002     c3/return
 6003 
 6004 index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
 6005     # . prologue
 6006     55/push-ebp
 6007     89/<- %ebp 4/r32/esp
 6008     # . save registers
 6009     56/push-esi
 6010     # TODO: bounds-check index
 6011     # esi = arr
 6012     8b/-> *(ebp+8) 6/r32/esi
 6013     # eax = index
 6014     8b/-> *(ebp+0xc) 0/r32/eax
 6015     # eax = *(arr + 12 + index)
 6016     8b/-> *(esi+eax+0xc) 0/r32/eax
 6017 $index:end:
 6018     # . restore registers
 6019     5e/pop-to-esi
 6020     # . epilogue
 6021     89/<- %esp 5/r32/ebp
 6022     5d/pop-to-ebp
 6023     c3/return
 6024 
 6025 #######################################################
 6026 # Compute type sizes
 6027 #######################################################
 6028 
 6029 # Compute the sizes of all user-defined types.
 6030 # We'll need the sizes of their elements, which may be other user-defined
 6031 # types, which we will compute as needed.
 6032 
 6033 # Initially, all user-defined types have their sizes set to -2 (invalid)
 6034 populate-mu-type-sizes:
 6035     # . prologue
 6036     55/push-ebp
 6037     89/<- %ebp 4/r32/esp
 6038     # . save registers
 6039     51/push-ecx
 6040 $populate-mu-type-sizes:total-sizes:
 6041     # var curr/ecx: (handle typeinfo) = *Program->types
 6042     8b/-> *_Program-types 1/r32/ecx
 6043     {
 6044       # if (curr == null) break
 6045       81 7/subop/compare %ecx 0/imm32
 6046       74/jump-if-= break/disp8
 6047       (populate-mu-type-sizes-in-type %ecx)
 6048       # curr = curr->next
 6049       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 6050       eb/jump loop/disp8
 6051     }
 6052 $populate-mu-type-sizes:offsets:
 6053     # var curr/ecx: (handle typeinfo) = *Program->types
 6054     8b/-> *_Program-types 1/r32/ecx
 6055     {
 6056       # if (curr == null) break
 6057       81 7/subop/compare %ecx 0/imm32
 6058       74/jump-if-= break/disp8
 6059       (populate-mu-type-offsets %ecx)
 6060       # curr = curr->next
 6061       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 6062       eb/jump loop/disp8
 6063     }
 6064 $populate-mu-type-sizes:end:
 6065     # . restore registers
 6066     59/pop-to-ecx
 6067     # . epilogue
 6068     89/<- %esp 5/r32/ebp
 6069     5d/pop-to-ebp
 6070     c3/return
 6071 
 6072 # compute sizes of all fields, recursing as necessary
 6073 # sum up all their sizes to arrive at total size
 6074 # fields may be out of order, but that doesn't affect the answer
 6075 populate-mu-type-sizes-in-type:  # T: (handle typeinfo)
 6076     # . prologue
 6077     55/push-ebp
 6078     89/<- %ebp 4/r32/esp
 6079     # . save registers
 6080     50/push-eax
 6081     51/push-ecx
 6082     52/push-edx
 6083     56/push-esi
 6084     57/push-edi
 6085     # esi = T
 6086     8b/-> *(ebp+8) 6/r32/esi
 6087     # if T is already computed, return
 6088     81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-total-size-in-bytes
 6089     7d/jump-if->= $populate-mu-type-sizes-in-type:end/disp8
 6090     # if T is being computed, abort
 6091     81 7/subop/compare *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 6092     74/jump-if-= $populate-mu-type-sizes-in-type:abort/disp8
 6093     # tag T (-2 to -1) to avoid infinite recursion
 6094     c7 0/subop/copy *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 6095     # var total-size/edi: int = 0
 6096     bf/copy-to-edi 0/imm32
 6097     # - for every field, if it's a user-defined type, compute its size
 6098     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 6099     8b/-> *(esi+4) 1/r32/ecx  # Typeinfo-fields
 6100     # var table-size/edx: int = table->write
 6101     8b/-> *ecx 2/r32/edx  # stream-write
 6102     # var curr/ecx: (addr table_row) = table->data
 6103     8d/copy-address *(ecx+0xc) 1/r32/ecx
 6104     # var max/edx: (addr table_row) = table->data + table->write
 6105     8d/copy-address *(ecx+edx) 2/r32/edx
 6106     {
 6107 $populate-mu-type-sizes-in-type:loop:
 6108       # if (curr >= max) break
 6109       39/compare %ecx 2/r32/edx
 6110       73/jump-if-addr>= break/disp8
 6111       # var t/eax: (handle typeinfo-entry) = curr->value
 6112       8b/-> *(ecx+4) 0/r32/eax
 6113       # compute size of t
 6114       (compute-size-of-var *eax)  # Typeinfo-entry-input-var => eax
 6115       # result += eax
 6116       01/add-to %edi 0/r32/eax
 6117       # curr += row-size
 6118       81 0/subop/add %ecx 8/imm32
 6119       #
 6120       eb/jump loop/disp8
 6121     }
 6122     # - save result
 6123     89/<- *(esi+8) 7/r32/edi  # Typeinfo-total-size-in-bytes
 6124 $populate-mu-type-sizes-in-type:end:
 6125     # . restore registers
 6126     5f/pop-to-edi
 6127     5e/pop-to-esi
 6128     5a/pop-to-edx
 6129     59/pop-to-ecx
 6130     58/pop-to-eax
 6131     # . epilogue
 6132     89/<- %esp 5/r32/ebp
 6133     5d/pop-to-ebp
 6134     c3/return
 6135 
 6136 $populate-mu-type-sizes-in-type:abort:
 6137     (write-buffered Stderr "cycle in type definitions\n")
 6138     (flush Stderr)
 6139     # . syscall(exit, 1)
 6140     bb/copy-to-ebx  1/imm32
 6141     b8/copy-to-eax  1/imm32/exit
 6142     cd/syscall  0x80/imm8
 6143     # never gets here
 6144 
 6145 # Analogous to size-of, except we need to compute what size-of can just read
 6146 # off the right data structures.
 6147 compute-size-of-var:  # in: (handle var) -> result/eax: int
 6148     # . prologue
 6149     55/push-ebp
 6150     89/<- %ebp 4/r32/esp
 6151     # . push registers
 6152     51/push-ecx
 6153     # var t/ecx: (handle tree type-id) = v->type
 6154     8b/-> *(ebp+8) 1/r32/ecx
 6155     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6156     # if (t->left >= *Max-type-id) t = t->left
 6157     {
 6158       8b/-> *Max-type-id 0/r32/eax
 6159       39/compare *ecx 0/r32/eax  # Tree-left
 6160       72/jump-if-addr< break/disp8
 6161       8b/-> *ecx 1/r32/ecx  # Tree-left
 6162     }
 6163     (compute-size-of-type-id *ecx)  # Atom-left => eax
 6164 $compute-size-of-var:end:
 6165     # . restore registers
 6166     59/pop-to-ecx
 6167     # . epilogue
 6168     89/<- %esp 5/r32/ebp
 6169     5d/pop-to-ebp
 6170     c3/return
 6171 
 6172 compute-size-of-type-id:  # t: type-id -> result/eax: int
 6173     # . prologue
 6174     55/push-ebp
 6175     89/<- %ebp 4/r32/esp
 6176     #
 6177     8b/-> *(ebp+8) 0/r32/eax
 6178     # if v is a literal, return 0
 6179     3d/compare-eax-and 0/imm32
 6180     74/jump-if-= $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
 6181     # if v has a user-defined type, compute its size
 6182     # TODO: support non-atom type
 6183     (find-typeinfo %eax)  # => eax
 6184     {
 6185       3d/compare-eax-and 0/imm32
 6186       74/jump-if-= break/disp8
 6187 $compute-size-of-type-id:user-defined:
 6188       (populate-mu-type-sizes %eax)
 6189       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6190       eb/jump $compute-size-of-type-id:end/disp8
 6191     }
 6192     # otherwise return the word size
 6193     b8/copy-to-eax 4/imm32
 6194 $compute-size-of-type-id:end:
 6195     # . epilogue
 6196     89/<- %esp 5/r32/ebp
 6197     5d/pop-to-ebp
 6198     c3/return
 6199 
 6200 # at this point we have total sizes for all user-defined types
 6201 # compute offsets for each element
 6202 # complication: fields may be out of order
 6203 populate-mu-type-offsets:  # in: (handle typeinfo)
 6204     # . prologue
 6205     55/push-ebp
 6206     89/<- %ebp 4/r32/esp
 6207     # . save registers
 6208     50/push-eax
 6209     51/push-ecx
 6210     52/push-edx
 6211     53/push-ebx
 6212     56/push-esi
 6213     57/push-edi
 6214     # var curr-offset/edi: int = 0
 6215     bf/copy-to-edi 0/imm32
 6216     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 6217     8b/-> *(ebp+8) 1/r32/ecx
 6218     8b/-> *(ecx+4) 1/r32/ecx  # Typeinfo-fields
 6219     # var num-elems/edx: int = table->write / 8
 6220     8b/-> *ecx 2/r32/edx  # stream-write
 6221     c1 5/subop/shift-right-logical  %edx 3/imm8
 6222     # var i/ebx: int = 0
 6223     bb/copy-to-ebx 0/imm32
 6224     {
 6225 $populate-mu-type-offsets:loop:
 6226       39/compare %ebx 2/r32/edx
 6227       7d/jump-if->= break/disp8
 6228       # var v/esi: (handle typeinfo-entry)
 6229       (locate-typeinfo-entry-with-index %ecx %ebx)  # => eax
 6230       89/<- %esi 0/r32/eax
 6231       # v->output-var->offset = curr-offset
 6232       8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 6233       89/<- *(eax+0xc) 7/r32/edi  # Var-offset
 6234       # curr-offset += size-of(v->input-var)
 6235       8b/-> *esi 0/r32/eax  # Typeinfo-entry-input-var
 6236       (size-of %eax)  # => eax
 6237       01/add-to %edi 0/r32/eax
 6238       # ++i
 6239       43/increment-ebx
 6240       eb/jump loop/disp8
 6241     }
 6242 $populate-mu-type-offsets:end:
 6243     # . restore registers
 6244     5f/pop-to-edi
 6245     5e/pop-to-esi
 6246     5b/pop-to-ebx
 6247     5a/pop-to-edx
 6248     59/pop-to-ecx
 6249     58/pop-to-eax
 6250     # . epilogue
 6251     89/<- %esp 5/r32/ebp
 6252     5d/pop-to-ebp
 6253     c3/return
 6254 
 6255 locate-typeinfo-entry-with-index:  # table: (handle table string_key (handle typeinfo-entry)), idx: int -> result/eax: (handle typeinfo-entry)
 6256     # . prologue
 6257     55/push-ebp
 6258     89/<- %ebp 4/r32/esp
 6259     # . save registers
 6260     51/push-ecx
 6261     52/push-edx
 6262     53/push-ebx
 6263     56/push-esi
 6264     57/push-edi
 6265     # esi = table
 6266     8b/-> *(ebp+8) 6/r32/esi
 6267     # var curr/ecx: (addr string_key) = table->data
 6268     8d/copy-address *(esi+0xc) 1/r32/ecx
 6269     # var max/edx: (addr byte) = &table->data[table->write]
 6270     8b/-> *esi 2/r32/edx
 6271     8d/copy-address *(ecx+edx) 2/r32/edx
 6272     {
 6273 $locate-typeinfo-entry-with-index:loop:
 6274       39/compare %ecx 2/r32/edx
 6275       73/jump-if-addr>= $locate-typeinfo-entry-with-index:abort/disp8
 6276       # var v/ebx: (handle typeinfo-entry)
 6277       8b/-> *(ecx+4) 3/r32/ebx
 6278       # if (v->index == idx) return v
 6279       8b/-> *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 6280       39/compare *(ebp+0xc) 0/r32/eax
 6281       89/<- %eax 3/r32/ebx
 6282       74/jump-if-= break/disp8
 6283       # curr += 8
 6284       81 0/subop/add %ecx 8/imm32
 6285       eb/jump loop/disp8
 6286     }
 6287 $locate-typeinfo-entry-with-index:end:
 6288     # . restore registers
 6289     5f/pop-to-edi
 6290     5e/pop-to-esi
 6291     5b/pop-to-ebx
 6292     5a/pop-to-edx
 6293     59/pop-to-ecx
 6294     # . epilogue
 6295     89/<- %esp 5/r32/ebp
 6296     5d/pop-to-ebp
 6297     c3/return
 6298 
 6299 $locate-typeinfo-entry-with-index:abort:
 6300     (write-buffered Stderr "overflowing typeinfo-entry->index ")
 6301     (print-int32-buffered Stderr %ecx)
 6302     (write-buffered Stderr "\n")
 6303     (flush Stderr)
 6304     # . syscall(exit, 1)
 6305     bb/copy-to-ebx  1/imm32
 6306     b8/copy-to-eax  1/imm32/exit
 6307     cd/syscall  0x80/imm8
 6308     # never gets here
 6309 
 6310 #######################################################
 6311 # Type-checking
 6312 #######################################################
 6313 
 6314 check-mu-types:
 6315     # . prologue
 6316     55/push-ebp
 6317     89/<- %ebp 4/r32/esp
 6318     #
 6319 $check-mu-types:end:
 6320     # . epilogue
 6321     89/<- %esp 5/r32/ebp
 6322     5d/pop-to-ebp
 6323     c3/return
 6324 
 6325 size-of:  # v: (handle var) -> result/eax: int
 6326     # . prologue
 6327     55/push-ebp
 6328     89/<- %ebp 4/r32/esp
 6329     # . save registers
 6330     51/push-ecx
 6331     # var t/ecx: (handle tree type-id) = v->type
 6332     8b/-> *(ebp+8) 1/r32/ecx
 6333     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6334     # if is-mu-array?(t) return size-of-array(t)
 6335     {
 6336       (is-mu-array? %ecx)  # => eax
 6337       3d/compare-eax-and 0/imm32/false
 6338       74/jump-if-= break/disp8
 6339       (size-of-array %ecx)  # => eax
 6340       eb/jump $size-of:end/disp8
 6341     }
 6342     # if (t->left >= *Max-type-id) t = t->left
 6343     {
 6344       8b/-> *Max-type-id 0/r32/eax
 6345       39/compare *ecx 0/r32/eax  # Tree-left
 6346       72/jump-if-addr< break/disp8
 6347       8b/-> *ecx 1/r32/ecx  # Tree-left
 6348     }
 6349     (size-of-type-id *ecx)  # Atom-left => eax
 6350 $size-of:end:
 6351     # . restore registers
 6352     59/pop-to-ecx
 6353     # . epilogue
 6354     89/<- %esp 5/r32/ebp
 6355     5d/pop-to-ebp
 6356     c3/return
 6357 
 6358 is-mu-array?:  # t: (handle tree type-id) -> result/eax: boolean
 6359     # . prologue
 6360     55/push-ebp
 6361     89/<- %ebp 4/r32/esp
 6362     # . save registers
 6363     51/push-ecx
 6364     # ecx = t->left
 6365     8b/-> *(ebp+8) 1/r32/ecx
 6366     8b/-> *ecx 1/r32/ecx  # Tree-left
 6367     # if t is an atomic type, return false
 6368     3b/compare 1/r32/ecx *Max-type-id
 6369     b8/copy-to-eax 0/imm32/false
 6370     72/jump-if-addr< $is-mu-array?:end/disp8
 6371     # return ecx->value == array
 6372     81 7/subop/compare *ecx 3/imm32/array-type-id  # Atom-value
 6373     0f 94/set-if-= %al
 6374     81 4/subop/and %eax 0xff/imm32
 6375 $is-mu-array?:end:
 6376     # . restore registers
 6377     59/pop-to-ecx
 6378     # . epilogue
 6379     89/<- %esp 5/r32/ebp
 6380     5d/pop-to-ebp
 6381     c3/return
 6382 
 6383 size-of-array:  # a: (handle tree type-id) -> result/eax: int
 6384     # . prologue
 6385     55/push-ebp
 6386     89/<- %ebp 4/r32/esp
 6387     # . save registers
 6388     51/push-ecx
 6389     52/push-edx
 6390     #
 6391     8b/-> *(ebp+8) 1/r32/ecx
 6392     # TODO: assert that a->left is 'array'
 6393     8b/-> *(ecx+4) 1/r32/ecx  # Tree-right
 6394     # var elem-type/edx: type-id = a->right->value
 6395     8b/-> *ecx 2/r32/edx  # Atom-value
 6396     8b/-> *edx 2/r32/edx  # Atom-value
 6397     # var array-size/ecx: int = a->right->right->left->value
 6398     8b/-> *(ecx+4) 1/r32/ecx  # Tree-right
 6399     8b/-> *ecx 1/r32/ecx  # Tree-left
 6400     8b/-> *ecx 1/r32/ecx  # Atom-value
 6401     # return array-size * size-of(elem-type)
 6402     (size-of-type-id %edx)  # => eax
 6403     f7 4/subop/multiply-into-eax %ecx
 6404     05/add-to-eax 4/imm32  # for array length
 6405 $size-of-array:end:
 6406     # . restore registers
 6407     5a/pop-to-edx
 6408     59/pop-to-ecx
 6409     # . epilogue
 6410     89/<- %esp 5/r32/ebp
 6411     5d/pop-to-ebp
 6412     c3/return
 6413 
 6414 size-of-type-id:  # t: type-id -> result/eax: int
 6415     # . prologue
 6416     55/push-ebp
 6417     89/<- %ebp 4/r32/esp
 6418     #
 6419     8b/-> *(ebp+8) 0/r32/eax
 6420     # if v is a literal, return 0
 6421     3d/compare-eax-and 0/imm32
 6422     74/jump-if-= $size-of-type-id:end/disp8  # eax changes type from type-id to int
 6423     # if v has a user-defined type, return its size
 6424     # TODO: support non-atom type
 6425     (find-typeinfo %eax)  # => eax
 6426     {
 6427       3d/compare-eax-and 0/imm32
 6428       74/jump-if-= break/disp8
 6429 $size-of-type-id:user-defined:
 6430       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6431       eb/jump $size-of-type-id:end/disp8
 6432     }
 6433     # otherwise return the word size
 6434     b8/copy-to-eax 4/imm32
 6435 $size-of-type-id:end:
 6436     # . epilogue
 6437     89/<- %esp 5/r32/ebp
 6438     5d/pop-to-ebp
 6439     c3/return
 6440 
 6441 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
 6442     # . prologue
 6443     55/push-ebp
 6444     89/<- %ebp 4/r32/esp
 6445     # . save registers
 6446     51/push-ecx
 6447     52/push-edx
 6448     # ecx = a
 6449     8b/-> *(ebp+8) 1/r32/ecx
 6450     # edx = b
 6451     8b/-> *(ebp+0xc) 2/r32/edx
 6452     # if (a == b) return true
 6453     8b/-> %ecx 0/r32/eax  # Var-type
 6454     39/compare %edx 0/r32/eax  # Var-type
 6455     b8/copy-to-eax 1/imm32/true
 6456     74/jump-if-= $type-equal?:end/disp8
 6457     # if (a < MAX_TYPE_ID) return false
 6458     81 7/subop/compare %ecx 0x10000/imm32
 6459     b8/copy-to-eax 0/imm32/false
 6460     72/jump-if-addr< $type-equal?:end/disp8
 6461     # if (b < MAX_TYPE_ID) return false
 6462     81 7/subop/compare %edx 0x10000/imm32
 6463     b8/copy-to-eax 0/imm32/false
 6464     72/jump-if-addr< $type-equal?:end/disp8
 6465     # if (!type-equal?(a->left, b->left)) return false
 6466     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
 6467     3d/compare-eax-and 0/imm32/false
 6468     74/jump-if-= $type-equal?:end/disp8
 6469     # return type-equal?(a->right, b->right)
 6470     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
 6471 $type-equal?:end:
 6472     # . restore registers
 6473     5a/pop-to-edx
 6474     59/pop-to-ecx
 6475     # . epilogue
 6476     89/<- %esp 5/r32/ebp
 6477     5d/pop-to-ebp
 6478     c3/return
 6479 
 6480 #######################################################
 6481 # Code-generation
 6482 #######################################################
 6483 
 6484 == data
 6485 
 6486 Curr-block-depth:  # (addr int)
 6487     0/imm32
 6488 Curr-local-stack-offset:  # (addr int)
 6489     0/imm32
 6490 
 6491 == code
 6492 
 6493 emit-subx:  # out: (addr buffered-file)
 6494     # . prologue
 6495     55/push-ebp
 6496     89/<- %ebp 4/r32/esp
 6497     # . save registers
 6498     50/push-eax
 6499     51/push-ecx
 6500     57/push-edi
 6501     # edi = out
 6502     8b/-> *(ebp+8) 7/r32/edi
 6503     # var curr/ecx: (handle function) = *Program->functions
 6504     8b/-> *_Program-functions 1/r32/ecx
 6505     {
 6506       # if (curr == null) break
 6507       81 7/subop/compare %ecx 0/imm32
 6508       0f 84/jump-if-= break/disp32
 6509       (emit-subx-function %edi %ecx)
 6510       # curr = curr->next
 6511       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 6512       e9/jump loop/disp32
 6513     }
 6514 $emit-subx:end:
 6515     # . restore registers
 6516     5f/pop-to-edi
 6517     59/pop-to-ecx
 6518     58/pop-to-eax
 6519     # . epilogue
 6520     89/<- %esp 5/r32/ebp
 6521     5d/pop-to-ebp
 6522     c3/return
 6523 
 6524 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
 6525     # . prologue
 6526     55/push-ebp
 6527     89/<- %ebp 4/r32/esp
 6528     # some preprocessing
 6529     (populate-mu-type-offsets-in-inouts *(ebp+0xc))
 6530     # . save registers
 6531     50/push-eax
 6532     51/push-ecx
 6533     52/push-edx
 6534     57/push-edi
 6535     # edi = out
 6536     8b/-> *(ebp+8) 7/r32/edi
 6537     # ecx = f
 6538     8b/-> *(ebp+0xc) 1/r32/ecx
 6539     # var vars/edx: (stack (addr var) 256)
 6540     81 5/subop/subtract %esp 0x400/imm32
 6541     68/push 0x400/imm32/length
 6542     68/push 0/imm32/top
 6543     89/<- %edx 4/r32/esp
 6544     #
 6545     (write-buffered %edi *ecx)
 6546     (write-buffered %edi ":\n")
 6547     # initialize some global state
 6548     c7 0/subop/copy *Curr-block-depth 1/imm32
 6549     c7 0/subop/copy *Curr-local-stack-offset 0/imm32
 6550     #
 6551     (emit-subx-prologue %edi)
 6552     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
 6553     (emit-subx-epilogue %edi)
 6554     # TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
 6555     # been cleaned up
 6556 $emit-subx-function:end:
 6557     # . reclaim locals
 6558     81 0/subop/add %esp 408/imm32
 6559     # . restore registers
 6560     5f/pop-to-edi
 6561     5a/pop-to-edx
 6562     59/pop-to-ecx
 6563     58/pop-to-eax
 6564     # . epilogue
 6565     89/<- %esp 5/r32/ebp
 6566     5d/pop-to-ebp
 6567     c3/return
 6568 
 6569 populate-mu-type-offsets-in-inouts:  # f: (handle function)
 6570     # . prologue
 6571     55/push-ebp
 6572     89/<- %ebp 4/r32/esp
 6573     # . save registers
 6574     50/push-eax
 6575     51/push-ecx
 6576     52/push-edx
 6577     53/push-ebx
 6578     57/push-edi
 6579     # var next-offset/edx: int = 8
 6580     ba/copy-to-edx 8/imm32
 6581     # var curr/ecx: (handle list var) = f->inouts
 6582     8b/-> *(ebp+8) 1/r32/ecx
 6583     8b/-> *(ecx+8) 1/r32/ecx  # Function-inouts
 6584     {
 6585 $populate-mu-type-offsets-in-inouts:loop:
 6586       81 7/subop/compare %ecx 0/imm32
 6587       74/jump-if-= break/disp8
 6588       # var v/ebx: (handle var) = curr->value
 6589       8b/-> *ecx 3/r32/ebx  # List-value
 6590       # v->offset = next-offset
 6591       89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
 6592       # next-offset += size-of(v)
 6593       (size-of %ebx)  # => eax
 6594       01/add %edx 0/r32/eax
 6595       # curr = curr->next
 6596       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 6597       eb/jump loop/disp8
 6598     }
 6599 $populate-mu-type-offsets-in-inouts:end:
 6600     # . restore registers
 6601     5f/pop-to-edi
 6602     5b/pop-to-ebx
 6603     5a/pop-to-edx
 6604     59/pop-to-ecx
 6605     58/pop-to-eax
 6606     # . epilogue
 6607     89/<- %esp 5/r32/ebp
 6608     5d/pop-to-ebp
 6609     c3/return
 6610 
 6611 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
 6612     # . prologue
 6613     55/push-ebp
 6614     89/<- %ebp 4/r32/esp
 6615     # . save registers
 6616     50/push-eax
 6617     51/push-ecx
 6618     52/push-edx
 6619     53/push-ebx
 6620     56/push-esi
 6621     # esi = stmts
 6622     8b/-> *(ebp+0xc) 6/r32/esi
 6623     # var var-seen?/edx: boolean <- copy false
 6624     ba/copy-to-edx 0/imm32/false
 6625     #
 6626     {
 6627 $emit-subx-stmt-list:loop:
 6628       81 7/subop/compare %esi 0/imm32
 6629       0f 84/jump-if-= break/disp32
 6630       # var curr-stmt/ecx = stmts->value
 6631       8b/-> *esi 1/r32/ecx  # List-value
 6632       {
 6633 $emit-subx-stmt-list:check-for-block:
 6634         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
 6635         75/jump-if-!= break/disp8
 6636 $emit-subx-stmt-list:block:
 6637         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
 6638       }
 6639       {
 6640 $emit-subx-stmt-list:check-for-stmt:
 6641         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
 6642         0f 85/jump-if-!= break/disp32
 6643 $emit-subx-stmt-list:stmt1:
 6644         {
 6645           (is-mu-branch? %ecx)  # => eax
 6646           3d/compare-eax-and 0/imm32/false
 6647           0f 84/jump-if-= break/disp32
 6648 $emit-subx-stmt-list:branch-stmt:
 6649           # if !var-seen? break
 6650           81 7/subop/compare %edx 0/imm32/false
 6651           0f 84/jump-if-= break/disp32
 6652 $emit-subx-stmt-list:branch-stmt-and-var-seen:
 6653 +-- 26 lines: # unconditional loops -----------------------------------------------------------------------------------------------------------------------------------------------------
 6679 +-- 15 lines: # unconditional breaks ----------------------------------------------------------------------------------------------------------------------------------------------------
 6694 +-- 37 lines: # simple conditional branches without a target ----------------------------------------------------------------------------------------------------------------------------
 6731 +-- 19 lines: # conditional branches with an explicit target ----------------------------------------------------------------------------------------------------------------------------
 6750         }
 6751 $emit-subx-stmt-list:1-to-1:
 6752         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
 6753       }
 6754       {
 6755 $emit-subx-stmt-list:check-for-var-def:
 6756         81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
 6757         75/jump-if-!= break/disp8
 6758 $emit-subx-stmt-list:var-def:
 6759         (emit-subx-var-def *(ebp+8) %ecx)
 6760         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
 6761         # var-seen? = true
 6762         ba/copy-to-edx 1/imm32/true
 6763       }
 6764       {
 6765 $emit-subx-stmt-list:check-for-reg-var-def:
 6766         81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
 6767         0f 85/jump-if-!= break/disp32
 6768 $emit-subx-stmt-list:reg-var-def:
 6769         # TODO: ensure that there's exactly one output
 6770         (compute-reg-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10))
 6771         # register variable definition
 6772         (push *(ebp+0x10) %eax)
 6773         # emit the instruction as usual
 6774         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
 6775         # var-seen? = true
 6776         ba/copy-to-edx 1/imm32/true
 6777       }
 6778 $emit-subx-stmt-list:continue:
 6779       # TODO: raise an error on unrecognized Stmt-tag
 6780       8b/-> *(esi+4) 6/r32/esi  # List-next
 6781       e9/jump loop/disp32
 6782     }
 6783 $emit-subx-stmt-list:emit-cleanup:
 6784     (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
 6785 $emit-subx-stmt-list:cleanup:
 6786     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
 6787 $emit-subx-stmt-list:end:
 6788     # . restore registers
 6789     5e/pop-to-esi
 6790     5b/pop-to-ebx
 6791     5a/pop-to-edx
 6792     59/pop-to-ecx
 6793     58/pop-to-eax
 6794     # . epilogue
 6795     89/<- %esp 5/r32/ebp
 6796     5d/pop-to-ebp
 6797     c3/return
 6798 
 6799 compute-reg-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (handle reg-var-def), vars: (addr stack (handle var)) -> result/eax: (handle var)
 6800     # . prologue
 6801     55/push-ebp
 6802     89/<- %ebp 4/r32/esp
 6803     # . save registers
 6804     51/push-ecx
 6805     # ecx = stmt
 6806     8b/-> *(ebp+0xc) 1/r32/ecx
 6807     # var output/ecx: (handle var) = curr-stmt->outputs->value
 6808     8b/-> *(ecx+0xc) 1/r32/ecx  # Regvardef-inouts
 6809     8b/-> *ecx 1/r32/ecx  # List-value
 6810     # v->block-depth = *Curr-block-depth
 6811     8b/-> *Curr-block-depth 0/r32/eax
 6812     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 6813     # var reg/eax: (handle array byte) = output->register
 6814     8b/-> *(ecx+0x10) 0/r32/eax  # Var-register
 6815     # ensure that output is in a register
 6816     3d/compare-eax-and 0/imm32
 6817     0f 84/jump-if-= $compute-reg-and-maybe-emit-spill:abort/disp32
 6818     # if already-spilled-this-block?(reg, vars) return
 6819     (already-spilled-this-block? %ecx *(ebp+0x10))  # => eax
 6820     3d/compare-eax-and 0/imm32/false
 6821     75/jump-if-!= $compute-reg-and-maybe-emit-spill:end/disp8
 6822     # TODO: assert(size-of(output) == 4)
 6823     # *Curr-local-stack-offset -= 4
 6824     81 5/subop/subtract *Curr-local-stack-offset 4/imm32
 6825     # emit spill
 6826     (emit-indent *(ebp+8) *Curr-block-depth)
 6827     (write-buffered *(ebp+8) "ff 6/subop/push %")
 6828     (write-buffered *(ebp+8) *(ecx+0x10))  # Var-register
 6829     (write-buffered *(ebp+8) Newline)
 6830 $compute-reg-and-maybe-emit-spill:end:
 6831     # return output
 6832     89/<- %eax 1/r32/ecx
 6833     # . restore registers
 6834     59/pop-to-ecx
 6835     # . epilogue
 6836     89/<- %esp 5/r32/ebp
 6837     5d/pop-to-ebp
 6838     c3/return
 6839 
 6840 $compute-reg-and-maybe-emit-spill:abort:
 6841     # error("var '" var->name "' initialized from an instruction must live in a register\n")
 6842     (write-buffered Stderr "var '")
 6843     (write-buffered Stderr *eax)  # Var-name
 6844     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
 6845     (flush Stderr)
 6846     # . syscall(exit, 1)
 6847     bb/copy-to-ebx  1/imm32
 6848     b8/copy-to-eax  1/imm32/exit
 6849     cd/syscall  0x80/imm8
 6850     # never gets here
 6851 
 6852 emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
 6853     # . prologue
 6854     55/push-ebp
 6855     89/<- %ebp 4/r32/esp
 6856     # . save registers
 6857     50/push-eax
 6858     51/push-ecx
 6859     52/push-edx
 6860     # ecx = stmt
 6861     8b/-> *(ebp+0xc) 1/r32/ecx
 6862     # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
 6863     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 6864     8b/-> *edx 2/r32/edx  # Stmt-var-value
 6865     8b/-> *edx 2/r32/edx  # Var-name
 6866     # clean up until target block
 6867     (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
 6868     # emit jump to target block
 6869     (emit-indent *(ebp+8) *Curr-block-depth)
 6870     (write-buffered *(ebp+8) "e9/jump ")
 6871     (write-buffered *(ebp+8) %edx)
 6872     (string-starts-with? *(ecx+4) "break")
 6873     3d/compare-eax-and 0/imm32/false
 6874     {
 6875       74/jump-if-= break/disp8
 6876       (write-buffered *(ebp+8) ":break/disp32\n")
 6877     }
 6878     3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
 6879     {
 6880       75/jump-if-!= break/disp8
 6881       (write-buffered *(ebp+8) ":loop/disp32\n")
 6882     }
 6883 $emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
 6884     # . restore registers
 6885     5a/pop-to-edx
 6886     59/pop-to-ecx
 6887     58/pop-to-eax
 6888     # . epilogue
 6889     89/<- %esp 5/r32/ebp
 6890     5d/pop-to-ebp
 6891     c3/return
 6892 
 6893 is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
 6894     # . prologue
 6895     55/push-ebp
 6896     89/<- %ebp 4/r32/esp
 6897     # . save registers
 6898     51/push-ecx
 6899     # ecx = stmt
 6900     8b/-> *(ebp+8) 1/r32/ecx
 6901     # if (stmt->operation starts with "loop") return true
 6902     (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
 6903     3d/compare-eax-and 0/imm32/false
 6904     75/jump-if-not-equal $is-mu-branch?:end/disp8
 6905     # otherwise return (stmt->operation starts with "break")
 6906     (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
 6907 $is-mu-branch?:end:
 6908     # . restore registers
 6909     59/pop-to-ecx
 6910     # . epilogue
 6911     89/<- %esp 5/r32/ebp
 6912     5d/pop-to-ebp
 6913     c3/return
 6914 
 6915 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
 6916     # . prologue
 6917     55/push-ebp
 6918     89/<- %ebp 4/r32/esp
 6919     # . save registers
 6920     50/push-eax
 6921     # eax = stmt
 6922     8b/-> *(ebp+0xc) 0/r32/eax
 6923     #
 6924     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
 6925     (emit-indent *(ebp+8) *Curr-block-depth)
 6926     (write-buffered *(ebp+8) *eax)
 6927     (write-buffered *(ebp+8) " break/disp32\n")
 6928 $emit-reverse-break:end:
 6929     # . restore registers
 6930     58/pop-to-eax
 6931     # . epilogue
 6932     89/<- %esp 5/r32/ebp
 6933     5d/pop-to-ebp
 6934     c3/return
 6935 
 6936 == data
 6937 
 6938 Reverse-branch:  # (table string string)
 6939   # a table is a stream
 6940   0xa0/imm32/write
 6941   0/imm32/read
 6942   0xa0/imm32/length
 6943   # data
 6944   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
 6945   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
 6946   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
 6947   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
 6948   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
 6949   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
 6950   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
 6951   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
 6952   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
 6953   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
 6954   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
 6955   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
 6956   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
 6957   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
 6958   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
 6959   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
 6960   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
 6961   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
 6962   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
 6963   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
 6964 
 6965 == code
 6966 
 6967 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
 6968     # . prologue
 6969     55/push-ebp
 6970     89/<- %ebp 4/r32/esp
 6971     # . save registers
 6972     50/push-eax
 6973     51/push-ecx
 6974     52/push-edx
 6975     53/push-ebx
 6976     # ecx = vars
 6977     8b/-> *(ebp+0xc) 1/r32/ecx
 6978     # var eax: int = vars->top
 6979     8b/-> *ecx 0/r32/eax
 6980     # var min/ecx: (address (handle var)) = vars->data
 6981     81 0/subop/add %ecx 8/imm32
 6982     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 6983     81 5/subop/subtract %eax 4/imm32
 6984     8d/copy-address *(ecx+eax) 0/r32/eax
 6985     # edx = depth
 6986     8b/-> *(ebp+0x10) 2/r32/edx
 6987     {
 6988 $emit-unconditional-jump-to-depth:loop:
 6989       # if (curr < min) break
 6990       39/compare %eax 1/r32/ecx
 6991       0f 82/jump-if-addr< break/disp32
 6992       # var v/ebx: (handle var) = *curr
 6993       8b/-> *eax 3/r32/ebx
 6994       # if (v->block-depth < until-block-depth) break
 6995       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 6996       0f 8c/jump-if-< break/disp32
 6997       {
 6998 $emit-unconditional-jump-to-depth:check:
 6999         # if v->block-depth != until-block-depth, continue
 7000         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7001         0f 85/jump-if-!= break/disp32
 7002 $emit-unconditional-jump-to-depth:depth-found:
 7003         # if v is not a literal, continue
 7004         # . var eax: int = size-of(v)
 7005         50/push-eax
 7006         (size-of %ebx)  # => eax
 7007         # . if (eax != 0) continue
 7008         3d/compare-eax-and 0/imm32
 7009         58/pop-to-eax
 7010         #
 7011         0f 85/jump-if-!= break/disp32
 7012 $emit-unconditional-jump-to-depth:label-found:
 7013         # emit unconditional jump, then return
 7014         (emit-indent *(ebp+8) *Curr-block-depth)
 7015         (write-buffered *(ebp+8) "e9/jump ")
 7016         (write-buffered *(ebp+8) *ebx)  # Var-name
 7017         (write-buffered *(ebp+8) ":")
 7018         (write-buffered *(ebp+8) *(ebp+0x14))
 7019         (write-buffered *(ebp+8) "/disp32\n")
 7020         eb/jump $emit-unconditional-jump-to-depth:end/disp8
 7021       }
 7022       # curr -= 4
 7023       2d/subtract-from-eax 4/imm32
 7024       e9/jump loop/disp32
 7025     }
 7026     # TODO: error if no label at 'depth' was found
 7027 $emit-unconditional-jump-to-depth:end:
 7028     # . restore registers
 7029     5b/pop-to-ebx
 7030     5a/pop-to-edx
 7031     59/pop-to-ecx
 7032     58/pop-to-eax
 7033     # . epilogue
 7034     89/<- %esp 5/r32/ebp
 7035     5d/pop-to-ebp
 7036     c3/return
 7037 
 7038 # emit clean-up code for 'vars' until some block depth
 7039 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 7040 emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
 7041     # . prologue
 7042     55/push-ebp
 7043     89/<- %ebp 4/r32/esp
 7044     # . save registers
 7045     50/push-eax
 7046     51/push-ecx
 7047     52/push-edx
 7048     53/push-ebx
 7049     # ecx = vars
 7050     8b/-> *(ebp+0xc) 1/r32/ecx
 7051     # var eax: int = vars->top
 7052     8b/-> *ecx 0/r32/eax
 7053     # var min/ecx: (address (handle var)) = vars->data
 7054     81 0/subop/add %ecx 8/imm32
 7055     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 7056     81 5/subop/subtract %eax 4/imm32
 7057     8d/copy-address *(ecx+eax) 0/r32/eax
 7058     # edx = until-block-depth
 7059     8b/-> *(ebp+0x10) 2/r32/edx
 7060     {
 7061 $emit-cleanup-code-until-depth:loop:
 7062       # if (curr < min) break
 7063       39/compare %eax 1/r32/ecx
 7064       0f 82/jump-if-addr< break/disp32
 7065       # var v/ebx: (handle var) = *curr
 7066       8b/-> *eax 3/r32/ebx
 7067       # if (v->block-depth < until-block-depth) break
 7068       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7069       0f 8c/jump-if-< break/disp32
 7070       # if v is in a register
 7071       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 7072       {
 7073         0f 84/jump-if-= break/disp32
 7074         50/push-eax
 7075         {
 7076 $emit-cleanup-code-until-depth:check-for-previous-spill:
 7077           (same-register-spilled-before? %ebx *(ebp+0xc) %eax)  # => eax
 7078           3d/compare-eax-and 0/imm32/false
 7079           0f 85/jump-if-!= break/disp32
 7080 $emit-cleanup-code-until-depth:reclaim-var-in-register:
 7081           (emit-indent *(ebp+8) *Curr-block-depth)
 7082           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 7083           (write-buffered *(ebp+8) *(ebx+0x10))
 7084           (write-buffered *(ebp+8) Newline)
 7085         }
 7086         58/pop-to-eax
 7087         eb/jump $emit-cleanup-code-until-depth:continue/disp8
 7088       }
 7089       # otherwise v is on the stack
 7090       {
 7091         75/jump-if-!= break/disp8
 7092 $emit-cleanup-code-until-depth:reclaim-var-on-stack:
 7093         50/push-eax
 7094         (size-of %ebx)  # => eax
 7095         # don't emit code for labels
 7096         3d/compare-eax-and 0/imm32
 7097         74/jump-if-= break/disp8
 7098         #
 7099         (emit-indent *(ebp+8) *Curr-block-depth)
 7100         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 7101         (print-int32-buffered *(ebp+8) %eax)
 7102         (write-buffered *(ebp+8) "/imm32\n")
 7103         58/pop-to-eax
 7104       }
 7105 $emit-cleanup-code-until-depth:continue:
 7106       # curr -= 4
 7107       2d/subtract-from-eax 4/imm32
 7108       e9/jump loop/disp32
 7109     }
 7110 $emit-cleanup-code-until-depth:end:
 7111     # . restore registers
 7112     5b/pop-to-ebx
 7113     5a/pop-to-edx
 7114     59/pop-to-ecx
 7115     58/pop-to-eax
 7116     # . epilogue
 7117     89/<- %esp 5/r32/ebp
 7118     5d/pop-to-ebp
 7119     c3/return
 7120 
 7121 # emit clean-up code for 'vars' until a given label is encountered
 7122 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 7123 emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
 7124     # . prologue
 7125     55/push-ebp
 7126     89/<- %ebp 4/r32/esp
 7127     # . save registers
 7128     50/push-eax
 7129     51/push-ecx
 7130     52/push-edx
 7131     53/push-ebx
 7132     # ecx = vars
 7133     8b/-> *(ebp+0xc) 1/r32/ecx
 7134     # var eax: int = vars->top
 7135     8b/-> *ecx 0/r32/eax
 7136     # var min/ecx: (address (handle var)) = vars->data
 7137     81 0/subop/add %ecx 8/imm32
 7138     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 7139     81 5/subop/subtract %eax 4/imm32
 7140     8d/copy-address *(ecx+eax) 2/r32/edx
 7141     {
 7142 $emit-cleanup-code-until-target:loop:
 7143       # if (curr < min) break
 7144       39/compare %edx 1/r32/ecx
 7145       0f 82/jump-if-addr< break/disp32
 7146       # var v/ebx: (handle var) = *curr
 7147       8b/-> *edx 3/r32/ebx
 7148       # if (v->name == until-block-label) break
 7149       (string-equal? *ebx *(ebp+0x10))  # => eax
 7150       3d/compare-eax-and 0/imm32/false
 7151       0f 85/jump-if-!= break/disp32
 7152       # if v is in a register
 7153       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 7154       {
 7155         74/jump-if-= break/disp8
 7156         50/push-eax
 7157         {
 7158 $emit-cleanup-code-until-target:check-for-previous-spill:
 7159           (same-register-spilled-before? %ebx *(ebp+0xc) %edx)  # => eax
 7160           3d/compare-eax-and 0/imm32/false
 7161           75/jump-if-!= break/disp8
 7162 $emit-cleanup-code-until-target:reclaim-var-in-register:
 7163           (emit-indent *(ebp+8) *Curr-block-depth)
 7164           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 7165           (write-buffered *(ebp+8) *(ebx+0x10))
 7166           (write-buffered *(ebp+8) Newline)
 7167         }
 7168         58/pop-to-eax
 7169         eb/jump $emit-cleanup-code-until-target:continue/disp8
 7170       }
 7171       # otherwise v is on the stack
 7172       {
 7173         75/jump-if-!= break/disp8
 7174 $emit-cleanup-code-until-target:reclaim-var-on-stack:
 7175         (size-of %ebx)  # => eax
 7176         # don't emit code for labels
 7177         3d/compare-eax-and 0/imm32
 7178         74/jump-if-= break/disp8
 7179         #
 7180         (emit-indent *(ebp+8) *Curr-block-depth)
 7181         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 7182         (print-int32-buffered *(ebp+8) %eax)
 7183         (write-buffered *(ebp+8) "/imm32\n")
 7184       }
 7185 $emit-cleanup-code-until-target:continue:
 7186       # curr -= 4
 7187       81 5/subop/subtract %edx 4/imm32
 7188       e9/jump loop/disp32
 7189     }
 7190 $emit-cleanup-code-until-target:end:
 7191     # . restore registers
 7192     5b/pop-to-ebx
 7193     5a/pop-to-edx
 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 # is there already a var with the same block-depth and register as 'v' on the 'vars' stack?
 7202 # v is guaranteed not to be within vars
 7203 already-spilled-this-block?:  # v: (handle var), vars: (addr stack (handle var)) -> result/eax: boolean
 7204     # . prologue
 7205     55/push-ebp
 7206     89/<- %ebp 4/r32/esp
 7207     # . save registers
 7208     51/push-ecx
 7209     52/push-edx
 7210     53/push-ebx
 7211     56/push-esi
 7212     57/push-edi
 7213     # ecx = vars
 7214     8b/-> *(ebp+0xc) 1/r32/ecx
 7215     # var eax: int = vars->top
 7216     8b/-> *ecx 0/r32/eax
 7217     # var min/ecx: (address (handle var)) = vars->data
 7218     81 0/subop/add %ecx 8/imm32
 7219     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 7220     81 5/subop/subtract %eax 4/imm32
 7221     8d/copy-address *(ecx+eax) 2/r32/edx
 7222     # var depth/ebx: int = v->block-depth
 7223     8b/-> *(ebp+8) 3/r32/ebx
 7224     8b/-> *(ebx+8) 3/r32/ebx  # Var-block-depth
 7225     # var needle/esi: (handle array byte) = v->register
 7226     8b/-> *(ebp+8) 6/r32/esi
 7227     8b/-> *(esi+0x10) 6/r32/esi  # Var-register
 7228     {
 7229 $already-spilled-this-block?:loop:
 7230       # if (curr < min) break
 7231       39/compare %edx 1/r32/ecx
 7232       0f 82/jump-if-addr< break/disp32
 7233       # var cand/edi: (handle var) = *curr
 7234       8b/-> *edx 7/r32/edi
 7235       # if (cand->block-depth < depth) break
 7236       39/compare *(edi+8) 3/r32/ebx  # Var-block-depth
 7237       0f 8c/jump-if-< break/disp32
 7238       # var cand-reg/edi: (handle array byte) = cand->reg
 7239       8b/-> *(edi+0x10) 7/r32/edi
 7240       # if (cand-reg == null) continue
 7241       {
 7242 $already-spilled-this-block?:check-reg:
 7243         81 7/subop/compare %edi 0/imm32
 7244         74/jump-if-= break/disp8
 7245         # if (cand-reg == needle) return true
 7246         (string-equal? %esi %edi)  # => eax
 7247         3d/compare-eax-and 0/imm32/false
 7248         74/jump-if-= break/disp8
 7249         b8/copy-to-eax 1/imm32/true
 7250         eb/jump $already-spilled-this-block?:end/disp8
 7251       }
 7252 $already-spilled-this-block?:continue:
 7253       # curr -= 4
 7254       81 5/subop/subtract %edx 4/imm32
 7255       e9/jump loop/disp32
 7256     }
 7257     # return false
 7258     b8/copy-to-eax 0/imm32/false
 7259 $already-spilled-this-block?:end:
 7260     # . restore registers
 7261     5f/pop-to-edi
 7262     5e/pop-to-esi
 7263     5b/pop-to-ebx
 7264     5a/pop-to-edx
 7265     59/pop-to-ecx
 7266     # . epilogue
 7267     89/<- %esp 5/r32/ebp
 7268     5d/pop-to-ebp
 7269     c3/return
 7270 
 7271 # is there a var before 'v' with the same block-depth and register on the 'vars' stack?
 7272 # v is guaranteed to be within vars
 7273 # 'start' is provided as an optimization, a pointer within vars
 7274 # *start == v
 7275 same-register-spilled-before?:  # v: (handle var), vars: (addr stack (handle var)), start: (addr (handle var)) -> result/eax: boolean
 7276     # . prologue
 7277     55/push-ebp
 7278     89/<- %ebp 4/r32/esp
 7279     # . save registers
 7280     51/push-ecx
 7281     52/push-edx
 7282     53/push-ebx
 7283     56/push-esi
 7284     57/push-edi
 7285     # ecx = v
 7286     8b/-> *(ebp+8) 1/r32/ecx
 7287     # var reg/edx: (handle array byte) = v->register
 7288     8b/-> *(ecx+0x10) 2/r32/edx  # Var-register
 7289     # var depth/ebx: int = v->block-depth
 7290     8b/-> *(ecx+8) 3/r32/ebx  # Var-block-depth
 7291     # var min/ecx: (address (handle var)) = vars->data
 7292     8b/-> *(ebp+0xc) 1/r32/ecx
 7293     81 0/subop/add %ecx 8/imm32
 7294     # TODO: check that start >= min and start < &vars->data[top]
 7295     # TODO: check that *start == v
 7296     # var curr/esi: (address (handle var)) = start
 7297     8b/-> *(ebp+0x10) 6/r32/esi
 7298     # curr -= 4
 7299     81 5/subop/subtract %esi 4/imm32
 7300     {
 7301 $same-register-spilled-before?:loop:
 7302       # if (curr < min) break
 7303       39/compare %esi 1/r32/ecx
 7304       0f 82/jump-if-addr< break/disp32
 7305       # var x/eax: (handle var) = *curr
 7306       8b/-> *esi 0/r32/eax
 7307       # if (x->block-depth < depth) break
 7308       39/compare *(eax+8) 3/r32/ebx  # Var-block-depth
 7309       0f 8c/jump-if-< break/disp32
 7310       # if (x->register == 0) continue
 7311       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7312       74/jump-if-= $same-register-spilled-before?:continue/disp8
 7313       # if (x->register == reg) return true
 7314       (string-equal? *(eax+0x10) %edx)  # Var-register => eax
 7315       3d/compare-eax-and 0/imm32/false
 7316       75/jump-if-!= $same-register-spilled-before?:end/disp8
 7317 $same-register-spilled-before?:continue:
 7318       # curr -= 4
 7319       81 5/subop/subtract %esi 4/imm32
 7320       e9/jump loop/disp32
 7321     }
 7322 $same-register-spilled-before?:false:
 7323     b8/copy-to-eax 0/imm32/false
 7324 $same-register-spilled-before?:end:
 7325     # . restore registers
 7326     5f/pop-to-edi
 7327     5e/pop-to-esi
 7328     5b/pop-to-ebx
 7329     5a/pop-to-edx
 7330     59/pop-to-ecx
 7331     # . epilogue
 7332     89/<- %esp 5/r32/ebp
 7333     5d/pop-to-ebp
 7334     c3/return
 7335 
 7336 # clean up global state for 'vars' until some block depth
 7337 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
 7338     # . prologue
 7339     55/push-ebp
 7340     89/<- %ebp 4/r32/esp
 7341     # . save registers
 7342     50/push-eax
 7343     51/push-ecx
 7344     56/push-esi
 7345     # esi = vars
 7346     8b/-> *(ebp+8) 6/r32/esi
 7347     # ecx = until-block-depth
 7348     8b/-> *(ebp+0xc) 1/r32/ecx
 7349     {
 7350 $clean-up-blocks:reclaim-loop:
 7351       # if (vars->top <= 0) break
 7352       81 7/subop/compare *esi 0/imm32  # Stack-top
 7353       7e/jump-if-<= break/disp8
 7354       # var v/eax: (handle var) = top(vars)
 7355       (top %esi)  # => eax
 7356       # if (v->block-depth < until-block-depth) break
 7357       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
 7358       7c/jump-if-< break/disp8
 7359       # if v is on the stack, update Curr-local-stack-offset
 7360       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7361       {
 7362         75/jump-if-!= break/disp8
 7363 $clean-up-blocks:reclaim-var-on-stack:
 7364         (size-of %eax)  # => eax
 7365         01/add *Curr-local-stack-offset 0/r32/eax
 7366       }
 7367       (pop %esi)
 7368       e9/jump loop/disp32
 7369     }
 7370 $clean-up-blocks:end:
 7371     # . restore registers
 7372     5e/pop-to-esi
 7373     59/pop-to-ecx
 7374     58/pop-to-eax
 7375     # . epilogue
 7376     89/<- %esp 5/r32/ebp
 7377     5d/pop-to-ebp
 7378     c3/return
 7379 
 7380 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle stmt)
 7381     # . prologue
 7382     55/push-ebp
 7383     89/<- %ebp 4/r32/esp
 7384     # . save registers
 7385     50/push-eax
 7386     51/push-ecx
 7387     52/push-edx
 7388     # eax = stmt
 7389     8b/-> *(ebp+0xc) 0/r32/eax
 7390     # var v/ecx: (handle var)
 7391     8b/-> *(eax+4) 1/r32/ecx  # Vardef-var
 7392     # v->block-depth = *Curr-block-depth
 7393     8b/-> *Curr-block-depth 0/r32/eax
 7394     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 7395     # var n/edx: int = size-of(stmt->var)
 7396     (size-of %ecx)  # => eax
 7397     89/<- %edx 0/r32/eax
 7398     # *Curr-local-stack-offset -= n
 7399     29/subtract-from *Curr-local-stack-offset 2/r32/edx
 7400     # v->offset = *Curr-local-stack-offset
 7401     8b/-> *Curr-local-stack-offset 0/r32/eax
 7402     89/<- *(ecx+0xc) 0/r32/eax  # Var-offset
 7403     # if v is an array, do something special
 7404     {
 7405       (is-mu-array? *(ecx+4))  # Var-type => eax
 7406       3d/compare-eax-and 0/imm32/false
 7407       0f 84/jump-if-= break/disp32
 7408       # var array-size-without-length/edx: int = n-4
 7409       81 5/subop/subtract %edx 4/imm32
 7410       (emit-indent *(ebp+8) *Curr-block-depth)
 7411       (write-buffered *(ebp+8) "(push-n-zero-bytes ")
 7412       (print-int32-buffered *(ebp+8) %edx)
 7413       (write-buffered *(ebp+8) ")\n")
 7414       (emit-indent *(ebp+8) *Curr-block-depth)
 7415       (write-buffered *(ebp+8) "68/push ")
 7416       (print-int32-buffered *(ebp+8) %edx)
 7417       (write-buffered *(ebp+8) "/imm32\n")
 7418       eb/jump $emit-subx-var-def:end/disp8
 7419     }
 7420     # while n > 0
 7421     {
 7422       81 7/subop/compare %edx 0/imm32
 7423       7e/jump-if-<= break/disp8
 7424       (emit-indent *(ebp+8) *Curr-block-depth)
 7425       (write-buffered *(ebp+8) "68/push 0/imm32\n")
 7426       # n -= 4
 7427       81 5/subop/subtract %edx 4/imm32
 7428       #
 7429       eb/jump loop/disp8
 7430     }
 7431 $emit-subx-var-def:end:
 7432     # . restore registers
 7433     5a/pop-to-edx
 7434     59/pop-to-ecx
 7435     58/pop-to-eax
 7436     # . epilogue
 7437     89/<- %esp 5/r32/ebp
 7438     5d/pop-to-ebp
 7439     c3/return
 7440 
 7441 emit-subx-stmt:  # out: (addr buffered-file), stmt: (handle stmt), primitives: (handle primitive), functions: (handle function)
 7442     # . prologue
 7443     55/push-ebp
 7444     89/<- %ebp 4/r32/esp
 7445     # . save registers
 7446     50/push-eax
 7447     51/push-ecx
 7448     # - some special-case primitives that don't actually use the 'primitives' data structure
 7449     # ecx = stmt
 7450     8b/-> *(ebp+0xc) 1/r32/ecx
 7451     # array length
 7452     {
 7453       # if (!string-equal?(stmt->operation, "length")) break
 7454       (string-equal? *(ecx+4) "length")  # Stmt1-operation => eax
 7455       3d/compare-eax-and 0/imm32
 7456       0f 84/jump-if-= break/disp32
 7457       (translate-mu-length-stmt *(ebp+8) *(ebp+0xc))
 7458       e9/jump $emit-subx-stmt:end/disp32
 7459     }
 7460     # index into array
 7461     {
 7462       # if (!string-equal?(var->operation, "index")) break
 7463       (string-equal? *(ecx+4) "index")  # Stmt1-operation => eax
 7464       3d/compare-eax-and 0/imm32
 7465       0f 84/jump-if-= break/disp32
 7466       (translate-mu-index-stmt *(ebp+8) *(ebp+0xc))
 7467       e9/jump $emit-subx-stmt:end/disp32
 7468     }
 7469     # compute-offset for index into array
 7470     {
 7471       # if (!string-equal?(var->operation, "compute-offset")) break
 7472       (string-equal? *(ecx+4) "compute-offset")  # Stmt1-operation => eax
 7473       3d/compare-eax-and 0/imm32
 7474       0f 84/jump-if-= break/disp32
 7475       (translate-mu-compute-index-stmt *(ebp+8) *(ebp+0xc))
 7476       e9/jump $emit-subx-stmt:end/disp32
 7477     }
 7478     # get field from record
 7479     {
 7480       # if (!string-equal?(var->operation, "get")) break
 7481       (string-equal? *(ecx+4) "get")  # Stmt1-operation => eax
 7482       3d/compare-eax-and 0/imm32
 7483       0f 84/jump-if-= break/disp32
 7484       (translate-mu-get-stmt *(ebp+8) *(ebp+0xc))
 7485       e9/jump $emit-subx-stmt:end/disp32
 7486     }
 7487     # - if stmt matches a primitive, emit it
 7488     {
 7489 $emit-subx-stmt:check-for-primitive:
 7490       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
 7491       3d/compare-eax-and 0/imm32
 7492       74/jump-if-= break/disp8
 7493 $emit-subx-stmt:primitive:
 7494       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7495       e9/jump $emit-subx-stmt:end/disp32
 7496     }
 7497     # - if stmt matches a function, emit a call to it
 7498     {
 7499 $emit-subx-stmt:check-for-call:
 7500       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
 7501       3d/compare-eax-and 0/imm32
 7502       74/jump-if-= break/disp8
 7503 $emit-subx-stmt:call:
 7504       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7505       e9/jump $emit-subx-stmt:end/disp32
 7506     }
 7507     # otherwise, assume it's a SubX function (TODO: how to type-check?!)
 7508     (emit-hailmary-call *(ebp+8) *(ebp+0xc))
 7509 $emit-subx-stmt:end:
 7510     # . restore registers
 7511     59/pop-to-ecx
 7512     58/pop-to-eax
 7513     # . epilogue
 7514     89/<- %esp 5/r32/ebp
 7515     5d/pop-to-ebp
 7516     c3/return
 7517 
 7518 translate-mu-length-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7519     # . prologue
 7520     55/push-ebp
 7521     89/<- %ebp 4/r32/esp
 7522     # . save registers
 7523     50/push-eax
 7524     51/push-ecx
 7525     # ecx = stmt
 7526     8b/-> *(ebp+0xc) 1/r32/ecx
 7527     #
 7528     (emit-indent *(ebp+8) *Curr-block-depth)
 7529     (write-buffered *(ebp+8) "8b/copy-from *")
 7530     # var base/eax: (handle var) = inouts[0]
 7531     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 7532     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7533     # if base is an (address array ...) in a register
 7534     {
 7535       81 7/subop/compare *(eax+0x10)) 0/imm32  # Var-register
 7536       74/jump-if-= break/disp8
 7537       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 7538       eb/jump $translate-mu-length-stmt:emit-output/disp8
 7539     }
 7540     # otherwise if base is an (array ...) on the stack
 7541     {
 7542       81 7/subop/compare *(eax+0xc)) 0/imm32  # Var-offset
 7543       74/jump-if-= break/disp8
 7544       (write-buffered *(ebp+8) "(ebp+")
 7545       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-offset
 7546       (write-buffered *(ebp+8) ")")
 7547     }
 7548 $translate-mu-length-stmt:emit-output:
 7549     (write-buffered *(ebp+8) " ")
 7550     # outputs[0] "/r32"
 7551     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7552     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7553     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7554     (print-int32-buffered *(ebp+8) *eax)
 7555     (write-buffered *(ebp+8) "/r32\n")
 7556 $translate-mu-length-stmt:end:
 7557     # . restore registers
 7558     59/pop-to-ecx
 7559     58/pop-to-eax
 7560     # . epilogue
 7561     89/<- %esp 5/r32/ebp
 7562     5d/pop-to-ebp
 7563     c3/return
 7564 
 7565 translate-mu-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7566     # . prologue
 7567     55/push-ebp
 7568     89/<- %ebp 4/r32/esp
 7569     # . save registers
 7570     51/push-ecx
 7571     # var base/ecx: (handle var) = stmt->inouts[0]
 7572     8b/-> *(ebp+0xc) 1/r32/ecx
 7573     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 7574     8b/-> *ecx 1/r32/ecx  # Stmt-var-value
 7575     # if (var->register) do one thing
 7576     {
 7577       81 7/subop/compare *(ecx+0x10) 0/imm32  # Var-register
 7578       74/jump-if-= break/disp8
 7579       # TODO: ensure there's no dereference
 7580       (translate-mu-index-stmt-with-array-in-register *(ebp+8) *(ebp+0xc))
 7581       eb/jump $translate-mu-index-stmt:end/disp8
 7582     }
 7583     # if (var->offset) do a different thing
 7584     {
 7585       81 7/subop/compare *(ecx+0xc) 0/imm32  # Var-offset
 7586       74/jump-if-= break/disp8
 7587       # TODO: ensure there's no dereference
 7588       (translate-mu-index-stmt-with-array-on-stack *(ebp+8) *(ebp+0xc))
 7589       eb/jump $translate-mu-index-stmt:end/disp8
 7590     }
 7591 $translate-mu-index-stmt:end:
 7592     # . restore registers
 7593     59/pop-to-ecx
 7594     # . epilogue
 7595     89/<- %esp 5/r32/ebp
 7596     5d/pop-to-ebp
 7597     c3/return
 7598 
 7599 $translate-mu-index-stmt-with-array:error1:
 7600     (write-buffered Stderr "couldn't translate an index instruction. second (index) input must either lie in a register or be a literal\n")
 7601     (flush Stderr)
 7602     # . syscall(exit, 1)
 7603     bb/copy-to-ebx  1/imm32
 7604     b8/copy-to-eax  1/imm32/exit
 7605     cd/syscall  0x80/imm8
 7606     # never gets here
 7607 
 7608 $translate-mu-index-stmt-with-array:error2:
 7609     (write-buffered Stderr "couldn't translate an index instruction. second (index) input when in a register must be an int or offset\n")
 7610     (flush Stderr)
 7611     # . syscall(exit, 1)
 7612     bb/copy-to-ebx  1/imm32
 7613     b8/copy-to-eax  1/imm32/exit
 7614     cd/syscall  0x80/imm8
 7615     # never gets here
 7616 
 7617 translate-mu-index-stmt-with-array-in-register:  # out: (address buffered-file), stmt: (handle stmt)
 7618     # . prologue
 7619     55/push-ebp
 7620     89/<- %ebp 4/r32/esp
 7621     # . save registers
 7622     50/push-eax
 7623     51/push-ecx
 7624     52/push-edx
 7625     53/push-ebx
 7626     #
 7627     (emit-indent *(ebp+8) *Curr-block-depth)
 7628     (write-buffered *(ebp+8) "8d/copy-address *(")
 7629     # TODO: ensure inouts[0] is in a register and not dereferenced
 7630 $translate-mu-index-stmt-with-array-in-register:emit-base:
 7631     # ecx = stmt
 7632     8b/-> *(ebp+0xc) 1/r32/ecx
 7633     # var base/ebx: (handle var) = inouts[0]
 7634     8b/-> *(ecx+8) 3/r32/ebx  # Stmt1-inouts
 7635     8b/-> *ebx 3/r32/ebx  # Stmt-var-value
 7636     # print base->register " + "
 7637     (write-buffered *(ebp+8) *(ebx+0x10))  # Var-register
 7638     #
 7639     (write-buffered *(ebp+8) " + ")
 7640     # var index/edx: (handle var) = inouts[1]
 7641     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7642     8b/-> *(edx+4) 2/r32/edx  # Stmt-var-next
 7643     8b/-> *edx 2/r32/edx  # Stmt-var-value
 7644     # if index->register
 7645     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 7646     {
 7647       0f 84/jump-if-= break/disp32
 7648 $translate-mu-index-stmt-with-array-in-register:emit-register-index:
 7649       # if index is an int
 7650       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 7651       3d/compare-eax-and 0/imm32/false
 7652       {
 7653         0f 84/jump-if-= break/disp32
 7654 $translate-mu-index-stmt-with-array-in-register:emit-int-register-index:
 7655         # print index->register "<<" log2(size-of(element(base->type))) " + 4) "
 7656         # . index->register "<<"
 7657         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7658         (write-buffered *(ebp+8) "<<")
 7659         # . log2(size-of(element(base->type)))
 7660         # TODO: ensure size is a power of 2
 7661         (array-element-type-id %ebx)  # => eax
 7662         (size-of-type-id %eax)  # => eax
 7663         (num-shift-rights %eax)  # => eax
 7664         (print-int32-buffered *(ebp+8) %eax)
 7665         e9/jump $translate-mu-index-stmt-with-array-in-register:emit-register-index-done/disp32
 7666       }
 7667       # if index->type is any other atom, abort
 7668       8b/-> *(edx+4) 0/r32/eax  # Var-type
 7669       8b/-> *eax 0/r32/eax  # Tree-left or Atom-value
 7670       3b/compare 0/r32/eax *Max-type-id
 7671       0f 82/jump-if-addr< $translate-mu-index-stmt-with-array:error2/disp32
 7672       # if index has type (offset ...)
 7673       (is-simple-mu-type? %eax 7)  # offset => eax
 7674       3d/compare-eax-and 0/imm32/false
 7675       {
 7676         0f 84/jump-if-= break/disp32
 7677         # print index->register " + 4) "
 7678 $translate-mu-index-stmt-with-array-in-register:emit-offset-register-index:
 7679         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7680       }
 7681 $translate-mu-index-stmt-with-array-in-register:emit-register-index-done:
 7682       (write-buffered *(ebp+8) " + 4) ")
 7683       e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
 7684     }
 7685     # otherwise if index is a literal
 7686     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 7687     3d/compare-eax-and 0/imm32/false
 7688     {
 7689       0f 84/jump-if-= break/disp32
 7690 $translate-mu-index-stmt-with-array-in-register:emit-literal-index:
 7691       # var index-value/edx: int = parse-hex-int(index->name)
 7692       (parse-hex-int *edx)  # Var-name => eax
 7693       89/<- %edx 0/r32/eax
 7694       # offset = idx-value * size-of(element(base->type))
 7695       (array-element-type-id %ebx)  # => eax
 7696       (size-of-type-id %eax)  # => eax
 7697       f7 4/subop/multiply-into-eax %edx  # clobbers edx
 7698       # offset += 4 for array size
 7699       05/add-to-eax 4/imm32
 7700       # TODO: check edx for overflow
 7701       # print offset
 7702       (print-int32-buffered *(ebp+8) %eax)
 7703       (write-buffered *(ebp+8) ") ")
 7704       e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
 7705     }
 7706     # otherwise abort
 7707     e9/jump $translate-mu-index-stmt-with-array:error1/disp32
 7708 $translate-mu-index-stmt-with-array-in-register:emit-output:
 7709     # outputs[0] "/r32"
 7710     8b/-> *(ebp+0xc) 1/r32/ecx
 7711     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7712     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7713     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7714     (print-int32-buffered *(ebp+8) *eax)
 7715     (write-buffered *(ebp+8) "/r32\n")
 7716 $translate-mu-index-stmt-with-array-in-register:end:
 7717     # . restore registers
 7718     5b/pop-to-ebx
 7719     5a/pop-to-edx
 7720     59/pop-to-ecx
 7721     58/pop-to-eax
 7722     # . epilogue
 7723     89/<- %esp 5/r32/ebp
 7724     5d/pop-to-ebp
 7725     c3/return
 7726 
 7727 translate-mu-index-stmt-with-array-on-stack:  # out: (address buffered-file), stmt: (handle stmt)
 7728     # . prologue
 7729     55/push-ebp
 7730     89/<- %ebp 4/r32/esp
 7731     # . save registers
 7732     50/push-eax
 7733     51/push-ecx
 7734     52/push-edx
 7735     53/push-ebx
 7736     #
 7737     (emit-indent *(ebp+8) *Curr-block-depth)
 7738     (write-buffered *(ebp+8) "8d/copy-address *(ebp + ")
 7739     # var curr/eax = stmt->inouts
 7740     8b/-> *(ebp+0xc) 0/r32/eax
 7741     # var base/ecx: (handle var) = stmt->inouts[0]
 7742     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 7743     8b/-> *eax 1/r32/ecx  # Stmt-var-value
 7744     # curr = curr->next
 7745     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 7746     # var index/edx: (handle var) = stmt->inouts[1]
 7747     8b/-> *eax 2/r32/edx  # Stmt-var-value
 7748     # if index->register
 7749     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 7750     {
 7751       0f 84/jump-if-= break/disp32
 7752 $translate-mu-index-stmt-with-array-on-stack:emit-register-index:
 7753       # if index is an int
 7754       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 7755       3d/compare-eax-and 0/imm32/false
 7756       {
 7757         0f 84/jump-if-= break/disp32
 7758 $translate-mu-index-stmt-with-array-on-stack:emit-int-register-index:
 7759         # print index->register "<<" log2(size-of(element-type(base))) " + " base->offset+4
 7760         # . inouts[1]->register "<<"
 7761         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7762         (write-buffered *(ebp+8) "<<")
 7763         # . log2(size-of(element(base)))
 7764         # TODO: ensure size is a power of 2
 7765         (array-element-type-id %ecx)  # => eax
 7766         (size-of-type-id %eax)  # => eax
 7767         (num-shift-rights %eax)  # => eax
 7768         (print-int32-buffered *(ebp+8) %eax)
 7769         #
 7770         (write-buffered *(ebp+8) " + ")
 7771         #
 7772         8b/-> *(ecx+0xc) 0/r32/eax  # Var-offset
 7773         05/add-to-eax 4/imm32
 7774         (print-int32-buffered *(ebp+8) %eax)
 7775         e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done/disp32
 7776       }
 7777       # if index->type is any other atom, abort
 7778       8b/-> *(edx+4) 0/r32/eax  # Var-type
 7779       8b/-> *eax 0/r32/eax  # Tree-left or Atom-value
 7780       3b/compare 0/r32/eax *Max-type-id
 7781       0f 82/jump-if-addr< $translate-mu-index-stmt-with-array:error2/disp32
 7782       # if index has type (offset ...)
 7783       (is-simple-mu-type? %eax 7)  # offset => eax
 7784       3d/compare-eax-and 0/imm32/false
 7785       {
 7786         0f 84/jump-if-= break/disp32
 7787         # print index->register
 7788 $translate-mu-index-stmt-with-array-on-stack:emit-offset-register-index:
 7789         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7790       }
 7791 $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done:
 7792       (write-buffered *(ebp+8) ") ")
 7793       e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
 7794     }
 7795     # otherwise if index is a literal
 7796     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 7797     3d/compare-eax-and 0/imm32/false
 7798     {
 7799       0f 84/jump-if-= break/disp32
 7800 $translate-mu-index-stmt-with-array-on-stack:emit-literal-index:
 7801       # var idx-value/edx: int = parse-hex-int(index->name)
 7802       (parse-hex-int *edx)  # Var-name => eax
 7803       89/<- %ebx 0/r32/eax
 7804       # offset = idx-value * size-of(element-type(base->type))
 7805       (array-element-type-id %ecx)  # => eax
 7806       (size-of-type-id %eax)  # => eax
 7807       f7 4/subop/multiply-into-eax %ebx  # clobbers edx
 7808       # offset += base->offset
 7809       03/add-to 0/r32/eax *(ecx+0xc)  # Var-offset
 7810       # offset += 4 for array size
 7811       05/add-to-eax 4/imm32
 7812       # TODO: check edx for overflow
 7813       # print offset
 7814       (print-int32-buffered *(ebp+8) %eax)
 7815       (write-buffered *(ebp+8) ") ")
 7816       e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
 7817     }
 7818     # otherwise abort
 7819     e9/jump $translate-mu-index-stmt-with-array:error1/disp32
 7820 $translate-mu-index-stmt-with-array-on-stack:emit-output:
 7821     # outputs[0] "/r32"
 7822     8b/-> *(ebp+0xc) 0/r32/eax
 7823     8b/-> *(eax+0xc) 0/r32/eax  # Stmt1-outputs
 7824     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7825     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7826     (print-int32-buffered *(ebp+8) *eax)
 7827     (write-buffered *(ebp+8) "/r32\n")
 7828 $translate-mu-index-stmt-with-array-on-stack:end:
 7829     # . restore registers
 7830     5b/pop-to-ebx
 7831     5a/pop-to-edx
 7832     59/pop-to-ecx
 7833     58/pop-to-eax
 7834     # . epilogue
 7835     89/<- %esp 5/r32/ebp
 7836     5d/pop-to-ebp
 7837     c3/return
 7838 
 7839 translate-mu-compute-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7840     # . prologue
 7841     55/push-ebp
 7842     89/<- %ebp 4/r32/esp
 7843     # . save registers
 7844     50/push-eax
 7845     51/push-ecx
 7846     52/push-edx
 7847     53/push-ebx
 7848     #
 7849     (emit-indent *(ebp+8) *Curr-block-depth)
 7850     (write-buffered *(ebp+8) "69/multiply ")
 7851 $translate-mu-compute-index-stmt:emit-elem-size:
 7852     # ecx = stmt
 7853     8b/-> *(ebp+0xc) 1/r32/ecx
 7854     # var first-inout/edx: (handle stmt-var) = stmt->inouts[0]
 7855     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7856     # var base/ebx: (handle var)
 7857     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 7858     # print size-of(element(base->type))
 7859     (array-element-type-id %ebx)  # => eax
 7860     (size-of-type-id %eax)  # => eax
 7861     (print-int32-buffered *(ebp+8) %eax)
 7862     (write-buffered *(ebp+8) "/imm32")
 7863 $translate-mu-compute-index-stmt:emit-index:
 7864     (emit-subx-var-as-rm32 *(ebp+8) *(edx+4))  # Stmt-var-next
 7865     (write-buffered *(ebp+8) Space)
 7866 $translate-mu-compute-index-stmt:emit-output:
 7867     # outputs[0] "/r32"
 7868     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7869     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7870     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7871     (print-int32-buffered *(ebp+8) *eax)
 7872     (write-buffered *(ebp+8) "/r32\n")
 7873 $translate-mu-compute-index-stmt:end:
 7874     # . restore registers
 7875     5b/pop-to-ebx
 7876     5a/pop-to-edx
 7877     59/pop-to-ecx
 7878     58/pop-to-eax
 7879     # . epilogue
 7880     89/<- %esp 5/r32/ebp
 7881     5d/pop-to-ebp
 7882     c3/return
 7883 
 7884 translate-mu-get-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7885     # . prologue
 7886     55/push-ebp
 7887     89/<- %ebp 4/r32/esp
 7888     # . save registers
 7889     50/push-eax
 7890     51/push-ecx
 7891     52/push-edx
 7892     #
 7893     (emit-indent *(ebp+8) *Curr-block-depth)
 7894     (write-buffered *(ebp+8) "8d/copy-address ")
 7895     # ecx = stmt
 7896     8b/-> *(ebp+0xc) 1/r32/ecx
 7897     # var offset/edx: int = get offset of stmt
 7898     (mu-get-offset %ecx)  # => eax
 7899     89/<- %edx 0/r32/eax
 7900     # var base/eax: (handle var) = stmt->inouts[0]
 7901     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
 7902     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7903     # if base is in a register
 7904     81 7/subop/compare *(eax+0x10) 0/imm32
 7905     {
 7906       0f 84/jump-if-= break/disp32
 7907 $translate-mu-get-stmt:emit-register-input:
 7908       # "*(" inouts[0]->register " + " offset ")"
 7909       (write-buffered *(ebp+8) "*(")
 7910       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 7911       (write-buffered *(ebp+8) " + ")
 7912       (print-int32-buffered *(ebp+8) %edx)
 7913       (write-buffered *(ebp+8) ") ")
 7914       e9/jump $translate-mu-get-stmt:emit-output/disp32
 7915     }
 7916     # otherwise base is on the stack
 7917     {
 7918 $translate-mu-get-stmt:emit-stack-input:
 7919       # "*(ebp + " inouts[0]->offset + offset ")"
 7920       (write-buffered *(ebp+8) "*(ebp+")
 7921       03/add-from *(eax+0xc) 2/r32/edx  # Var-offset
 7922       (print-int32-buffered *(ebp+8) %edx)
 7923       (write-buffered *(ebp+8) ") ")
 7924       eb/jump $translate-mu-get-stmt:emit-output/disp8
 7925     }
 7926 $translate-mu-get-stmt:emit-output:
 7927     # outputs[0] "/r32"
 7928     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7929     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7930     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7931     (print-int32-buffered *(ebp+8) *eax)
 7932     (write-buffered *(ebp+8) "/r32\n")
 7933 $translate-mu-get-stmt:end:
 7934     # . restore registers
 7935     5a/pop-to-edx
 7936     59/pop-to-ecx
 7937     58/pop-to-eax
 7938     # . epilogue
 7939     89/<- %esp 5/r32/ebp
 7940     5d/pop-to-ebp
 7941     c3/return
 7942 
 7943 array-element-type-id:  # v: (handle var) -> result/eax: type-id
 7944     # precondition: n is positive
 7945     # . prologue
 7946     55/push-ebp
 7947     89/<- %ebp 4/r32/esp
 7948     #
 7949     8b/-> *(ebp+8) 0/r32/eax
 7950     8b/-> *(eax+4) 0/r32/eax  # Var-type
 7951     # TODO: ensure type->left is 'addr'
 7952     8b/-> *(eax+4) 0/r32/eax  # Tree-right
 7953     # TODO: ensure that type->right is non-null
 7954     # TODO: ensure that type->right->left is 'array'
 7955     8b/-> *(eax+4) 0/r32/eax  # Tree-right
 7956     # TODO: ensure that type->right->right is non-null
 7957     8b/-> *eax 0/r32/eax  # Tree-left
 7958     8b/-> *eax 0/r32/eax  # Atom-value
 7959 $array-element-type-id:end:
 7960     # . epilogue
 7961     89/<- %esp 5/r32/ebp
 7962     5d/pop-to-ebp
 7963     c3/return
 7964 
 7965 power-of-2?:  # n: int -> result/eax: boolean
 7966     # precondition: n is positive
 7967     # . prologue
 7968     55/push-ebp
 7969     89/<- %ebp 4/r32/esp
 7970     # var tmp/eax: int = n-1
 7971     8b/-> *(ebp+8) 0/r32/eax
 7972     48/decrement-eax
 7973     # var tmp2/eax: int = n & tmp
 7974     0b/and-> *(ebp+8) 0/r32/eax
 7975     # return (tmp2 == 0)
 7976     3d/compare-eax-and 0/imm32
 7977     0f 94/set-byte-if-= %al
 7978     81 4/subop/and %eax 0xff/imm32
 7979 $power-of-2?:end:
 7980     # . epilogue
 7981     89/<- %esp 5/r32/ebp
 7982     5d/pop-to-ebp
 7983     c3/return
 7984 
 7985 num-shift-rights:  # n: int -> result/eax: int
 7986     # precondition: n is a positive power of 2
 7987     # . prologue
 7988     55/push-ebp
 7989     89/<- %ebp 4/r32/esp
 7990     # . save registers
 7991     51/push-ecx
 7992     # var curr/ecx: int = n
 7993     8b/-> *(ebp+8) 1/r32/ecx
 7994     # result = 0
 7995     b8/copy-to-eax 0/imm32
 7996     {
 7997       # if (curr <= 1) break
 7998       81 7/subop/compare %ecx 1/imm32
 7999       7e/jump-if-<= break/disp8
 8000       40/increment-eax
 8001       c1/shift 5/subop/arithmetic-right %ecx 1/imm8
 8002       eb/jump loop/disp8
 8003     }
 8004 $num-shift-rights:end:
 8005     # . restore registers
 8006     59/pop-to-ecx
 8007     # . epilogue
 8008     89/<- %esp 5/r32/ebp
 8009     5d/pop-to-ebp
 8010     c3/return
 8011 
 8012 mu-get-offset:  # stmt: (handle stmt) -> result/eax: int
 8013     # . prologue
 8014     55/push-ebp
 8015     89/<- %ebp 4/r32/esp
 8016     # var second-inout/eax: (handle stmt-var) = stmt->inouts->next
 8017     8b/-> *(ebp+8) 0/r32/eax
 8018     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 8019     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 8020     # var output-var/eax: (handle var) = second-inout->value
 8021     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8022     # return output-var->offset
 8023     8b/-> *(eax+0xc) 0/r32/eax  # Var-offset
 8024 $emit-get-offset:end:
 8025     # . epilogue
 8026     89/<- %esp 5/r32/ebp
 8027     5d/pop-to-ebp
 8028     c3/return
 8029 
 8030 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
 8031     # . prologue
 8032     55/push-ebp
 8033     89/<- %ebp 4/r32/esp
 8034     # . save registers
 8035     50/push-eax
 8036     51/push-ecx
 8037     56/push-esi
 8038     # esi = block
 8039     8b/-> *(ebp+0xc) 6/r32/esi
 8040     # block->var->block-depth = *Curr-block-depth
 8041     8b/-> *(esi+8) 0/r32/eax  # Block-var
 8042     8b/-> *Curr-block-depth 1/r32/ecx
 8043     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
 8044     # var stmts/eax: (handle list stmt) = block->statements
 8045     8b/-> *(esi+4) 0/r32/eax  # Block-stmts
 8046     #
 8047     {
 8048 $emit-subx-block:check-empty:
 8049       3d/compare-eax-and 0/imm32
 8050       0f 84/jump-if-= break/disp32
 8051       (emit-indent *(ebp+8) *Curr-block-depth)
 8052       (write-buffered *(ebp+8) "{\n")
 8053       # var v/ecx: (handle var)
 8054       8b/-> *(esi+8) 1/r32/ecx  # Block-var
 8055       #
 8056       (write-buffered *(ebp+8) *ecx)  # Var-name
 8057       (write-buffered *(ebp+8) ":loop:\n")
 8058       ff 0/subop/increment *Curr-block-depth
 8059       (push *(ebp+0x10) %ecx)
 8060       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
 8061       (pop *(ebp+0x10))  # => eax
 8062       ff 1/subop/decrement *Curr-block-depth
 8063       (emit-indent *(ebp+8) *Curr-block-depth)
 8064       (write-buffered *(ebp+8) "}\n")
 8065       (write-buffered *(ebp+8) *ecx)  # Var-name
 8066       (write-buffered *(ebp+8) ":break:\n")
 8067     }
 8068 $emit-subx-block:end:
 8069     # . restore registers
 8070     5e/pop-to-esi
 8071     59/pop-to-ecx
 8072     58/pop-to-eax
 8073     # . epilogue
 8074     89/<- %esp 5/r32/ebp
 8075     5d/pop-to-ebp
 8076     c3/return
 8077 
 8078 # Primitives supported
 8079 # For each operation, put variants with hard-coded registers before flexible ones.
 8080 == data
 8081 Primitives:
 8082 # - increment/decrement
 8083 _Primitive-inc-eax:
 8084     # var/eax <- increment => 40/increment-eax
 8085     "increment"/imm32/name
 8086     0/imm32/no-inouts
 8087     Single-int-var-in-eax/imm32/outputs
 8088     "40/increment-eax"/imm32/subx-name
 8089     0/imm32/no-rm32
 8090     0/imm32/no-r32
 8091     0/imm32/no-imm32
 8092     0/imm32/no-disp32
 8093     0/imm32/output-is-write-only
 8094     _Primitive-inc-ecx/imm32/next
 8095 _Primitive-inc-ecx:
 8096     # var/ecx <- increment => 41/increment-ecx
 8097     "increment"/imm32/name
 8098     0/imm32/no-inouts
 8099     Single-int-var-in-ecx/imm32/outputs
 8100     "41/increment-ecx"/imm32/subx-name
 8101     0/imm32/no-rm32
 8102     0/imm32/no-r32
 8103     0/imm32/no-imm32
 8104     0/imm32/no-disp32
 8105     0/imm32/output-is-write-only
 8106     _Primitive-inc-edx/imm32/next
 8107 _Primitive-inc-edx:
 8108     # var/edx <- increment => 42/increment-edx
 8109     "increment"/imm32/name
 8110     0/imm32/no-inouts
 8111     Single-int-var-in-edx/imm32/outputs
 8112     "42/increment-edx"/imm32/subx-name
 8113     0/imm32/no-rm32
 8114     0/imm32/no-r32
 8115     0/imm32/no-imm32
 8116     0/imm32/no-disp32
 8117     0/imm32/output-is-write-only
 8118     _Primitive-inc-ebx/imm32/next
 8119 _Primitive-inc-ebx:
 8120     # var/ebx <- increment => 43/increment-ebx
 8121     "increment"/imm32/name
 8122     0/imm32/no-inouts
 8123     Single-int-var-in-ebx/imm32/outputs
 8124     "43/increment-ebx"/imm32/subx-name
 8125     0/imm32/no-rm32
 8126     0/imm32/no-r32
 8127     0/imm32/no-imm32
 8128     0/imm32/no-disp32
 8129     0/imm32/output-is-write-only
 8130     _Primitive-inc-esi/imm32/next
 8131 _Primitive-inc-esi:
 8132     # var/esi <- increment => 46/increment-esi
 8133     "increment"/imm32/name
 8134     0/imm32/no-inouts
 8135     Single-int-var-in-esi/imm32/outputs
 8136     "46/increment-esi"/imm32/subx-name
 8137     0/imm32/no-rm32
 8138     0/imm32/no-r32
 8139     0/imm32/no-imm32
 8140     0/imm32/no-disp32
 8141     0/imm32/output-is-write-only
 8142     _Primitive-inc-edi/imm32/next
 8143 _Primitive-inc-edi:
 8144     # var/edi <- increment => 47/increment-edi
 8145     "increment"/imm32/name
 8146     0/imm32/no-inouts
 8147     Single-int-var-in-edi/imm32/outputs
 8148     "47/increment-edi"/imm32/subx-name
 8149     0/imm32/no-rm32
 8150     0/imm32/no-r32
 8151     0/imm32/no-imm32
 8152     0/imm32/no-disp32
 8153     0/imm32/output-is-write-only
 8154     _Primitive-dec-eax/imm32/next
 8155 _Primitive-dec-eax:
 8156     # var/eax <- decrement => 48/decrement-eax
 8157     "decrement"/imm32/name
 8158     0/imm32/no-inouts
 8159     Single-int-var-in-eax/imm32/outputs
 8160     "48/decrement-eax"/imm32/subx-name
 8161     0/imm32/no-rm32
 8162     0/imm32/no-r32
 8163     0/imm32/no-imm32
 8164     0/imm32/no-disp32
 8165     0/imm32/output-is-write-only
 8166     _Primitive-dec-ecx/imm32/next
 8167 _Primitive-dec-ecx:
 8168     # var/ecx <- decrement => 49/decrement-ecx
 8169     "decrement"/imm32/name
 8170     0/imm32/no-inouts
 8171     Single-int-var-in-ecx/imm32/outputs
 8172     "49/decrement-ecx"/imm32/subx-name
 8173     0/imm32/no-rm32
 8174     0/imm32/no-r32
 8175     0/imm32/no-imm32
 8176     0/imm32/no-disp32
 8177     0/imm32/output-is-write-only
 8178     _Primitive-dec-edx/imm32/next
 8179 _Primitive-dec-edx:
 8180     # var/edx <- decrement => 4a/decrement-edx
 8181     "decrement"/imm32/name
 8182     0/imm32/no-inouts
 8183     Single-int-var-in-edx/imm32/outputs
 8184     "4a/decrement-edx"/imm32/subx-name
 8185     0/imm32/no-rm32
 8186     0/imm32/no-r32
 8187     0/imm32/no-imm32
 8188     0/imm32/no-disp32
 8189     0/imm32/output-is-write-only
 8190     _Primitive-dec-ebx/imm32/next
 8191 _Primitive-dec-ebx:
 8192     # var/ebx <- decrement => 4b/decrement-ebx
 8193     "decrement"/imm32/name
 8194     0/imm32/no-inouts
 8195     Single-int-var-in-ebx/imm32/outputs
 8196     "4b/decrement-ebx"/imm32/subx-name
 8197     0/imm32/no-rm32
 8198     0/imm32/no-r32
 8199     0/imm32/no-imm32
 8200     0/imm32/no-disp32
 8201     0/imm32/output-is-write-only
 8202     _Primitive-dec-esi/imm32/next
 8203 _Primitive-dec-esi:
 8204     # var/esi <- decrement => 4e/decrement-esi
 8205     "decrement"/imm32/name
 8206     0/imm32/no-inouts
 8207     Single-int-var-in-esi/imm32/outputs
 8208     "4e/decrement-esi"/imm32/subx-name
 8209     0/imm32/no-rm32
 8210     0/imm32/no-r32
 8211     0/imm32/no-imm32
 8212     0/imm32/no-disp32
 8213     0/imm32/output-is-write-only
 8214     _Primitive-dec-edi/imm32/next
 8215 _Primitive-dec-edi:
 8216     # var/edi <- decrement => 4f/decrement-edi
 8217     "decrement"/imm32/name
 8218     0/imm32/no-inouts
 8219     Single-int-var-in-edi/imm32/outputs
 8220     "4f/decrement-edi"/imm32/subx-name
 8221     0/imm32/no-rm32
 8222     0/imm32/no-r32
 8223     0/imm32/no-imm32
 8224     0/imm32/no-disp32
 8225     0/imm32/output-is-write-only
 8226     _Primitive-inc-mem/imm32/next
 8227 _Primitive-inc-mem:
 8228     # increment var => ff 0/subop/increment *(ebp+__)
 8229     "increment"/imm32/name
 8230     Single-int-var-in-mem/imm32/inouts
 8231     0/imm32/no-outputs
 8232     "ff 0/subop/increment"/imm32/subx-name
 8233     1/imm32/rm32-is-first-inout
 8234     0/imm32/no-r32
 8235     0/imm32/no-imm32
 8236     0/imm32/no-disp32
 8237     0/imm32/output-is-write-only
 8238     _Primitive-inc-reg/imm32/next
 8239 _Primitive-inc-reg:
 8240     # var/reg <- increment => ff 0/subop/increment %__
 8241     "increment"/imm32/name
 8242     0/imm32/no-inouts
 8243     Single-int-var-in-some-register/imm32/outputs
 8244     "ff 0/subop/increment"/imm32/subx-name
 8245     3/imm32/rm32-is-first-output
 8246     0/imm32/no-r32
 8247     0/imm32/no-imm32
 8248     0/imm32/no-disp32
 8249     0/imm32/output-is-write-only
 8250     _Primitive-dec-mem/imm32/next
 8251 _Primitive-dec-mem:
 8252     # decrement var => ff 1/subop/decrement *(ebp+__)
 8253     "decrement"/imm32/name
 8254     Single-int-var-in-mem/imm32/inouts
 8255     0/imm32/no-outputs
 8256     "ff 1/subop/decrement"/imm32/subx-name
 8257     1/imm32/rm32-is-first-inout
 8258     0/imm32/no-r32
 8259     0/imm32/no-imm32
 8260     0/imm32/no-disp32
 8261     0/imm32/output-is-write-only
 8262     _Primitive-dec-reg/imm32/next
 8263 _Primitive-dec-reg:
 8264     # var/reg <- decrement => ff 1/subop/decrement %__
 8265     "decrement"/imm32/name
 8266     0/imm32/no-inouts
 8267     Single-int-var-in-some-register/imm32/outputs
 8268     "ff 1/subop/decrement"/imm32/subx-name
 8269     3/imm32/rm32-is-first-output
 8270     0/imm32/no-r32
 8271     0/imm32/no-imm32
 8272     0/imm32/no-disp32
 8273     0/imm32/output-is-write-only
 8274     _Primitive-add-to-eax/imm32/next
 8275 # - add
 8276 _Primitive-add-to-eax:
 8277     # var/eax <- add lit => 05/add-to-eax lit/imm32
 8278     "add"/imm32/name
 8279     Single-lit-var/imm32/inouts
 8280     Single-int-var-in-eax/imm32/outputs
 8281     "05/add-to-eax"/imm32/subx-name
 8282     0/imm32/no-rm32
 8283     0/imm32/no-r32
 8284     1/imm32/imm32-is-first-inout
 8285     0/imm32/no-disp32
 8286     0/imm32/output-is-write-only
 8287     _Primitive-add-reg-to-reg/imm32/next
 8288 _Primitive-add-reg-to-reg:
 8289     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
 8290     "add"/imm32/name
 8291     Single-int-var-in-some-register/imm32/inouts
 8292     Single-int-var-in-some-register/imm32/outputs
 8293     "01/add-to"/imm32/subx-name
 8294     3/imm32/rm32-is-first-output
 8295     1/imm32/r32-is-first-inout
 8296     0/imm32/no-imm32
 8297     0/imm32/no-disp32
 8298     0/imm32/output-is-write-only
 8299     _Primitive-add-reg-to-mem/imm32/next
 8300 _Primitive-add-reg-to-mem:
 8301     # add-to var1 var2/reg => 01/add-to var1 var2/r32
 8302     "add-to"/imm32/name
 8303     Two-args-int-stack-int-reg/imm32/inouts
 8304     0/imm32/outputs
 8305     "01/add-to"/imm32/subx-name
 8306     1/imm32/rm32-is-first-inout
 8307     2/imm32/r32-is-second-inout
 8308     0/imm32/no-imm32
 8309     0/imm32/no-disp32
 8310     0/imm32/output-is-write-only
 8311     _Primitive-add-mem-to-reg/imm32/next
 8312 _Primitive-add-mem-to-reg:
 8313     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
 8314     "add"/imm32/name
 8315     Single-int-var-in-mem/imm32/inouts
 8316     Single-int-var-in-some-register/imm32/outputs
 8317     "03/add"/imm32/subx-name
 8318     1/imm32/rm32-is-first-inout
 8319     3/imm32/r32-is-first-output
 8320     0/imm32/no-imm32
 8321     0/imm32/no-disp32
 8322     0/imm32/output-is-write-only
 8323     _Primitive-add-lit-to-reg/imm32/next
 8324 _Primitive-add-lit-to-reg:
 8325     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
 8326     "add"/imm32/name
 8327     Single-lit-var/imm32/inouts
 8328     Single-int-var-in-some-register/imm32/outputs
 8329     "81 0/subop/add"/imm32/subx-name
 8330     3/imm32/rm32-is-first-output
 8331     0/imm32/no-r32
 8332     1/imm32/imm32-is-first-inout
 8333     0/imm32/no-disp32
 8334     0/imm32/output-is-write-only
 8335     _Primitive-add-lit-to-mem/imm32/next
 8336 _Primitive-add-lit-to-mem:
 8337     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
 8338     "add-to"/imm32/name
 8339     Int-var-and-literal/imm32/inouts
 8340     0/imm32/outputs
 8341     "81 0/subop/add"/imm32/subx-name
 8342     1/imm32/rm32-is-first-inout
 8343     0/imm32/no-r32
 8344     2/imm32/imm32-is-second-inout
 8345     0/imm32/no-disp32
 8346     0/imm32/output-is-write-only
 8347     _Primitive-subtract-from-eax/imm32/next
 8348 # - subtract
 8349 _Primitive-subtract-from-eax:
 8350     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
 8351     "subtract"/imm32/name
 8352     Single-lit-var/imm32/inouts
 8353     Single-int-var-in-eax/imm32/outputs
 8354     "2d/subtract-from-eax"/imm32/subx-name
 8355     0/imm32/no-rm32
 8356     0/imm32/no-r32
 8357     1/imm32/imm32-is-first-inout
 8358     0/imm32/no-disp32
 8359     0/imm32/output-is-write-only
 8360     _Primitive-subtract-reg-from-reg/imm32/next
 8361 _Primitive-subtract-reg-from-reg:
 8362     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
 8363     "subtract"/imm32/name
 8364     Single-int-var-in-some-register/imm32/inouts
 8365     Single-int-var-in-some-register/imm32/outputs
 8366     "29/subtract-from"/imm32/subx-name
 8367     3/imm32/rm32-is-first-output
 8368     1/imm32/r32-is-first-inout
 8369     0/imm32/no-imm32
 8370     0/imm32/no-disp32
 8371     0/imm32/output-is-write-only
 8372     _Primitive-subtract-reg-from-mem/imm32/next
 8373 _Primitive-subtract-reg-from-mem:
 8374     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
 8375     "subtract-from"/imm32/name
 8376     Two-args-int-stack-int-reg/imm32/inouts
 8377     0/imm32/outputs
 8378     "29/subtract-from"/imm32/subx-name
 8379     1/imm32/rm32-is-first-inout
 8380     2/imm32/r32-is-second-inout
 8381     0/imm32/no-imm32
 8382     0/imm32/no-disp32
 8383     0/imm32/output-is-write-only
 8384     _Primitive-subtract-mem-from-reg/imm32/next
 8385 _Primitive-subtract-mem-from-reg:
 8386     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
 8387     "subtract"/imm32/name
 8388     Single-int-var-in-mem/imm32/inouts
 8389     Single-int-var-in-some-register/imm32/outputs
 8390     "2b/subtract"/imm32/subx-name
 8391     1/imm32/rm32-is-first-inout
 8392     3/imm32/r32-is-first-output
 8393     0/imm32/no-imm32
 8394     0/imm32/no-disp32
 8395     0/imm32/output-is-write-only
 8396     _Primitive-subtract-lit-from-reg/imm32/next
 8397 _Primitive-subtract-lit-from-reg:
 8398     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
 8399     "subtract"/imm32/name
 8400     Single-lit-var/imm32/inouts
 8401     Single-int-var-in-some-register/imm32/outputs
 8402     "81 5/subop/subtract"/imm32/subx-name
 8403     3/imm32/rm32-is-first-output
 8404     0/imm32/no-r32
 8405     1/imm32/imm32-is-first-inout
 8406     0/imm32/no-disp32
 8407     0/imm32/output-is-write-only
 8408     _Primitive-subtract-lit-from-mem/imm32/next
 8409 _Primitive-subtract-lit-from-mem:
 8410     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
 8411     "subtract-from"/imm32/name
 8412     Int-var-and-literal/imm32/inouts
 8413     0/imm32/outputs
 8414     "81 5/subop/subtract"/imm32/subx-name
 8415     1/imm32/rm32-is-first-inout
 8416     0/imm32/no-r32
 8417     2/imm32/imm32-is-first-inout
 8418     0/imm32/no-disp32
 8419     0/imm32/output-is-write-only
 8420     _Primitive-and-with-eax/imm32/next
 8421 # - and
 8422 _Primitive-and-with-eax:
 8423     # var/eax <- and lit => 25/and-with-eax lit/imm32
 8424     "and"/imm32/name
 8425     Single-lit-var/imm32/inouts
 8426     Single-int-var-in-eax/imm32/outputs
 8427     "25/and-with-eax"/imm32/subx-name
 8428     0/imm32/no-rm32
 8429     0/imm32/no-r32
 8430     1/imm32/imm32-is-first-inout
 8431     0/imm32/no-disp32
 8432     0/imm32/output-is-write-only
 8433     _Primitive-and-reg-with-reg/imm32/next
 8434 _Primitive-and-reg-with-reg:
 8435     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
 8436     "and"/imm32/name
 8437     Single-int-var-in-some-register/imm32/inouts
 8438     Single-int-var-in-some-register/imm32/outputs
 8439     "21/and-with"/imm32/subx-name
 8440     3/imm32/rm32-is-first-output
 8441     1/imm32/r32-is-first-inout
 8442     0/imm32/no-imm32
 8443     0/imm32/no-disp32
 8444     0/imm32/output-is-write-only
 8445     _Primitive-and-reg-with-mem/imm32/next
 8446 _Primitive-and-reg-with-mem:
 8447     # and-with var1 var2/reg => 21/and-with var1 var2/r32
 8448     "and-with"/imm32/name
 8449     Two-args-int-stack-int-reg/imm32/inouts
 8450     0/imm32/outputs
 8451     "21/and-with"/imm32/subx-name
 8452     1/imm32/rm32-is-first-inout
 8453     2/imm32/r32-is-second-inout
 8454     0/imm32/no-imm32
 8455     0/imm32/no-disp32
 8456     0/imm32/output-is-write-only
 8457     _Primitive-and-mem-with-reg/imm32/next
 8458 _Primitive-and-mem-with-reg:
 8459     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
 8460     "and"/imm32/name
 8461     Single-int-var-in-mem/imm32/inouts
 8462     Single-int-var-in-some-register/imm32/outputs
 8463     "23/and"/imm32/subx-name
 8464     1/imm32/rm32-is-first-inout
 8465     3/imm32/r32-is-first-output
 8466     0/imm32/no-imm32
 8467     0/imm32/no-disp32
 8468     0/imm32/output-is-write-only
 8469     _Primitive-and-lit-with-reg/imm32/next
 8470 _Primitive-and-lit-with-reg:
 8471     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
 8472     "and"/imm32/name
 8473     Single-lit-var/imm32/inouts
 8474     Single-int-var-in-some-register/imm32/outputs
 8475     "81 4/subop/and"/imm32/subx-name
 8476     3/imm32/rm32-is-first-output
 8477     0/imm32/no-r32
 8478     1/imm32/imm32-is-first-inout
 8479     0/imm32/no-disp32
 8480     0/imm32/output-is-write-only
 8481     _Primitive-and-lit-with-mem/imm32/next
 8482 _Primitive-and-lit-with-mem:
 8483     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
 8484     "and-with"/imm32/name
 8485     Int-var-and-literal/imm32/inouts
 8486     0/imm32/outputs
 8487     "81 4/subop/and"/imm32/subx-name
 8488     1/imm32/rm32-is-first-inout
 8489     0/imm32/no-r32
 8490     2/imm32/imm32-is-first-inout
 8491     0/imm32/no-disp32
 8492     0/imm32/output-is-write-only
 8493     _Primitive-or-with-eax/imm32/next
 8494 # - or
 8495 _Primitive-or-with-eax:
 8496     # var/eax <- or lit => 0d/or-with-eax lit/imm32
 8497     "or"/imm32/name
 8498     Single-lit-var/imm32/inouts
 8499     Single-int-var-in-eax/imm32/outputs
 8500     "0d/or-with-eax"/imm32/subx-name
 8501     0/imm32/no-rm32
 8502     0/imm32/no-r32
 8503     1/imm32/imm32-is-first-inout
 8504     0/imm32/no-disp32
 8505     0/imm32/output-is-write-only
 8506     _Primitive-or-reg-with-reg/imm32/next
 8507 _Primitive-or-reg-with-reg:
 8508     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
 8509     "or"/imm32/name
 8510     Single-int-var-in-some-register/imm32/inouts
 8511     Single-int-var-in-some-register/imm32/outputs
 8512     "09/or-with"/imm32/subx-name
 8513     3/imm32/rm32-is-first-output
 8514     1/imm32/r32-is-first-inout
 8515     0/imm32/no-imm32
 8516     0/imm32/no-disp32
 8517     0/imm32/output-is-write-only
 8518     _Primitive-or-reg-with-mem/imm32/next
 8519 _Primitive-or-reg-with-mem:
 8520     # or-with var1 var2/reg => 09/or-with var1 var2/r32
 8521     "or-with"/imm32/name
 8522     Two-args-int-stack-int-reg/imm32/inouts
 8523     0/imm32/outputs
 8524     "09/or-with"/imm32/subx-name
 8525     1/imm32/rm32-is-first-inout
 8526     2/imm32/r32-is-second-inout
 8527     0/imm32/no-imm32
 8528     0/imm32/no-disp32
 8529     0/imm32/output-is-write-only
 8530     _Primitive-or-mem-with-reg/imm32/next
 8531 _Primitive-or-mem-with-reg:
 8532     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
 8533     "or"/imm32/name
 8534     Single-int-var-in-mem/imm32/inouts
 8535     Single-int-var-in-some-register/imm32/outputs
 8536     "0b/or"/imm32/subx-name
 8537     1/imm32/rm32-is-first-inout
 8538     3/imm32/r32-is-first-output
 8539     0/imm32/no-imm32
 8540     0/imm32/no-disp32
 8541     0/imm32/output-is-write-only
 8542     _Primitive-or-lit-with-reg/imm32/next
 8543 _Primitive-or-lit-with-reg:
 8544     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
 8545     "or"/imm32/name
 8546     Single-lit-var/imm32/inouts
 8547     Single-int-var-in-some-register/imm32/outputs
 8548     "81 1/subop/or"/imm32/subx-name
 8549     3/imm32/rm32-is-first-output
 8550     0/imm32/no-r32
 8551     1/imm32/imm32-is-first-inout
 8552     0/imm32/no-disp32
 8553     0/imm32/output-is-write-only
 8554     _Primitive-or-lit-with-mem/imm32/next
 8555 _Primitive-or-lit-with-mem:
 8556     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
 8557     "or-with"/imm32/name
 8558     Int-var-and-literal/imm32/inouts
 8559     0/imm32/outputs
 8560     "81 1/subop/or"/imm32/subx-name
 8561     1/imm32/rm32-is-first-inout
 8562     0/imm32/no-r32
 8563     2/imm32/imm32-is-second-inout
 8564     0/imm32/no-disp32
 8565     0/imm32/output-is-write-only
 8566     _Primitive-xor-with-eax/imm32/next
 8567 # - xor
 8568 _Primitive-xor-with-eax:
 8569     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
 8570     "xor"/imm32/name
 8571     Single-lit-var/imm32/inouts
 8572     Single-int-var-in-eax/imm32/outputs
 8573     "35/xor-with-eax"/imm32/subx-name
 8574     0/imm32/no-rm32
 8575     0/imm32/no-r32
 8576     1/imm32/imm32-is-first-inout
 8577     0/imm32/no-disp32
 8578     0/imm32/output-is-write-only
 8579     _Primitive-xor-reg-with-reg/imm32/next
 8580 _Primitive-xor-reg-with-reg:
 8581     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
 8582     "xor"/imm32/name
 8583     Single-int-var-in-some-register/imm32/inouts
 8584     Single-int-var-in-some-register/imm32/outputs
 8585     "31/xor-with"/imm32/subx-name
 8586     3/imm32/rm32-is-first-output
 8587     1/imm32/r32-is-first-inout
 8588     0/imm32/no-imm32
 8589     0/imm32/no-disp32
 8590     0/imm32/output-is-write-only
 8591     _Primitive-xor-reg-with-mem/imm32/next
 8592 _Primitive-xor-reg-with-mem:
 8593     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
 8594     "xor-with"/imm32/name
 8595     Two-args-int-stack-int-reg/imm32/inouts
 8596     0/imm32/outputs
 8597     "31/xor-with"/imm32/subx-name
 8598     1/imm32/rm32-is-first-inout
 8599     2/imm32/r32-is-second-inout
 8600     0/imm32/no-imm32
 8601     0/imm32/no-disp32
 8602     0/imm32/output-is-write-only
 8603     _Primitive-xor-mem-with-reg/imm32/next
 8604 _Primitive-xor-mem-with-reg:
 8605     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
 8606     "xor"/imm32/name
 8607     Single-int-var-in-mem/imm32/inouts
 8608     Single-int-var-in-some-register/imm32/outputs
 8609     "33/xor"/imm32/subx-name
 8610     1/imm32/rm32-is-first-inout
 8611     3/imm32/r32-is-first-output
 8612     0/imm32/no-imm32
 8613     0/imm32/no-disp32
 8614     0/imm32/output-is-write-only
 8615     _Primitive-xor-lit-with-reg/imm32/next
 8616 _Primitive-xor-lit-with-reg:
 8617     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
 8618     "xor"/imm32/name
 8619     Single-lit-var/imm32/inouts
 8620     Single-int-var-in-some-register/imm32/outputs
 8621     "81 6/subop/xor"/imm32/subx-name
 8622     3/imm32/rm32-is-first-output
 8623     0/imm32/no-r32
 8624     1/imm32/imm32-is-first-inout
 8625     0/imm32/no-disp32
 8626     0/imm32/output-is-write-only
 8627     _Primitive-xor-lit-with-mem/imm32/next
 8628 _Primitive-xor-lit-with-mem:
 8629     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
 8630     "xor-with"/imm32/name
 8631     Int-var-and-literal/imm32/inouts
 8632     0/imm32/outputs
 8633     "81 6/subop/xor"/imm32/subx-name
 8634     1/imm32/rm32-is-first-inout
 8635     0/imm32/no-r32
 8636     2/imm32/imm32-is-first-inout
 8637     0/imm32/no-disp32
 8638     0/imm32/output-is-write-only
 8639     _Primitive-copy-to-eax/imm32/next
 8640 # - copy
 8641 _Primitive-copy-to-eax:
 8642     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
 8643     "copy"/imm32/name
 8644     Single-lit-var/imm32/inouts
 8645     Single-int-var-in-eax/imm32/outputs
 8646     "b8/copy-to-eax"/imm32/subx-name
 8647     0/imm32/no-rm32
 8648     0/imm32/no-r32
 8649     1/imm32/imm32-is-first-inout
 8650     0/imm32/no-disp32
 8651     1/imm32/output-is-write-only
 8652     _Primitive-copy-to-ecx/imm32/next
 8653 _Primitive-copy-to-ecx:
 8654     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
 8655     "copy"/imm32/name
 8656     Single-lit-var/imm32/inouts
 8657     Single-int-var-in-ecx/imm32/outputs
 8658     "b9/copy-to-ecx"/imm32/subx-name
 8659     0/imm32/no-rm32
 8660     0/imm32/no-r32
 8661     1/imm32/imm32-is-first-inout
 8662     0/imm32/no-disp32
 8663     1/imm32/output-is-write-only
 8664     _Primitive-copy-to-edx/imm32/next
 8665 _Primitive-copy-to-edx:
 8666     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
 8667     "copy"/imm32/name
 8668     Single-lit-var/imm32/inouts
 8669     Single-int-var-in-edx/imm32/outputs
 8670     "ba/copy-to-edx"/imm32/subx-name
 8671     0/imm32/no-rm32
 8672     0/imm32/no-r32
 8673     1/imm32/imm32-is-first-inout
 8674     0/imm32/no-disp32
 8675     1/imm32/output-is-write-only
 8676     _Primitive-copy-to-ebx/imm32/next
 8677 _Primitive-copy-to-ebx:
 8678     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
 8679     "copy"/imm32/name
 8680     Single-lit-var/imm32/inouts
 8681     Single-int-var-in-ebx/imm32/outputs
 8682     "bb/copy-to-ebx"/imm32/subx-name
 8683     0/imm32/no-rm32
 8684     0/imm32/no-r32
 8685     1/imm32/imm32-is-first-inout
 8686     0/imm32/no-disp32
 8687     1/imm32/output-is-write-only
 8688     _Primitive-copy-to-esi/imm32/next
 8689 _Primitive-copy-to-esi:
 8690     # var/esi <- copy lit => be/copy-to-esi lit/imm32
 8691     "copy"/imm32/name
 8692     Single-lit-var/imm32/inouts
 8693     Single-int-var-in-esi/imm32/outputs
 8694     "be/copy-to-esi"/imm32/subx-name
 8695     0/imm32/no-rm32
 8696     0/imm32/no-r32
 8697     1/imm32/imm32-is-first-inout
 8698     0/imm32/no-disp32
 8699     1/imm32/output-is-write-only
 8700     _Primitive-copy-to-edi/imm32/next
 8701 _Primitive-copy-to-edi:
 8702     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
 8703     "copy"/imm32/name
 8704     Single-lit-var/imm32/inouts
 8705     Single-int-var-in-edi/imm32/outputs
 8706     "bf/copy-to-edi"/imm32/subx-name
 8707     0/imm32/no-rm32
 8708     0/imm32/no-r32
 8709     1/imm32/imm32-is-first-inout
 8710     0/imm32/no-disp32
 8711     1/imm32/output-is-write-only
 8712     _Primitive-copy-reg-to-reg/imm32/next
 8713 _Primitive-copy-reg-to-reg:
 8714     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
 8715     "copy"/imm32/name
 8716     Single-int-var-in-some-register/imm32/inouts
 8717     Single-int-var-in-some-register/imm32/outputs
 8718     "89/copy-to"/imm32/subx-name
 8719     3/imm32/rm32-is-first-output
 8720     1/imm32/r32-is-first-inout
 8721     0/imm32/no-imm32
 8722     0/imm32/no-disp32
 8723     1/imm32/output-is-write-only
 8724     _Primitive-copy-reg-to-mem/imm32/next
 8725 _Primitive-copy-reg-to-mem:
 8726     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
 8727     "copy-to"/imm32/name
 8728     Two-args-int-stack-int-reg/imm32/inouts
 8729     0/imm32/outputs
 8730     "89/copy-to"/imm32/subx-name
 8731     1/imm32/rm32-is-first-inout
 8732     2/imm32/r32-is-second-inout
 8733     0/imm32/no-imm32
 8734     0/imm32/no-disp32
 8735     1/imm32/output-is-write-only
 8736     _Primitive-copy-mem-to-reg/imm32/next
 8737 _Primitive-copy-mem-to-reg:
 8738     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
 8739     "copy"/imm32/name
 8740     Single-int-var-in-mem/imm32/inouts
 8741     Single-int-var-in-some-register/imm32/outputs
 8742     "8b/copy-from"/imm32/subx-name
 8743     1/imm32/rm32-is-first-inout
 8744     3/imm32/r32-is-first-output
 8745     0/imm32/no-imm32
 8746     0/imm32/no-disp32
 8747     1/imm32/output-is-write-only
 8748     _Primitive-copy-lit-to-reg/imm32/next
 8749 _Primitive-copy-lit-to-reg:
 8750     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
 8751     "copy"/imm32/name
 8752     Single-lit-var/imm32/inouts
 8753     Single-int-var-in-some-register/imm32/outputs
 8754     "c7 0/subop/copy"/imm32/subx-name
 8755     3/imm32/rm32-is-first-output
 8756     0/imm32/no-r32
 8757     1/imm32/imm32-is-first-inout
 8758     0/imm32/no-disp32
 8759     1/imm32/output-is-write-only
 8760     _Primitive-copy-lit-to-mem/imm32/next
 8761 _Primitive-copy-lit-to-mem:
 8762     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
 8763     "copy-to"/imm32/name
 8764     Int-var-and-literal/imm32/inouts
 8765     0/imm32/outputs
 8766     "c7 0/subop/copy"/imm32/subx-name
 8767     1/imm32/rm32-is-first-inout
 8768     0/imm32/no-r32
 8769     2/imm32/imm32-is-first-inout
 8770     0/imm32/no-disp32
 8771     1/imm32/output-is-write-only
 8772     _Primitive-compare-mem-with-reg/imm32/next
 8773 # - compare
 8774 _Primitive-compare-mem-with-reg:
 8775     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
 8776     "compare"/imm32/name
 8777     Two-args-int-stack-int-reg/imm32/inouts
 8778     0/imm32/outputs
 8779     "39/compare->"/imm32/subx-name
 8780     1/imm32/rm32-is-first-inout
 8781     2/imm32/r32-is-second-inout
 8782     0/imm32/no-imm32
 8783     0/imm32/no-disp32
 8784     0/imm32/output-is-write-only
 8785     _Primitive-compare-reg-with-mem/imm32/next
 8786 _Primitive-compare-reg-with-mem:
 8787     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
 8788     "compare"/imm32/name
 8789     Two-args-int-reg-int-stack/imm32/inouts
 8790     0/imm32/outputs
 8791     "3b/compare<-"/imm32/subx-name
 8792     2/imm32/rm32-is-second-inout
 8793     1/imm32/r32-is-first-inout
 8794     0/imm32/no-imm32
 8795     0/imm32/no-disp32
 8796     0/imm32/output-is-write-only
 8797     _Primitive-compare-eax-with-literal/imm32/next
 8798 _Primitive-compare-eax-with-literal:
 8799     # compare var1/eax n => 3d/compare-eax-with n/imm32
 8800     "compare"/imm32/name
 8801     Two-args-int-eax-int-literal/imm32/inouts
 8802     0/imm32/outputs
 8803     "3d/compare-eax-with"/imm32/subx-name
 8804     0/imm32/no-rm32
 8805     0/imm32/no-r32
 8806     2/imm32/imm32-is-second-inout
 8807     0/imm32/no-disp32
 8808     0/imm32/output-is-write-only
 8809     _Primitive-compare-reg-with-literal/imm32/next
 8810 _Primitive-compare-reg-with-literal:
 8811     # compare var1/reg n => 81 7/subop/compare %reg n/imm32
 8812     "compare"/imm32/name
 8813     Int-var-in-register-and-literal/imm32/inouts
 8814     0/imm32/outputs
 8815     "81 7/subop/compare"/imm32/subx-name
 8816     1/imm32/rm32-is-first-inout
 8817     0/imm32/no-r32
 8818     2/imm32/imm32-is-second-inout
 8819     0/imm32/no-disp32
 8820     0/imm32/output-is-write-only
 8821     _Primitive-compare-mem-with-literal/imm32/next
 8822 _Primitive-compare-mem-with-literal:
 8823     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
 8824     "compare"/imm32/name
 8825     Int-var-and-literal/imm32/inouts
 8826     0/imm32/outputs
 8827     "81 7/subop/compare"/imm32/subx-name
 8828     1/imm32/rm32-is-first-inout
 8829     0/imm32/no-r32
 8830     2/imm32/imm32-is-second-inout
 8831     0/imm32/no-disp32
 8832     0/imm32/output-is-write-only
 8833     _Primitive-multiply-reg-by-mem/imm32/next
 8834 # - multiply
 8835 _Primitive-multiply-reg-by-mem:
 8836     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
 8837     "multiply"/imm32/name
 8838     Single-int-var-in-mem/imm32/inouts
 8839     Single-int-var-in-some-register/imm32/outputs
 8840     "0f af/multiply"/imm32/subx-name
 8841     1/imm32/rm32-is-first-inout
 8842     3/imm32/r32-is-first-output
 8843     0/imm32/no-imm32
 8844     0/imm32/no-disp32
 8845     0/imm32/output-is-write-only
 8846     _Primitive-break-if-addr</imm32/next
 8847 # - branches
 8848 _Primitive-break-if-addr<:
 8849     "break-if-addr<"/imm32/name
 8850     0/imm32/inouts
 8851     0/imm32/outputs
 8852     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
 8853     0/imm32/no-rm32
 8854     0/imm32/no-r32
 8855     0/imm32/no-imm32
 8856     0/imm32/no-disp32
 8857     0/imm32/no-output
 8858     _Primitive-break-if-addr>=/imm32/next
 8859 _Primitive-break-if-addr>=:
 8860     "break-if-addr>="/imm32/name
 8861     0/imm32/inouts
 8862     0/imm32/outputs
 8863     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
 8864     0/imm32/no-rm32
 8865     0/imm32/no-r32
 8866     0/imm32/no-imm32
 8867     0/imm32/no-disp32
 8868     0/imm32/no-output
 8869     _Primitive-break-if-=/imm32/next
 8870 _Primitive-break-if-=:
 8871     "break-if-="/imm32/name
 8872     0/imm32/inouts
 8873     0/imm32/outputs
 8874     "0f 84/jump-if-= break/disp32"/imm32/subx-name
 8875     0/imm32/no-rm32
 8876     0/imm32/no-r32
 8877     0/imm32/no-imm32
 8878     0/imm32/no-disp32
 8879     0/imm32/no-output
 8880     _Primitive-break-if-!=/imm32/next
 8881 _Primitive-break-if-!=:
 8882     "break-if-!="/imm32/name
 8883     0/imm32/inouts
 8884     0/imm32/outputs
 8885     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
 8886     0/imm32/no-rm32
 8887     0/imm32/no-r32
 8888     0/imm32/no-imm32
 8889     0/imm32/no-disp32
 8890     0/imm32/no-output
 8891     _Primitive-break-if-addr<=/imm32/next
 8892 _Primitive-break-if-addr<=:
 8893     "break-if-addr<="/imm32/name
 8894     0/imm32/inouts
 8895     0/imm32/outputs
 8896     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
 8897     0/imm32/no-rm32
 8898     0/imm32/no-r32
 8899     0/imm32/no-imm32
 8900     0/imm32/no-disp32
 8901     0/imm32/no-output
 8902     _Primitive-break-if-addr>/imm32/next
 8903 _Primitive-break-if-addr>:
 8904     "break-if-addr>"/imm32/name
 8905     0/imm32/inouts
 8906     0/imm32/outputs
 8907     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
 8908     0/imm32/no-rm32
 8909     0/imm32/no-r32
 8910     0/imm32/no-imm32
 8911     0/imm32/no-disp32
 8912     0/imm32/no-output
 8913     _Primitive-break-if-</imm32/next
 8914 _Primitive-break-if-<:
 8915     "break-if-<"/imm32/name
 8916     0/imm32/inouts
 8917     0/imm32/outputs
 8918     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
 8919     0/imm32/no-rm32
 8920     0/imm32/no-r32
 8921     0/imm32/no-imm32
 8922     0/imm32/no-disp32
 8923     0/imm32/no-output
 8924     _Primitive-break-if->=/imm32/next
 8925 _Primitive-break-if->=:
 8926     "break-if->="/imm32/name
 8927     0/imm32/inouts
 8928     0/imm32/outputs
 8929     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
 8930     0/imm32/no-rm32
 8931     0/imm32/no-r32
 8932     0/imm32/no-imm32
 8933     0/imm32/no-disp32
 8934     0/imm32/no-output
 8935     _Primitive-break-if-<=/imm32/next
 8936 _Primitive-break-if-<=:
 8937     "break-if-<="/imm32/name
 8938     0/imm32/inouts
 8939     0/imm32/outputs
 8940     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
 8941     0/imm32/no-rm32
 8942     0/imm32/no-r32
 8943     0/imm32/no-imm32
 8944     0/imm32/no-disp32
 8945     0/imm32/no-output
 8946     _Primitive-break-if->/imm32/next
 8947 _Primitive-break-if->:
 8948     "break-if->"/imm32/name
 8949     0/imm32/inouts
 8950     0/imm32/outputs
 8951     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
 8952     0/imm32/no-rm32
 8953     0/imm32/no-r32
 8954     0/imm32/no-imm32
 8955     0/imm32/no-disp32
 8956     0/imm32/no-output
 8957     _Primitive-break/imm32/next
 8958 _Primitive-break:
 8959     "break"/imm32/name
 8960     0/imm32/inouts
 8961     0/imm32/outputs
 8962     "e9/jump break/disp32"/imm32/subx-name
 8963     0/imm32/no-rm32
 8964     0/imm32/no-r32
 8965     0/imm32/no-imm32
 8966     0/imm32/no-disp32
 8967     0/imm32/no-output
 8968     _Primitive-loop-if-addr</imm32/next
 8969 _Primitive-loop-if-addr<:
 8970     "loop-if-addr<"/imm32/name
 8971     0/imm32/inouts
 8972     0/imm32/outputs
 8973     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
 8974     0/imm32/no-rm32
 8975     0/imm32/no-r32
 8976     0/imm32/no-imm32
 8977     0/imm32/no-disp32
 8978     0/imm32/no-output
 8979     _Primitive-loop-if-addr>=/imm32/next
 8980 _Primitive-loop-if-addr>=:
 8981     "loop-if-addr>="/imm32/name
 8982     0/imm32/inouts
 8983     0/imm32/outputs
 8984     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
 8985     0/imm32/no-rm32
 8986     0/imm32/no-r32
 8987     0/imm32/no-imm32
 8988     0/imm32/no-disp32
 8989     0/imm32/no-output
 8990     _Primitive-loop-if-=/imm32/next
 8991 _Primitive-loop-if-=:
 8992     "loop-if-="/imm32/name
 8993     0/imm32/inouts
 8994     0/imm32/outputs
 8995     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
 8996     0/imm32/no-rm32
 8997     0/imm32/no-r32
 8998     0/imm32/no-imm32
 8999     0/imm32/no-disp32
 9000     0/imm32/no-output
 9001     _Primitive-loop-if-!=/imm32/next
 9002 _Primitive-loop-if-!=:
 9003     "loop-if-!="/imm32/name
 9004     0/imm32/inouts
 9005     0/imm32/outputs
 9006     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
 9007     0/imm32/no-rm32
 9008     0/imm32/no-r32
 9009     0/imm32/no-imm32
 9010     0/imm32/no-disp32
 9011     0/imm32/no-output
 9012     _Primitive-loop-if-addr<=/imm32/next
 9013 _Primitive-loop-if-addr<=:
 9014     "loop-if-addr<="/imm32/name
 9015     0/imm32/inouts
 9016     0/imm32/outputs
 9017     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
 9018     0/imm32/no-rm32
 9019     0/imm32/no-r32
 9020     0/imm32/no-imm32
 9021     0/imm32/no-disp32
 9022     0/imm32/no-output
 9023     _Primitive-loop-if-addr>/imm32/next
 9024 _Primitive-loop-if-addr>:
 9025     "loop-if-addr>"/imm32/name
 9026     0/imm32/inouts
 9027     0/imm32/outputs
 9028     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
 9029     0/imm32/no-rm32
 9030     0/imm32/no-r32
 9031     0/imm32/no-imm32
 9032     0/imm32/no-disp32
 9033     0/imm32/no-output
 9034     _Primitive-loop-if-</imm32/next
 9035 _Primitive-loop-if-<:
 9036     "loop-if-<"/imm32/name
 9037     0/imm32/inouts
 9038     0/imm32/outputs
 9039     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
 9040     0/imm32/no-rm32
 9041     0/imm32/no-r32
 9042     0/imm32/no-imm32
 9043     0/imm32/no-disp32
 9044     0/imm32/no-output
 9045     _Primitive-loop-if->=/imm32/next
 9046 _Primitive-loop-if->=:
 9047     "loop-if->="/imm32/name
 9048     0/imm32/inouts
 9049     0/imm32/outputs
 9050     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
 9051     0/imm32/no-rm32
 9052     0/imm32/no-r32
 9053     0/imm32/no-imm32
 9054     0/imm32/no-disp32
 9055     0/imm32/no-output
 9056     _Primitive-loop-if-<=/imm32/next
 9057 _Primitive-loop-if-<=:
 9058     "loop-if-<="/imm32/name
 9059     0/imm32/inouts
 9060     0/imm32/outputs
 9061     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
 9062     0/imm32/no-rm32
 9063     0/imm32/no-r32
 9064     0/imm32/no-imm32
 9065     0/imm32/no-disp32
 9066     0/imm32/no-output
 9067     _Primitive-loop-if->/imm32/next
 9068 _Primitive-loop-if->:
 9069     "loop-if->"/imm32/name
 9070     0/imm32/inouts
 9071     0/imm32/outputs
 9072     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
 9073     0/imm32/no-rm32
 9074     0/imm32/no-r32
 9075     0/imm32/no-imm32
 9076     0/imm32/no-disp32
 9077     0/imm32/no-output
 9078     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
 9079 _Primitive-loop:
 9080     "loop"/imm32/name
 9081     0/imm32/inouts
 9082     0/imm32/outputs
 9083     "e9/jump loop/disp32"/imm32/subx-name
 9084     0/imm32/no-rm32
 9085     0/imm32/no-r32
 9086     0/imm32/no-imm32
 9087     0/imm32/no-disp32
 9088     0/imm32/no-output
 9089     _Primitive-break-if-addr<-named/imm32/next
 9090 # - branches to named blocks
 9091 _Primitive-break-if-addr<-named:
 9092     "break-if-addr<"/imm32/name
 9093     Single-lit-var/imm32/inouts
 9094     0/imm32/outputs
 9095     "0f 82/jump-if-addr<"/imm32/subx-name
 9096     0/imm32/no-rm32
 9097     0/imm32/no-r32
 9098     0/imm32/no-imm32
 9099     1/imm32/disp32-is-first-inout
 9100     0/imm32/no-output
 9101     _Primitive-break-if-addr>=-named/imm32/next
 9102 _Primitive-break-if-addr>=-named:
 9103     "break-if-addr>="/imm32/name
 9104     Single-lit-var/imm32/inouts
 9105     0/imm32/outputs
 9106     "0f 83/jump-if-addr>="/imm32/subx-name
 9107     0/imm32/no-rm32
 9108     0/imm32/no-r32
 9109     0/imm32/no-imm32
 9110     1/imm32/disp32-is-first-inout
 9111     0/imm32/no-output
 9112     _Primitive-break-if-=-named/imm32/next
 9113 _Primitive-break-if-=-named:
 9114     "break-if-="/imm32/name
 9115     Single-lit-var/imm32/inouts
 9116     0/imm32/outputs
 9117     "0f 84/jump-if-="/imm32/subx-name
 9118     0/imm32/no-rm32
 9119     0/imm32/no-r32
 9120     0/imm32/no-imm32
 9121     1/imm32/disp32-is-first-inout
 9122     0/imm32/no-output
 9123     _Primitive-break-if-!=-named/imm32/next
 9124 _Primitive-break-if-!=-named:
 9125     "break-if-!="/imm32/name
 9126     Single-lit-var/imm32/inouts
 9127     0/imm32/outputs
 9128     "0f 85/jump-if-!="/imm32/subx-name
 9129     0/imm32/no-rm32
 9130     0/imm32/no-r32
 9131     0/imm32/no-imm32
 9132     1/imm32/disp32-is-first-inout
 9133     0/imm32/no-output
 9134     _Primitive-break-if-addr<=-named/imm32/next
 9135 _Primitive-break-if-addr<=-named:
 9136     "break-if-addr<="/imm32/name
 9137     Single-lit-var/imm32/inouts
 9138     0/imm32/outputs
 9139     "0f 86/jump-if-addr<="/imm32/subx-name
 9140     0/imm32/no-rm32
 9141     0/imm32/no-r32
 9142     0/imm32/no-imm32
 9143     1/imm32/disp32-is-first-inout
 9144     0/imm32/no-output
 9145     _Primitive-break-if-addr>-named/imm32/next
 9146 _Primitive-break-if-addr>-named:
 9147     "break-if-addr>"/imm32/name
 9148     Single-lit-var/imm32/inouts
 9149     0/imm32/outputs
 9150     "0f 87/jump-if-addr>"/imm32/subx-name
 9151     0/imm32/no-rm32
 9152     0/imm32/no-r32
 9153     0/imm32/no-imm32
 9154     1/imm32/disp32-is-first-inout
 9155     0/imm32/no-output
 9156     _Primitive-break-if-<-named/imm32/next
 9157 _Primitive-break-if-<-named:
 9158     "break-if-<"/imm32/name
 9159     Single-lit-var/imm32/inouts
 9160     0/imm32/outputs
 9161     "0f 8c/jump-if-<"/imm32/subx-name
 9162     0/imm32/no-rm32
 9163     0/imm32/no-r32
 9164     0/imm32/no-imm32
 9165     1/imm32/disp32-is-first-inout
 9166     0/imm32/no-output
 9167     _Primitive-break-if->=-named/imm32/next
 9168 _Primitive-break-if->=-named:
 9169     "break-if->="/imm32/name
 9170     Single-lit-var/imm32/inouts
 9171     0/imm32/outputs
 9172     "0f 8d/jump-if->="/imm32/subx-name
 9173     0/imm32/no-rm32
 9174     0/imm32/no-r32
 9175     0/imm32/no-imm32
 9176     1/imm32/disp32-is-first-inout
 9177     0/imm32/no-output
 9178     _Primitive-break-if-<=-named/imm32/next
 9179 _Primitive-break-if-<=-named:
 9180     "break-if-<="/imm32/name
 9181     Single-lit-var/imm32/inouts
 9182     0/imm32/outputs
 9183     "0f 8e/jump-if-<="/imm32/subx-name
 9184     0/imm32/no-rm32
 9185     0/imm32/no-r32
 9186     0/imm32/no-imm32
 9187     1/imm32/disp32-is-first-inout
 9188     0/imm32/no-output
 9189     _Primitive-break-if->-named/imm32/next
 9190 _Primitive-break-if->-named:
 9191     "break-if->"/imm32/name
 9192     Single-lit-var/imm32/inouts
 9193     0/imm32/outputs
 9194     "0f 8f/jump-if->"/imm32/subx-name
 9195     0/imm32/no-rm32
 9196     0/imm32/no-r32
 9197     0/imm32/no-imm32
 9198     1/imm32/disp32-is-first-inout
 9199     0/imm32/no-output
 9200     _Primitive-break-named/imm32/next
 9201 _Primitive-break-named:
 9202     "break"/imm32/name
 9203     Single-lit-var/imm32/inouts
 9204     0/imm32/outputs
 9205     "e9/jump"/imm32/subx-name
 9206     0/imm32/no-rm32
 9207     0/imm32/no-r32
 9208     0/imm32/no-imm32
 9209     1/imm32/disp32-is-first-inout
 9210     0/imm32/no-output
 9211     _Primitive-loop-if-addr<-named/imm32/next
 9212 _Primitive-loop-if-addr<-named:
 9213     "loop-if-addr<"/imm32/name
 9214     Single-lit-var/imm32/inouts
 9215     0/imm32/outputs
 9216     "0f 82/jump-if-addr<"/imm32/subx-name
 9217     0/imm32/no-rm32
 9218     0/imm32/no-r32
 9219     0/imm32/no-imm32
 9220     1/imm32/disp32-is-first-inout
 9221     0/imm32/no-output
 9222     _Primitive-loop-if-addr>=-named/imm32/next
 9223 _Primitive-loop-if-addr>=-named:
 9224     "loop-if-addr>="/imm32/name
 9225     Single-lit-var/imm32/inouts
 9226     0/imm32/outputs
 9227     "0f 83/jump-if-addr>="/imm32/subx-name
 9228     0/imm32/no-rm32
 9229     0/imm32/no-r32
 9230     0/imm32/no-imm32
 9231     1/imm32/disp32-is-first-inout
 9232     0/imm32/no-output
 9233     _Primitive-loop-if-=-named/imm32/next
 9234 _Primitive-loop-if-=-named:
 9235     "loop-if-="/imm32/name
 9236     Single-lit-var/imm32/inouts
 9237     0/imm32/outputs
 9238     "0f 84/jump-if-="/imm32/subx-name
 9239     0/imm32/no-rm32
 9240     0/imm32/no-r32
 9241     0/imm32/no-imm32
 9242     1/imm32/disp32-is-first-inout
 9243     0/imm32/no-output
 9244     _Primitive-loop-if-!=-named/imm32/next
 9245 _Primitive-loop-if-!=-named:
 9246     "loop-if-!="/imm32/name
 9247     Single-lit-var/imm32/inouts
 9248     0/imm32/outputs
 9249     "0f 85/jump-if-!="/imm32/subx-name
 9250     0/imm32/no-rm32
 9251     0/imm32/no-r32
 9252     0/imm32/no-imm32
 9253     1/imm32/disp32-is-first-inout
 9254     0/imm32/no-output
 9255     _Primitive-loop-if-addr<=-named/imm32/next
 9256 _Primitive-loop-if-addr<=-named:
 9257     "loop-if-addr<="/imm32/name
 9258     Single-lit-var/imm32/inouts
 9259     0/imm32/outputs
 9260     "0f 86/jump-if-addr<="/imm32/subx-name
 9261     0/imm32/no-rm32
 9262     0/imm32/no-r32
 9263     0/imm32/no-imm32
 9264     1/imm32/disp32-is-first-inout
 9265     0/imm32/no-output
 9266     _Primitive-loop-if-addr>-named/imm32/next
 9267 _Primitive-loop-if-addr>-named:
 9268     "loop-if-addr>"/imm32/name
 9269     Single-lit-var/imm32/inouts
 9270     0/imm32/outputs
 9271     "0f 87/jump-if-addr>"/imm32/subx-name
 9272     0/imm32/no-rm32
 9273     0/imm32/no-r32
 9274     0/imm32/no-imm32
 9275     1/imm32/disp32-is-first-inout
 9276     0/imm32/no-output
 9277     _Primitive-loop-if-<-named/imm32/next
 9278 _Primitive-loop-if-<-named:
 9279     "loop-if-<"/imm32/name
 9280     Single-lit-var/imm32/inouts
 9281     0/imm32/outputs
 9282     "0f 8c/jump-if-<"/imm32/subx-name
 9283     0/imm32/no-rm32
 9284     0/imm32/no-r32
 9285     0/imm32/no-imm32
 9286     1/imm32/disp32-is-first-inout
 9287     0/imm32/no-output
 9288     _Primitive-loop-if->=-named/imm32/next
 9289 _Primitive-loop-if->=-named:
 9290     "loop-if->="/imm32/name
 9291     Single-lit-var/imm32/inouts
 9292     0/imm32/outputs
 9293     "0f 8d/jump-if->="/imm32/subx-name
 9294     0/imm32/no-rm32
 9295     0/imm32/no-r32
 9296     0/imm32/no-imm32
 9297     1/imm32/disp32-is-first-inout
 9298     0/imm32/no-output
 9299     _Primitive-loop-if-<=-named/imm32/next
 9300 _Primitive-loop-if-<=-named:
 9301     "loop-if-<="/imm32/name
 9302     Single-lit-var/imm32/inouts
 9303     0/imm32/outputs
 9304     "0f 8e/jump-if-<="/imm32/subx-name
 9305     0/imm32/no-rm32
 9306     0/imm32/no-r32
 9307     0/imm32/no-imm32
 9308     1/imm32/disp32-is-first-inout
 9309     0/imm32/no-output
 9310     _Primitive-loop-if->-named/imm32/next
 9311 _Primitive-loop-if->-named:
 9312     "loop-if->"/imm32/name
 9313     Single-lit-var/imm32/inouts
 9314     0/imm32/outputs
 9315     "0f 8f/jump-if->"/imm32/subx-name
 9316     0/imm32/no-rm32
 9317     0/imm32/no-r32
 9318     0/imm32/no-imm32
 9319     1/imm32/disp32-is-first-inout
 9320     0/imm32/no-output
 9321     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
 9322 _Primitive-loop-named:
 9323     "loop"/imm32/name
 9324     Single-lit-var/imm32/inouts
 9325     0/imm32/outputs
 9326     "e9/jump"/imm32/subx-name
 9327     0/imm32/no-rm32
 9328     0/imm32/no-r32
 9329     0/imm32/no-imm32
 9330     1/imm32/disp32-is-first-inout
 9331     0/imm32/no-output
 9332     0/imm32/next
 9333 
 9334 Single-int-var-in-mem:
 9335     Int-var-in-mem/imm32
 9336     0/imm32/next
 9337 
 9338 Int-var-in-mem:
 9339     "arg1"/imm32/name
 9340     Type-int/imm32
 9341     1/imm32/some-block-depth
 9342     1/imm32/some-stack-offset
 9343     0/imm32/no-register
 9344 
 9345 Two-args-int-stack-int-reg:
 9346     Int-var-in-mem/imm32
 9347     Single-int-var-in-some-register/imm32/next
 9348 
 9349 Two-args-int-reg-int-stack:
 9350     Int-var-in-some-register/imm32
 9351     Single-int-var-in-mem/imm32/next
 9352 
 9353 Two-args-int-eax-int-literal:
 9354     Int-var-in-eax/imm32
 9355     Single-lit-var/imm32/next
 9356 
 9357 Int-var-and-literal:
 9358     Int-var-in-mem/imm32
 9359     Single-lit-var/imm32/next
 9360 
 9361 Int-var-in-register-and-literal:
 9362     Int-var-in-some-register/imm32
 9363     Single-lit-var/imm32/next
 9364 
 9365 Single-int-var-in-some-register:
 9366     Int-var-in-some-register/imm32
 9367     0/imm32/next
 9368 
 9369 Int-var-in-some-register:
 9370     "arg1"/imm32/name
 9371     Type-int/imm32
 9372     1/imm32/some-block-depth
 9373     0/imm32/no-stack-offset
 9374     Any-register/imm32
 9375 
 9376 Single-int-var-in-eax:
 9377     Int-var-in-eax/imm32
 9378     0/imm32/next
 9379 
 9380 Int-var-in-eax:
 9381     "arg1"/imm32/name
 9382     Type-int/imm32
 9383     1/imm32/some-block-depth
 9384     0/imm32/no-stack-offset
 9385     "eax"/imm32/register
 9386 
 9387 Single-int-var-in-ecx:
 9388     Int-var-in-ecx/imm32
 9389     0/imm32/next
 9390 
 9391 Int-var-in-ecx:
 9392     "arg1"/imm32/name
 9393     Type-int/imm32
 9394     1/imm32/some-block-depth
 9395     0/imm32/no-stack-offset
 9396     "ecx"/imm32/register
 9397 
 9398 Single-int-var-in-edx:
 9399     Int-var-in-edx/imm32
 9400     0/imm32/next
 9401 
 9402 Int-var-in-edx:
 9403     "arg1"/imm32/name
 9404     Type-int/imm32
 9405     1/imm32/some-block-depth
 9406     0/imm32/no-stack-offset
 9407     "edx"/imm32/register
 9408 
 9409 Single-int-var-in-ebx:
 9410     Int-var-in-ebx/imm32
 9411     0/imm32/next
 9412 
 9413 Int-var-in-ebx:
 9414     "arg1"/imm32/name
 9415     Type-int/imm32
 9416     1/imm32/some-block-depth
 9417     0/imm32/no-stack-offset
 9418     "ebx"/imm32/register
 9419 
 9420 Single-int-var-in-esi:
 9421     Int-var-in-esi/imm32
 9422     0/imm32/next
 9423 
 9424 Int-var-in-esi:
 9425     "arg1"/imm32/name
 9426     Type-int/imm32
 9427     1/imm32/some-block-depth
 9428     0/imm32/no-stack-offset
 9429     "esi"/imm32/register
 9430 
 9431 Single-int-var-in-edi:
 9432     Int-var-in-edi/imm32
 9433     0/imm32/next
 9434 
 9435 Int-var-in-edi:
 9436     "arg1"/imm32/name
 9437     Type-int/imm32
 9438     1/imm32/some-block-depth
 9439     0/imm32/no-stack-offset
 9440     "edi"/imm32/register
 9441 
 9442 Single-lit-var:
 9443     Lit-var/imm32
 9444     0/imm32/next
 9445 
 9446 Lit-var:
 9447     "literal"/imm32/name
 9448     Type-literal/imm32
 9449     1/imm32/some-block-depth
 9450     0/imm32/no-stack-offset
 9451     0/imm32/no-register
 9452 
 9453 Type-int:
 9454     1/imm32/left/int
 9455     0/imm32/right/null
 9456 
 9457 Type-literal:
 9458     0/imm32/left/literal
 9459     0/imm32/right/null
 9460 
 9461 == code
 9462 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle stmt), primitive: (handle function)
 9463     # . prologue
 9464     55/push-ebp
 9465     89/<- %ebp 4/r32/esp
 9466     # . save registers
 9467     50/push-eax
 9468     51/push-ecx
 9469     # ecx = primitive
 9470     8b/-> *(ebp+0x10) 1/r32/ecx
 9471     # emit primitive name
 9472     (emit-indent *(ebp+8) *Curr-block-depth)
 9473     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
 9474     # emit rm32 if necessary
 9475     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
 9476     # emit r32 if necessary
 9477     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
 9478     # emit imm32 if necessary
 9479     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
 9480     # emit disp32 if necessary
 9481     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
 9482     (write-buffered *(ebp+8) Newline)
 9483 $emit-subx-primitive:end:
 9484     # . restore registers
 9485     59/pop-to-ecx
 9486     58/pop-to-eax
 9487     # . epilogue
 9488     89/<- %esp 5/r32/ebp
 9489     5d/pop-to-ebp
 9490     c3/return
 9491 
 9492 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9493     # . prologue
 9494     55/push-ebp
 9495     89/<- %ebp 4/r32/esp
 9496     # . save registers
 9497     50/push-eax
 9498     # if (l == 0) return
 9499     81 7/subop/compare *(ebp+0xc) 0/imm32
 9500     74/jump-if-= $emit-subx-rm32:end/disp8
 9501     # var v/eax: (handle var)
 9502     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9503     (emit-subx-var-as-rm32 *(ebp+8) %eax)
 9504 $emit-subx-rm32:end:
 9505     # . restore registers
 9506     58/pop-to-eax
 9507     # . epilogue
 9508     89/<- %esp 5/r32/ebp
 9509     5d/pop-to-ebp
 9510     c3/return
 9511 
 9512 get-stmt-operand-from-arg-location:  # stmt: (handle stmt), l: arg-location -> var/eax: (handle stmt-var)
 9513     # . prologue
 9514     55/push-ebp
 9515     89/<- %ebp 4/r32/esp
 9516     # . save registers
 9517     51/push-ecx
 9518     # eax = l
 9519     8b/-> *(ebp+0xc) 0/r32/eax
 9520     # ecx = stmt
 9521     8b/-> *(ebp+8) 1/r32/ecx
 9522     # if (l == 1) return stmt->inouts
 9523     {
 9524       3d/compare-eax-and 1/imm32
 9525       75/jump-if-!= break/disp8
 9526 $get-stmt-operand-from-arg-location:1:
 9527       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9528       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9529     }
 9530     # if (l == 2) return stmt->inouts->next
 9531     {
 9532       3d/compare-eax-and 2/imm32
 9533       75/jump-if-!= break/disp8
 9534 $get-stmt-operand-from-arg-location:2:
 9535       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9536       8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 9537       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9538     }
 9539     # if (l == 3) return stmt->outputs
 9540     {
 9541       3d/compare-eax-and 3/imm32
 9542       75/jump-if-!= break/disp8
 9543 $get-stmt-operand-from-arg-location:3:
 9544       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 9545       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9546     }
 9547     # abort
 9548     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
 9549 $get-stmt-operand-from-arg-location:end:
 9550     # . restore registers
 9551     59/pop-to-ecx
 9552     # . epilogue
 9553     89/<- %esp 5/r32/ebp
 9554     5d/pop-to-ebp
 9555     c3/return
 9556 
 9557 $get-stmt-operand-from-arg-location:abort:
 9558     # error("invalid arg-location " eax)
 9559     (write-buffered Stderr "invalid arg-location ")
 9560     (print-int32-buffered Stderr %eax)
 9561     (write-buffered Stderr Newline)
 9562     (flush Stderr)
 9563     # . syscall(exit, 1)
 9564     bb/copy-to-ebx  1/imm32
 9565     b8/copy-to-eax  1/imm32/exit
 9566     cd/syscall  0x80/imm8
 9567     # never gets here
 9568 
 9569 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9570     # . prologue
 9571     55/push-ebp
 9572     89/<- %ebp 4/r32/esp
 9573     # . save registers
 9574     50/push-eax
 9575     51/push-ecx
 9576     # if (location == 0) return
 9577     81 7/subop/compare *(ebp+0xc) 0/imm32
 9578     0f 84/jump-if-= $emit-subx-r32:end/disp32
 9579     # var v/eax: (handle var)
 9580     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9581     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9582     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
 9583     (write-buffered *(ebp+8) Space)
 9584     (print-int32-buffered *(ebp+8) *eax)
 9585     (write-buffered *(ebp+8) "/r32")
 9586 $emit-subx-r32:end:
 9587     # . restore registers
 9588     59/pop-to-ecx
 9589     58/pop-to-eax
 9590     # . epilogue
 9591     89/<- %esp 5/r32/ebp
 9592     5d/pop-to-ebp
 9593     c3/return
 9594 
 9595 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9596     # . prologue
 9597     55/push-ebp
 9598     89/<- %ebp 4/r32/esp
 9599     # . save registers
 9600     50/push-eax
 9601     51/push-ecx
 9602     # if (location == 0) return
 9603     81 7/subop/compare *(ebp+0xc) 0/imm32
 9604     74/jump-if-= $emit-subx-imm32:end/disp8
 9605     # var v/eax: (handle var)
 9606     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9607     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9608     (write-buffered *(ebp+8) Space)
 9609     (write-buffered *(ebp+8) *eax)  # Var-name
 9610     (write-buffered *(ebp+8) "/imm32")
 9611 $emit-subx-imm32:end:
 9612     # . restore registers
 9613     59/pop-to-ecx
 9614     58/pop-to-eax
 9615     # . epilogue
 9616     89/<- %esp 5/r32/ebp
 9617     5d/pop-to-ebp
 9618     c3/return
 9619 
 9620 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9621     # . prologue
 9622     55/push-ebp
 9623     89/<- %ebp 4/r32/esp
 9624     # . save registers
 9625     50/push-eax
 9626     51/push-ecx
 9627     # if (location == 0) return
 9628     81 7/subop/compare *(ebp+0xc) 0/imm32
 9629     0f 84/jump-if-= $emit-subx-disp32:end/disp32
 9630     # var v/eax: (handle var)
 9631     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9632     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9633     (write-buffered *(ebp+8) Space)
 9634     (write-buffered *(ebp+8) *eax)  # Var-name
 9635     # hack: if instruction operation starts with "break", emit ":break"
 9636     # var name/ecx: (addr array byte) = stmt->operation
 9637     8b/-> *(ebp+0x10) 0/r32/eax
 9638     8b/-> *(eax+4) 1/r32/ecx
 9639     {
 9640       (string-starts-with? %ecx "break")  # => eax
 9641       3d/compare-eax-and 0/imm32/false
 9642       74/jump-if-= break/disp8
 9643       (write-buffered *(ebp+8) ":break")
 9644     }
 9645     # hack: if instruction operation starts with "loop", emit ":loop"
 9646     {
 9647       (string-starts-with? %ecx "loop")  # => eax
 9648       3d/compare-eax-and 0/imm32/false
 9649       74/jump-if-= break/disp8
 9650       (write-buffered *(ebp+8) ":loop")
 9651     }
 9652     (write-buffered *(ebp+8) "/disp32")
 9653 $emit-subx-disp32:end:
 9654     # . restore registers
 9655     59/pop-to-ecx
 9656     58/pop-to-eax
 9657     # . epilogue
 9658     89/<- %esp 5/r32/ebp
 9659     5d/pop-to-ebp
 9660     c3/return
 9661 
 9662 emit-subx-call:  # out: (addr buffered-file), stmt: (handle stmt), callee: (handle function)
 9663     # . prologue
 9664     55/push-ebp
 9665     89/<- %ebp 4/r32/esp
 9666     # . save registers
 9667     51/push-ecx
 9668     #
 9669     (emit-indent *(ebp+8) *Curr-block-depth)
 9670     (write-buffered *(ebp+8) "(")
 9671     # - emit function name
 9672     8b/-> *(ebp+0x10) 1/r32/ecx
 9673     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
 9674     # - emit arguments
 9675     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9676     8b/-> *(ebp+0xc) 1/r32/ecx
 9677     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9678     {
 9679       # if (curr == null) break
 9680       81 7/subop/compare %ecx 0/imm32
 9681       74/jump-if-= break/disp8
 9682       #
 9683       (emit-subx-call-operand *(ebp+8) %ecx)
 9684       # curr = curr->next
 9685       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9686       eb/jump loop/disp8
 9687     }
 9688     #
 9689     (write-buffered *(ebp+8) ")\n")
 9690 $emit-subx-call:end:
 9691     # . restore registers
 9692     59/pop-to-ecx
 9693     # . epilogue
 9694     89/<- %esp 5/r32/ebp
 9695     5d/pop-to-ebp
 9696     c3/return
 9697 
 9698 # like a function call, except we have no idea what function it is
 9699 # we hope it's defined in SubX and that the types are ok
 9700 emit-hailmary-call:  # out: (addr buffered-file), stmt: (handle stmt)
 9701     # . prologue
 9702     55/push-ebp
 9703     89/<- %ebp 4/r32/esp
 9704     # . save registers
 9705     51/push-ecx
 9706     #
 9707     (emit-indent *(ebp+8) *Curr-block-depth)
 9708     (write-buffered *(ebp+8) "(")
 9709     # ecx = stmt
 9710     8b/-> *(ebp+0xc) 1/r32/ecx
 9711     # - emit function name
 9712     (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
 9713     # - emit arguments
 9714     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9715     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9716     {
 9717       # if (curr == null) break
 9718       81 7/subop/compare %ecx 0/imm32
 9719       74/jump-if-= break/disp8
 9720       #
 9721       (emit-subx-call-operand *(ebp+8) %ecx)
 9722       # curr = curr->next
 9723       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9724       eb/jump loop/disp8
 9725     }
 9726     #
 9727     (write-buffered *(ebp+8) ")\n")
 9728 $emit-hailmary-call:end:
 9729     # . restore registers
 9730     59/pop-to-ecx
 9731     # . epilogue
 9732     89/<- %esp 5/r32/ebp
 9733     5d/pop-to-ebp
 9734     c3/return
 9735 
 9736 emit-subx-call-operand:  # out: (addr buffered-file), s: (handle stmt-var)
 9737     # shares code with emit-subx-var-as-rm32
 9738     # . prologue
 9739     55/push-ebp
 9740     89/<- %ebp 4/r32/esp
 9741     # . save registers
 9742     50/push-eax
 9743     51/push-ecx
 9744     56/push-esi
 9745     # ecx = s
 9746     8b/-> *(ebp+0xc) 1/r32/ecx
 9747     # var operand/esi: (handle var) = s->value
 9748     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9749     # if (operand->register && s->is-deref?) emit "*__"
 9750     {
 9751 $emit-subx-call-operand:check-for-register-indirect:
 9752       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9753       74/jump-if-= break/disp8
 9754       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9755       74/jump-if-= break/disp8
 9756 $emit-subx-call-operand:register-indirect:
 9757       (write-buffered *(ebp+8) " *")
 9758       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9759       e9/jump $emit-subx-call-operand:end/disp32
 9760     }
 9761     # if (operand->register && !s->is-deref?) emit "%__"
 9762     {
 9763 $emit-subx-call-operand:check-for-register-direct:
 9764       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9765       74/jump-if-= break/disp8
 9766       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9767       75/jump-if-!= break/disp8
 9768 $emit-subx-call-operand:register-direct:
 9769       (write-buffered *(ebp+8) " %")
 9770       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9771       e9/jump $emit-subx-call-operand:end/disp32
 9772     }
 9773     # else if (operand->stack-offset) emit "*(ebp+__)"
 9774     {
 9775       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
 9776       74/jump-if-= break/disp8
 9777 $emit-subx-call-operand:stack:
 9778       (write-buffered *(ebp+8) Space)
 9779       (write-buffered *(ebp+8) "*(ebp+")
 9780       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
 9781       (write-buffered *(ebp+8) ")")
 9782       e9/jump $emit-subx-call-operand:end/disp32
 9783     }
 9784     # else if (operand->type == literal) emit "__"
 9785     {
 9786       8b/-> *(esi+4) 0/r32/eax  # Var-type
 9787       81 7/subop/compare *eax 0/imm32  # Tree-left
 9788       75/jump-if-!= break/disp8
 9789 $emit-subx-call-operand:literal:
 9790       (write-buffered *(ebp+8) Space)
 9791       (write-buffered *(ebp+8) *esi)
 9792     }
 9793 $emit-subx-call-operand:end:
 9794     # . restore registers
 9795     5e/pop-to-esi
 9796     59/pop-to-ecx
 9797     58/pop-to-eax
 9798     # . epilogue
 9799     89/<- %esp 5/r32/ebp
 9800     5d/pop-to-ebp
 9801     c3/return
 9802 
 9803 emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (handle stmt-var)
 9804     # . prologue
 9805     55/push-ebp
 9806     89/<- %ebp 4/r32/esp
 9807     # . save registers
 9808     50/push-eax
 9809     51/push-ecx
 9810     56/push-esi
 9811     # ecx = s
 9812     8b/-> *(ebp+0xc) 1/r32/ecx
 9813     # var operand/esi: (handle var) = s->value
 9814     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9815     # if (operand->register && s->is-deref?) emit "*__"
 9816     {
 9817 $emit-subx-var-as-rm32:check-for-register-indirect:
 9818       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9819       74/jump-if-= break/disp8
 9820       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9821       74/jump-if-= break/disp8
 9822 $emit-subx-var-as-rm32:register-indirect:
 9823       (write-buffered *(ebp+8) " *")
 9824       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9825     }
 9826     # if (operand->register && !s->is-deref?) emit "%__"
 9827     {
 9828 $emit-subx-var-as-rm32:check-for-register-direct:
 9829       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9830       74/jump-if-= break/disp8
 9831       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9832       75/jump-if-!= break/disp8
 9833 $emit-subx-var-as-rm32:register-direct:
 9834       (write-buffered *(ebp+8) " %")
 9835       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9836     }
 9837     # else if (operand->stack-offset) emit "*(ebp+__)"
 9838     {
 9839       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
 9840       74/jump-if-= break/disp8
 9841 $emit-subx-var-as-rm32:stack:
 9842       (write-buffered *(ebp+8) Space)
 9843       (write-buffered *(ebp+8) "*(ebp+")
 9844       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
 9845       (write-buffered *(ebp+8) ")")
 9846     }
 9847 $emit-subx-var-as-rm32:end:
 9848     # . restore registers
 9849     5e/pop-to-esi
 9850     59/pop-to-ecx
 9851     58/pop-to-eax
 9852     # . epilogue
 9853     89/<- %esp 5/r32/ebp
 9854     5d/pop-to-ebp
 9855     c3/return
 9856 
 9857 find-matching-function:  # functions: (addr function), stmt: (handle stmt) -> result/eax: (handle function)
 9858     # . prologue
 9859     55/push-ebp
 9860     89/<- %ebp 4/r32/esp
 9861     # . save registers
 9862     51/push-ecx
 9863     # var curr/ecx: (handle function) = functions
 9864     8b/-> *(ebp+8) 1/r32/ecx
 9865     {
 9866       # if (curr == null) break
 9867       81 7/subop/compare %ecx 0/imm32
 9868       74/jump-if-= break/disp8
 9869       # if match(stmt, curr) return curr
 9870       {
 9871         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
 9872         3d/compare-eax-and 0/imm32/false
 9873         74/jump-if-= break/disp8
 9874         89/<- %eax 1/r32/ecx
 9875         eb/jump $find-matching-function:end/disp8
 9876       }
 9877       # curr = curr->next
 9878       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 9879       eb/jump loop/disp8
 9880     }
 9881     # return null
 9882     b8/copy-to-eax 0/imm32
 9883 $find-matching-function:end:
 9884     # . restore registers
 9885     59/pop-to-ecx
 9886     # . epilogue
 9887     89/<- %esp 5/r32/ebp
 9888     5d/pop-to-ebp
 9889     c3/return
 9890 
 9891 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle stmt) -> result/eax: (handle primitive)
 9892     # . prologue
 9893     55/push-ebp
 9894     89/<- %ebp 4/r32/esp
 9895     # . save registers
 9896     51/push-ecx
 9897     # var curr/ecx: (handle primitive) = primitives
 9898     8b/-> *(ebp+8) 1/r32/ecx
 9899     {
 9900 $find-matching-primitive:loop:
 9901       # if (curr == null) break
 9902       81 7/subop/compare %ecx 0/imm32
 9903       0f 84/jump-if-= break/disp32
 9904 #?       (write-buffered Stderr "prim: ")
 9905 #?       (write-buffered Stderr *ecx)  # Primitive-name
 9906 #?       (write-buffered Stderr " => ")
 9907 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
 9908 #?       (write-buffered Stderr Newline)
 9909 #?       (flush Stderr)
 9910       # if match(curr, stmt) return curr
 9911       {
 9912         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
 9913         3d/compare-eax-and 0/imm32/false
 9914         74/jump-if-= break/disp8
 9915         89/<- %eax 1/r32/ecx
 9916         eb/jump $find-matching-primitive:end/disp8
 9917       }
 9918 $find-matching-primitive:next-primitive:
 9919       # curr = curr->next
 9920       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
 9921       e9/jump loop/disp32
 9922     }
 9923     # return null
 9924     b8/copy-to-eax 0/imm32
 9925 $find-matching-primitive:end:
 9926     # . restore registers
 9927     59/pop-to-ecx
 9928     # . epilogue
 9929     89/<- %esp 5/r32/ebp
 9930     5d/pop-to-ebp
 9931     c3/return
 9932 
 9933 mu-stmt-matches-function?:  # stmt: (handle stmt), function: (handle function) -> result/eax: boolean
 9934     # . prologue
 9935     55/push-ebp
 9936     89/<- %ebp 4/r32/esp
 9937     # . save registers
 9938     51/push-ecx
 9939     # return function->name == stmt->operation
 9940     8b/-> *(ebp+8) 1/r32/ecx
 9941     8b/-> *(ebp+0xc) 0/r32/eax
 9942     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
 9943 $mu-stmt-matches-function?:end:
 9944     # . restore registers
 9945     59/pop-to-ecx
 9946     # . epilogue
 9947     89/<- %esp 5/r32/ebp
 9948     5d/pop-to-ebp
 9949     c3/return
 9950 
 9951 mu-stmt-matches-primitive?:  # stmt: (handle stmt), primitive: (handle primitive) -> result/eax: boolean
 9952     # A mu stmt matches a primitive if the name matches, all the inout vars
 9953     # match, and all the output vars match.
 9954     # Vars match if types match and registers match.
 9955     # In addition, a stmt output matches a primitive's output if types match
 9956     # and the primitive has a wildcard register.
 9957     # . prologue
 9958     55/push-ebp
 9959     89/<- %ebp 4/r32/esp
 9960     # . save registers
 9961     51/push-ecx
 9962     52/push-edx
 9963     53/push-ebx
 9964     56/push-esi
 9965     57/push-edi
 9966     # ecx = stmt
 9967     8b/-> *(ebp+8) 1/r32/ecx
 9968     # edx = primitive
 9969     8b/-> *(ebp+0xc) 2/r32/edx
 9970     {
 9971 $mu-stmt-matches-primitive?:check-name:
 9972       # if (primitive->name != stmt->operation) return false
 9973       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
 9974       3d/compare-eax-and 0/imm32/false
 9975       75/jump-if-!= break/disp8
 9976       b8/copy-to-eax 0/imm32
 9977       e9/jump $mu-stmt-matches-primitive?:end/disp32
 9978     }
 9979 $mu-stmt-matches-primitive?:check-inouts:
 9980     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
 9981     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
 9982     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
 9983     {
 9984       # if (curr == 0 && curr2 == 0) move on to check outputs
 9985       {
 9986         81 7/subop/compare %esi 0/imm32
 9987         75/jump-if-!= break/disp8
 9988 $mu-stmt-matches-primitive?:stmt-inout-is-null:
 9989         {
 9990           81 7/subop/compare %edi 0/imm32
 9991           75/jump-if-!= break/disp8
 9992           #
 9993           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
 9994         }
 9995         # return false
 9996         b8/copy-to-eax 0/imm32/false
 9997         e9/jump $mu-stmt-matches-primitive?:end/disp32
 9998       }
 9999       # if (curr2 == 0) return false
10000       {
10001         81 7/subop/compare %edi 0/imm32
10002         75/jump-if-!= break/disp8
10003 $mu-stmt-matches-primitive?:prim-inout-is-null:
10004         b8/copy-to-eax 0/imm32/false
10005         e9/jump $mu-stmt-matches-primitive?:end/disp32
10006       }
10007       # if (curr != curr2) return false
10008       {
10009         (operand-matches-primitive? %esi *edi)  # List-value => eax
10010         3d/compare-eax-and 0/imm32/false
10011         75/jump-if-!= break/disp8
10012         b8/copy-to-eax 0/imm32/false
10013         e9/jump $mu-stmt-matches-primitive?:end/disp32
10014       }
10015       # curr=curr->next
10016       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
10017       # curr2=curr2->next
10018       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
10019       eb/jump loop/disp8
10020     }
10021 $mu-stmt-matches-primitive?:check-outputs:
10022     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
10023     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
10024     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
10025     {
10026       # if (curr == 0) return (curr2 == 0)
10027       {
10028 $mu-stmt-matches-primitive?:check-output:
10029         81 7/subop/compare %esi 0/imm32
10030         75/jump-if-!= break/disp8
10031         {
10032           81 7/subop/compare %edi 0/imm32
10033           75/jump-if-!= break/disp8
10034           # return true
10035           b8/copy-to-eax 1/imm32
10036           e9/jump $mu-stmt-matches-primitive?:end/disp32
10037         }
10038         # return false
10039         b8/copy-to-eax 0/imm32
10040         e9/jump $mu-stmt-matches-primitive?:end/disp32
10041       }
10042       # if (curr2 == 0) return false
10043       {
10044         81 7/subop/compare %edi 0/imm32
10045         75/jump-if-!= break/disp8
10046         b8/copy-to-eax 0/imm32
10047         e9/jump $mu-stmt-matches-primitive?:end/disp32
10048       }
10049       # if (curr != curr2) return false
10050       {
10051         (operand-matches-primitive? %esi *edi)  # List-value => eax
10052         3d/compare-eax-and 0/imm32/false
10053         75/jump-if-!= break/disp8
10054         b8/copy-to-eax 0/imm32
10055         e9/jump $mu-stmt-matches-primitive?:end/disp32
10056       }
10057       # curr=curr->next
10058       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
10059       # curr2=curr2->next
10060       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
10061       eb/jump loop/disp8
10062     }
10063 $mu-stmt-matches-primitive?:return-true:
10064     b8/copy-to-eax 1/imm32
10065 $mu-stmt-matches-primitive?:end:
10066     # . restore registers
10067     5f/pop-to-edi
10068     5e/pop-to-esi
10069     5b/pop-to-ebx
10070     5a/pop-to-edx
10071     59/pop-to-ecx
10072     # . epilogue
10073     89/<- %esp 5/r32/ebp
10074     5d/pop-to-ebp
10075     c3/return
10076 
10077 operand-matches-primitive?:  # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
10078     # . prologue
10079     55/push-ebp
10080     89/<- %ebp 4/r32/esp
10081     # . save registers
10082     51/push-ecx
10083     56/push-esi
10084     57/push-edi
10085     # ecx = s
10086     8b/-> *(ebp+8) 1/r32/ecx
10087     # var var/esi : (handle var) = s->value
10088     8b/-> *ecx 6/r32/esi  # Stmt-var-value
10089     # edi = prim-var
10090     8b/-> *(ebp+0xc) 7/r32/edi
10091 $operand-matches-primitive?:check-type:
10092     # if (var->type != prim-var->type) return false
10093     (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
10094     3d/compare-eax-and 0/imm32/false
10095     b8/copy-to-eax 0/imm32/false
10096     0f 84/jump-if-= $operand-matches-primitive?:end/disp32
10097     {
10098 $operand-matches-primitive?:check-register:
10099       # if prim-var is in memory and var is in register but dereference, match
10100       {
10101         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10102         0f 85/jump-if-!= break/disp32
10103         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10104         74/jump-if-= break/disp8
10105         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10106         74/jump-if-= break/disp8
10107         e9/jump $operand-matches-primitive?:return-true/disp32
10108       }
10109       # if prim-var is in register and var is in register but dereference, no match
10110       {
10111         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10112         0f 84/jump-if-= break/disp32
10113         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10114         0f 84/jump-if-= break/disp32
10115         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10116         74/jump-if-= break/disp8
10117         e9/jump $operand-matches-primitive?:return-false/disp32
10118       }
10119       # return false if var->register doesn't match prim-var->register
10120       {
10121         # if register addresses are equal, it's a match
10122         8b/-> *(esi+0x10) 0/r32/eax  # Var-register
10123         39/compare *(edi+0x10) 0/r32/eax  # Var-register
10124         74/jump-if-= break/disp8
10125         # if either address is 0, return false
10126         3d/compare-eax-and 0/imm32
10127         74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
10128         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10129         74/jump-if-=  $operand-matches-primitive?:return-false/disp8
10130         # if prim-var->register is wildcard, it's a match
10131         (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
10132         3d/compare-eax-and 0/imm32/false
10133         75/jump-if-!= break/disp8
10134         # if string contents aren't equal, return false
10135         (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
10136         3d/compare-eax-and 0/imm32/false
10137         74/jump-if-= $operand-matches-primitive?:return-false/disp8
10138       }
10139     }
10140 $operand-matches-primitive?:return-true:
10141     b8/copy-to-eax 1/imm32/true
10142     eb/jump $operand-matches-primitive?:end/disp8
10143 $operand-matches-primitive?:return-false:
10144     b8/copy-to-eax 0/imm32/false
10145 $operand-matches-primitive?:end:
10146     # . restore registers
10147     5f/pop-to-edi
10148     5e/pop-to-esi
10149     59/pop-to-ecx
10150     # . epilogue
10151     89/<- %esp 5/r32/ebp
10152     5d/pop-to-ebp
10153     c3/return
10154 
10155 subx-type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
10156     # . prologue
10157     55/push-ebp
10158     89/<- %ebp 4/r32/esp
10159     # . save registers
10160     51/push-ecx
10161     # var alit/ecx: boolean = is-literal-type?(a)
10162     (is-simple-mu-type? *(ebp+8) 0)  # => eax
10163     89/<- %ecx 0/r32/eax
10164     # var blit/eax: boolean = is-literal-type?(b)
10165     (is-simple-mu-type? *(ebp+0xc) 0)  # => eax
10166     # return alit == blit
10167     39/compare %eax 1/r32/ecx
10168     0f 94/set-byte-if-= %al
10169     81 4/subop/and %eax 0xff/imm32
10170 $subx-type-equal?:end:
10171     # . restore registers
10172     59/pop-to-ecx
10173     # . epilogue
10174     89/<- %esp 5/r32/ebp
10175     5d/pop-to-ebp
10176     c3/return
10177 
10178 is-simple-mu-type?:  # a: (handle tree type-id), n: type-id -> result/eax: boolean
10179     # . prologue
10180     55/push-ebp
10181     89/<- %ebp 4/r32/esp
10182     # . save registers
10183     51/push-ecx
10184     # ecx = n
10185     8b/-> *(ebp+0xc) 1/r32/ecx
10186     # return (a->value == n)
10187     8b/-> *(ebp+8) 0/r32/eax
10188     39/compare *eax 1/r32/ecx  # Atom-type
10189     0f 94/set-byte-if-= %al
10190     81 4/subop/and %eax 0xff/imm32
10191 $is-simple-mu-type?:end:
10192     # . restore registers
10193     59/pop-to-ecx
10194     # . epilogue
10195     89/<- %esp 5/r32/ebp
10196     5d/pop-to-ebp
10197     c3/return
10198 
10199 test-emit-subx-stmt-primitive:
10200     # Primitive operation on a variable on the stack.
10201     #   increment foo
10202     # =>
10203     #   ff 0/subop/increment *(ebp-8)
10204     #
10205     # There's a variable on the var stack as follows:
10206     #   name: 'foo'
10207     #   type: int
10208     #   stack-offset: -8
10209     #
10210     # There's a primitive with this info:
10211     #   name: 'increment'
10212     #   inouts: int/mem
10213     #   value: 'ff 0/subop/increment'
10214     #
10215     # There's nothing in functions.
10216     #
10217     # . prologue
10218     55/push-ebp
10219     89/<- %ebp 4/r32/esp
10220     # setup
10221     (clear-stream _test-output-stream)
10222     (clear-stream $_test-output-buffered-file->buffer)
10223     # var type/ecx: (handle tree type-id) = int
10224     68/push 0/imm32/right/null
10225     68/push 1/imm32/left/int
10226     89/<- %ecx 4/r32/esp
10227     # var var-foo/ecx: var
10228     68/push 0/imm32/no-register
10229     68/push -8/imm32/stack-offset
10230     68/push 1/imm32/block-depth
10231     51/push-ecx
10232     68/push "foo"/imm32
10233     89/<- %ecx 4/r32/esp
10234     # var operand/ebx: (handle stmt-var)
10235     68/push 0/imm32/is-deref:false
10236     68/push 0/imm32/next
10237     51/push-ecx/var-foo
10238     89/<- %ebx 4/r32/esp
10239     # var stmt/esi: statement
10240     68/push 0/imm32/next
10241     68/push 0/imm32/outputs
10242     53/push-ebx/operands
10243     68/push "increment"/imm32/operation
10244     68/push 1/imm32
10245     89/<- %esi 4/r32/esp
10246     # var primitives/ebx: primitive
10247     68/push 0/imm32/next
10248     68/push 0/imm32/output-is-write-only
10249     68/push 0/imm32/no-disp32
10250     68/push 0/imm32/no-imm32
10251     68/push 0/imm32/no-r32
10252     68/push 1/imm32/rm32-is-first-inout
10253     68/push "ff 0/subop/increment"/imm32/subx-name
10254     68/push 0/imm32/outputs
10255     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
10256     68/push "increment"/imm32/name
10257     89/<- %ebx 4/r32/esp
10258     # convert
10259     c7 0/subop/copy *Curr-block-depth 0/imm32
10260     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10261     (flush _test-output-buffered-file)
10262 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10268     # check output
10269     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
10270     # . epilogue
10271     89/<- %esp 5/r32/ebp
10272     5d/pop-to-ebp
10273     c3/return
10274 
10275 test-emit-subx-stmt-primitive-register:
10276     # Primitive operation on a variable in a register.
10277     #   foo <- increment
10278     # =>
10279     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10280     #
10281     # There's a variable on the var stack as follows:
10282     #   name: 'foo'
10283     #   type: int
10284     #   register: 'eax'
10285     #
10286     # There's a primitive with this info:
10287     #   name: 'increment'
10288     #   out: int/reg
10289     #   value: 'ff 0/subop/increment'
10290     #
10291     # There's nothing in functions.
10292     #
10293     # . prologue
10294     55/push-ebp
10295     89/<- %ebp 4/r32/esp
10296     # setup
10297     (clear-stream _test-output-stream)
10298     (clear-stream $_test-output-buffered-file->buffer)
10299     # var type/ecx: (handle tree type-id) = int
10300     68/push 0/imm32/right/null
10301     68/push 1/imm32/left/int
10302     89/<- %ecx 4/r32/esp
10303     # var var-foo/ecx: var in eax
10304     68/push "eax"/imm32/register
10305     68/push 0/imm32/no-stack-offset
10306     68/push 1/imm32/block-depth
10307     51/push-ecx
10308     68/push "foo"/imm32
10309     89/<- %ecx 4/r32/esp
10310     # var operand/ebx: (handle stmt-var)
10311     68/push 0/imm32/is-deref:false
10312     68/push 0/imm32/next
10313     51/push-ecx/var-foo
10314     89/<- %ebx 4/r32/esp
10315     # var stmt/esi: statement
10316     68/push 0/imm32/next
10317     53/push-ebx/outputs
10318     68/push 0/imm32/inouts
10319     68/push "increment"/imm32/operation
10320     68/push 1/imm32
10321     89/<- %esi 4/r32/esp
10322     # var formal-var/ebx: var in any register
10323     68/push Any-register/imm32
10324     68/push 0/imm32/no-stack-offset
10325     68/push 1/imm32/block-depth
10326     ff 6/subop/push *(ecx+4)  # Var-type
10327     68/push "dummy"/imm32
10328     89/<- %ebx 4/r32/esp
10329     # var operand/ebx: (handle stmt-var)
10330     68/push 0/imm32/is-deref:false
10331     68/push 0/imm32/next
10332     53/push-ebx/formal-var
10333     89/<- %ebx 4/r32/esp
10334     # var primitives/ebx: primitive
10335     68/push 0/imm32/next
10336     68/push 0/imm32/output-is-write-only
10337     68/push 0/imm32/no-disp32
10338     68/push 0/imm32/no-imm32
10339     68/push 0/imm32/no-r32
10340     68/push 3/imm32/rm32-in-first-output
10341     68/push "ff 0/subop/increment"/imm32/subx-name
10342     53/push-ebx/outputs
10343     68/push 0/imm32/inouts
10344     68/push "increment"/imm32/name
10345     89/<- %ebx 4/r32/esp
10346     # convert
10347     c7 0/subop/copy *Curr-block-depth 0/imm32
10348     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10349     (flush _test-output-buffered-file)
10350 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10356     # check output
10357     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
10358     # . epilogue
10359     89/<- %esp 5/r32/ebp
10360     5d/pop-to-ebp
10361     c3/return
10362 
10363 test-emit-subx-stmt-select-primitive:
10364     # Select the right primitive between overloads.
10365     #   foo <- increment
10366     # =>
10367     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10368     #
10369     # There's a variable on the var stack as follows:
10370     #   name: 'foo'
10371     #   type: int
10372     #   register: 'eax'
10373     #
10374     # There's two primitives, as follows:
10375     #   - name: 'increment'
10376     #     out: int/reg
10377     #     value: 'ff 0/subop/increment'
10378     #   - name: 'increment'
10379     #     inout: int/mem
10380     #     value: 'ff 0/subop/increment'
10381     #
10382     # There's nothing in functions.
10383     #
10384     # . prologue
10385     55/push-ebp
10386     89/<- %ebp 4/r32/esp
10387     # setup
10388     (clear-stream _test-output-stream)
10389     (clear-stream $_test-output-buffered-file->buffer)
10390     # var type/ecx: (handle tree type-id) = int
10391     68/push 0/imm32/right/null
10392     68/push 1/imm32/left/int
10393     89/<- %ecx 4/r32/esp
10394     # var var-foo/ecx: var in eax
10395     68/push "eax"/imm32/register
10396     68/push 0/imm32/no-stack-offset
10397     68/push 1/imm32/block-depth
10398     51/push-ecx
10399     68/push "foo"/imm32
10400     89/<- %ecx 4/r32/esp
10401     # var real-outputs/edi: (handle stmt-var)
10402     68/push 0/imm32/is-deref:false
10403     68/push 0/imm32/next
10404     51/push-ecx/var-foo
10405     89/<- %edi 4/r32/esp
10406     # var stmt/esi: statement
10407     68/push 0/imm32/next
10408     57/push-edi/outputs
10409     68/push 0/imm32/inouts
10410     68/push "increment"/imm32/operation
10411     68/push 1/imm32
10412     89/<- %esi 4/r32/esp
10413     # var formal-var/ebx: var in any register
10414     68/push Any-register/imm32
10415     68/push 0/imm32/no-stack-offset
10416     68/push 1/imm32/block-depth
10417     ff 6/subop/push *(ecx+4)  # Var-type
10418     68/push "dummy"/imm32
10419     89/<- %ebx 4/r32/esp
10420     # var formal-outputs/ebx: (handle stmt-var)
10421     68/push 0/imm32/is-deref:false
10422     68/push 0/imm32/next
10423     53/push-ebx/formal-var
10424     89/<- %ebx 4/r32/esp
10425     # var primitive1/ebx: primitive
10426     68/push 0/imm32/next
10427     68/push 0/imm32/output-is-write-only
10428     68/push 0/imm32/no-disp32
10429     68/push 0/imm32/no-imm32
10430     68/push 0/imm32/no-r32
10431     68/push 3/imm32/rm32-in-first-output
10432     68/push "ff 0/subop/increment"/imm32/subx-name
10433     53/push-ebx/outputs/formal-outputs
10434     68/push 0/imm32/inouts
10435     68/push "increment"/imm32/name
10436     89/<- %ebx 4/r32/esp
10437     # var primitives/ebx: primitive
10438     53/push-ebx/next
10439     68/push 0/imm32/output-is-write-only
10440     68/push 0/imm32/no-disp32
10441     68/push 0/imm32/no-imm32
10442     68/push 0/imm32/no-r32
10443     68/push 1/imm32/rm32-is-first-inout
10444     68/push "ff 0/subop/increment"/imm32/subx-name
10445     68/push 0/imm32/outputs
10446     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10447     68/push "increment"/imm32/name
10448     89/<- %ebx 4/r32/esp
10449     # convert
10450     c7 0/subop/copy *Curr-block-depth 0/imm32
10451     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10452     (flush _test-output-buffered-file)
10453 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10459     # check output
10460     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
10461     # . epilogue
10462     89/<- %esp 5/r32/ebp
10463     5d/pop-to-ebp
10464     c3/return
10465 
10466 test-emit-subx-stmt-select-primitive-2:
10467     # Select the right primitive between overloads.
10468     #   foo <- increment
10469     # =>
10470     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10471     #
10472     # There's a variable on the var stack as follows:
10473     #   name: 'foo'
10474     #   type: int
10475     #   register: 'eax'
10476     #
10477     # There's two primitives, as follows:
10478     #   - name: 'increment'
10479     #     out: int/reg
10480     #     value: 'ff 0/subop/increment'
10481     #   - name: 'increment'
10482     #     inout: int/mem
10483     #     value: 'ff 0/subop/increment'
10484     #
10485     # There's nothing in functions.
10486     #
10487     # . prologue
10488     55/push-ebp
10489     89/<- %ebp 4/r32/esp
10490     # setup
10491     (clear-stream _test-output-stream)
10492     (clear-stream $_test-output-buffered-file->buffer)
10493     # var type/ecx: (handle tree type-id) = int
10494     68/push 0/imm32/right/null
10495     68/push 1/imm32/left/int
10496     89/<- %ecx 4/r32/esp
10497     # var var-foo/ecx: var in eax
10498     68/push "eax"/imm32/register
10499     68/push 0/imm32/no-stack-offset
10500     68/push 1/imm32/block-depth
10501     51/push-ecx
10502     68/push "foo"/imm32
10503     89/<- %ecx 4/r32/esp
10504     # var inouts/edi: (handle stmt-var)
10505     68/push 0/imm32/is-deref:false
10506     68/push 0/imm32/next
10507     51/push-ecx/var-foo
10508     89/<- %edi 4/r32/esp
10509     # var stmt/esi: statement
10510     68/push 0/imm32/next
10511     68/push 0/imm32/outputs
10512     57/push-edi/inouts
10513     68/push "increment"/imm32/operation
10514     68/push 1/imm32
10515     89/<- %esi 4/r32/esp
10516     # var formal-var/ebx: var in any register
10517     68/push Any-register/imm32
10518     68/push 0/imm32/no-stack-offset
10519     68/push 1/imm32/block-depth
10520     ff 6/subop/push *(ecx+4)  # Var-type
10521     68/push "dummy"/imm32
10522     89/<- %ebx 4/r32/esp
10523     # var operand/ebx: (handle stmt-var)
10524     68/push 0/imm32/is-deref:false
10525     68/push 0/imm32/next
10526     53/push-ebx/formal-var
10527     89/<- %ebx 4/r32/esp
10528     # var primitive1/ebx: primitive
10529     68/push 0/imm32/next
10530     68/push 0/imm32/output-is-write-only
10531     68/push 0/imm32/no-disp32
10532     68/push 0/imm32/no-imm32
10533     68/push 0/imm32/no-r32
10534     68/push 3/imm32/rm32-in-first-output
10535     68/push "ff 0/subop/increment"/imm32/subx-name
10536     53/push-ebx/outputs/formal-outputs
10537     68/push 0/imm32/inouts
10538     68/push "increment"/imm32/name
10539     89/<- %ebx 4/r32/esp
10540     # var primitives/ebx: primitive
10541     53/push-ebx/next
10542     68/push 0/imm32/output-is-write-only
10543     68/push 0/imm32/no-disp32
10544     68/push 0/imm32/no-imm32
10545     68/push 0/imm32/no-r32
10546     68/push 1/imm32/rm32-is-first-inout
10547     68/push "ff 0/subop/increment"/imm32/subx-name
10548     68/push 0/imm32/outputs
10549     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10550     68/push "increment"/imm32/name
10551     89/<- %ebx 4/r32/esp
10552     # convert
10553     c7 0/subop/copy *Curr-block-depth 0/imm32
10554     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10555     (flush _test-output-buffered-file)
10556 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10562     # check output
10563     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
10564     # . epilogue
10565     89/<- %esp 5/r32/ebp
10566     5d/pop-to-ebp
10567     c3/return
10568 
10569 test-increment-register:
10570     # Select the right register between overloads.
10571     #   foo <- increment
10572     # =>
10573     #   50/increment-eax
10574     #
10575     # There's a variable on the var stack as follows:
10576     #   name: 'foo'
10577     #   type: int
10578     #   register: 'eax'
10579     #
10580     # Primitives are the global definitions.
10581     #
10582     # There are no functions defined.
10583     #
10584     # . prologue
10585     55/push-ebp
10586     89/<- %ebp 4/r32/esp
10587     # setup
10588     (clear-stream _test-output-stream)
10589     (clear-stream $_test-output-buffered-file->buffer)
10590     # var type/ecx: (handle tree type-id) = int
10591     68/push 0/imm32/right/null
10592     68/push 1/imm32/left/int
10593     89/<- %ecx 4/r32/esp
10594     # var var-foo/ecx: var in eax
10595     68/push "eax"/imm32/register
10596     68/push 0/imm32/no-stack-offset
10597     68/push 1/imm32/block-depth
10598     51/push-ecx
10599     68/push "foo"/imm32
10600     89/<- %ecx 4/r32/esp
10601     # var real-outputs/edi: (handle stmt-var)
10602     68/push 0/imm32/is-deref:false
10603     68/push 0/imm32/next
10604     51/push-ecx/var-foo
10605     89/<- %edi 4/r32/esp
10606     # var stmt/esi: statement
10607     68/push 0/imm32/next
10608     57/push-edi/outputs
10609     68/push 0/imm32/inouts
10610     68/push "increment"/imm32/operation
10611     68/push 1/imm32/regular-stmt
10612     89/<- %esi 4/r32/esp
10613     # convert
10614     c7 0/subop/copy *Curr-block-depth 0/imm32
10615     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10616     (flush _test-output-buffered-file)
10617 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10623     # check output
10624     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
10625     # . epilogue
10626     89/<- %esp 5/r32/ebp
10627     5d/pop-to-ebp
10628     c3/return
10629 
10630 test-increment-var:
10631     # Select the right primitive between overloads.
10632     #   foo <- increment
10633     # =>
10634     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10635     #
10636     # There's a variable on the var stack as follows:
10637     #   name: 'foo'
10638     #   type: int
10639     #   register: 'eax'
10640     #
10641     # Primitives are the global definitions.
10642     #
10643     # There are no functions defined.
10644     #
10645     # . prologue
10646     55/push-ebp
10647     89/<- %ebp 4/r32/esp
10648     # setup
10649     (clear-stream _test-output-stream)
10650     (clear-stream $_test-output-buffered-file->buffer)
10651     # var type/ecx: (handle tree type-id) = int
10652     68/push 0/imm32/right/null
10653     68/push 1/imm32/left/int
10654     89/<- %ecx 4/r32/esp
10655     # var var-foo/ecx: var in eax
10656     68/push "eax"/imm32/register
10657     68/push 0/imm32/no-stack-offset
10658     68/push 1/imm32/block-depth
10659     51/push-ecx
10660     68/push "foo"/imm32
10661     89/<- %ecx 4/r32/esp
10662     # var inouts/edi: (handle stmt-var)
10663     68/push 0/imm32/is-deref:false
10664     68/push 0/imm32/next
10665     51/push-ecx/var-foo
10666     89/<- %edi 4/r32/esp
10667     # var stmt/esi: statement
10668     68/push 0/imm32/next
10669     57/push-edi/outputs
10670     68/push 0/imm32/inouts
10671     68/push "increment"/imm32/operation
10672     68/push 1/imm32
10673     89/<- %esi 4/r32/esp
10674     # convert
10675     c7 0/subop/copy *Curr-block-depth 0/imm32
10676     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10677     (flush _test-output-buffered-file)
10678 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10684     # check output
10685     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
10686     # . epilogue
10687     89/<- %esp 5/r32/ebp
10688     5d/pop-to-ebp
10689     c3/return
10690 
10691 test-add-reg-to-reg:
10692     #   var1/reg <- add var2/reg
10693     # =>
10694     #   01/add %var1 var2
10695     #
10696     # . prologue
10697     55/push-ebp
10698     89/<- %ebp 4/r32/esp
10699     # setup
10700     (clear-stream _test-output-stream)
10701     (clear-stream $_test-output-buffered-file->buffer)
10702     # var type/ecx: (handle tree type-id) = int
10703     68/push 0/imm32/right/null
10704     68/push 1/imm32/left/int
10705     89/<- %ecx 4/r32/esp
10706     # var var-var1/ecx: var in eax
10707     68/push "eax"/imm32/register
10708     68/push 0/imm32/no-stack-offset
10709     68/push 1/imm32/block-depth
10710     51/push-ecx
10711     68/push "var1"/imm32
10712     89/<- %ecx 4/r32/esp
10713     # var var-var2/edx: var in ecx
10714     68/push "ecx"/imm32/register
10715     68/push 0/imm32/no-stack-offset
10716     68/push 1/imm32/block-depth
10717     ff 6/subop/push *(ecx+4)  # Var-type
10718     68/push "var2"/imm32
10719     89/<- %edx 4/r32/esp
10720     # var inouts/esi: (handle stmt-var) = [var2]
10721     68/push 0/imm32/is-deref:false
10722     68/push 0/imm32/next
10723     52/push-edx/var-var2
10724     89/<- %esi 4/r32/esp
10725     # var outputs/edi: (handle stmt-var) = [var1, var2]
10726     68/push 0/imm32/is-deref:false
10727     68/push 0/imm32/next
10728     51/push-ecx/var-var1
10729     89/<- %edi 4/r32/esp
10730     # var stmt/esi: statement
10731     68/push 0/imm32/next
10732     57/push-edi/outputs
10733     56/push-esi/inouts
10734     68/push "add"/imm32/operation
10735     68/push 1/imm32
10736     89/<- %esi 4/r32/esp
10737     # convert
10738     c7 0/subop/copy *Curr-block-depth 0/imm32
10739     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10740     (flush _test-output-buffered-file)
10741 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10747     # check output
10748     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
10749     # . epilogue
10750     89/<- %esp 5/r32/ebp
10751     5d/pop-to-ebp
10752     c3/return
10753 
10754 test-add-reg-to-mem:
10755     #   add-to var1 var2/reg
10756     # =>
10757     #   01/add *(ebp+__) var2
10758     #
10759     # . prologue
10760     55/push-ebp
10761     89/<- %ebp 4/r32/esp
10762     # setup
10763     (clear-stream _test-output-stream)
10764     (clear-stream $_test-output-buffered-file->buffer)
10765     # var type/ecx: (handle tree type-id) = int
10766     68/push 0/imm32/right/null
10767     68/push 1/imm32/left/int
10768     89/<- %ecx 4/r32/esp
10769     # var var-var1/ecx: var
10770     68/push 0/imm32/no-register
10771     68/push 8/imm32/stack-offset
10772     68/push 1/imm32/block-depth
10773     51/push-ecx
10774     68/push "var1"/imm32
10775     89/<- %ecx 4/r32/esp
10776     # var var-var2/edx: var in ecx
10777     68/push "ecx"/imm32/register
10778     68/push 0/imm32/no-stack-offset
10779     68/push 1/imm32/block-depth
10780     ff 6/subop/push *(ecx+4)  # Var-type
10781     68/push "var2"/imm32
10782     89/<- %edx 4/r32/esp
10783     # var inouts/esi: (handle stmt-var) = [var2]
10784     68/push 0/imm32/is-deref:false
10785     68/push 0/imm32/next
10786     52/push-edx/var-var2
10787     89/<- %esi 4/r32/esp
10788     # var inouts = (handle stmt-var) = [var1, var2]
10789     56/push-esi/next
10790     51/push-ecx/var-var1
10791     89/<- %esi 4/r32/esp
10792     # var stmt/esi: statement
10793     68/push 0/imm32/next
10794     68/push 0/imm32/outputs
10795     56/push-esi/inouts
10796     68/push "add-to"/imm32/operation
10797     68/push 1/imm32
10798     89/<- %esi 4/r32/esp
10799     # convert
10800     c7 0/subop/copy *Curr-block-depth 0/imm32
10801     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10802     (flush _test-output-buffered-file)
10803 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10809     # check output
10810     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
10811     # . epilogue
10812     89/<- %esp 5/r32/ebp
10813     5d/pop-to-ebp
10814     c3/return
10815 
10816 test-add-mem-to-reg:
10817     #   var1/reg <- add var2
10818     # =>
10819     #   03/add *(ebp+__) var1
10820     #
10821     # . prologue
10822     55/push-ebp
10823     89/<- %ebp 4/r32/esp
10824     # setup
10825     (clear-stream _test-output-stream)
10826     (clear-stream $_test-output-buffered-file->buffer)
10827     # var type/ecx: (handle tree type-id) = int
10828     68/push 0/imm32/right/null
10829     68/push 1/imm32/left/int
10830     89/<- %ecx 4/r32/esp
10831     # var var-var1/ecx: var in eax
10832     68/push "eax"/imm32/register
10833     68/push 0/imm32/no-stack-offset
10834     68/push 1/imm32/block-depth
10835     51/push-ecx
10836     68/push "var1"/imm32
10837     89/<- %ecx 4/r32/esp
10838     # var var-var2/edx: var
10839     68/push 0/imm32/no-register
10840     68/push 8/imm32/stack-offset
10841     68/push 1/imm32/block-depth
10842     ff 6/subop/push *(ecx+4)  # Var-type
10843     68/push "var2"/imm32
10844     89/<- %edx 4/r32/esp
10845     # var inouts/esi: (handle stmt-var) = [var2]
10846     68/push 0/imm32/is-deref:false
10847     68/push 0/imm32/next
10848     52/push-edx/var-var2
10849     89/<- %esi 4/r32/esp
10850     # var outputs/edi = (handle stmt-var) = [var1]
10851     68/push 0/imm32/is-deref:false
10852     68/push 0/imm32/next
10853     51/push-ecx/var-var1
10854     89/<- %edi 4/r32/esp
10855     # var stmt/esi: statement
10856     68/push 0/imm32/next
10857     57/push-edi/outputs
10858     56/push-esi/inouts
10859     68/push "add"/imm32/operation
10860     68/push 1/imm32
10861     89/<- %esi 4/r32/esp
10862     # convert
10863     c7 0/subop/copy *Curr-block-depth 0/imm32
10864     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10865     (flush _test-output-buffered-file)
10866 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10872     # check output
10873     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
10874     # . epilogue
10875     89/<- %esp 5/r32/ebp
10876     5d/pop-to-ebp
10877     c3/return
10878 
10879 test-add-literal-to-eax:
10880     #   var1/eax <- add 0x34
10881     # =>
10882     #   05/add-to-eax 0x34/imm32
10883     #
10884     # . prologue
10885     55/push-ebp
10886     89/<- %ebp 4/r32/esp
10887     # setup
10888     (clear-stream _test-output-stream)
10889     (clear-stream $_test-output-buffered-file->buffer)
10890     # var type/ecx: (handle tree type-id) = int
10891     68/push 0/imm32/right/null
10892     68/push 1/imm32/left/int
10893     89/<- %ecx 4/r32/esp
10894     # var var-var1/ecx: var in eax
10895     68/push "eax"/imm32/register
10896     68/push 0/imm32/no-stack-offset
10897     68/push 1/imm32/block-depth
10898     51/push-ecx
10899     68/push "var1"/imm32
10900     89/<- %ecx 4/r32/esp
10901     # var type/edx: (handle tree type-id) = literal
10902     68/push 0/imm32/right/null
10903     68/push 0/imm32/left/literal
10904     89/<- %edx 4/r32/esp
10905     # var var-var2/edx: var literal
10906     68/push 0/imm32/no-register
10907     68/push 0/imm32/no-stack-offset
10908     68/push 1/imm32/block-depth
10909     52/push-edx
10910     68/push "0x34"/imm32
10911     89/<- %edx 4/r32/esp
10912     # var inouts/esi: (handle stmt-var) = [var2]
10913     68/push 0/imm32/is-deref:false
10914     68/push 0/imm32/next
10915     52/push-edx/var-var2
10916     89/<- %esi 4/r32/esp
10917     # var outputs/edi: (handle stmt-var) = [var1]
10918     68/push 0/imm32/is-deref:false
10919     68/push 0/imm32/next
10920     51/push-ecx/var-var1
10921     89/<- %edi 4/r32/esp
10922     # var stmt/esi: statement
10923     68/push 0/imm32/next
10924     57/push-edi/outputs
10925     56/push-esi/inouts
10926     68/push "add"/imm32/operation
10927     68/push 1/imm32
10928     89/<- %esi 4/r32/esp
10929     # convert
10930     c7 0/subop/copy *Curr-block-depth 0/imm32
10931     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10932     (flush _test-output-buffered-file)
10933 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10939     # check output
10940     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
10941     # . epilogue
10942     89/<- %esp 5/r32/ebp
10943     5d/pop-to-ebp
10944     c3/return
10945 
10946 test-add-literal-to-reg:
10947     #   var1/ecx <- add 0x34
10948     # =>
10949     #   81 0/subop/add %ecx 0x34/imm32
10950     #
10951     # . prologue
10952     55/push-ebp
10953     89/<- %ebp 4/r32/esp
10954     # setup
10955     (clear-stream _test-output-stream)
10956     (clear-stream $_test-output-buffered-file->buffer)
10957     # var type/ecx: (handle tree type-id) = int
10958     68/push 0/imm32/right/null
10959     68/push 1/imm32/left/int
10960     89/<- %ecx 4/r32/esp
10961     # var var-var1/ecx: var in ecx
10962     68/push "ecx"/imm32/register
10963     68/push 0/imm32/no-stack-offset
10964     68/push 1/imm32/block-depth
10965     51/push-ecx
10966     68/push "var1"/imm32
10967     89/<- %ecx 4/r32/esp
10968     # var type/edx: (handle tree type-id) = literal
10969     68/push 0/imm32/right/null
10970     68/push 0/imm32/left/literal
10971     89/<- %edx 4/r32/esp
10972     # var var-var2/edx: var literal
10973     68/push 0/imm32/no-register
10974     68/push 0/imm32/no-stack-offset
10975     68/push 1/imm32/block-depth
10976     52/push-edx
10977     68/push "0x34"/imm32
10978     89/<- %edx 4/r32/esp
10979     # var inouts/esi: (handle stmt-var) = [var2]
10980     68/push 0/imm32/is-deref:false
10981     68/push 0/imm32/next
10982     52/push-edx/var-var2
10983     89/<- %esi 4/r32/esp
10984     # var outputs/edi: (handle stmt-var) = [var1]
10985     68/push 0/imm32/is-deref:false
10986     68/push 0/imm32/next
10987     51/push-ecx/var-var1
10988     89/<- %edi 4/r32/esp
10989     # var stmt/esi: statement
10990     68/push 0/imm32/next
10991     57/push-edi/outputs
10992     56/push-esi/inouts
10993     68/push "add"/imm32/operation
10994     68/push 1/imm32
10995     89/<- %esi 4/r32/esp
10996     # convert
10997     c7 0/subop/copy *Curr-block-depth 0/imm32
10998     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10999     (flush _test-output-buffered-file)
11000 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11006     # check output
11007     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
11008     # . epilogue
11009     89/<- %esp 5/r32/ebp
11010     5d/pop-to-ebp
11011     c3/return
11012 
11013 test-add-literal-to-mem:
11014     #   add-to var1, 0x34
11015     # =>
11016     #   81 0/subop/add %eax 0x34/imm32
11017     #
11018     # . prologue
11019     55/push-ebp
11020     89/<- %ebp 4/r32/esp
11021     # setup
11022     (clear-stream _test-output-stream)
11023     (clear-stream $_test-output-buffered-file->buffer)
11024     # var type/ecx: (handle tree type-id) = int
11025     68/push 0/imm32/right/null
11026     68/push 1/imm32/left/int
11027     89/<- %ecx 4/r32/esp
11028     # var var-var1/ecx: var
11029     68/push 0/imm32/no-register
11030     68/push 8/imm32/stack-offset
11031     68/push 1/imm32/block-depth
11032     51/push-ecx
11033     68/push "var1"/imm32
11034     89/<- %ecx 4/r32/esp
11035     # var type/edx: (handle tree type-id) = literal
11036     68/push 0/imm32/right/null
11037     68/push 0/imm32/left/literal
11038     89/<- %edx 4/r32/esp
11039     # var var-var2/edx: var literal
11040     68/push 0/imm32/no-register
11041     68/push 0/imm32/no-stack-offset
11042     68/push 1/imm32/block-depth
11043     52/push-edx
11044     68/push "0x34"/imm32
11045     89/<- %edx 4/r32/esp
11046     # var inouts/esi: (handle stmt-var) = [var2]
11047     68/push 0/imm32/is-deref:false
11048     68/push 0/imm32/next
11049     52/push-edx/var-var2
11050     89/<- %esi 4/r32/esp
11051     # var inouts = (handle stmt-var) = [var1, var2]
11052     68/push 0/imm32/is-deref:false
11053     56/push-esi/next
11054     51/push-ecx/var-var1
11055     89/<- %esi 4/r32/esp
11056     # var stmt/esi: statement
11057     68/push 0/imm32/next
11058     68/push 0/imm32/outputs
11059     56/push-esi/inouts
11060     68/push "add-to"/imm32/operation
11061     68/push 1/imm32
11062     89/<- %esi 4/r32/esp
11063     # convert
11064     c7 0/subop/copy *Curr-block-depth 0/imm32
11065     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11066     (flush _test-output-buffered-file)
11067 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11073     # check output
11074     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
11075     # . epilogue
11076     89/<- %esp 5/r32/ebp
11077     5d/pop-to-ebp
11078     c3/return
11079 
11080 test-compare-mem-with-reg:
11081     #   compare var1, var2/eax
11082     # =>
11083     #   39/compare *(ebp+___) 0/r32/eax
11084     #
11085     # . prologue
11086     55/push-ebp
11087     89/<- %ebp 4/r32/esp
11088     # setup
11089     (clear-stream _test-output-stream)
11090     (clear-stream $_test-output-buffered-file->buffer)
11091     # var type/ecx: (handle tree type-id) = int
11092     68/push 0/imm32/right/null
11093     68/push 1/imm32/left/int
11094     89/<- %ecx 4/r32/esp
11095     # var var-var2/ecx: var in eax
11096     68/push "eax"/imm32/register
11097     68/push 0/imm32/no-stack-offset
11098     68/push 1/imm32/block-depth
11099     51/push-ecx
11100     68/push "var2"/imm32
11101     89/<- %ecx 4/r32/esp
11102     # var var-var1/edx: var
11103     68/push 0/imm32/no-register
11104     68/push 8/imm32/stack-offset
11105     68/push 1/imm32/block-depth
11106     ff 6/subop/push *(ecx+4)  # Var-type
11107     68/push "var1"/imm32
11108     89/<- %edx 4/r32/esp
11109     # var inouts/esi: (handle stmt-var) = [var2]
11110     68/push 0/imm32/is-deref:false
11111     68/push 0/imm32/next
11112     51/push-ecx/var-var2
11113     89/<- %esi 4/r32/esp
11114     # inouts = [var1, var2]
11115     68/push 0/imm32/is-deref:false
11116     56/push-esi
11117     52/push-edx/var-var1
11118     89/<- %esi 4/r32/esp
11119     # var stmt/esi: statement
11120     68/push 0/imm32/next
11121     68/push 0/imm32/outputs
11122     56/push-esi/inouts
11123     68/push "compare"/imm32/operation
11124     68/push 1/imm32
11125     89/<- %esi 4/r32/esp
11126     # convert
11127     c7 0/subop/copy *Curr-block-depth 0/imm32
11128     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11129     (flush _test-output-buffered-file)
11130 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11136     # check output
11137     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
11138     # . epilogue
11139     89/<- %esp 5/r32/ebp
11140     5d/pop-to-ebp
11141     c3/return
11142 
11143 test-compare-reg-with-mem:
11144     #   compare var1/eax, var2
11145     # =>
11146     #   3b/compare *(ebp+___) 0/r32/eax
11147     #
11148     # . prologue
11149     55/push-ebp
11150     89/<- %ebp 4/r32/esp
11151     # setup
11152     (clear-stream _test-output-stream)
11153     (clear-stream $_test-output-buffered-file->buffer)
11154     # var type/ecx: (handle tree type-id) = int
11155     68/push 0/imm32/right/null
11156     68/push 1/imm32/left/int
11157     89/<- %ecx 4/r32/esp
11158     # var var-var1/ecx: var in eax
11159     68/push "eax"/imm32/register
11160     68/push 0/imm32/no-stack-offset
11161     68/push 1/imm32/block-depth
11162     51/push-ecx
11163     68/push "var1"/imm32
11164     89/<- %ecx 4/r32/esp
11165     # var var-var2/edx: var
11166     68/push 0/imm32/no-register
11167     68/push 8/imm32/stack-offset
11168     68/push 1/imm32/block-depth
11169     ff 6/subop/push *(ecx+4)  # Var-type
11170     68/push "var2"/imm32
11171     89/<- %edx 4/r32/esp
11172     # var inouts/esi: (handle stmt-var) = [var2]
11173     68/push 0/imm32/is-deref:false
11174     68/push 0/imm32/next
11175     52/push-edx/var-var2
11176     89/<- %esi 4/r32/esp
11177     # inouts = [var1, var2]
11178     68/push 0/imm32/is-deref:false
11179     56/push-esi
11180     51/push-ecx/var-var1
11181     89/<- %esi 4/r32/esp
11182     # var stmt/esi: statement
11183     68/push 0/imm32/next
11184     68/push 0/imm32/outputs
11185     56/push-esi/inouts
11186     68/push "compare"/imm32/operation
11187     68/push 1/imm32
11188     89/<- %esi 4/r32/esp
11189     # convert
11190     c7 0/subop/copy *Curr-block-depth 0/imm32
11191     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11192     (flush _test-output-buffered-file)
11193 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11199     # check output
11200     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
11201     # . epilogue
11202     89/<- %esp 5/r32/ebp
11203     5d/pop-to-ebp
11204     c3/return
11205 
11206 test-compare-mem-with-literal:
11207     #   compare var1, 0x34
11208     # =>
11209     #   81 7/subop/compare *(ebp+___) 0x34/imm32
11210     #
11211     # . prologue
11212     55/push-ebp
11213     89/<- %ebp 4/r32/esp
11214     # setup
11215     (clear-stream _test-output-stream)
11216     (clear-stream $_test-output-buffered-file->buffer)
11217     # var type/ecx: (handle tree type-id) = int
11218     68/push 0/imm32/right/null
11219     68/push 1/imm32/left/int
11220     89/<- %ecx 4/r32/esp
11221     # var var-var1/ecx: var
11222     68/push 0/imm32/no-register
11223     68/push 8/imm32/stack-offset
11224     68/push 1/imm32/block-depth
11225     51/push-ecx
11226     68/push "var1"/imm32
11227     89/<- %ecx 4/r32/esp
11228     # var type/edx: (handle tree type-id) = literal
11229     68/push 0/imm32/right/null
11230     68/push 0/imm32/left/literal
11231     89/<- %edx 4/r32/esp
11232     # var var-var2/edx: var literal
11233     68/push 0/imm32/no-register
11234     68/push 0/imm32/no-stack-offset
11235     68/push 1/imm32/block-depth
11236     52/push-edx
11237     68/push "0x34"/imm32
11238     89/<- %edx 4/r32/esp
11239     # var inouts/esi: (handle stmt-var) = [var2]
11240     68/push 0/imm32/is-deref:false
11241     68/push 0/imm32/next
11242     52/push-edx/var-var2
11243     89/<- %esi 4/r32/esp
11244     # inouts = [var1, var2]
11245     68/push 0/imm32/is-deref:false
11246     56/push-esi/next
11247     51/push-ecx/var-var1
11248     89/<- %esi 4/r32/esp
11249     # var stmt/esi: statement
11250     68/push 0/imm32/next
11251     68/push 0/imm32/outputs
11252     56/push-esi/inouts
11253     68/push "compare"/imm32/operation
11254     68/push 1/imm32
11255     89/<- %esi 4/r32/esp
11256     # convert
11257     c7 0/subop/copy *Curr-block-depth 0/imm32
11258     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11259     (flush _test-output-buffered-file)
11260 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11266     # check output
11267     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
11268     # . epilogue
11269     89/<- %esp 5/r32/ebp
11270     5d/pop-to-ebp
11271     c3/return
11272 
11273 test-compare-eax-with-literal:
11274     #   compare var1/eax 0x34
11275     # =>
11276     #   3d/compare-eax-with 0x34/imm32
11277     #
11278     # . prologue
11279     55/push-ebp
11280     89/<- %ebp 4/r32/esp
11281     # setup
11282     (clear-stream _test-output-stream)
11283     (clear-stream $_test-output-buffered-file->buffer)
11284     # var type/ecx: (handle tree type-id) = int
11285     68/push 0/imm32/right/null
11286     68/push 1/imm32/left/int
11287     89/<- %ecx 4/r32/esp
11288     # var var-var1/ecx: var in eax
11289     68/push "eax"/imm32/register
11290     68/push 0/imm32/no-stack-offset
11291     68/push 1/imm32/block-depth
11292     51/push-ecx
11293     68/push "var1"/imm32
11294     89/<- %ecx 4/r32/esp
11295     # var type/edx: (handle tree type-id) = literal
11296     68/push 0/imm32/right/null
11297     68/push 0/imm32/left/literal
11298     89/<- %edx 4/r32/esp
11299     # var var-var2/edx: var literal
11300     68/push 0/imm32/no-register
11301     68/push 0/imm32/no-stack-offset
11302     68/push 1/imm32/block-depth
11303     52/push-edx
11304     68/push "0x34"/imm32
11305     89/<- %edx 4/r32/esp
11306     # var inouts/esi: (handle stmt-var) = [var2]
11307     68/push 0/imm32/is-deref:false
11308     68/push 0/imm32/next
11309     52/push-edx/var-var2
11310     89/<- %esi 4/r32/esp
11311     # inouts = [var1, var2]
11312     68/push 0/imm32/is-deref:false
11313     56/push-esi/next
11314     51/push-ecx/var-var1
11315     89/<- %esi 4/r32/esp
11316     # var stmt/esi: statement
11317     68/push 0/imm32/next
11318     68/push 0/imm32/outputs
11319     56/push-esi/inouts
11320     68/push "compare"/imm32/operation
11321     68/push 1/imm32/regular-stmt
11322     89/<- %esi 4/r32/esp
11323     # convert
11324     c7 0/subop/copy *Curr-block-depth 0/imm32
11325     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11326     (flush _test-output-buffered-file)
11327 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11333     # check output
11334     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
11335     # . epilogue
11336     89/<- %esp 5/r32/ebp
11337     5d/pop-to-ebp
11338     c3/return
11339 
11340 test-compare-reg-with-literal:
11341     #   compare var1/ecx 0x34
11342     # =>
11343     #   81 7/subop/compare %ecx 0x34/imm32
11344     #
11345     # . prologue
11346     55/push-ebp
11347     89/<- %ebp 4/r32/esp
11348     # setup
11349     (clear-stream _test-output-stream)
11350     (clear-stream $_test-output-buffered-file->buffer)
11351     # var type/ecx: (handle tree type-id) = int
11352     68/push 0/imm32/right/null
11353     68/push 1/imm32/left/int
11354     89/<- %ecx 4/r32/esp
11355     # var var-var1/ecx: var in ecx
11356     68/push "ecx"/imm32/register
11357     68/push 0/imm32/no-stack-offset
11358     68/push 1/imm32/block-depth
11359     51/push-ecx
11360     68/push "var1"/imm32
11361     89/<- %ecx 4/r32/esp
11362     # var type/edx: (handle tree type-id) = literal
11363     68/push 0/imm32/right/null
11364     68/push 0/imm32/left/literal
11365     89/<- %edx 4/r32/esp
11366     # var var-var2/edx: var literal
11367     68/push 0/imm32/no-register
11368     68/push 0/imm32/no-stack-offset
11369     68/push 1/imm32/block-depth
11370     52/push-edx
11371     68/push "0x34"/imm32
11372     89/<- %edx 4/r32/esp
11373     # var inouts/esi: (handle stmt-var) = [var2]
11374     68/push 0/imm32/is-deref:false
11375     68/push 0/imm32/next
11376     52/push-edx/var-var2
11377     89/<- %esi 4/r32/esp
11378     # inouts = [var1, var2]
11379     68/push 0/imm32/is-deref:false
11380     56/push-esi/next
11381     51/push-ecx/var-var1
11382     89/<- %esi 4/r32/esp
11383     # var stmt/esi: statement
11384     68/push 0/imm32/next
11385     68/push 0/imm32/outputs
11386     56/push-esi/inouts
11387     68/push "compare"/imm32/operation
11388     68/push 1/imm32/regular-stmt
11389     89/<- %esi 4/r32/esp
11390     # convert
11391     c7 0/subop/copy *Curr-block-depth 0/imm32
11392     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11393     (flush _test-output-buffered-file)
11394 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11400     # check output
11401     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
11402     # . epilogue
11403     89/<- %esp 5/r32/ebp
11404     5d/pop-to-ebp
11405     c3/return
11406 
11407 test-emit-subx-stmt-function-call:
11408     # Call a function on a variable on the stack.
11409     #   f foo
11410     # =>
11411     #   (f2 *(ebp-8))
11412     # (Changing the function name supports overloading in general, but here it
11413     # just serves to help disambiguate things.)
11414     #
11415     # There's a variable on the var stack as follows:
11416     #   name: 'foo'
11417     #   type: int
11418     #   stack-offset: -8
11419     #
11420     # There's nothing in primitives.
11421     #
11422     # There's a function with this info:
11423     #   name: 'f'
11424     #   inout: int/mem
11425     #   value: 'f2'
11426     #
11427     # . prologue
11428     55/push-ebp
11429     89/<- %ebp 4/r32/esp
11430     # setup
11431     (clear-stream _test-output-stream)
11432     (clear-stream $_test-output-buffered-file->buffer)
11433     # var type/ecx: (handle tree type-id) = int
11434     68/push 0/imm32/right/null
11435     68/push 1/imm32/left/int
11436     89/<- %ecx 4/r32/esp
11437     # var var-foo/ecx: var
11438     68/push 0/imm32/no-register
11439     68/push -8/imm32/stack-offset
11440     68/push 0/imm32/block-depth
11441     51/push-ecx
11442     68/push "foo"/imm32
11443     89/<- %ecx 4/r32/esp
11444     # var inouts/esi: (handle stmt-var)
11445     68/push 0/imm32/is-deref:false
11446     68/push 0/imm32/next
11447     51/push-ecx/var-foo
11448     89/<- %esi 4/r32/esp
11449     # var stmt/esi: statement
11450     68/push 0/imm32/next
11451     68/push 0/imm32/outputs
11452     56/push-esi/inouts
11453     68/push "f"/imm32/operation
11454     68/push 1/imm32
11455     89/<- %esi 4/r32/esp
11456     # var functions/ebx: function
11457     68/push 0/imm32/next
11458     68/push 0/imm32/body
11459     68/push 0/imm32/outputs
11460     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
11461     68/push "f2"/imm32/subx-name
11462     68/push "f"/imm32/name
11463     89/<- %ebx 4/r32/esp
11464     # convert
11465     c7 0/subop/copy *Curr-block-depth 0/imm32
11466     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
11467     (flush _test-output-buffered-file)
11468 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11474     # check output
11475     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
11476     # . epilogue
11477     89/<- %esp 5/r32/ebp
11478     5d/pop-to-ebp
11479     c3/return
11480 
11481 test-emit-subx-stmt-function-call-with-literal-arg:
11482     # Call a function on a literal.
11483     #   f 34
11484     # =>
11485     #   (f2 34)
11486     #
11487     # . prologue
11488     55/push-ebp
11489     89/<- %ebp 4/r32/esp
11490     # setup
11491     (clear-stream _test-output-stream)
11492     (clear-stream $_test-output-buffered-file->buffer)
11493     # var type/ecx: (handle tree type-id) = literal
11494     68/push 0/imm32/right/null
11495     68/push 0/imm32/left/literal
11496     89/<- %ecx 4/r32/esp
11497     # var var-foo/ecx: var literal
11498     68/push 0/imm32/no-register
11499     68/push 0/imm32/no-stack-offset
11500     68/push 0/imm32/block-depth
11501     51/push-ecx
11502     68/push "34"/imm32
11503     89/<- %ecx 4/r32/esp
11504     # var inouts/esi: (handle stmt-var)
11505     68/push 0/imm32/is-deref:false
11506     68/push 0/imm32/next
11507     51/push-ecx/var-foo
11508     89/<- %esi 4/r32/esp
11509     # var stmt/esi: statement
11510     68/push 0/imm32/next
11511     68/push 0/imm32/outputs
11512     56/push-esi/inouts
11513     68/push "f"/imm32/operation
11514     68/push 1/imm32
11515     89/<- %esi 4/r32/esp
11516     # var functions/ebx: function
11517     68/push 0/imm32/next
11518     68/push 0/imm32/body
11519     68/push 0/imm32/outputs
11520     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
11521     68/push "f2"/imm32/subx-name
11522     68/push "f"/imm32/name
11523     89/<- %ebx 4/r32/esp
11524     # convert
11525     c7 0/subop/copy *Curr-block-depth 0/imm32
11526     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
11527     (flush _test-output-buffered-file)
11528 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11534     # check output
11535     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
11536     # . epilogue
11537     89/<- %esp 5/r32/ebp
11538     5d/pop-to-ebp
11539     c3/return
11540 
11541 emit-indent:  # out: (addr buffered-file), n: int
11542     # . prologue
11543     55/push-ebp
11544     89/<- %ebp 4/r32/esp
11545     # . save registers
11546     50/push-eax
11547     # var i/eax: int = n
11548     8b/-> *(ebp+0xc) 0/r32/eax
11549     {
11550       # if (i <= 0) break
11551       3d/compare-eax-with 0/imm32
11552       7e/jump-if-<= break/disp8
11553       (write-buffered *(ebp+8) "  ")
11554       48/decrement-eax
11555       eb/jump loop/disp8
11556     }
11557 $emit-indent:end:
11558     # . restore registers
11559     58/pop-to-eax
11560     # . epilogue
11561     89/<- %esp 5/r32/ebp
11562     5d/pop-to-ebp
11563     c3/return
11564 
11565 emit-subx-prologue:  # out: (addr buffered-file)
11566     # . prologue
11567     55/push-ebp
11568     89/<- %ebp 4/r32/esp
11569     #
11570     (write-buffered *(ebp+8) "  # . prologue\n")
11571     (write-buffered *(ebp+8) "  55/push-ebp\n")
11572     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
11573 $emit-subx-prologue:end:
11574     # . epilogue
11575     89/<- %esp 5/r32/ebp
11576     5d/pop-to-ebp
11577     c3/return
11578 
11579 emit-subx-epilogue:  # out: (addr buffered-file)
11580     # . prologue
11581     55/push-ebp
11582     89/<- %ebp 4/r32/esp
11583     #
11584     (write-buffered *(ebp+8) "  # . epilogue\n")
11585     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
11586     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
11587     (write-buffered *(ebp+8) "  c3/return\n")
11588 $emit-subx-epilogue:end:
11589     # . epilogue
11590     89/<- %esp 5/r32/ebp
11591     5d/pop-to-ebp
11592     c3/return