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-address:
 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 {\n")
 2272     (write _test-input-stream "  var a: int\n")
 2273     (write _test-input-stream "  var b/eax: (addr int) <- address a\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-address/0")
 2286     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-address/1")
 2287     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-address/2")
 2288     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-address/3")
 2289     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-address/4")
 2290     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-address/5")
 2291     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-address/6")
 2292     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-address/7")
 2293     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000000/r32"  "F - test-convert-address/8")
 2294     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-address/9")
 2295     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-address/10")
 2296     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-address/11")
 2297     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-address/12")
 2298     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-address/13")
 2299     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-address/14")
 2300     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-address/15")
 2301     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-address/16")
 2302     # . epilogue
 2303     89/<- %esp 5/r32/ebp
 2304     5d/pop-to-ebp
 2305     c3/return
 2306 
 2307 test-convert-length-of-array:
 2308     # . prologue
 2309     55/push-ebp
 2310     89/<- %ebp 4/r32/esp
 2311     # setup
 2312     (clear-stream _test-input-stream)
 2313     (clear-stream $_test-input-buffered-file->buffer)
 2314     (clear-stream _test-output-stream)
 2315     (clear-stream $_test-output-buffered-file->buffer)
 2316     #
 2317     (write _test-input-stream "fn foo a: (addr array int) {\n")
 2318     (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
 2319     (write _test-input-stream "  var c/eax: int <- length b\n")
 2320     (write _test-input-stream "}\n")
 2321     # convert
 2322     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2323     (flush _test-output-buffered-file)
 2324 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2330     # check output
 2331     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
 2332     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
 2333     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
 2334     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
 2335     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
 2336     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
 2337     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
 2338     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
 2339     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
 2340     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
 2341     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
 2342     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
 2343     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
 2344     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
 2345     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
 2346     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
 2347     # . epilogue
 2348     89/<- %esp 5/r32/ebp
 2349     5d/pop-to-ebp
 2350     c3/return
 2351 
 2352 test-convert-length-of-array-on-stack:
 2353     # . prologue
 2354     55/push-ebp
 2355     89/<- %ebp 4/r32/esp
 2356     # setup
 2357     (clear-stream _test-input-stream)
 2358     (clear-stream $_test-input-buffered-file->buffer)
 2359     (clear-stream _test-output-stream)
 2360     (clear-stream $_test-output-buffered-file->buffer)
 2361     #
 2362     (write _test-input-stream "fn foo {\n")
 2363     (write _test-input-stream "  var a: (array int 3)\n")
 2364     (write _test-input-stream "  var b/eax: int <- length a\n")
 2365     (write _test-input-stream "}\n")
 2366     # convert
 2367     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2368     (flush _test-output-buffered-file)
 2369 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2375     # check output
 2376     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-on-stack/0")
 2377     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-on-stack/1")
 2378     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-on-stack/2")
 2379     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-on-stack/3")
 2380     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-on-stack/4")
 2381     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-on-stack/5")
 2382     # define x
 2383     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-length-of-array-on-stack/6")
 2384     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-length-of-array-on-stack/7")
 2385     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-on-stack/8")
 2386     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0xfffffff0) 0x00000000/r32"  "F - test-convert-length-of-array-on-stack/9")
 2387     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-on-stack/10")
 2388     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-length-of-array-on-stack/11")
 2389     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-on-stack/12")
 2390     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-on-stack/13")
 2391     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-on-stack/14")
 2392     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-on-stack/15")
 2393     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-on-stack/16")
 2394     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-on-stack/17")
 2395     # . epilogue
 2396     89/<- %esp 5/r32/ebp
 2397     5d/pop-to-ebp
 2398     c3/return
 2399 
 2400 test-convert-index-into-array:
 2401     # . prologue
 2402     55/push-ebp
 2403     89/<- %ebp 4/r32/esp
 2404     # setup
 2405     (clear-stream _test-input-stream)
 2406     (clear-stream $_test-input-buffered-file->buffer)
 2407     (clear-stream _test-output-stream)
 2408     (clear-stream $_test-output-buffered-file->buffer)
 2409     #
 2410     (write _test-input-stream "fn foo {\n")
 2411     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2412     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2413     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2414     (write _test-input-stream "}\n")
 2415     # convert
 2416     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2417     (flush _test-output-buffered-file)
 2418 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2424     # check output
 2425     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array/0")
 2426     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array/1")
 2427     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array/2")
 2428     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array/3")
 2429     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array/4")
 2430     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array/5")
 2431     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array/6")
 2432     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array/7")
 2433     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array/8")
 2434     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array/9")
 2435     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000002 + 4) 0x00000000/r32"  "F - test-convert-index-into-array/11")
 2436     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array/13")
 2437     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array/14")
 2438     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array/15")
 2439     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array/16")
 2440     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array/17")
 2441     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array/18")
 2442     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array/19")
 2443     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array/20")
 2444     # . epilogue
 2445     89/<- %esp 5/r32/ebp
 2446     5d/pop-to-ebp
 2447     c3/return
 2448 
 2449 test-convert-index-into-array-with-literal:
 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/eax: (addr array int) <- copy 0\n")
 2461     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
 2462     (write _test-input-stream "}\n")
 2463     # convert
 2464     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2465     (flush _test-output-buffered-file)
 2466 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2472     # check output
 2473     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-with-literal/0")
 2474     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-with-literal/1")
 2475     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-with-literal/2")
 2476     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-with-literal/3")
 2477     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-with-literal/4")
 2478     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-with-literal/5")
 2479     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-with-literal/6")
 2480     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-with-literal/7")
 2481                                                                                  # 2 * 4 bytes/elem + 4 bytes for length = offset 12
 2482     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x0000000c) 0x00000000/r32"  "F - test-convert-index-into-array-with-literal/8")
 2483     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-with-literal/9")
 2484     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-with-literal/10")
 2485     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-with-literal/11")
 2486     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-with-literal/12")
 2487     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-with-literal/13")
 2488     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-with-literal/14")
 2489     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-with-literal/15")
 2490     # . epilogue
 2491     89/<- %esp 5/r32/ebp
 2492     5d/pop-to-ebp
 2493     c3/return
 2494 
 2495 test-convert-index-into-array-on-stack:
 2496     # . prologue
 2497     55/push-ebp
 2498     89/<- %ebp 4/r32/esp
 2499     # setup
 2500     (clear-stream _test-input-stream)
 2501     (clear-stream $_test-input-buffered-file->buffer)
 2502     (clear-stream _test-output-stream)
 2503     (clear-stream $_test-output-buffered-file->buffer)
 2504     #
 2505     (write _test-input-stream "fn foo {\n")
 2506     (write _test-input-stream "  var arr: (array int 3)\n")
 2507     (write _test-input-stream "  var idx/eax: int <- copy 2\n")
 2508     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2509     (write _test-input-stream "}\n")
 2510     # convert
 2511     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2512     (flush _test-output-buffered-file)
 2513 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2519     # check output
 2520     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack/0")
 2521     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack/1")
 2522     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack/2")
 2523     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack/3")
 2524     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack/4")
 2525     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack/5")
 2526     # var arr
 2527     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack/6")
 2528     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack/7")
 2529     # var idx
 2530     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack/8")
 2531     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 2/imm32"                  "F - test-convert-index-into-array-on-stack/9")
 2532     # var x is at (ebp-0x10) + idx<<2 + 4 = ebp + idx<<2 - 0xc
 2533     (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")
 2534     # reclaim idx
 2535     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack/11")
 2536     # reclaim arr
 2537     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack/12")
 2538     #
 2539     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack/13")
 2540     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack/14")
 2541     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack/15")
 2542     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack/16")
 2543     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack/17")
 2544     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack/18")
 2545     # . epilogue
 2546     89/<- %esp 5/r32/ebp
 2547     5d/pop-to-ebp
 2548     c3/return
 2549 
 2550 test-convert-index-into-array-on-stack-with-literal:
 2551     # . prologue
 2552     55/push-ebp
 2553     89/<- %ebp 4/r32/esp
 2554     # setup
 2555     (clear-stream _test-input-stream)
 2556     (clear-stream $_test-input-buffered-file->buffer)
 2557     (clear-stream _test-output-stream)
 2558     (clear-stream $_test-output-buffered-file->buffer)
 2559     #
 2560     (write _test-input-stream "fn foo {\n")
 2561     (write _test-input-stream "  var arr: (array int 3)\n")
 2562     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
 2563     (write _test-input-stream "}\n")
 2564     # convert
 2565     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2566     (flush _test-output-buffered-file)
 2567 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2573     # check output
 2574     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack-with-literal/0")
 2575     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack-with-literal/1")
 2576     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack-with-literal/2")
 2577     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack-with-literal/3")
 2578     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack-with-literal/4")
 2579     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack-with-literal/5")
 2580     # var arr
 2581     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack-with-literal/6")
 2582     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack-with-literal/7")
 2583     # var x
 2584     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack-with-literal/8")
 2585     # x is at (ebp-0x10) + 4 + 2*4 = ebp-4
 2586     (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")
 2587     # reclaim x
 2588     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack-with-literal/10")
 2589     # reclaim arr
 2590     (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")
 2591     #
 2592     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack-with-literal/12")
 2593     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack-with-literal/13")
 2594     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack-with-literal/14")
 2595     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack-with-literal/15")
 2596     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack-with-literal/16")
 2597     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack-with-literal/17")
 2598     # . epilogue
 2599     89/<- %esp 5/r32/ebp
 2600     5d/pop-to-ebp
 2601     c3/return
 2602 
 2603 test-convert-index-into-array-using-offset:
 2604     # . prologue
 2605     55/push-ebp
 2606     89/<- %ebp 4/r32/esp
 2607     # setup
 2608     (clear-stream _test-input-stream)
 2609     (clear-stream $_test-input-buffered-file->buffer)
 2610     (clear-stream _test-output-stream)
 2611     (clear-stream $_test-output-buffered-file->buffer)
 2612     #
 2613     (write _test-input-stream "fn foo {\n")
 2614     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2615     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2616     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2617     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2618     (write _test-input-stream "}\n")
 2619     # convert
 2620     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2621     (flush _test-output-buffered-file)
 2622 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2628     # check output
 2629     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset/0")
 2630     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset/1")
 2631     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset/2")
 2632     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset/3")
 2633     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset/4")
 2634     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset/5")
 2635     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset/6")
 2636     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset/7")
 2637     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset/8")
 2638     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-using-offset/9")
 2639     (check-next-stream-line-equal _test-output-stream "    69/multiply 0x00000004/imm32 %ecx 0x00000001/r32"  "F - test-convert-index-into-array-using-offset/10")
 2640     (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")
 2641     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset/12")
 2642     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset/13")
 2643     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset/14")
 2644     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset/15")
 2645     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset/16")
 2646     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset/17")
 2647     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset/18")
 2648     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset/19")
 2649     # . epilogue
 2650     89/<- %esp 5/r32/ebp
 2651     5d/pop-to-ebp
 2652     c3/return
 2653 
 2654 test-convert-index-into-array-using-offset-on-stack:
 2655     # . prologue
 2656     55/push-ebp
 2657     89/<- %ebp 4/r32/esp
 2658     # setup
 2659     (clear-stream _test-input-stream)
 2660     (clear-stream $_test-input-buffered-file->buffer)
 2661     (clear-stream _test-output-stream)
 2662     (clear-stream $_test-output-buffered-file->buffer)
 2663     #
 2664     (write _test-input-stream "fn foo {\n")
 2665     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2666     (write _test-input-stream "  var idx: int\n")
 2667     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2668     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2669     (write _test-input-stream "}\n")
 2670     # convert
 2671     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2672     (flush _test-output-buffered-file)
 2673 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2679     # check output
 2680     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset-on-stack/0")
 2681     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset-on-stack/1")
 2682     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset-on-stack/2")
 2683     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset-on-stack/3")
 2684     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset-on-stack/4")
 2685     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset-on-stack/5")
 2686     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset-on-stack/6")
 2687     (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")
 2688     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-using-offset-on-stack/8")
 2689     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset-on-stack/9")
 2690     (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")
 2691     (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")
 2692     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset-on-stack/12")
 2693     (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")
 2694     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset-on-stack/14")
 2695     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset-on-stack/15")
 2696     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset-on-stack/16")
 2697     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset-on-stack/17")
 2698     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset-on-stack/18")
 2699     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset-on-stack/19")
 2700     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset-on-stack/20")
 2701     # . epilogue
 2702     89/<- %esp 5/r32/ebp
 2703     5d/pop-to-ebp
 2704     c3/return
 2705 
 2706 test-convert-function-and-type-definition:
 2707     # . prologue
 2708     55/push-ebp
 2709     89/<- %ebp 4/r32/esp
 2710     # setup
 2711     (clear-stream _test-input-stream)
 2712     (clear-stream $_test-input-buffered-file->buffer)
 2713     (clear-stream _test-output-stream)
 2714     (clear-stream $_test-output-buffered-file->buffer)
 2715     #
 2716     (write _test-input-stream "fn foo a: (addr t) {\n")
 2717     (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
 2718     (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
 2719     (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
 2720     (write _test-input-stream "}\n")
 2721     (write _test-input-stream "type t {\n")
 2722     (write _test-input-stream "  x: int\n")
 2723     (write _test-input-stream "  y: int\n")
 2724     (write _test-input-stream "}\n")
 2725     # convert
 2726     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2727     (flush _test-output-buffered-file)
 2728 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2734     # check output
 2735     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
 2736     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
 2737     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
 2738     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
 2739     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
 2740     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
 2741     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
 2742     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
 2743     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
 2744     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
 2745     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
 2746     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
 2747     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
 2748     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
 2749     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
 2750     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
 2751     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
 2752     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
 2753     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
 2754     # . epilogue
 2755     89/<- %esp 5/r32/ebp
 2756     5d/pop-to-ebp
 2757     c3/return
 2758 
 2759 test-convert-function-with-local-var-with-user-defined-type:
 2760     # . prologue
 2761     55/push-ebp
 2762     89/<- %ebp 4/r32/esp
 2763     # setup
 2764     (clear-stream _test-input-stream)
 2765     (clear-stream $_test-input-buffered-file->buffer)
 2766     (clear-stream _test-output-stream)
 2767     (clear-stream $_test-output-buffered-file->buffer)
 2768     #
 2769     (write _test-input-stream "fn foo {\n")
 2770     (write _test-input-stream "  var a: t\n")
 2771     (write _test-input-stream "}\n")
 2772     (write _test-input-stream "type t {\n")
 2773     (write _test-input-stream "  x: int\n")
 2774     (write _test-input-stream "  y: int\n")
 2775     (write _test-input-stream "}\n")
 2776     # convert
 2777     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2778     (flush _test-output-buffered-file)
 2779 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2785     # check output
 2786     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
 2787     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
 2788     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
 2789     (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")
 2790     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
 2791     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
 2792     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
 2793     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
 2794     (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")
 2795     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
 2796     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
 2797     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
 2798     (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")
 2799     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
 2800     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
 2801     # . epilogue
 2802     89/<- %esp 5/r32/ebp
 2803     5d/pop-to-ebp
 2804     c3/return
 2805 
 2806 test-convert-get-of-type-on-stack:
 2807     # . prologue
 2808     55/push-ebp
 2809     89/<- %ebp 4/r32/esp
 2810     # setup
 2811     (clear-stream _test-input-stream)
 2812     (clear-stream $_test-input-buffered-file->buffer)
 2813     (clear-stream _test-output-stream)
 2814     (clear-stream $_test-output-buffered-file->buffer)
 2815     #
 2816     (write _test-input-stream "fn foo {\n")
 2817     (write _test-input-stream "  var a: t\n")
 2818     (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
 2819     (write _test-input-stream "}\n")
 2820     (write _test-input-stream "type t {\n")
 2821     (write _test-input-stream "  x: int\n")
 2822     (write _test-input-stream "  y: int\n")
 2823     (write _test-input-stream "}\n")
 2824     # convert
 2825     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2826     (flush _test-output-buffered-file)
 2827 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2833     # check output
 2834     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-of-type-on-stack/0")
 2835     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-of-type-on-stack/1")
 2836     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-of-type-on-stack/2")
 2837     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-of-type-on-stack/3")
 2838     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-of-type-on-stack/4")
 2839     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-of-type-on-stack/5")
 2840     # var a
 2841     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/6")
 2842     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/7")
 2843     # var c
 2844     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-of-type-on-stack/8")
 2845     # get
 2846     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000001/r32"  "F - test-convert-get-of-type-on-stack/9")
 2847     # reclaim c
 2848     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-of-type-on-stack/10")
 2849     # reclaim a
 2850     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-get-of-type-on-stack/11")
 2851     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-of-type-on-stack/12")
 2852     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-of-type-on-stack/13")
 2853     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-of-type-on-stack/14")
 2854     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-of-type-on-stack/15")
 2855     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-of-type-on-stack/16")
 2856     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-of-type-on-stack/17")
 2857     # . epilogue
 2858     89/<- %esp 5/r32/ebp
 2859     5d/pop-to-ebp
 2860     c3/return
 2861 
 2862 test-convert-array-of-user-defined-types:
 2863     # . prologue
 2864     55/push-ebp
 2865     89/<- %ebp 4/r32/esp
 2866     # setup
 2867     (clear-stream _test-input-stream)
 2868     (clear-stream $_test-input-buffered-file->buffer)
 2869     (clear-stream _test-output-stream)
 2870     (clear-stream $_test-output-buffered-file->buffer)
 2871     #
 2872     (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
 2873     (write _test-input-stream "  x: int\n")
 2874     (write _test-input-stream "  y: int\n")
 2875     (write _test-input-stream "}\n")
 2876     (write _test-input-stream "fn foo {\n")
 2877     (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
 2878     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2879     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2880     (write _test-input-stream "}\n")
 2881     # convert
 2882     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2883     (flush _test-output-buffered-file)
 2884 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 2890     # check output
 2891     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-array-of-user-defined-types/0")
 2892     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-array-of-user-defined-types/1")
 2893     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-array-of-user-defined-types/2")
 2894     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-array-of-user-defined-types/3")
 2895     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-array-of-user-defined-types/4")
 2896     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-array-of-user-defined-types/5")
 2897     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-array-of-user-defined-types/6")
 2898     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-array-of-user-defined-types/7")
 2899     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-array-of-user-defined-types/8")
 2900     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-array-of-user-defined-types/9")
 2901     (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")
 2902     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-array-of-user-defined-types/13")
 2903     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-array-of-user-defined-types/14")
 2904     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-array-of-user-defined-types/15")
 2905     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-array-of-user-defined-types/16")
 2906     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-array-of-user-defined-types/17")
 2907     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-array-of-user-defined-types/18")
 2908     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-array-of-user-defined-types/19")
 2909     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-array-of-user-defined-types/20")
 2910     # . epilogue
 2911     89/<- %esp 5/r32/ebp
 2912     5d/pop-to-ebp
 2913     c3/return
 2914 
 2915 #######################################################
 2916 # Parsing
 2917 #######################################################
 2918 
 2919 parse-mu:  # in: (addr buffered-file)
 2920     # pseudocode
 2921     #   var curr-function: (addr (handle function)) = Program->functions
 2922     #   var curr-type: (addr (handle typeinfo)) = Program->types
 2923     #   var line: (stream byte 512)
 2924     #   var word-slice: slice
 2925     #   while true                                  # line loop
 2926     #     clear-stream(line)
 2927     #     read-line-buffered(in, line)
 2928     #     if (line->write == 0) break               # end of file
 2929     #     word-slice = next-mu-token(line)
 2930     #     if slice-empty?(word-slice)               # end of line
 2931     #       continue
 2932     #     else if slice-starts-with?(word-slice, "#")  # comment
 2933     #       continue                                # end of line
 2934     #     else if slice-equal?(word-slice, "fn")
 2935     #       var new-function: (handle function) = allocate(function)
 2936     #       var vars: (stack (addr var) 256)
 2937     #       populate-mu-function-header(in, new-function, vars)
 2938     #       populate-mu-function-body(in, new-function, vars)
 2939     #       assert(vars->top == 0)
 2940     #       *curr-function = new-function
 2941     #       curr-function = &new-function->next
 2942     #     else if slice-equal?(word-slice, "type")
 2943     #       word-slice = next-mu-token(line)
 2944     #       type-id = pos-or-insert-slice(Type-id, word-slice)
 2945     #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
 2946     #       assert(next-word(line) == "{")
 2947     #       populate-mu-type(in, new-type)
 2948     #     else
 2949     #       abort()
 2950     #
 2951     # . prologue
 2952     55/push-ebp
 2953     89/<- %ebp 4/r32/esp
 2954     # . save registers
 2955     50/push-eax
 2956     51/push-ecx
 2957     52/push-edx
 2958     53/push-ebx
 2959     56/push-esi
 2960     57/push-edi
 2961     # var line/ecx: (stream byte 512)
 2962     81 5/subop/subtract %esp 0x200/imm32
 2963     68/push 0x200/imm32/length
 2964     68/push 0/imm32/read
 2965     68/push 0/imm32/write
 2966     89/<- %ecx 4/r32/esp
 2967     # var word-slice/edx: slice
 2968     68/push 0/imm32/end
 2969     68/push 0/imm32/start
 2970     89/<- %edx 4/r32/esp
 2971     # var curr-function/edi: (addr (handle function))
 2972     bf/copy-to-edi _Program-functions/imm32
 2973     # var curr-type/esi: (addr (handle typeinfo))
 2974     be/copy-to-esi _Program-types/imm32
 2975     # var vars/ebx: (stack (addr var) 256)
 2976     81 5/subop/subtract %esp 0x400/imm32
 2977     68/push 0x400/imm32/length
 2978     68/push 0/imm32/top
 2979     89/<- %ebx 4/r32/esp
 2980     {
 2981 $parse-mu:line-loop:
 2982       (clear-stream %ecx)
 2983       (read-line-buffered *(ebp+8) %ecx)
 2984       # if (line->write == 0) break
 2985       81 7/subop/compare *ecx 0/imm32
 2986       0f 84/jump-if-= break/disp32
 2987 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
 2993       (next-mu-token %ecx %edx)
 2994       # if slice-empty?(word-slice) continue
 2995       (slice-empty? %edx)
 2996       3d/compare-eax-and 0/imm32/false
 2997       0f 85/jump-if-!= loop/disp32
 2998       # if (*word-slice->start == "#") continue
 2999       # . eax = *word-slice->start
 3000       8b/-> *edx 0/r32/eax
 3001       8a/copy-byte *eax 0/r32/AL
 3002       81 4/subop/and %eax 0xff/imm32
 3003       # . if (eax == '#') continue
 3004       3d/compare-eax-and 0x23/imm32/hash
 3005       0f 84/jump-if-= loop/disp32
 3006       # if (slice-equal?(word-slice, "fn")) parse a function
 3007       {
 3008 $parse-mu:fn:
 3009         (slice-equal? %edx "fn")
 3010         3d/compare-eax-and 0/imm32/false
 3011         0f 84/jump-if-= break/disp32
 3012         # var new-function/eax: (handle function) = populate-mu-function(line, in, vars)
 3013         (allocate Heap *Function-size)  # => eax
 3014         (zero-out %eax *Function-size)
 3015         (clear-stack %ebx)
 3016         (populate-mu-function-header %ecx %eax %ebx)
 3017         (populate-mu-function-body *(ebp+8) %eax %ebx)
 3018         # *curr-function = new-function
 3019         89/<- *edi 0/r32/eax
 3020         # curr-function = &new-function->next
 3021         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
 3022         e9/jump $parse-mu:line-loop/disp32
 3023       }
 3024       # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
 3025       {
 3026 $parse-mu:type:
 3027         (slice-equal? %edx "type")
 3028         3d/compare-eax-and 0/imm32
 3029         0f 84/jump-if-= break/disp32
 3030         (next-mu-token %ecx %edx)
 3031         # var type-id/eax: int
 3032         (pos-or-insert-slice Type-id %edx)  # => eax
 3033         # var new-type/eax: (handle typeinfo)
 3034         (find-or-create-typeinfo %eax)  # => eax
 3035         # TODO: ensure that 'line' has nothing else but '{'
 3036         (populate-mu-type *(ebp+8) %eax)  # => eax
 3037         e9/jump $parse-mu:line-loop/disp32
 3038       }
 3039       # otherwise abort
 3040       e9/jump $parse-mu:error1/disp32
 3041     } # end line loop
 3042 $parse-mu:end:
 3043     # . reclaim locals
 3044     81 0/subop/add %esp 0x630/imm32
 3045     # . restore registers
 3046     5f/pop-to-edi
 3047     5e/pop-to-esi
 3048     5b/pop-to-ebx
 3049     5a/pop-to-edx
 3050     59/pop-to-ecx
 3051     58/pop-to-eax
 3052     # . epilogue
 3053     89/<- %esp 5/r32/ebp
 3054     5d/pop-to-ebp
 3055     c3/return
 3056 
 3057 $parse-mu:error1:
 3058     # error("unexpected top-level command: " word-slice "\n")
 3059     (write-buffered Stderr "unexpected top-level command: ")
 3060     (write-slice-buffered Stderr %edx)
 3061     (write-buffered Stderr "\n")
 3062     (flush Stderr)
 3063     # . syscall(exit, 1)
 3064     bb/copy-to-ebx  1/imm32
 3065     b8/copy-to-eax  1/imm32/exit
 3066     cd/syscall  0x80/imm8
 3067     # never gets here
 3068 
 3069 $parse-mu:error2:
 3070     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
 3071     (print-int32-buffered Stderr *ebx)
 3072     (write-buffered Stderr " vars not reclaimed after fn '")
 3073     (write-slice-buffered Stderr *eax)  # Function-name
 3074     (write-buffered Stderr "'\n")
 3075     (flush Stderr)
 3076     # . syscall(exit, 1)
 3077     bb/copy-to-ebx  1/imm32
 3078     b8/copy-to-eax  1/imm32/exit
 3079     cd/syscall  0x80/imm8
 3080     # never gets here
 3081 
 3082 # scenarios considered:
 3083 # ✗ fn foo  # no block
 3084 # ✓ fn foo {
 3085 # ✗ fn foo { {
 3086 # ✗ fn foo { }
 3087 # ✗ fn foo { } {
 3088 # ✗ fn foo x {
 3089 # ✗ fn foo x: {
 3090 # ✓ fn foo x: int {
 3091 # ✓ fn foo x: int {
 3092 # ✓ fn foo x: int -> y/eax: int {
 3093 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
 3094     # pseudocode:
 3095     #   var name: slice
 3096     #   next-mu-token(first-line, name)
 3097     #   assert(name not in '{' '}' '->')
 3098     #   out->name = slice-to-string(name)
 3099     #   ## inouts
 3100     #   while true
 3101     #     ## name
 3102     #     name = next-mu-token(first-line)
 3103     #     if (name == '{') goto done
 3104     #     if (name == '->') break
 3105     #     assert(name != '}')
 3106     #     var v: (handle var) = parse-var-with-type(name, first-line)
 3107     #     assert(v->register == null)
 3108     #     # v->block-depth is implicitly 0
 3109     #     out->inouts = append(out->inouts, v)
 3110     #     push(vars, v)
 3111     #   ## outputs
 3112     #   while true
 3113     #     ## name
 3114     #     name = next-mu-token(first-line)
 3115     #     assert(name not in '{' '}' '->')
 3116     #     var v: (handle var) = parse-var-with-type(name, first-line)
 3117     #     assert(v->register != null)
 3118     #     out->outputs = append(out->outputs, v)
 3119     #   done:
 3120     #
 3121     # . prologue
 3122     55/push-ebp
 3123     89/<- %ebp 4/r32/esp
 3124     # . save registers
 3125     50/push-eax
 3126     51/push-ecx
 3127     52/push-edx
 3128     53/push-ebx
 3129     57/push-edi
 3130     # edi = out
 3131     8b/-> *(ebp+0xc) 7/r32/edi
 3132     # var word-slice/ecx: slice
 3133     68/push 0/imm32/end
 3134     68/push 0/imm32/start
 3135     89/<- %ecx 4/r32/esp
 3136     # read function name
 3137     (next-mu-token *(ebp+8) %ecx)
 3138     # error checking
 3139     # TODO: error if name starts with 'break' or 'loop'
 3140     # if (word-slice == '{') abort
 3141     (slice-equal? %ecx "{")   # => eax
 3142     3d/compare-eax-and 0/imm32/false
 3143     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3144     # if (word-slice == '->') abort
 3145     (slice-equal? %ecx "->")   # => eax
 3146     3d/compare-eax-and 0/imm32/false
 3147     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3148     # if (word-slice == '}') abort
 3149     (slice-equal? %ecx "}")   # => eax
 3150     3d/compare-eax-and 0/imm32/false
 3151     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3152     # save function name
 3153     (slice-to-string Heap %ecx)  # => eax
 3154     89/<- *edi 0/r32/eax  # Function-name
 3155     # initialize default subx-name as well
 3156     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
 3157     # save function inouts
 3158     {
 3159 $populate-mu-function-header:check-for-inout:
 3160       (next-mu-token *(ebp+8) %ecx)
 3161       # if (word-slice == '{') goto done
 3162       (slice-equal? %ecx "{")   # => eax
 3163       3d/compare-eax-and 0/imm32/false
 3164       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
 3165       # if (word-slice == '->') break
 3166       (slice-equal? %ecx "->")   # => eax
 3167       3d/compare-eax-and 0/imm32/false
 3168       0f 85/jump-if-!= break/disp32
 3169       # if (word-slice == '}') abort
 3170       (slice-equal? %ecx "}")   # => eax
 3171       3d/compare-eax-and 0/imm32/false
 3172       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3173       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
 3174       (parse-var-with-type %ecx *(ebp+8))  # => eax
 3175       89/<- %ebx 0/r32/eax
 3176       # assert(v->register == null)
 3177       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 3178       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
 3179       # v->block-depth is implicitly 0
 3180       #
 3181       # out->inouts = append(out->inouts, v)
 3182       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
 3183       89/<- *(edi+8) 0/r32/eax  # Function-inouts
 3184       # push(vars, v)
 3185       (push *(ebp+0x10) %ebx)
 3186       #
 3187       e9/jump loop/disp32
 3188     }
 3189     # save function outputs
 3190     {
 3191 $populate-mu-function-header:check-for-out:
 3192       (next-mu-token *(ebp+8) %ecx)
 3193       # if (word-slice == '{') break
 3194       (slice-equal? %ecx "{")   # => eax
 3195       3d/compare-eax-and 0/imm32/false
 3196       0f 85/jump-if-!= break/disp32
 3197       # if (word-slice == '->') abort
 3198       (slice-equal? %ecx "->")   # => eax
 3199       3d/compare-eax-and 0/imm32/false
 3200       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3201       # if (word-slice == '}') abort
 3202       (slice-equal? %ecx "}")   # => eax
 3203       3d/compare-eax-and 0/imm32/false
 3204       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3205       #
 3206       (parse-var-with-type %ecx *(ebp+8))  # => eax
 3207       89/<- %ebx 0/r32/eax
 3208       # assert(var->register != null)
 3209       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 3210       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
 3211       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
 3212       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
 3213       e9/jump loop/disp32
 3214     }
 3215 $populate-mu-function-header:done:
 3216     (check-no-tokens-left *(ebp+8))
 3217 $populate-mu-function-header:end:
 3218     # . reclaim locals
 3219     81 0/subop/add %esp 8/imm32
 3220     # . restore registers
 3221     5f/pop-to-edi
 3222     5b/pop-to-ebx
 3223     5a/pop-to-edx
 3224     59/pop-to-ecx
 3225     58/pop-to-eax
 3226     # . epilogue
 3227     89/<- %esp 5/r32/ebp
 3228     5d/pop-to-ebp
 3229     c3/return
 3230 
 3231 $populate-mu-function-header:error1:
 3232     # error("function header not in form 'fn <name> {'")
 3233     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
 3234     (flush Stderr)
 3235     (rewind-stream *(ebp+8))
 3236     (write-stream 2 *(ebp+8))
 3237     (write-buffered Stderr "'\n")
 3238     (flush Stderr)
 3239     # . syscall(exit, 1)
 3240     bb/copy-to-ebx  1/imm32
 3241     b8/copy-to-eax  1/imm32/exit
 3242     cd/syscall  0x80/imm8
 3243     # never gets here
 3244 
 3245 $populate-mu-function-header:error2:
 3246     # error("function input '" var "' cannot be in a register")
 3247     (write-buffered Stderr "function input '")
 3248     (write-buffered Stderr *ebx)  # Var-name
 3249     (write-buffered Stderr "' cannot be in a register")
 3250     (flush Stderr)
 3251     # . syscall(exit, 1)
 3252     bb/copy-to-ebx  1/imm32
 3253     b8/copy-to-eax  1/imm32/exit
 3254     cd/syscall  0x80/imm8
 3255     # never gets here
 3256 
 3257 $populate-mu-function-header:error3:
 3258     # error("function input '" var "' must be in a register")
 3259     (write-buffered Stderr "function input '")
 3260     (write-buffered Stderr *eax)  # Var-name
 3261     (write-buffered Stderr " must be in a register'")
 3262     (flush Stderr)
 3263     (rewind-stream *(ebp+8))
 3264     (write-stream 2 *(ebp+8))
 3265     (write-buffered Stderr "'\n")
 3266     (flush Stderr)
 3267     # . syscall(exit, 1)
 3268     bb/copy-to-ebx  1/imm32
 3269     b8/copy-to-eax  1/imm32/exit
 3270     cd/syscall  0x80/imm8
 3271     # never gets here
 3272 
 3273 test-function-header-with-arg:
 3274     # . prologue
 3275     55/push-ebp
 3276     89/<- %ebp 4/r32/esp
 3277     # setup
 3278     (clear-stream _test-input-stream)
 3279     (write _test-input-stream "foo n: int {\n")
 3280     # var result/ecx: function
 3281     2b/subtract-> *Function-size 4/r32/esp
 3282     89/<- %ecx 4/r32/esp
 3283     (zero-out %ecx *Function-size)
 3284     # var vars/ebx: (stack (addr var) 16)
 3285     81 5/subop/subtract %esp 0x10/imm32
 3286     68/push 0x10/imm32/length
 3287     68/push 0/imm32/top
 3288     89/<- %ebx 4/r32/esp
 3289     # convert
 3290     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3291     # check result
 3292     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
 3293     # edx: (handle list var) = result->inouts
 3294     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3295     # ebx: (handle var) = result->inouts->value
 3296     8b/-> *edx 3/r32/ebx  # List-value
 3297     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
 3298     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3299     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
 3300     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
 3301     # . epilogue
 3302     89/<- %esp 5/r32/ebp
 3303     5d/pop-to-ebp
 3304     c3/return
 3305 
 3306 test-function-header-with-multiple-args:
 3307     # . prologue
 3308     55/push-ebp
 3309     89/<- %ebp 4/r32/esp
 3310     # setup
 3311     (clear-stream _test-input-stream)
 3312     (write _test-input-stream "foo a: int, b: int c: int {\n")
 3313     # result/ecx: (handle function)
 3314     2b/subtract-> *Function-size 4/r32/esp
 3315     89/<- %ecx 4/r32/esp
 3316     (zero-out %ecx *Function-size)
 3317     # var vars/ebx: (stack (addr var) 16)
 3318     81 5/subop/subtract %esp 0x10/imm32
 3319     68/push 0x10/imm32/length
 3320     68/push 0/imm32/top
 3321     89/<- %ebx 4/r32/esp
 3322     # convert
 3323     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3324     # check result
 3325     (check-strings-equal *ecx "foo")  # Function-name
 3326     # edx: (handle list var) = result->inouts
 3327     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3328 $test-function-header-with-multiple-args:inout0:
 3329     # ebx: (handle var) = result->inouts->value
 3330     8b/-> *edx 3/r32/ebx  # List-value
 3331     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
 3332     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3333     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
 3334     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
 3335     # edx = result->inouts->next
 3336     8b/-> *(edx+4) 2/r32/edx  # List-next
 3337 $test-function-header-with-multiple-args:inout1:
 3338     # ebx = result->inouts->next->value
 3339     8b/-> *edx 3/r32/ebx  # List-value
 3340     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
 3341     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3342     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
 3343     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
 3344     # edx = result->inouts->next->next
 3345     8b/-> *(edx+4) 2/r32/edx  # List-next
 3346 $test-function-header-with-multiple-args:inout2:
 3347     # ebx = result->inouts->next->next->value
 3348     8b/-> *edx 3/r32/ebx  # List-value
 3349     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
 3350     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3351     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
 3352     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
 3353     # . epilogue
 3354     89/<- %esp 5/r32/ebp
 3355     5d/pop-to-ebp
 3356     c3/return
 3357 
 3358 test-function-with-multiple-args-and-outputs:
 3359     # . prologue
 3360     55/push-ebp
 3361     89/<- %ebp 4/r32/esp
 3362     # setup
 3363     (clear-stream _test-input-stream)
 3364     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
 3365     # result/ecx: (handle function)
 3366     2b/subtract-> *Function-size 4/r32/esp
 3367     89/<- %ecx 4/r32/esp
 3368     (zero-out %ecx *Function-size)
 3369     # var vars/ebx: (stack (addr var) 16)
 3370     81 5/subop/subtract %esp 0x10/imm32
 3371     68/push 0x10/imm32/length
 3372     68/push 0/imm32/top
 3373     89/<- %ebx 4/r32/esp
 3374     # convert
 3375     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3376     # check result
 3377     (check-strings-equal *ecx "foo")  # Function-name
 3378     # edx: (handle list var) = result->inouts
 3379     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3380     # ebx: (handle var) = result->inouts->value
 3381     8b/-> *edx 3/r32/ebx  # List-value
 3382     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
 3383     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3384     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
 3385     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
 3386     # edx = result->inouts->next
 3387     8b/-> *(edx+4) 2/r32/edx  # List-next
 3388     # ebx = result->inouts->next->value
 3389     8b/-> *edx 3/r32/ebx  # List-value
 3390     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
 3391     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3392     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
 3393     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
 3394     # edx = result->inouts->next->next
 3395     8b/-> *(edx+4) 2/r32/edx  # List-next
 3396     # ebx = result->inouts->next->next->value
 3397     8b/-> *edx 3/r32/ebx  # List-value
 3398     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
 3399     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3400     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
 3401     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
 3402     # edx: (handle list var) = result->outputs
 3403     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
 3404     # ebx: (handle var) = result->outputs->value
 3405     8b/-> *edx 3/r32/ebx  # List-value
 3406     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
 3407     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3408     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3409     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
 3410     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
 3411     # edx = result->outputs->next
 3412     8b/-> *(edx+4) 2/r32/edx  # List-next
 3413     # ebx = result->outputs->next->value
 3414     8b/-> *edx 3/r32/ebx  # List-value
 3415     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
 3416     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3417     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3418     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
 3419     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
 3420     # . epilogue
 3421     89/<- %esp 5/r32/ebp
 3422     5d/pop-to-ebp
 3423     c3/return
 3424 
 3425 # format for variables with types
 3426 #   x: int
 3427 #   x: int,
 3428 #   x/eax: int
 3429 #   x/eax: int,
 3430 # ignores at most one trailing comma
 3431 # WARNING: modifies name
 3432 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
 3433     # pseudocode:
 3434     #   var s: slice
 3435     #   if (!slice-ends-with(name, ":"))
 3436     #     abort
 3437     #   --name->end to skip ':'
 3438     #   next-token-from-slice(name->start, name->end, '/', s)
 3439     #   result = new-var-from-slice(s)
 3440     #   ## register
 3441     #   next-token-from-slice(s->end, name->end, '/', s)
 3442     #   if (!slice-empty?(s))
 3443     #     v->register = slice-to-string(s)
 3444     #   ## type
 3445     #   var type: (handle tree type-id) = parse-type(first-line)
 3446     #   v->type = type
 3447     #   return v
 3448     #
 3449     # . prologue
 3450     55/push-ebp
 3451     89/<- %ebp 4/r32/esp
 3452     # . save registers
 3453     51/push-ecx
 3454     52/push-edx
 3455     53/push-ebx
 3456     56/push-esi
 3457     57/push-edi
 3458     # esi = name
 3459     8b/-> *(ebp+8) 6/r32/esi
 3460     # if (!slice-ends-with?(name, ":")) abort
 3461     8b/-> *(esi+4) 1/r32/ecx  # Slice-end
 3462     49/decrement-ecx
 3463     8a/copy-byte *ecx 1/r32/CL
 3464     81 4/subop/and %ecx 0xff/imm32
 3465     81 7/subop/compare %ecx 0x3a/imm32/colon
 3466     0f 85/jump-if-!= $parse-var-with-type:abort/disp32
 3467     # --name->end to skip ':'
 3468     ff 1/subop/decrement *(esi+4)
 3469     # var s/ecx: slice
 3470     68/push 0/imm32/end
 3471     68/push 0/imm32/start
 3472     89/<- %ecx 4/r32/esp
 3473 $parse-var-with-type:parse-name:
 3474     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
 3475 $parse-var-with-type:create-var:
 3476     # edi = new-var-from-slice(s)
 3477     (new-var-from-slice Heap %ecx)  # => eax
 3478     89/<- %edi 0/r32/eax
 3479     # save v->register
 3480 $parse-var-with-type:save-register:
 3481     # s = next-token(...)
 3482     (next-token-from-slice *(ecx+4) *(esi+4) 0x2f %ecx)  # s->end, name->end, '/'
 3483     # if (!slice-empty?(s)) v->register = slice-to-string(s)
 3484     {
 3485 $parse-var-with-type:write-register:
 3486       (slice-empty? %ecx)  # => eax
 3487       3d/compare-eax-and 0/imm32/false
 3488       75/jump-if-!= break/disp8
 3489       (slice-to-string Heap %ecx)
 3490       89/<- *(edi+0x10) 0/r32/eax  # Var-register
 3491     }
 3492 $parse-var-with-type:save-type:
 3493     (parse-type Heap *(ebp+0xc))  # => eax
 3494 #?     (write-buffered Stderr "saving to var ")
 3495 #?     (print-int32-buffered Stderr %edi)
 3496 #?     (write-buffered Stderr Newline)
 3497 #?     (flush Stderr)
 3498     89/<- *(edi+4) 0/r32/eax  # Var-type
 3499 $parse-var-with-type:end:
 3500     # return result
 3501     89/<- %eax 7/r32/edi
 3502     # . reclaim locals
 3503     81 0/subop/add %esp 8/imm32
 3504     # . restore registers
 3505     5f/pop-to-edi
 3506     5e/pop-to-esi
 3507     5b/pop-to-ebx
 3508     5a/pop-to-edx
 3509     59/pop-to-ecx
 3510     # . epilogue
 3511     89/<- %esp 5/r32/ebp
 3512     5d/pop-to-ebp
 3513     c3/return
 3514 
 3515 $parse-var-with-type:abort:
 3516     # error("var should have form 'name: type' in '" line "'\n")
 3517     (write-buffered Stderr "var should have form 'name: type' in '")
 3518     (flush Stderr)
 3519     (rewind-stream *(ebp+0xc))
 3520     (write-stream 2 *(ebp+0xc))
 3521     (write-buffered Stderr "'\n")
 3522     (flush Stderr)
 3523     # . syscall(exit, 1)
 3524     bb/copy-to-ebx  1/imm32
 3525     b8/copy-to-eax  1/imm32/exit
 3526     cd/syscall  0x80/imm8
 3527     # never gets here
 3528 
 3529 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3530     # pseudocode:
 3531     #   var s: slice = next-mu-token(in)
 3532     #   assert s != ""
 3533     #   assert s != "->"
 3534     #   assert s != "{"
 3535     #   assert s != "}"
 3536     #   if s == ")"
 3537     #     return 0
 3538     #   result = allocate(Tree)
 3539     #   zero-out(result, *Tree-size)
 3540     #   if s != "("
 3541     #     result->left = pos-or-insert-slice(Type-id, s)
 3542     #     return
 3543     #   result->left = parse-type(ad, in)
 3544     #   result->right = parse-type-tree(ad, in)
 3545     #
 3546     # . prologue
 3547     55/push-ebp
 3548     89/<- %ebp 4/r32/esp
 3549     # . save registers
 3550     51/push-ecx
 3551     52/push-edx
 3552     # var s/ecx: slice
 3553     68/push 0/imm32
 3554     68/push 0/imm32
 3555     89/<- %ecx 4/r32/esp
 3556     # s = next-mu-token(in)
 3557     (next-mu-token *(ebp+0xc) %ecx)
 3558 #?     (write-buffered Stderr "tok: ")
 3559 #?     (write-slice-buffered Stderr %ecx)
 3560 #?     (write-buffered Stderr "$\n")
 3561 #?     (flush Stderr)
 3562     # assert s != ""
 3563     (slice-equal? %ecx "")
 3564     3d/compare-eax-and 0/imm32/false
 3565     0f 85/jump-if-!= $parse-type:abort/disp32
 3566     # assert s != "{"
 3567     (slice-equal? %ecx "{")
 3568     3d/compare-eax-and 0/imm32/false
 3569     0f 85/jump-if-!= $parse-type:abort/disp32
 3570     # assert s != "}"
 3571     (slice-equal? %ecx "}")
 3572     3d/compare-eax-and 0/imm32/false
 3573     0f 85/jump-if-!= $parse-type:abort/disp32
 3574     # assert s != "->"
 3575     (slice-equal? %ecx "->")
 3576     3d/compare-eax-and 0/imm32/false
 3577     0f 85/jump-if-!= $parse-type:abort/disp32
 3578     # if (s == ")") return 0
 3579     (slice-equal? %ecx ")")
 3580     3d/compare-eax-and 0/imm32/false
 3581     b8/copy-to-eax 0/imm32
 3582     0f 85/jump-if-!= $parse-type:end/disp32
 3583     # var result/edx: (handle tree type-id)
 3584     (allocate *(ebp+8) *Tree-size)  # => eax
 3585     (zero-out %eax *Tree-size)
 3586     89/<- %edx 0/r32/eax
 3587     {
 3588       # if (s != "(") break
 3589       (slice-equal? %ecx "(")
 3590       3d/compare-eax-and 0/imm32/false
 3591       75/jump-if-!= break/disp8
 3592       # EGREGIOUS HACK for static array sizes: if s is a number, parse it
 3593       {
 3594         (is-hex-int? %ecx)  # => eax
 3595         3d/compare-eax-and 0/imm32/false
 3596         74/jump-if-= break/disp8
 3597         (parse-hex-int-from-slice %ecx)  # => eax
 3598         89/<- *edx 0/r32/eax  # Tree-left
 3599         e9/jump $parse-type:return-edx/disp32
 3600       }
 3601       # result->left = pos-or-insert-slice(Type-id, s)
 3602       (pos-or-insert-slice Type-id %ecx)  # => eax
 3603 #?       (write-buffered Stderr "=> {")
 3604 #?       (print-int32-buffered Stderr %eax)
 3605 #?       (write-buffered Stderr ", 0}\n")
 3606 #?       (flush Stderr)
 3607       89/<- *edx 0/r32/eax  # Tree-left
 3608       e9/jump $parse-type:return-edx/disp32
 3609     }
 3610     # otherwise s == "("
 3611     # result->left = parse-type(ad, in)
 3612     (parse-type *(ebp+8) *(ebp+0xc))
 3613 #?     (write-buffered Stderr "=> {")
 3614 #?     (print-int32-buffered Stderr %eax)
 3615     89/<- *edx 0/r32/eax  # Tree-left
 3616     # result->right = parse-type-tree(ad, in)
 3617     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3618 #?     (write-buffered Stderr Space)
 3619 #?     (print-int32-buffered Stderr %eax)
 3620 #?     (write-buffered Stderr "}\n")
 3621 #?     (flush Stderr)
 3622     89/<- *(edx+4) 0/r32/eax  # Tree-right
 3623 $parse-type:return-edx:
 3624     89/<- %eax 2/r32/edx
 3625 $parse-type:end:
 3626     # . reclaim locals
 3627     81 0/subop/add %esp 8/imm32
 3628     # . restore registers
 3629     5a/pop-to-edx
 3630     59/pop-to-ecx
 3631     # . epilogue
 3632     89/<- %esp 5/r32/ebp
 3633     5d/pop-to-ebp
 3634     c3/return
 3635 
 3636 $parse-type:abort:
 3637     # error("unexpected token when parsing type: '" s "'\n")
 3638     (write-buffered Stderr "unexpected token when parsing type: '")
 3639     (write-slice-buffered Stderr %ecx)
 3640     (write-buffered Stderr "'\n")
 3641     (flush Stderr)
 3642     # . syscall(exit, 1)
 3643     bb/copy-to-ebx  1/imm32
 3644     b8/copy-to-eax  1/imm32/exit
 3645     cd/syscall  0x80/imm8
 3646     # never gets here
 3647 
 3648 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3649     # pseudocode:
 3650     #   var tmp: (handle tree type-id) = parse-type(ad, in)
 3651     #   if tmp == 0
 3652     #     return 0
 3653     #   result = allocate(Tree)
 3654     #   zero-out(result, *Tree-size)
 3655     #   result->left = tmp
 3656     #   result->right = parse-type-tree(ad, in)
 3657     #
 3658     # . prologue
 3659     55/push-ebp
 3660     89/<- %ebp 4/r32/esp
 3661     # . save registers
 3662     51/push-ecx
 3663     52/push-edx
 3664     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
 3665     (parse-type *(ebp+8) *(ebp+0xc))
 3666     # if (tmp == 0) return tmp
 3667     3d/compare-eax-and 0/imm32
 3668     74/jump-if-= $parse-type-tree:end/disp8
 3669     # var tmp2/ecx = tmp
 3670     89/<- %ecx 0/r32/eax
 3671     # var result/edx: (handle tree type-id)
 3672     (allocate *(ebp+8) *Tree-size)  # => eax
 3673     (zero-out %eax *Tree-size)
 3674     89/<- %edx 0/r32/eax
 3675     # result->left = tmp2
 3676     89/<- *edx 1/r32/ecx  # Tree-left
 3677     # result->right = parse-type-tree(ad, in)
 3678     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3679     89/<- *(edx+4) 0/r32/eax  # Tree-right
 3680 $parse-type-tree:return-edx:
 3681     89/<- %eax 2/r32/edx
 3682 $parse-type-tree:end:
 3683     # . restore registers
 3684     5a/pop-to-edx
 3685     59/pop-to-ecx
 3686     # . epilogue
 3687     89/<- %esp 5/r32/ebp
 3688     5d/pop-to-ebp
 3689     c3/return
 3690 
 3691 next-mu-token:  # in: (addr stream byte), out: (addr slice)
 3692     # pseudocode:
 3693     # start:
 3694     #   skip-chars-matching-whitespace(in)
 3695     #   if in->read >= in->write              # end of in
 3696     #     out = {0, 0}
 3697     #     return
 3698     #   out->start = &in->data[in->read]
 3699     #   var curr-byte/eax: byte = in->data[in->read]
 3700     #   if curr->byte == ','                  # comment token
 3701     #     ++in->read
 3702     #     goto start
 3703     #   if curr-byte == '#'                   # comment
 3704     #     goto done                             # treat as eof
 3705     #   if curr-byte == '"'                   # string literal
 3706     #     skip-string(in)
 3707     #     goto done                           # no metadata
 3708     #   if curr-byte == '('
 3709     #     ++in->read
 3710     #     goto done
 3711     #   if curr-byte == ')'
 3712     #     ++in->read
 3713     #     goto done
 3714     #   # read a word
 3715     #   while true
 3716     #     if in->read >= in->write
 3717     #       break
 3718     #     curr-byte = in->data[in->read]
 3719     #     if curr-byte == ' '
 3720     #       break
 3721     #     if curr-byte == '\r'
 3722     #       break
 3723     #     if curr-byte == '\n'
 3724     #       break
 3725     #     if curr-byte == '('
 3726     #       break
 3727     #     if curr-byte == ')'
 3728     #       break
 3729     #     if curr-byte == ','
 3730     #       break
 3731     #     ++in->read
 3732     # done:
 3733     #   out->end = &in->data[in->read]
 3734     #
 3735     # . prologue
 3736     55/push-ebp
 3737     89/<- %ebp 4/r32/esp
 3738     # . save registers
 3739     50/push-eax
 3740     51/push-ecx
 3741     56/push-esi
 3742     57/push-edi
 3743     # esi = in
 3744     8b/-> *(ebp+8) 6/r32/esi
 3745     # edi = out
 3746     8b/-> *(ebp+0xc) 7/r32/edi
 3747 $next-mu-token:start:
 3748     (skip-chars-matching-whitespace %esi)
 3749 $next-mu-token:check0:
 3750     # if (in->read >= in->write) return out = {0, 0}
 3751     # . ecx = in->read
 3752     8b/-> *(esi+4) 1/r32/ecx
 3753     # . if (ecx >= in->write) return out = {0, 0}
 3754     3b/compare 1/r32/ecx *esi
 3755     c7 0/subop/copy *edi 0/imm32
 3756     c7 0/subop/copy *(edi+4) 0/imm32
 3757     0f 8d/jump-if->= $next-mu-token:end/disp32
 3758     # out->start = &in->data[in->read]
 3759     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3760     89/<- *edi 0/r32/eax
 3761     # var curr-byte/eax: byte = in->data[in->read]
 3762     31/xor %eax 0/r32/eax
 3763     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3764     {
 3765 $next-mu-token:check-for-comma:
 3766       # if (curr-byte != ',') break
 3767       3d/compare-eax-and 0x2c/imm32/comma
 3768       75/jump-if-!= break/disp8
 3769       # ++in->read
 3770       ff 0/subop/increment *(esi+4)
 3771       # restart
 3772       e9/jump $next-mu-token:start/disp32
 3773     }
 3774     {
 3775 $next-mu-token:check-for-comment:
 3776       # if (curr-byte != '#') break
 3777       3d/compare-eax-and 0x23/imm32/pound
 3778       75/jump-if-!= break/disp8
 3779       # return eof
 3780       e9/jump $next-mu-token:done/disp32
 3781     }
 3782     {
 3783 $next-mu-token:check-for-string-literal:
 3784       # if (curr-byte != '"') break
 3785       3d/compare-eax-and 0x22/imm32/dquote
 3786       75/jump-if-!= break/disp8
 3787       (skip-string %esi)
 3788       # return
 3789       e9/jump $next-mu-token:done/disp32
 3790     }
 3791     {
 3792 $next-mu-token:check-for-open-paren:
 3793       # if (curr-byte != '(') break
 3794       3d/compare-eax-and 0x28/imm32/open-paren
 3795       75/jump-if-!= break/disp8
 3796       # ++in->read
 3797       ff 0/subop/increment *(esi+4)
 3798       # return
 3799       e9/jump $next-mu-token:done/disp32
 3800     }
 3801     {
 3802 $next-mu-token:check-for-close-paren:
 3803       # if (curr-byte != ')') break
 3804       3d/compare-eax-and 0x29/imm32/close-paren
 3805       75/jump-if-!= break/disp8
 3806       # ++in->read
 3807       ff 0/subop/increment *(esi+4)
 3808       # return
 3809       e9/jump $next-mu-token:done/disp32
 3810     }
 3811     {
 3812 $next-mu-token:regular-word-without-metadata:
 3813       # if (in->read >= in->write) break
 3814       # . ecx = in->read
 3815       8b/-> *(esi+4) 1/r32/ecx
 3816       # . if (ecx >= in->write) break
 3817       3b/compare *esi 1/r32/ecx
 3818       7d/jump-if->= break/disp8
 3819       # var c/eax: byte = in->data[in->read]
 3820       31/xor %eax 0/r32/eax
 3821       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3822       # if (c == ' ') break
 3823       3d/compare-eax-and 0x20/imm32/space
 3824       74/jump-if-= break/disp8
 3825       # if (c == '\r') break
 3826       3d/compare-eax-and 0xd/imm32/carriage-return
 3827       74/jump-if-= break/disp8
 3828       # if (c == '\n') break
 3829       3d/compare-eax-and 0xa/imm32/newline
 3830       74/jump-if-= break/disp8
 3831       # if (c == '(') break
 3832       3d/compare-eax-and 0x28/imm32/open-paren
 3833       0f 84/jump-if-= break/disp32
 3834       # if (c == ')') break
 3835       3d/compare-eax-and 0x29/imm32/close-paren
 3836       0f 84/jump-if-= break/disp32
 3837       # if (c == ',') break
 3838       3d/compare-eax-and 0x2c/imm32/comma
 3839       0f 84/jump-if-= break/disp32
 3840       # ++in->read
 3841       ff 0/subop/increment *(esi+4)
 3842       #
 3843       e9/jump loop/disp32
 3844     }
 3845 $next-mu-token:done:
 3846     # out->end = &in->data[in->read]
 3847     8b/-> *(esi+4) 1/r32/ecx
 3848     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3849     89/<- *(edi+4) 0/r32/eax
 3850 $next-mu-token:end:
 3851     # . restore registers
 3852     5f/pop-to-edi
 3853     5e/pop-to-esi
 3854     59/pop-to-ecx
 3855     58/pop-to-eax
 3856     # . epilogue
 3857     89/<- %esp 5/r32/ebp
 3858     5d/pop-to-ebp
 3859     c3/return
 3860 
 3861 pos-or-insert-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 3862     # . prologue
 3863     55/push-ebp
 3864     89/<- %ebp 4/r32/esp
 3865     # if (pos-slice(arr, s) != -1) return it
 3866     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 3867     3d/compare-eax-and -1/imm32
 3868     75/jump-if-!= $pos-or-insert-slice:end/disp8
 3869 $pos-or-insert-slice:insert:
 3870     (slice-to-string Heap *(ebp+0xc))  # => eax
 3871     (write-int *(ebp+8) %eax)
 3872     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 3873 $pos-or-insert-slice:end:
 3874     # . epilogue
 3875     89/<- %esp 5/r32/ebp
 3876     5d/pop-to-ebp
 3877     c3/return
 3878 
 3879 # return the index in an array of strings matching 's', -1 if not found
 3880 # index is denominated in elements, not bytes
 3881 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 3882     # . prologue
 3883     55/push-ebp
 3884     89/<- %ebp 4/r32/esp
 3885     # . save registers
 3886     51/push-ecx
 3887     52/push-edx
 3888     53/push-ebx
 3889     56/push-esi
 3890 #?     (write-buffered Stderr "pos-slice: ")
 3891 #?     (write-slice-buffered Stderr *(ebp+0xc))
 3892 #?     (write-buffered Stderr "\n")
 3893 #?     (flush Stderr)
 3894     # esi = arr
 3895     8b/-> *(ebp+8) 6/r32/esi
 3896     # var index/ecx: int = 0
 3897     b9/copy-to-ecx 0/imm32
 3898     # var curr/edx: (addr (addr array byte)) = arr->data
 3899     8d/copy-address *(esi+0xc) 2/r32/edx
 3900     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
 3901     8b/-> *esi 3/r32/ebx
 3902     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
 3903     {
 3904 #?       (write-buffered Stderr "  ")
 3905 #?       (print-int32-buffered Stderr %ecx)
 3906 #?       (write-buffered Stderr "\n")
 3907 #?       (flush Stderr)
 3908       # if (curr >= max) return -1
 3909       39/compare %edx 3/r32/ebx
 3910       b8/copy-to-eax -1/imm32
 3911       73/jump-if-addr>= $pos-slice:end/disp8
 3912       # if (slice-equal?(s, *curr)) break
 3913       (slice-equal? *(ebp+0xc) *edx)  # => eax
 3914       3d/compare-eax-and 0/imm32/false
 3915       75/jump-if-!= break/disp8
 3916       # ++index
 3917       41/increment-ecx
 3918       # curr += 4
 3919       81 0/subop/add %edx 4/imm32
 3920       #
 3921       eb/jump loop/disp8
 3922     }
 3923     # return index
 3924     89/<- %eax 1/r32/ecx
 3925 $pos-slice:end:
 3926 #?     (write-buffered Stderr "=> ")
 3927 #?     (print-int32-buffered Stderr %eax)
 3928 #?     (write-buffered Stderr "\n")
 3929     # . restore registers
 3930     5e/pop-to-esi
 3931     5b/pop-to-ebx
 3932     5a/pop-to-edx
 3933     59/pop-to-ecx
 3934     # . epilogue
 3935     89/<- %esp 5/r32/ebp
 3936     5d/pop-to-ebp
 3937     c3/return
 3938 
 3939 test-parse-var-with-type:
 3940     # . prologue
 3941     55/push-ebp
 3942     89/<- %ebp 4/r32/esp
 3943     # (eax..ecx) = "x:"
 3944     b8/copy-to-eax "x:"/imm32
 3945     8b/-> *eax 1/r32/ecx
 3946     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3947     05/add-to-eax 4/imm32
 3948     # var slice/ecx: slice = {eax, ecx}
 3949     51/push-ecx
 3950     50/push-eax
 3951     89/<- %ecx 4/r32/esp
 3952     # _test-input-stream contains "int"
 3953     (clear-stream _test-input-stream)
 3954     (write _test-input-stream "int")
 3955     #
 3956     (parse-var-with-type %ecx _test-input-stream)
 3957     8b/-> *eax 2/r32/edx  # Var-name
 3958     (check-strings-equal %edx "x" "F - test-var-with-type/name")
 3959     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3960     (check-ints-equal *edx 1 "F - test-var-with-type/type")
 3961     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
 3962     # . epilogue
 3963     89/<- %esp 5/r32/ebp
 3964     5d/pop-to-ebp
 3965     c3/return
 3966 
 3967 test-parse-var-with-type-and-register:
 3968     # . prologue
 3969     55/push-ebp
 3970     89/<- %ebp 4/r32/esp
 3971     # (eax..ecx) = "x/eax:"
 3972     b8/copy-to-eax "x/eax:"/imm32
 3973     8b/-> *eax 1/r32/ecx
 3974     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 3975     05/add-to-eax 4/imm32
 3976     # var slice/ecx: slice = {eax, ecx}
 3977     51/push-ecx
 3978     50/push-eax
 3979     89/<- %ecx 4/r32/esp
 3980     # _test-input-stream contains "int"
 3981     (clear-stream _test-input-stream)
 3982     (write _test-input-stream "int")
 3983     #
 3984     (parse-var-with-type %ecx _test-input-stream)
 3985     8b/-> *eax 2/r32/edx  # Var-name
 3986     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
 3987     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 3988     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
 3989     8b/-> *(eax+4) 2/r32/edx  # Var-type
 3990     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
 3991     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
 3992     # . epilogue
 3993     89/<- %esp 5/r32/ebp
 3994     5d/pop-to-ebp
 3995     c3/return
 3996 
 3997 test-parse-var-with-trailing-characters:
 3998     # . prologue
 3999     55/push-ebp
 4000     89/<- %ebp 4/r32/esp
 4001     # (eax..ecx) = "x:"
 4002     b8/copy-to-eax "x:"/imm32
 4003     8b/-> *eax 1/r32/ecx
 4004     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4005     05/add-to-eax 4/imm32
 4006     # var slice/ecx: slice = {eax, ecx}
 4007     51/push-ecx
 4008     50/push-eax
 4009     89/<- %ecx 4/r32/esp
 4010     # _test-input-stream contains "int,"
 4011     (clear-stream _test-input-stream)
 4012     (write _test-input-stream "int,")
 4013     #
 4014     (parse-var-with-type %ecx _test-input-stream)
 4015     8b/-> *eax 2/r32/edx  # Var-name
 4016     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
 4017     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4018     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
 4019     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4020     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
 4021     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
 4022     # . epilogue
 4023     89/<- %esp 5/r32/ebp
 4024     5d/pop-to-ebp
 4025     c3/return
 4026 
 4027 test-parse-var-with-register-and-trailing-characters:
 4028     # . prologue
 4029     55/push-ebp
 4030     89/<- %ebp 4/r32/esp
 4031     # (eax..ecx) = "x/eax:"
 4032     b8/copy-to-eax "x/eax:"/imm32
 4033     8b/-> *eax 1/r32/ecx
 4034     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4035     05/add-to-eax 4/imm32
 4036     # var slice/ecx: slice = {eax, ecx}
 4037     51/push-ecx
 4038     50/push-eax
 4039     89/<- %ecx 4/r32/esp
 4040     # _test-input-stream contains "int,"
 4041     (clear-stream _test-input-stream)
 4042     (write _test-input-stream "int,")
 4043     #
 4044     (parse-var-with-type %ecx _test-input-stream)
 4045     8b/-> *eax 2/r32/edx  # Var-name
 4046     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
 4047     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4048     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
 4049     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4050     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
 4051     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
 4052     # . epilogue
 4053     89/<- %esp 5/r32/ebp
 4054     5d/pop-to-ebp
 4055     c3/return
 4056 
 4057 test-parse-var-with-compound-type:
 4058     # . prologue
 4059     55/push-ebp
 4060     89/<- %ebp 4/r32/esp
 4061     # (eax..ecx) = "x:"
 4062     b8/copy-to-eax "x:"/imm32
 4063     8b/-> *eax 1/r32/ecx
 4064     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4065     05/add-to-eax 4/imm32
 4066     # var slice/ecx: slice = {eax, ecx}
 4067     51/push-ecx
 4068     50/push-eax
 4069     89/<- %ecx 4/r32/esp
 4070     # _test-input-stream contains "(addr int)"
 4071     (clear-stream _test-input-stream)
 4072     (write _test-input-stream "(addr int)")
 4073     #
 4074     (parse-var-with-type %ecx _test-input-stream)
 4075     8b/-> *eax 2/r32/edx  # Var-name
 4076     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
 4077     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4078     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
 4079     # var type/edx: (handle tree type-id) = var->type
 4080     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4081     # type->left == atom(addr)
 4082     8b/-> *edx 0/r32/eax  # Atom-value
 4083     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
 4084     # type->right->left == atom(int)
 4085     8b/-> *(edx+4) 2/r32/edx  # Tree-right
 4086     8b/-> *edx 0/r32/eax  # Tree-left
 4087     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
 4088     # type->right->right == null
 4089     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
 4090     # . epilogue
 4091     89/<- %esp 5/r32/ebp
 4092     5d/pop-to-ebp
 4093     c3/return
 4094 
 4095 # identifier starts with a letter or '$' or '_'
 4096 # no constraints at the moment on later letters
 4097 # all we really want to do so far is exclude '{', '}' and '->'
 4098 is-identifier?:  # in: (addr slice) -> result/eax: boolean
 4099     # . prologue
 4100     55/push-ebp
 4101     89/<- %ebp 4/r32/esp
 4102     # if (slice-empty?(in)) return false
 4103     (slice-empty? *(ebp+8))  # => eax
 4104     3d/compare-eax-and 0/imm32/false
 4105     75/jump-if-!= $is-identifier?:false/disp8
 4106     # var c/eax: byte = *in->start
 4107     8b/-> *(ebp+8) 0/r32/eax
 4108     8b/-> *eax 0/r32/eax
 4109     8a/copy-byte *eax 0/r32/AL
 4110     81 4/subop/and %eax 0xff/imm32
 4111     # if (c == '$') return true
 4112     3d/compare-eax-and 0x24/imm32/$
 4113     74/jump-if-= $is-identifier?:true/disp8
 4114     # if (c == '_') return true
 4115     3d/compare-eax-and 0x5f/imm32/_
 4116     74/jump-if-= $is-identifier?:true/disp8
 4117     # drop case
 4118     25/and-eax-with 0x5f/imm32
 4119     # if (c < 'A') return false
 4120     3d/compare-eax-and 0x41/imm32/A
 4121     7c/jump-if-< $is-identifier?:false/disp8
 4122     # if (c > 'Z') return false
 4123     3d/compare-eax-and 0x5a/imm32/Z
 4124     7f/jump-if-> $is-identifier?:false/disp8
 4125     # otherwise return true
 4126 $is-identifier?:true:
 4127     b8/copy-to-eax 1/imm32/true
 4128     eb/jump $is-identifier?:end/disp8
 4129 $is-identifier?:false:
 4130     b8/copy-to-eax 0/imm32/false
 4131 $is-identifier?:end:
 4132     # . epilogue
 4133     89/<- %esp 5/r32/ebp
 4134     5d/pop-to-ebp
 4135     c3/return
 4136 
 4137 test-is-identifier-dollar:
 4138     # . prologue
 4139     55/push-ebp
 4140     89/<- %ebp 4/r32/esp
 4141     # (eax..ecx) = "$a"
 4142     b8/copy-to-eax "$a"/imm32
 4143     8b/-> *eax 1/r32/ecx
 4144     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4145     05/add-to-eax 4/imm32
 4146     # var slice/ecx: slice = {eax, ecx}
 4147     51/push-ecx
 4148     50/push-eax
 4149     89/<- %ecx 4/r32/esp
 4150     #
 4151     (is-identifier? %ecx)
 4152     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
 4153     # . epilogue
 4154     89/<- %esp 5/r32/ebp
 4155     5d/pop-to-ebp
 4156     c3/return
 4157 
 4158 test-is-identifier-underscore:
 4159     # . prologue
 4160     55/push-ebp
 4161     89/<- %ebp 4/r32/esp
 4162     # (eax..ecx) = "_a"
 4163     b8/copy-to-eax "_a"/imm32
 4164     8b/-> *eax 1/r32/ecx
 4165     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4166     05/add-to-eax 4/imm32
 4167     # var slice/ecx: slice = {eax, ecx}
 4168     51/push-ecx
 4169     50/push-eax
 4170     89/<- %ecx 4/r32/esp
 4171     #
 4172     (is-identifier? %ecx)
 4173     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
 4174     # . epilogue
 4175     89/<- %esp 5/r32/ebp
 4176     5d/pop-to-ebp
 4177     c3/return
 4178 
 4179 test-is-identifier-a:
 4180     # . prologue
 4181     55/push-ebp
 4182     89/<- %ebp 4/r32/esp
 4183     # (eax..ecx) = "a$"
 4184     b8/copy-to-eax "a$"/imm32
 4185     8b/-> *eax 1/r32/ecx
 4186     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4187     05/add-to-eax 4/imm32
 4188     # var slice/ecx: slice = {eax, ecx}
 4189     51/push-ecx
 4190     50/push-eax
 4191     89/<- %ecx 4/r32/esp
 4192     #
 4193     (is-identifier? %ecx)
 4194     (check-ints-equal %eax 1 "F - test-is-identifier-a")
 4195     # . epilogue
 4196     89/<- %esp 5/r32/ebp
 4197     5d/pop-to-ebp
 4198     c3/return
 4199 
 4200 test-is-identifier-z:
 4201     # . prologue
 4202     55/push-ebp
 4203     89/<- %ebp 4/r32/esp
 4204     # (eax..ecx) = "z$"
 4205     b8/copy-to-eax "z$"/imm32
 4206     8b/-> *eax 1/r32/ecx
 4207     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4208     05/add-to-eax 4/imm32
 4209     # var slice/ecx: slice = {eax, ecx}
 4210     51/push-ecx
 4211     50/push-eax
 4212     89/<- %ecx 4/r32/esp
 4213     #
 4214     (is-identifier? %ecx)
 4215     (check-ints-equal %eax 1 "F - test-is-identifier-z")
 4216     # . epilogue
 4217     89/<- %esp 5/r32/ebp
 4218     5d/pop-to-ebp
 4219     c3/return
 4220 
 4221 test-is-identifier-A:
 4222     # . prologue
 4223     55/push-ebp
 4224     89/<- %ebp 4/r32/esp
 4225     # (eax..ecx) = "A$"
 4226     b8/copy-to-eax "A$"/imm32
 4227     8b/-> *eax 1/r32/ecx
 4228     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4229     05/add-to-eax 4/imm32
 4230     # var slice/ecx: slice = {eax, ecx}
 4231     51/push-ecx
 4232     50/push-eax
 4233     89/<- %ecx 4/r32/esp
 4234     #
 4235     (is-identifier? %ecx)
 4236     (check-ints-equal %eax 1 "F - test-is-identifier-A")
 4237     # . epilogue
 4238     89/<- %esp 5/r32/ebp
 4239     5d/pop-to-ebp
 4240     c3/return
 4241 
 4242 test-is-identifier-Z:
 4243     # . prologue
 4244     55/push-ebp
 4245     89/<- %ebp 4/r32/esp
 4246     # (eax..ecx) = "Z$"
 4247     b8/copy-to-eax "Z$"/imm32
 4248     8b/-> *eax 1/r32/ecx
 4249     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4250     05/add-to-eax 4/imm32
 4251     # var slice/ecx: slice = {eax, ecx}
 4252     51/push-ecx
 4253     50/push-eax
 4254     89/<- %ecx 4/r32/esp
 4255     #
 4256     (is-identifier? %ecx)
 4257     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
 4258     # . epilogue
 4259     89/<- %esp 5/r32/ebp
 4260     5d/pop-to-ebp
 4261     c3/return
 4262 
 4263 test-is-identifier-@:
 4264     # character before 'A' is invalid
 4265     # . prologue
 4266     55/push-ebp
 4267     89/<- %ebp 4/r32/esp
 4268     # (eax..ecx) = "@a"
 4269     b8/copy-to-eax "@a"/imm32
 4270     8b/-> *eax 1/r32/ecx
 4271     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4272     05/add-to-eax 4/imm32
 4273     # var slice/ecx: slice = {eax, ecx}
 4274     51/push-ecx
 4275     50/push-eax
 4276     89/<- %ecx 4/r32/esp
 4277     #
 4278     (is-identifier? %ecx)
 4279     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4280     # . epilogue
 4281     89/<- %esp 5/r32/ebp
 4282     5d/pop-to-ebp
 4283     c3/return
 4284 
 4285 test-is-identifier-square-bracket:
 4286     # character after 'Z' is invalid
 4287     # . prologue
 4288     55/push-ebp
 4289     89/<- %ebp 4/r32/esp
 4290     # (eax..ecx) = "[a"
 4291     b8/copy-to-eax "[a"/imm32
 4292     8b/-> *eax 1/r32/ecx
 4293     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4294     05/add-to-eax 4/imm32
 4295     # var slice/ecx: slice = {eax, ecx}
 4296     51/push-ecx
 4297     50/push-eax
 4298     89/<- %ecx 4/r32/esp
 4299     #
 4300     (is-identifier? %ecx)
 4301     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4302     # . epilogue
 4303     89/<- %esp 5/r32/ebp
 4304     5d/pop-to-ebp
 4305     c3/return
 4306 
 4307 test-is-identifier-backtick:
 4308     # character before 'a' is invalid
 4309     # . prologue
 4310     55/push-ebp
 4311     89/<- %ebp 4/r32/esp
 4312     # (eax..ecx) = "`a"
 4313     b8/copy-to-eax "`a"/imm32
 4314     8b/-> *eax 1/r32/ecx
 4315     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4316     05/add-to-eax 4/imm32
 4317     # var slice/ecx: slice = {eax, ecx}
 4318     51/push-ecx
 4319     50/push-eax
 4320     89/<- %ecx 4/r32/esp
 4321     #
 4322     (is-identifier? %ecx)
 4323     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
 4324     # . epilogue
 4325     89/<- %esp 5/r32/ebp
 4326     5d/pop-to-ebp
 4327     c3/return
 4328 
 4329 test-is-identifier-curly-brace-open:
 4330     # character after 'z' is invalid; also used for blocks
 4331     # . prologue
 4332     55/push-ebp
 4333     89/<- %ebp 4/r32/esp
 4334     # (eax..ecx) = "{a"
 4335     b8/copy-to-eax "{a"/imm32
 4336     8b/-> *eax 1/r32/ecx
 4337     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4338     05/add-to-eax 4/imm32
 4339     # var slice/ecx: slice = {eax, ecx}
 4340     51/push-ecx
 4341     50/push-eax
 4342     89/<- %ecx 4/r32/esp
 4343     #
 4344     (is-identifier? %ecx)
 4345     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
 4346     # . epilogue
 4347     89/<- %esp 5/r32/ebp
 4348     5d/pop-to-ebp
 4349     c3/return
 4350 
 4351 test-is-identifier-curly-brace-close:
 4352     # . prologue
 4353     55/push-ebp
 4354     89/<- %ebp 4/r32/esp
 4355     # (eax..ecx) = "}a"
 4356     b8/copy-to-eax "}a"/imm32
 4357     8b/-> *eax 1/r32/ecx
 4358     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4359     05/add-to-eax 4/imm32
 4360     # var slice/ecx: slice = {eax, ecx}
 4361     51/push-ecx
 4362     50/push-eax
 4363     89/<- %ecx 4/r32/esp
 4364     #
 4365     (is-identifier? %ecx)
 4366     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
 4367     # . epilogue
 4368     89/<- %esp 5/r32/ebp
 4369     5d/pop-to-ebp
 4370     c3/return
 4371 
 4372 test-is-identifier-hyphen:
 4373     # disallow leading '-' since '->' has special meaning
 4374     # . prologue
 4375     55/push-ebp
 4376     89/<- %ebp 4/r32/esp
 4377     # (eax..ecx) = "-a"
 4378     b8/copy-to-eax "-a"/imm32
 4379     8b/-> *eax 1/r32/ecx
 4380     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4381     05/add-to-eax 4/imm32
 4382     # var slice/ecx: slice = {eax, ecx}
 4383     51/push-ecx
 4384     50/push-eax
 4385     89/<- %ecx 4/r32/esp
 4386     #
 4387     (is-identifier? %ecx)
 4388     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
 4389     # . epilogue
 4390     89/<- %esp 5/r32/ebp
 4391     5d/pop-to-ebp
 4392     c3/return
 4393 
 4394 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
 4395     # . prologue
 4396     55/push-ebp
 4397     89/<- %ebp 4/r32/esp
 4398     # . save registers
 4399     50/push-eax
 4400     56/push-esi
 4401     57/push-edi
 4402     # esi = in
 4403     8b/-> *(ebp+8) 6/r32/esi
 4404     # edi = out
 4405     8b/-> *(ebp+0xc) 7/r32/edi
 4406     # var eax: (handle block) = parse-mu-block(in, vars, fn)
 4407     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
 4408     # out->body = eax
 4409     89/<- *(edi+0x10) 0/r32/eax  # Function-body
 4410 $populate-mu-function-body:end:
 4411     # . restore registers
 4412     5f/pop-to-edi
 4413     5e/pop-to-esi
 4414     58/pop-to-eax
 4415     # . epilogue
 4416     89/<- %esp 5/r32/ebp
 4417     5d/pop-to-ebp
 4418     c3/return
 4419 
 4420 # parses a block, assuming that the leading '{' has already been read by the caller
 4421 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
 4422     # pseudocode:
 4423     #   var line: (stream byte 512)
 4424     #   var word-slice: slice
 4425     #   result/eax = allocate(Heap, Stmt-size)
 4426     #   result->tag = 0/block
 4427     #   result->name = some unique name
 4428     #   while true                                  # line loop
 4429     #     clear-stream(line)
 4430     #     read-line-buffered(in, line)
 4431     #     if (line->write == 0) break               # end of file
 4432     #     word-slice = next-mu-token(line)
 4433     #     if slice-empty?(word-slice)               # end of line
 4434     #       continue
 4435     #     else if slice-starts-with?(word-slice, "#")
 4436     #       continue
 4437     #     else if slice-equal?(word-slice, "{")
 4438     #       assert(no-tokens-in(line))
 4439     #       block = parse-mu-block(in, vars, fn)
 4440     #       append-to-block(result, block)
 4441     #     else if slice-equal?(word-slice, "}")
 4442     #       break
 4443     #     else if slice-ends-with?(word-slice, ":")
 4444     #       # TODO: error-check the rest of 'line'
 4445     #       --word-slice->end to skip ':'
 4446     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
 4447     #       append-to-block(result, named-block)
 4448     #     else if slice-equal?(word-slice, "var")
 4449     #       var-def = parse-mu-var-def(line, vars)
 4450     #       append-to-block(result, var-def)
 4451     #     else
 4452     #       stmt = parse-mu-stmt(line, vars, fn)
 4453     #       append-to-block(result, stmt)
 4454     #   return result
 4455     #
 4456     # . prologue
 4457     55/push-ebp
 4458     89/<- %ebp 4/r32/esp
 4459     # . save registers
 4460     51/push-ecx
 4461     52/push-edx
 4462     53/push-ebx
 4463     57/push-edi
 4464     # var line/ecx: (stream byte 512)
 4465     81 5/subop/subtract %esp 0x200/imm32
 4466     68/push 0x200/imm32/length
 4467     68/push 0/imm32/read
 4468     68/push 0/imm32/write
 4469     89/<- %ecx 4/r32/esp
 4470     # var word-slice/edx: slice
 4471     68/push 0/imm32/end
 4472     68/push 0/imm32/start
 4473     89/<- %edx 4/r32/esp
 4474     # edi = result
 4475     (allocate Heap *Stmt-size)  # => eax
 4476     (zero-out %eax *Stmt-size)
 4477     89/<- %edi 0/r32/eax
 4478     # set result->tag
 4479     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
 4480     # set result->var
 4481     (new-block-name *(ebp+0x10))  # => eax
 4482     89/<- *(edi+8) 0/r32/eax  # Block-var
 4483     # push result->var to vars
 4484     (push *(ebp+0xc) %eax)
 4485     {
 4486 $parse-mu-block:line-loop:
 4487       # line = read-line-buffered(in)
 4488       (clear-stream %ecx)
 4489       (read-line-buffered *(ebp+8) %ecx)
 4490 #?       (write-buffered Stderr "line: ")
 4491 #?       (write-stream-data Stderr %ecx)
 4492 #?       (write-buffered Stderr Newline)
 4493 #?       (flush Stderr)
 4494       # if (line->write == 0) break
 4495       81 7/subop/compare *ecx 0/imm32
 4496       0f 84/jump-if-= break/disp32
 4497       # word-slice = next-mu-token(line)
 4498       (next-mu-token %ecx %edx)
 4499 #?       (write-buffered Stderr "word: ")
 4500 #?       (write-slice-buffered Stderr %edx)
 4501 #?       (write-buffered Stderr Newline)
 4502 #?       (flush Stderr)
 4503       # if slice-empty?(word-slice) continue
 4504       (slice-empty? %edx)
 4505       3d/compare-eax-and 0/imm32/false
 4506       0f 85/jump-if-!= loop/disp32
 4507       # if (slice-starts-with?(word-slice, '#') continue
 4508       # . eax = *word-slice->start
 4509       8b/-> *edx 0/r32/eax
 4510       8a/copy-byte *eax 0/r32/AL
 4511       81 4/subop/and %eax 0xff/imm32
 4512       # . if (eax == '#') continue
 4513       3d/compare-eax-and 0x23/imm32/hash
 4514       0f 84/jump-if-= loop/disp32
 4515       # if slice-equal?(word-slice, "{")
 4516       {
 4517 $parse-mu-block:check-for-block:
 4518         (slice-equal? %edx "{")
 4519         3d/compare-eax-and 0/imm32/false
 4520         74/jump-if-= break/disp8
 4521         (check-no-tokens-left %ecx)
 4522         # parse new block and append
 4523         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4524         (append-to-block Heap %edi %eax)
 4525         e9/jump $parse-mu-block:line-loop/disp32
 4526       }
 4527       # if slice-equal?(word-slice, "}") break
 4528 $parse-mu-block:check-for-end:
 4529       (slice-equal? %edx "}")
 4530       3d/compare-eax-and 0/imm32/false
 4531       0f 85/jump-if-!= break/disp32
 4532       # if slice-ends-with?(word-slice, ":") parse named block and append
 4533       {
 4534 $parse-mu-block:check-for-named-block:
 4535         # . eax = *(word-slice->end-1)
 4536         8b/-> *(edx+4) 0/r32/eax
 4537         48/decrement-eax
 4538         8a/copy-byte *eax 0/r32/AL
 4539         81 4/subop/and %eax 0xff/imm32
 4540         # . if (eax != ':') break
 4541         3d/compare-eax-and 0x3a/imm32/colon
 4542         0f 85/jump-if-!= break/disp32
 4543         # TODO: error-check the rest of 'line'
 4544         #
 4545         # skip ':'
 4546         ff 1/subop/decrement *(edx+4)  # Slice-end
 4547         #
 4548         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4549         (append-to-block Heap %edi %eax)
 4550         e9/jump $parse-mu-block:line-loop/disp32
 4551       }
 4552       # if slice-equal?(word-slice, "var")
 4553       {
 4554 $parse-mu-block:check-for-var:
 4555         (slice-equal? %edx "var")
 4556         3d/compare-eax-and 0/imm32/false
 4557         74/jump-if-= break/disp8
 4558         #
 4559         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
 4560         (append-to-block Heap %edi %eax)
 4561         e9/jump $parse-mu-block:line-loop/disp32
 4562       }
 4563 $parse-mu-block:regular-stmt:
 4564       # otherwise
 4565       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4566       (append-to-block Heap %edi %eax)
 4567       e9/jump loop/disp32
 4568     } # end line loop
 4569     #
 4570     (pop *(ebp+0xc))  # => eax
 4571     # return result
 4572     89/<- %eax 7/r32/edi
 4573 $parse-mu-block:end:
 4574     # . reclaim locals
 4575     81 0/subop/add %esp 0x214/imm32
 4576     # . restore registers
 4577     5f/pop-to-edi
 4578     5b/pop-to-ebx
 4579     5a/pop-to-edx
 4580     59/pop-to-ecx
 4581     # . epilogue
 4582     89/<- %esp 5/r32/ebp
 4583     5d/pop-to-ebp
 4584     c3/return
 4585 
 4586 $parse-mu-block:abort:
 4587     # error("'{' or '}' should be on its own line, but got '")
 4588     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4589     (rewind-stream %ecx)
 4590     (write-stream 2 %ecx)
 4591     (write-buffered Stderr "'\n")
 4592     (flush Stderr)
 4593     # . syscall(exit, 1)
 4594     bb/copy-to-ebx  1/imm32
 4595     b8/copy-to-eax  1/imm32/exit
 4596     cd/syscall  0x80/imm8
 4597     # never gets here
 4598 
 4599 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
 4600     # . prologue
 4601     55/push-ebp
 4602     89/<- %ebp 4/r32/esp
 4603     # . save registers
 4604     51/push-ecx
 4605     52/push-edx
 4606     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
 4607     8b/-> *(ebp+8) 0/r32/eax
 4608     8b/-> *eax 0/r32/eax  # Function-name
 4609     8b/-> *eax 0/r32/eax  # String-length
 4610     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
 4611     89/<- %ecx 0/r32/eax
 4612     # var name/edx: (stream byte n)
 4613     29/subtract %esp 1/r32/ecx
 4614     ff 6/subop/push %ecx
 4615     68/push 0/imm32/read
 4616     68/push 0/imm32/write
 4617     89/<- %edx 4/r32/esp
 4618     (clear-stream %edx)
 4619     # eax = fn->name
 4620     8b/-> *(ebp+8) 0/r32/eax
 4621     8b/-> *eax 0/r32/eax  # Function-name
 4622     # construct result using Next-block-index (and increment it)
 4623     (write %edx "$")
 4624     (write %edx %eax)
 4625     (write %edx ":")
 4626     (print-int32 %edx *Next-block-index)
 4627     ff 0/subop/increment *Next-block-index
 4628     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
 4629     # . eax = name->write
 4630     8b/-> *edx 0/r32/eax
 4631     # . edx = name->data
 4632     8d/copy-address *(edx+0xc) 2/r32/edx
 4633     # . eax = name->write + name->data
 4634     01/add %eax 2/r32/edx
 4635     # . push {edx, eax}
 4636     ff 6/subop/push %eax
 4637     ff 6/subop/push %edx
 4638     89/<- %eax 4/r32/esp
 4639     # result->var = new literal(s)
 4640     (new-literal Heap %eax)  # => eax
 4641 $new-block-name:end:
 4642     # . reclaim locals
 4643     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
 4644     81 0/subop/add %ecx 8/imm32  # slice
 4645     01/add %esp 1/r32/ecx
 4646     # . restore registers
 4647     5a/pop-to-edx
 4648     59/pop-to-ecx
 4649     # . epilogue
 4650     89/<- %esp 5/r32/ebp
 4651     5d/pop-to-ebp
 4652     c3/return
 4653 
 4654 == data
 4655 
 4656 # Global state added to each var record when parsing a function
 4657 Next-block-index:  # (addr int)
 4658     1/imm32
 4659 
 4660 == code
 4661 
 4662 check-no-tokens-left:  # line: (addr stream byte)
 4663     # . prologue
 4664     55/push-ebp
 4665     89/<- %ebp 4/r32/esp
 4666     # . save registers
 4667     50/push-eax
 4668     51/push-ecx
 4669     # var s/ecx: slice
 4670     68/push 0/imm32/end
 4671     68/push 0/imm32/start
 4672     89/<- %ecx 4/r32/esp
 4673     #
 4674     (next-mu-token *(ebp+8) %ecx)
 4675     # if slice-empty?(s) return
 4676     (slice-empty? %ecx)
 4677     3d/compare-eax-and 0/imm32/false
 4678     75/jump-if-!= $check-no-tokens-left:end/disp8
 4679     # if (slice-starts-with?(s, '#') return
 4680     # . eax = *s->start
 4681     8b/-> *edx 0/r32/eax
 4682     8a/copy-byte *eax 0/r32/AL
 4683     81 4/subop/and %eax 0xff/imm32
 4684     # . if (eax == '#') continue
 4685     3d/compare-eax-and 0x23/imm32/hash
 4686     74/jump-if-= $check-no-tokens-left:end/disp8
 4687     # abort
 4688     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4689     (rewind-stream %ecx)
 4690     (write-stream 2 %ecx)
 4691     (write-buffered Stderr "'\n")
 4692     (flush Stderr)
 4693     # . syscall(exit, 1)
 4694     bb/copy-to-ebx  1/imm32
 4695     b8/copy-to-eax  1/imm32/exit
 4696     cd/syscall  0x80/imm8
 4697     # never gets here
 4698 $check-no-tokens-left:end:
 4699     # . reclaim locals
 4700     81 0/subop/add %esp 8/imm32
 4701     # . restore registers
 4702     59/pop-to-ecx
 4703     58/pop-to-eax
 4704     # . epilogue
 4705     89/<- %esp 5/r32/ebp
 4706     5d/pop-to-ebp
 4707     c3/return
 4708 
 4709 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4710     # pseudocode:
 4711     #   var v: (handle var) = new-literal(name)
 4712     #   push(vars, v)
 4713     #   result = parse-mu-block(in, vars, fn)
 4714     #   pop(vars)
 4715     #   result->name = s
 4716     #   return result
 4717     #
 4718     # . prologue
 4719     55/push-ebp
 4720     89/<- %ebp 4/r32/esp
 4721     # . save registers
 4722     51/push-ecx
 4723     # var v/ecx: (handle var)
 4724     (new-literal Heap *(ebp+8))  # => eax
 4725     89/<- %ecx 0/r32/eax
 4726     # push(vars, v)
 4727     (push *(ebp+0x10) %ecx)
 4728     # eax = result
 4729     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
 4730     # pop the var
 4731     50/push-eax
 4732     (pop *(ebp+0x10))  # => eax
 4733     58/pop-to-eax
 4734     # result->tag = named-block
 4735     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
 4736     # result->var = v
 4737     89/<- *(eax+8) 1/r32/ecx  # Block-var
 4738 $parse-mu-named-block:end:
 4739     # . restore registers
 4740     59/pop-to-ecx
 4741     # . epilogue
 4742     89/<- %esp 5/r32/ebp
 4743     5d/pop-to-ebp
 4744     c3/return
 4745 
 4746 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
 4747     # . prologue
 4748     55/push-ebp
 4749     89/<- %ebp 4/r32/esp
 4750     # . save registers
 4751     51/push-ecx
 4752     52/push-edx
 4753     # var word-slice/ecx: slice
 4754     68/push 0/imm32/end
 4755     68/push 0/imm32/start
 4756     89/<- %ecx 4/r32/esp
 4757     # var v/edx: (handle var) = parse-var-with-type(line)
 4758     (next-mu-token *(ebp+8) %ecx)
 4759     (parse-var-with-type %ecx *(ebp+8))  # => eax
 4760     89/<- %edx 0/r32/eax
 4761     #
 4762     (push *(ebp+0xc) %edx)
 4763     # either v has no register and there's no more to this line
 4764     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
 4765     3d/compare-eax-and 0/imm32
 4766     {
 4767       75/jump-if-!= break/disp8
 4768       # TODO: ensure that there's nothing else on this line
 4769       (new-var-def Heap %edx)  # => eax
 4770       eb/jump $parse-mu-var-def:end/disp8
 4771     }
 4772     # or v has a register and there's more to this line
 4773     {
 4774       74/jump-if-= break/disp8
 4775       # ensure that the next word is '<-'
 4776       (next-mu-token *(ebp+8) %ecx)
 4777       (slice-equal? %ecx "<-")  # => eax
 4778       3d/compare-eax-and 0/imm32/false
 4779       74/jump-if-= $parse-mu-var-def:abort/disp8
 4780       #
 4781       (new-reg-var-def Heap %edx)  # => eax
 4782       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
 4783     }
 4784 $parse-mu-var-def:end:
 4785     # . reclaim locals
 4786     81 0/subop/add %esp 8/imm32
 4787     # . restore registers
 4788     5a/pop-to-edx
 4789     59/pop-to-ecx
 4790     # . epilogue
 4791     89/<- %esp 5/r32/ebp
 4792     5d/pop-to-ebp
 4793     c3/return
 4794 
 4795 $parse-mu-var-def:abort:
 4796     (rewind-stream *(ebp+8))
 4797     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
 4798     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
 4799     (flush Stderr)
 4800     (write-stream 2 *(ebp+8))
 4801     (write-buffered Stderr "'\n")
 4802     (flush Stderr)
 4803     # . syscall(exit, 1)
 4804     bb/copy-to-ebx  1/imm32
 4805     b8/copy-to-eax  1/imm32/exit
 4806     cd/syscall  0x80/imm8
 4807     # never gets here
 4808 
 4809 test-parse-mu-var-def:
 4810     # 'var n: int'
 4811     # . prologue
 4812     55/push-ebp
 4813     89/<- %ebp 4/r32/esp
 4814     # setup
 4815     (clear-stream _test-input-stream)
 4816     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
 4817     # var vars/ecx: (stack (addr var) 4)
 4818     81 5/subop/subtract %esp 0x10/imm32
 4819     68/push 0x10/imm32/length
 4820     68/push 0/imm32/top
 4821     89/<- %ecx 4/r32/esp
 4822     (clear-stack %ecx)
 4823     # convert
 4824     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 4825     # check result
 4826     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
 4827     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
 4828     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
 4829     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
 4830     # ensure type is int
 4831     8b/-> *(eax+4) 0/r32/eax  # Var-type
 4832     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
 4833     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
 4834     # . epilogue
 4835     89/<- %esp 5/r32/ebp
 4836     5d/pop-to-ebp
 4837     c3/return
 4838 
 4839 test-parse-mu-reg-var-def:
 4840     # 'var n/eax: int <- copy 0'
 4841     # . prologue
 4842     55/push-ebp
 4843     89/<- %ebp 4/r32/esp
 4844     # setup
 4845     (clear-stream _test-input-stream)
 4846     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
 4847     # var vars/ecx: (stack (addr var) 4)
 4848     81 5/subop/subtract %esp 0x10/imm32
 4849     68/push 0x10/imm32/length
 4850     68/push 0/imm32/top
 4851     89/<- %ecx 4/r32/esp
 4852     (clear-stack %ecx)
 4853     # convert
 4854     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 4855     # check result
 4856     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
 4857     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
 4858     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
 4859     8b/-> *eax 0/r32/eax  # Stmt-var-value
 4860     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
 4861     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
 4862     # ensure type is int
 4863     8b/-> *(eax+4) 0/r32/eax  # Var-type
 4864     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
 4865     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
 4866     # . epilogue
 4867     89/<- %esp 5/r32/ebp
 4868     5d/pop-to-ebp
 4869     c3/return
 4870 
 4871 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4872     # pseudocode:
 4873     #   var name: slice
 4874     #   result = allocate(Heap, Stmt-size)
 4875     #   if stmt-has-outputs?(line)
 4876     #     while true
 4877     #       name = next-mu-token(line)
 4878     #       if (name == '<-') break
 4879     #       assert(is-identifier?(name))
 4880     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
 4881     #       result->outputs = append(result->outputs, v)
 4882     #   add-operation-and-inputs-to-stmt(result, line, vars)
 4883     #
 4884     # . prologue
 4885     55/push-ebp
 4886     89/<- %ebp 4/r32/esp
 4887     # . save registers
 4888     51/push-ecx
 4889     52/push-edx
 4890     57/push-edi
 4891     # var name/ecx: slice
 4892     68/push 0/imm32/end
 4893     68/push 0/imm32/start
 4894     89/<- %ecx 4/r32/esp
 4895     # var is-deref?/edx: boolean = false
 4896     ba/copy-to-edx 0/imm32/false
 4897     # result/edi: (handle stmt)
 4898     (allocate Heap *Stmt-size)  # => eax
 4899     (zero-out %eax *Stmt-size)
 4900     89/<- %edi 0/r32/eax
 4901     # result->tag = 1/stmt
 4902     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
 4903     {
 4904       (stmt-has-outputs? *(ebp+8))
 4905       3d/compare-eax-and 0/imm32/false
 4906       0f 84/jump-if-= break/disp32
 4907       {
 4908 $parse-mu-stmt:read-outputs:
 4909         # name = next-mu-token(line)
 4910         (next-mu-token *(ebp+8) %ecx)
 4911         # if slice-empty?(word-slice) break
 4912         (slice-empty? %ecx)  # => eax
 4913         3d/compare-eax-and 0/imm32/false
 4914         0f 85/jump-if-!= break/disp32
 4915         # if (name == "<-") break
 4916         (slice-equal? %ecx "<-")  # => eax
 4917         3d/compare-eax-and 0/imm32/false
 4918         0f 85/jump-if-!= break/disp32
 4919         # is-deref? = false
 4920         ba/copy-to-edx 0/imm32/false
 4921         # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 4922         8b/-> *ecx 0/r32/eax  # Slice-start
 4923         8a/copy-byte *eax 0/r32/AL
 4924         81 4/subop/and %eax 0xff/imm32
 4925         3d/compare-eax-and 0x2a/imm32/asterisk
 4926         {
 4927           75/jump-if-!= break/disp8
 4928           ff 0/subop/increment *ecx
 4929           ba/copy-to-edx 1/imm32/true
 4930         }
 4931         # assert(is-identifier?(name))
 4932         (is-identifier? %ecx)  # => eax
 4933         3d/compare-eax-and 0/imm32/false
 4934         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
 4935         # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
 4936         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4937         (append-stmt-var Heap %eax *(edi+0xc) %edx)  # Stmt1-outputs => eax
 4938         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
 4939         e9/jump loop/disp32
 4940       }
 4941     }
 4942     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
 4943 $parse-mu-stmt:end:
 4944     # return result
 4945     89/<- %eax 7/r32/edi
 4946     # . reclaim locals
 4947     81 0/subop/add %esp 8/imm32
 4948     # . restore registers
 4949     5f/pop-to-edi
 4950     5a/pop-to-edx
 4951     59/pop-to-ecx
 4952     # . epilogue
 4953     89/<- %esp 5/r32/ebp
 4954     5d/pop-to-ebp
 4955     c3/return
 4956 
 4957 $parse-mu-stmt:abort:
 4958     # error("invalid identifier '" name "'\n")
 4959     (write-buffered Stderr "invalid identifier '")
 4960     (write-slice-buffered Stderr %ecx)
 4961     (write-buffered Stderr "'\n")
 4962     (flush Stderr)
 4963     # . syscall(exit, 1)
 4964     bb/copy-to-ebx  1/imm32
 4965     b8/copy-to-eax  1/imm32/exit
 4966     cd/syscall  0x80/imm8
 4967     # never gets here
 4968 
 4969 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
 4970     # pseudocode:
 4971     #   stmt->name = slice-to-string(next-mu-token(line))
 4972     #   while true
 4973     #     name = next-mu-token(line)
 4974     #     v = lookup-var-or-literal(name)
 4975     #     stmt->inouts = append(stmt->inouts, v)
 4976     #
 4977     # . prologue
 4978     55/push-ebp
 4979     89/<- %ebp 4/r32/esp
 4980     # . save registers
 4981     50/push-eax
 4982     51/push-ecx
 4983     52/push-edx
 4984     53/push-ebx
 4985     57/push-edi
 4986     # edi = stmt
 4987     8b/-> *(ebp+8) 7/r32/edi
 4988     # var name/ecx: slice
 4989     68/push 0/imm32/end
 4990     68/push 0/imm32/start
 4991     89/<- %ecx 4/r32/esp
 4992     # var is-deref?/edx: boolean = false
 4993     ba/copy-to-edx 0/imm32/false
 4994 $add-operation-and-inputs-to-stmt:read-operation:
 4995     (next-mu-token *(ebp+0xc) %ecx)
 4996     (slice-to-string Heap %ecx)  # => eax
 4997     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
 4998     # var is-get?/ebx: boolean = (name == "get")
 4999     (slice-equal? %ecx "get")  # => eax
 5000     89/<- %ebx 0/r32/eax
 5001     {
 5002 $add-operation-and-inputs-to-stmt:read-inouts:
 5003       # name = next-mu-token(line)
 5004       (next-mu-token *(ebp+0xc) %ecx)
 5005       # if slice-empty?(word-slice) break
 5006       (slice-empty? %ecx)  # => eax
 5007       3d/compare-eax-and 0/imm32/false
 5008       0f 85/jump-if-!= break/disp32
 5009       # if (name == "<-") abort
 5010       (slice-equal? %ecx "<-")
 5011       3d/compare-eax-and 0/imm32/false
 5012       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
 5013       # if (is-get? && second operand) lookup or create offset
 5014       {
 5015         81 7/subop/compare %ebx 0/imm32/false
 5016         74/jump-if-= break/disp8
 5017         81 7/subop/compare *(edi+8) 0/imm32  # Stmt1-inouts or Regvardef-inouts
 5018         74/jump-if-= break/disp8
 5019         (lookup-or-create-constant *(edi+8) %ecx)  # Stmt1-inouts => eax
 5020 #?         (write-buffered Stderr "creating new output var ")
 5021 #?         (print-int32-buffered Stderr %eax)
 5022 #?         (write-buffered Stderr " for field called ")
 5023 #?         (write-slice-buffered Stderr %ecx)
 5024 #?         (write-buffered Stderr Newline)
 5025 #?         (flush Stderr)
 5026         e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
 5027       }
 5028       # is-deref? = false
 5029       ba/copy-to-edx 0/imm32/false
 5030       # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 5031       8b/-> *ecx 0/r32/eax  # Slice-start
 5032       8a/copy-byte *eax 0/r32/AL
 5033       81 4/subop/and %eax 0xff/imm32
 5034       3d/compare-eax-and 0x2a/imm32/asterisk
 5035       {
 5036         75/jump-if-!= break/disp8
 5037 $add-operation-and-inputs-to-stmt:inout-is-deref:
 5038         ff 0/subop/increment *ecx
 5039         ba/copy-to-edx 1/imm32/true
 5040       }
 5041       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
 5042 $add-operation-and-inputs-to-stmt:save-var:
 5043       (append-stmt-var Heap %eax *(edi+8) %edx)  # Stmt1-inouts or Regvardef-inouts => eax
 5044       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 5045       e9/jump loop/disp32
 5046     }
 5047 $add-operation-and-inputs-to-stmt:end:
 5048     # . reclaim locals
 5049     81 0/subop/add %esp 8/imm32
 5050     # . restore registers
 5051     5f/pop-to-edi
 5052     5b/pop-to-ebx
 5053     5a/pop-to-edx
 5054     59/pop-to-ecx
 5055     58/pop-to-eax
 5056     # . epilogue
 5057     89/<- %esp 5/r32/ebp
 5058     5d/pop-to-ebp
 5059     c3/return
 5060 
 5061 $add-operation-and-inputs-to-stmt:abort:
 5062     # error("invalid statement '" line "'\n")
 5063     (rewind-stream *(ebp+8))
 5064     (write-buffered Stderr "invalid identifier '")
 5065     (flush Stderr)
 5066     (write-stream 2 *(ebp+8))
 5067     (write-buffered Stderr "'\n")
 5068     (flush Stderr)
 5069     # . syscall(exit, 1)
 5070     bb/copy-to-ebx  1/imm32
 5071     b8/copy-to-eax  1/imm32/exit
 5072     cd/syscall  0x80/imm8
 5073     # never gets here
 5074 
 5075 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
 5076     # . prologue
 5077     55/push-ebp
 5078     89/<- %ebp 4/r32/esp
 5079     # . save registers
 5080     51/push-ecx
 5081     # var word-slice/ecx: slice
 5082     68/push 0/imm32/end
 5083     68/push 0/imm32/start
 5084     89/<- %ecx 4/r32/esp
 5085     # result = false
 5086     b8/copy-to-eax 0/imm32/false
 5087     (rewind-stream *(ebp+8))
 5088     {
 5089       (next-mu-token *(ebp+8) %ecx)
 5090       # if slice-empty?(word-slice) break
 5091       (slice-empty? %ecx)
 5092       3d/compare-eax-and 0/imm32/false
 5093       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 5094       0f 85/jump-if-!= break/disp32
 5095       # if slice-starts-with?(word-slice, '#') break
 5096       # . eax = *word-slice->start
 5097       8b/-> *ecx 0/r32/eax
 5098       8a/copy-byte *eax 0/r32/AL
 5099       81 4/subop/and %eax 0xff/imm32
 5100       # . if (eax == '#') break
 5101       3d/compare-eax-and 0x23/imm32/hash
 5102       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 5103       0f 84/jump-if-= break/disp32
 5104       # if slice-equal?(word-slice, '<-') return true
 5105       (slice-equal? %ecx "<-")
 5106       3d/compare-eax-and 0/imm32/false
 5107       74/jump-if-= loop/disp8
 5108       b8/copy-to-eax 1/imm32/true
 5109     }
 5110 $stmt-has-outputs:end:
 5111     (rewind-stream *(ebp+8))
 5112     # . reclaim locals
 5113     81 0/subop/add %esp 8/imm32
 5114     # . restore registers
 5115     59/pop-to-ecx
 5116     # . epilogue
 5117     89/<- %esp 5/r32/ebp
 5118     5d/pop-to-ebp
 5119     c3/return
 5120 
 5121 # if 'name' starts with a digit, create a new literal var for it
 5122 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
 5123 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5124     # . prologue
 5125     55/push-ebp
 5126     89/<- %ebp 4/r32/esp
 5127     # . save registers
 5128     51/push-ecx
 5129     56/push-esi
 5130     # esi = name
 5131     8b/-> *(ebp+8) 6/r32/esi
 5132     # if slice-empty?(name) abort
 5133     (slice-empty? %esi)  # => eax
 5134     3d/compare-eax-and 0/imm32/false
 5135     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
 5136     # var c/ecx: byte = *name->start
 5137     8b/-> *esi 1/r32/ecx
 5138     8a/copy-byte *ecx 1/r32/CL
 5139     81 4/subop/and %ecx 0xff/imm32
 5140     # if is-decimal-digit?(c) return new var(name)
 5141     {
 5142       (is-decimal-digit? %ecx)  # => eax
 5143       3d/compare-eax-and 0/imm32/false
 5144       74/jump-if-= break/disp8
 5145       (new-literal-integer Heap %esi)  # => eax
 5146       eb/jump $lookup-var-or-literal:end/disp8
 5147     }
 5148     # else if (c == '"') return new var(name)
 5149     {
 5150       81 7/subop/compare %ecx 0x22/imm32/dquote
 5151       75/jump-if-!= break/disp8
 5152       (new-literal Heap %esi)  # => eax
 5153       eb/jump $lookup-var-or-literal:end/disp8
 5154     }
 5155     # otherwise return lookup-var(name, vars)
 5156     {
 5157       (lookup-var %esi *(ebp+0xc))  # => eax
 5158     }
 5159 $lookup-var-or-literal:end:
 5160     # . restore registers
 5161     5e/pop-to-esi
 5162     59/pop-to-ecx
 5163     # . epilogue
 5164     89/<- %esp 5/r32/ebp
 5165     5d/pop-to-ebp
 5166     c3/return
 5167 
 5168 $lookup-var-or-literal:abort:
 5169     (write-buffered Stderr "empty variable!")
 5170     (flush Stderr)
 5171     # . syscall(exit, 1)
 5172     bb/copy-to-ebx  1/imm32
 5173     b8/copy-to-eax  1/imm32/exit
 5174     cd/syscall  0x80/imm8
 5175     # never gets here
 5176 
 5177 # return first 'name' from the top (back) of 'vars' and abort if not found
 5178 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5179     # . prologue
 5180     55/push-ebp
 5181     89/<- %ebp 4/r32/esp
 5182     # var target/eax: (handle array byte) = slice-to-string(name)
 5183     (slice-to-string Heap *(ebp+8))  # => eax
 5184     #
 5185     (lookup-var-helper %eax *(ebp+0xc))  # => eax
 5186     # if (result == 0) abort
 5187     3d/compare-eax-and 0/imm32
 5188     74/jump-if-= $lookup-var:abort/disp8
 5189 $lookup-var:end:
 5190     # . epilogue
 5191     89/<- %esp 5/r32/ebp
 5192     5d/pop-to-ebp
 5193     c3/return
 5194 
 5195 $lookup-var:abort:
 5196     (write-buffered Stderr "unknown variable '")
 5197     (write-slice-buffered Stderr *(ebp+8))
 5198     (write-buffered Stderr "'\n")
 5199     (flush Stderr)
 5200     # . syscall(exit, 1)
 5201     bb/copy-to-ebx  1/imm32
 5202     b8/copy-to-eax  1/imm32/exit
 5203     cd/syscall  0x80/imm8
 5204     # never gets here
 5205 
 5206 # return first 'name' from the top (back) of 'vars', and 0/null if not found
 5207 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5208     # pseudocode:
 5209     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
 5210     #   var min = vars->data
 5211     #   while curr >= min
 5212     #     var v: (handle var) = *curr
 5213     #     if v->name == name
 5214     #       return v
 5215     #   return 0
 5216     #
 5217     # . prologue
 5218     55/push-ebp
 5219     89/<- %ebp 4/r32/esp
 5220     # . save registers
 5221     52/push-edx
 5222     53/push-ebx
 5223     56/push-esi
 5224     # esi = vars
 5225     8b/-> *(ebp+0xc) 6/r32/esi
 5226     # ebx = vars->top
 5227     8b/-> *esi 3/r32/ebx
 5228     # if (vars->top > vars->length) abort
 5229     3b/compare 0/r32/eax *(esi+4)
 5230     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
 5231     # var min/edx: (addr handle var) = vars->data
 5232     8d/copy-address *(esi+8) 2/r32/edx
 5233     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
 5234     81 5/subop/subtract %ebx 4/imm32
 5235     8d/copy-address *(esi+ebx+8) 3/r32/ebx
 5236     {
 5237       # if (curr < min) return 0
 5238       39/compare %ebx 2/r32/edx
 5239       b8/copy-to-eax 0/imm32
 5240       0f 82/jump-if-addr< break/disp32
 5241       # var v/eax: (handle var) = *curr
 5242       8b/-> *ebx 0/r32/eax
 5243       # if (v->name == name) return v
 5244       (string-equal? *eax *(ebp+8))  # Var-name
 5245       3d/compare-eax-and 0/imm32/false
 5246       8b/-> *ebx 0/r32/eax
 5247       75/jump-if-!= break/disp8
 5248       # curr -= 4
 5249       81 5/subop/subtract %ebx 4/imm32
 5250       e9/jump loop/disp32
 5251     }
 5252 $lookup-var-helper:end:
 5253     # . restore registers
 5254     5e/pop-to-esi
 5255     5b/pop-to-ebx
 5256     5a/pop-to-edx
 5257     # . epilogue
 5258     89/<- %esp 5/r32/ebp
 5259     5d/pop-to-ebp
 5260     c3/return
 5261 
 5262 $lookup-var-helper:error1:
 5263     (write-buffered Stderr "malformed stack when looking up '")
 5264     (write-slice-buffered Stderr *(ebp+8))
 5265     (write-buffered Stderr "'\n")
 5266     (flush Stderr)
 5267     # . syscall(exit, 1)
 5268     bb/copy-to-ebx  1/imm32
 5269     b8/copy-to-eax  1/imm32/exit
 5270     cd/syscall  0x80/imm8
 5271     # never gets here
 5272 
 5273 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
 5274 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
 5275     # . prologue
 5276     55/push-ebp
 5277     89/<- %ebp 4/r32/esp
 5278     # . save registers
 5279     51/push-ecx
 5280     # var target/ecx: (handle array byte) = slice-to-string(name)
 5281     (slice-to-string Heap *(ebp+8))  # => eax
 5282     89/<- %ecx 0/r32/eax
 5283     #
 5284     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
 5285     {
 5286       # if (result != 0) return
 5287       3d/compare-eax-and 0/imm32
 5288       75/jump-if-!= break/disp8
 5289       # if name is one of fn's outputs, return it
 5290       {
 5291         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
 5292         3d/compare-eax-and 0/imm32
 5293         # otherwise abort
 5294         0f 84/jump-if-!= $lookup-var:abort/disp32
 5295       }
 5296     }
 5297 $lookup-or-define-var:end:
 5298     # . restore registers
 5299     59/pop-to-ecx
 5300     # . epilogue
 5301     89/<- %esp 5/r32/ebp
 5302     5d/pop-to-ebp
 5303     c3/return
 5304 
 5305 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
 5306     # . prologue
 5307     55/push-ebp
 5308     89/<- %ebp 4/r32/esp
 5309     # . save registers
 5310     51/push-ecx
 5311     # var curr/ecx: (handle list var) = fn->outputs
 5312     8b/-> *(ebp+8) 1/r32/ecx
 5313     8b/-> *(ecx+0xc) 1/r32/ecx
 5314     # while curr != null
 5315     {
 5316       81 7/subop/compare %ecx 0/imm32
 5317       74/jump-if-= break/disp8
 5318       # var v: (handle var) = *curr
 5319       8b/-> *ecx 0/r32/eax  # List-value
 5320       # if (curr->name == name) return curr
 5321       50/push-eax
 5322       (string-equal? *eax *(ebp+0xc))
 5323       3d/compare-eax-and 0/imm32/false
 5324       58/pop-to-eax
 5325       75/jump-if-!= $find-in-function-outputs:end/disp8
 5326       # curr = curr->next
 5327       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 5328       eb/jump loop/disp8
 5329     }
 5330     b8/copy-to-eax 0/imm32
 5331 $find-in-function-outputs:end:
 5332     # . restore registers
 5333     59/pop-to-ecx
 5334     # . epilogue
 5335     89/<- %esp 5/r32/ebp
 5336     5d/pop-to-ebp
 5337     c3/return
 5338 
 5339 test-parse-mu-stmt:
 5340     # . prologue
 5341     55/push-ebp
 5342     89/<- %ebp 4/r32/esp
 5343     # setup
 5344     (clear-stream _test-input-stream)
 5345     (write _test-input-stream "increment n\n")
 5346     # var vars/ecx: (stack (addr var) 4)
 5347     81 5/subop/subtract %esp 0x10/imm32
 5348     68/push 0x10/imm32/length
 5349     68/push 0/imm32/top
 5350     89/<- %ecx 4/r32/esp
 5351     (clear-stack %ecx)
 5352     # var v/edx: var
 5353     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5354     89/<- %edx 4/r32/esp
 5355     (zero-out %edx 0x14)  # Var-size
 5356     # v->name = "n"
 5357     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5358     #
 5359     (push %ecx %edx)
 5360     # convert
 5361     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5362     # check result
 5363     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
 5364     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
 5365     # edx: (handle list var) = result->inouts
 5366     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5367     # ebx: (handle var) = result->inouts->value
 5368     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5369     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
 5370     # . epilogue
 5371     89/<- %esp 5/r32/ebp
 5372     5d/pop-to-ebp
 5373     c3/return
 5374 
 5375 test-parse-mu-stmt-with-comma:
 5376     # . prologue
 5377     55/push-ebp
 5378     89/<- %ebp 4/r32/esp
 5379     # setup
 5380     (clear-stream _test-input-stream)
 5381     (write _test-input-stream "copy-to n, 3\n")
 5382     # var vars/ecx: (stack (addr var) 4)
 5383     81 5/subop/subtract %esp 0x10/imm32
 5384     68/push 0x10/imm32/length
 5385     68/push 0/imm32/top
 5386     89/<- %ecx 4/r32/esp
 5387     (clear-stack %ecx)
 5388     # var v/edx: var
 5389     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5390     89/<- %edx 4/r32/esp
 5391     (zero-out %edx 0x14)  # Var-size
 5392     # v->name = "n"
 5393     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5394     #
 5395     (push %ecx %edx)
 5396     # convert
 5397     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5398     # check result
 5399     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
 5400     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
 5401     # edx: (handle list var) = result->inouts
 5402     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5403     # ebx: (handle var) = result->inouts->value
 5404     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5405     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
 5406     # . epilogue
 5407     89/<- %esp 5/r32/ebp
 5408     5d/pop-to-ebp
 5409     c3/return
 5410 
 5411 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)
 5412     # . prologue
 5413     55/push-ebp
 5414     89/<- %ebp 4/r32/esp
 5415     # . save registers
 5416     51/push-ecx
 5417     #
 5418     (allocate *(ebp+8) *Function-size)  # => eax
 5419     8b/-> *(ebp+0xc) 1/r32/ecx
 5420     89/<- *eax 1/r32/ecx  # Function-name
 5421     8b/-> *(ebp+0x10) 1/r32/ecx
 5422     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
 5423     8b/-> *(ebp+0x14) 1/r32/ecx
 5424     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
 5425     8b/-> *(ebp+0x18) 1/r32/ecx
 5426     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
 5427     8b/-> *(ebp+0x1c) 1/r32/ecx
 5428     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
 5429     8b/-> *(ebp+0x20) 1/r32/ecx
 5430     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
 5431 $new-function:end:
 5432     # . restore registers
 5433     59/pop-to-ecx
 5434     # . epilogue
 5435     89/<- %esp 5/r32/ebp
 5436     5d/pop-to-ebp
 5437     c3/return
 5438 
 5439 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte) -> result/eax: (handle var)
 5440     # . prologue
 5441     55/push-ebp
 5442     89/<- %ebp 4/r32/esp
 5443     # . save registers
 5444     51/push-ecx
 5445     #
 5446     (allocate *(ebp+8) *Var-size)  # => eax
 5447     (zero-out %eax *Var-size)
 5448     8b/-> *(ebp+0xc) 1/r32/ecx
 5449     89/<- *eax 1/r32/ecx  # Var-name
 5450 $new-var:end:
 5451     # . restore registers
 5452     59/pop-to-ecx
 5453     # . epilogue
 5454     89/<- %esp 5/r32/ebp
 5455     5d/pop-to-ebp
 5456     c3/return
 5457 
 5458 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5459     # . prologue
 5460     55/push-ebp
 5461     89/<- %ebp 4/r32/esp
 5462     # . save registers
 5463     51/push-ecx
 5464     # if (!is-hex-int?(name)) abort
 5465     (is-hex-int? *(ebp+0xc))  # => eax
 5466     3d/compare-eax-and 0/imm32/false
 5467     0f 84/jump-if-= $new-literal-integer:abort/disp32
 5468     # var type/ecx: (handle tree type-id) = new type()
 5469     (allocate *(ebp+8) *Tree-size)  # => eax
 5470     (zero-out %eax *Tree-size)  # default type is 'literal'
 5471     89/<- %ecx 0/r32/eax
 5472     # result = new var(s)
 5473     (new-var-from-slice *(ebp+8) *(ebp+0xc))  # => eax
 5474     89/<- *(eax+4) 1/r32/ecx
 5475 $new-literal-integer:end:
 5476     # . restore registers
 5477     59/pop-to-ecx
 5478     # . epilogue
 5479     89/<- %esp 5/r32/ebp
 5480     5d/pop-to-ebp
 5481     c3/return
 5482 
 5483 $new-literal-integer:abort:
 5484     (write-buffered Stderr "variable cannot begin with a digit '")
 5485     (write-slice-buffered Stderr *(ebp+0xc))
 5486     (write-buffered Stderr "'\n")
 5487     (flush Stderr)
 5488     # . syscall(exit, 1)
 5489     bb/copy-to-ebx  1/imm32
 5490     b8/copy-to-eax  1/imm32/exit
 5491     cd/syscall  0x80/imm8
 5492     # never gets here
 5493 
 5494 new-literal:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5495     # . prologue
 5496     55/push-ebp
 5497     89/<- %ebp 4/r32/esp
 5498     # . save registers
 5499     51/push-ecx
 5500     52/push-edx
 5501     # var s/ecx: (addr array byte)
 5502     (slice-to-string Heap *(ebp+0xc))  # => eax
 5503     89/<- %ecx 0/r32/eax
 5504     # type = new type()
 5505     (allocate *(ebp+8) *Tree-size)  # => eax
 5506     (zero-out %eax *Tree-size)  # default type is 'literal'
 5507     89/<- %edx 0/r32/eax
 5508     # eax = result
 5509     (new-var *(ebp+8) %ecx)  # => eax
 5510     # result->type = type
 5511     89/<- *(eax+4) 2/r32/edx  # Var-type
 5512 $new-literal:end:
 5513     # . restore registers
 5514     5a/pop-to-edx
 5515     59/pop-to-ecx
 5516     # . epilogue
 5517     89/<- %esp 5/r32/ebp
 5518     5d/pop-to-ebp
 5519     c3/return
 5520 
 5521 new-var-from-slice:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5522     # . prologue
 5523     55/push-ebp
 5524     89/<- %ebp 4/r32/esp
 5525     # . save registers
 5526     51/push-ecx
 5527     # result = new-var(slice-to-string(name))
 5528     (slice-to-string Heap *(ebp+0xc))  # => eax
 5529     (new-var *(ebp+8) %eax)
 5530 $new-var-from-slice:end:
 5531     # . restore registers
 5532     59/pop-to-ecx
 5533     # . epilogue
 5534     89/<- %esp 5/r32/ebp
 5535     5d/pop-to-ebp
 5536     c3/return
 5537 
 5538 new-block:  # ad: (addr allocation-descriptor), data: (handle list stmt) -> result/eax: (handle stmt)
 5539     # . prologue
 5540     55/push-ebp
 5541     89/<- %ebp 4/r32/esp
 5542     # . save registers
 5543     51/push-ecx
 5544     #
 5545     (allocate *(ebp+8) *Stmt-size)  # => eax
 5546     (zero-out %eax *Stmt-size)
 5547     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
 5548     8b/-> *(ebp+0xc) 1/r32/ecx
 5549     89/<- *(eax+4) 1/r32/ecx  # Block-stmts
 5550 $new-block:end:
 5551     # . restore registers
 5552     59/pop-to-ecx
 5553     # . epilogue
 5554     89/<- %esp 5/r32/ebp
 5555     5d/pop-to-ebp
 5556     c3/return
 5557 
 5558 new-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5559     # . prologue
 5560     55/push-ebp
 5561     89/<- %ebp 4/r32/esp
 5562     # . save registers
 5563     51/push-ecx
 5564     #
 5565     (allocate *(ebp+8) *Stmt-size)  # => eax
 5566     (zero-out %eax *Stmt-size)
 5567     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
 5568     # result->var = var
 5569     8b/-> *(ebp+0xc) 1/r32/ecx
 5570     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
 5571 $new-var-def:end:
 5572     # . restore registers
 5573     59/pop-to-ecx
 5574     # . epilogue
 5575     89/<- %esp 5/r32/ebp
 5576     5d/pop-to-ebp
 5577     c3/return
 5578 
 5579 new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5580     # . prologue
 5581     55/push-ebp
 5582     89/<- %ebp 4/r32/esp
 5583     # . save registers
 5584     51/push-ecx
 5585     57/push-edi
 5586     # ecx = var
 5587     8b/-> *(ebp+0xc) 1/r32/ecx
 5588     # edi = result
 5589     (allocate *(ebp+8) *Stmt-size)  # => eax
 5590     89/<- %edi 0/r32/eax
 5591     (zero-out %edi *Stmt-size)
 5592     # set tag
 5593     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
 5594     # set output
 5595     (append-stmt-var Heap %ecx *(edi+0xc) 0)  # Regvardef-outputs => eax
 5596     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
 5597 $new-reg-var-def:end:
 5598     89/<- %eax 7/r32/edi
 5599     # . restore registers
 5600     5f/pop-to-edi
 5601     59/pop-to-ecx
 5602     # . epilogue
 5603     89/<- %esp 5/r32/ebp
 5604     5d/pop-to-ebp
 5605     c3/return
 5606 
 5607 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
 5608     # . prologue
 5609     55/push-ebp
 5610     89/<- %ebp 4/r32/esp
 5611     # . save registers
 5612     51/push-ecx
 5613     #
 5614     (allocate *(ebp+8) *List-size)  # => eax
 5615     (zero-out %eax *List-size)
 5616     8b/-> *(ebp+0xc) 1/r32/ecx
 5617     89/<- *eax 1/r32/ecx  # List-value
 5618     # if (list == null) return result
 5619     81 7/subop/compare *(ebp+0x10) 0/imm32
 5620     74/jump-if-= $append-list:end/disp8
 5621     # otherwise append
 5622     # var curr/ecx = list
 5623     8b/-> *(ebp+0x10) 1/r32/ecx
 5624     # while (curr->next != null) curr = curr->next
 5625     {
 5626       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5627       74/jump-if-= break/disp8
 5628       # curr = curr->next
 5629       8b/-> *(ecx+4) 1/r32/ecx
 5630       eb/jump loop/disp8
 5631     }
 5632     # curr->next = result
 5633     89/<- *(ecx+4) 0/r32/eax
 5634     # return list
 5635     8b/-> *(ebp+0x10) 0/r32/eax
 5636 $append-list:end:
 5637     # . restore registers
 5638     59/pop-to-ecx
 5639     # . epilogue
 5640     89/<- %esp 5/r32/ebp
 5641     5d/pop-to-ebp
 5642     c3/return
 5643 
 5644 append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
 5645     # . prologue
 5646     55/push-ebp
 5647     89/<- %ebp 4/r32/esp
 5648     # . save registers
 5649     51/push-ecx
 5650     #
 5651     (allocate *(ebp+8) *Stmt-var-size)  # => eax
 5652     (zero-out %eax *Stmt-var-size)
 5653     8b/-> *(ebp+0xc) 1/r32/ecx
 5654     89/<- *eax 1/r32/ecx  # Stmt-var-value
 5655     8b/-> *(ebp+0x14) 1/r32/ecx
 5656     89/<- *(eax+8) 1/r32/ecx  # Stmt-var-is-deref
 5657     # if (list == null) return result
 5658     81 7/subop/compare *(ebp+0x10) 0/imm32
 5659     74/jump-if-= $append-stmt-var:end/disp8
 5660     # otherwise append
 5661     # var curr/ecx: (handle stmt-var) = vars
 5662     8b/-> *(ebp+0x10) 1/r32/ecx
 5663     # while (curr->next != null) curr = curr->next
 5664     {
 5665       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5666       74/jump-if-= break/disp8
 5667       # curr = curr->next
 5668       8b/-> *(ecx+4) 1/r32/ecx
 5669       eb/jump loop/disp8
 5670     }
 5671     # curr->next = result
 5672     89/<- *(ecx+4) 0/r32/eax
 5673     # return vars
 5674     8b/-> *(ebp+0x10) 0/r32/eax
 5675 $append-stmt-var:end:
 5676     # . restore registers
 5677     59/pop-to-ecx
 5678     # . epilogue
 5679     89/<- %esp 5/r32/ebp
 5680     5d/pop-to-ebp
 5681     c3/return
 5682 
 5683 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
 5684     # . prologue
 5685     55/push-ebp
 5686     89/<- %ebp 4/r32/esp
 5687     # . save registers
 5688     56/push-esi
 5689     # esi = block
 5690     8b/-> *(ebp+0xc) 6/r32/esi
 5691     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-stmts
 5692     89/<- *(esi+4) 0/r32/eax  # Block-stmts
 5693 $append-to-block:end:
 5694     # . restore registers
 5695     5e/pop-to-esi
 5696     # . epilogue
 5697     89/<- %esp 5/r32/ebp
 5698     5d/pop-to-ebp
 5699     c3/return
 5700 
 5701 ## Parsing types
 5702 # We need to create metadata on user-defined types, and we need to use this
 5703 # metadata as we parse instructions.
 5704 # However, we also want to allow types to be used before their definitions.
 5705 # This means we can't ever assume any type data structures exist.
 5706 
 5707 lookup-or-create-constant:  # container: (handle stmt-var), field-name: (addr slice) -> result/eax: (handle var)
 5708     # . prologue
 5709     55/push-ebp
 5710     89/<- %ebp 4/r32/esp
 5711     # . save registers
 5712     56/push-esi
 5713     # var container-type/esi: type-id
 5714     (container-type *(ebp+8))  # => eax
 5715 #?     (write-buffered Stderr "lookup-or-create-constant: container type-id: ")
 5716 #?     (print-int32-buffered Stderr %eax)
 5717 #?     (write-buffered Stderr Newline)
 5718 #?     (flush Stderr)
 5719     89/<- %esi 0/r32/eax
 5720     # var typeinfo/eax: (addr typeinfo)
 5721     (find-or-create-typeinfo %esi)  # => eax
 5722 #?     (write-buffered Stderr "lookup-or-create-constant: typeinfo: ")
 5723 #?     (print-int32-buffered Stderr %eax)
 5724 #?     (write-buffered Stderr Newline)
 5725 #?     (flush Stderr)
 5726     # result = find-or-create-typeinfo-output-var(typeinfo, field-name)
 5727     (find-or-create-typeinfo-output-var %eax *(ebp+0xc))  # => eax
 5728 $lookup-or-create-constant:end:
 5729     # . restore registers
 5730     5e/pop-to-esi
 5731     # . epilogue
 5732     89/<- %esp 5/r32/ebp
 5733     5d/pop-to-ebp
 5734     c3/return
 5735 
 5736 # if addr var:
 5737 #   container->var->type->right->left->value
 5738 # otherwise
 5739 #   container->var->type->value
 5740 container-type:  # container: (handle stmt-var) -> result/eax: type-id
 5741     # . prologue
 5742     55/push-ebp
 5743     89/<- %ebp 4/r32/esp
 5744     #
 5745     8b/-> *(ebp+8) 0/r32/eax
 5746     8b/-> *eax 0/r32/eax  # Stmt-var-value
 5747     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5748     {
 5749       81 7/subop/compare *(eax+4) 0/imm32
 5750       74/jump-if-= break/disp8
 5751       8b/-> *(eax+4) 0/r32/eax  # Tree-right
 5752       8b/-> *eax 0/r32/eax  # Tree-left
 5753     }
 5754     8b/-> *eax 0/r32/eax  # Atom-value
 5755 $container-type:end:
 5756     # . epilogue
 5757     89/<- %esp 5/r32/ebp
 5758     5d/pop-to-ebp
 5759     c3/return
 5760 
 5761 find-or-create-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5762     # . prologue
 5763     55/push-ebp
 5764     89/<- %ebp 4/r32/esp
 5765     # . save registers
 5766     51/push-ecx
 5767     # eax = find-typeinfo(t)
 5768     (find-typeinfo *(ebp+8))  # => eax
 5769     {
 5770       # if (curr != 0) break
 5771       3d/compare-eax-and 0/imm32
 5772       75/jump-if-!= break/disp8
 5773 $find-or-create-typeinfo:create:
 5774       (allocate Heap *Typeinfo-size)  # => eax
 5775       (zero-out %eax *Typeinfo-size)
 5776       # result->id = t
 5777       8b/-> *(ebp+8) 1/r32/ecx
 5778       89/<- *eax 1/r32/ecx  # Typeinfo-id
 5779       # result->fields = new table
 5780       # . ecx = new table
 5781       50/push-eax
 5782       (new-stream Heap 0x40 *Typeinfo-fields-row-size)  # => eax
 5783       89/<- %ecx 0/r32/eax
 5784       58/pop-to-eax
 5785       # . result->fields = ecx
 5786       89/<- *(eax+4) 1/r32/ecx  # Typeinfo-fields
 5787       # result->next = Program->types
 5788       8b/-> *_Program-types 1/r32/ecx
 5789       89/<- *(eax+0xc) 1/r32/ecx  # Typeinfo-next
 5790       # Program->types = result
 5791       89/<- *_Program-types 0/r32/eax
 5792     }
 5793 $find-or-create-typeinfo:end:
 5794     # . restore registers
 5795     59/pop-to-ecx
 5796     # . epilogue
 5797     89/<- %esp 5/r32/ebp
 5798     5d/pop-to-ebp
 5799     c3/return
 5800 
 5801 find-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5802     # . prologue
 5803     55/push-ebp
 5804     89/<- %ebp 4/r32/esp
 5805     # . save registers
 5806     51/push-ecx
 5807     # ecx = t
 5808     8b/-> *(ebp+8) 1/r32/ecx
 5809     # var curr/eax: (handle typeinfo) = Program->types
 5810     8b/-> *_Program-types 0/r32/eax
 5811     {
 5812       # if (curr == 0) break
 5813       3d/compare-eax-and 0/imm32
 5814       74/jump-if-= break/disp8
 5815       # if (curr->id == t) return curr
 5816       39/compare *eax 1/r32/ecx  # Typeinfo-id
 5817       0f 84/jump-if-= $find-or-create-typeinfo:end/disp32
 5818       # curr = curr->next
 5819       8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-next
 5820       #
 5821       eb/jump loop/disp8
 5822     }
 5823 $find-typeinfo:end:
 5824     # . restore registers
 5825     59/pop-to-ecx
 5826     # . epilogue
 5827     89/<- %esp 5/r32/ebp
 5828     5d/pop-to-ebp
 5829     c3/return
 5830 
 5831 find-or-create-typeinfo-output-var:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle var)
 5832     # . prologue
 5833     55/push-ebp
 5834     89/<- %ebp 4/r32/esp
 5835     # . save registers
 5836     51/push-ecx
 5837     56/push-esi
 5838     # esi = find-or-create-typeinfo-fields(T, f)
 5839     (find-or-create-typeinfo-fields *(ebp+8) *(ebp+0xc))  # => eax
 5840     89/<- %esi 0/r32/eax
 5841     # if output var doesn't exist, create it
 5842     {
 5843       81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-entry-output-var
 5844       75/jump-if-!= break/disp8
 5845       # var type/eax: (handle tree type-id) = new var("dummy name", constant type, -1 offset)
 5846       (allocate Heap *Tree-size)  # => eax
 5847       c7 0/subop/copy *eax 6/imm32/constant  # Atom-value
 5848       c7 0/subop/copy *(eax+4) 0/imm32  # Tree-right
 5849       89/<- %ecx 0/r32/eax
 5850       # eax = result
 5851       (new-var Heap "field")  # => eax
 5852       # result->type = type
 5853       89/<- *(eax+4) 1/r32/ecx  # Var-type
 5854       # result->offset isn't filled out yet
 5855       c7 0/subop/copy *(eax+0xc) -1/imm32/uninitialized  # Var-offset
 5856       # save result as output var
 5857       89/<- *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5858     }
 5859     # return the output var
 5860     8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 5861 $find-or-create-typeinfo-output-var:end:
 5862     # . restore registers
 5863     5e/pop-to-esi
 5864     59/pop-to-ecx
 5865     # . epilogue
 5866     89/<- %esp 5/r32/ebp
 5867     5d/pop-to-ebp
 5868     c3/return
 5869 
 5870 find-or-create-typeinfo-fields:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle typeinfo-entry)
 5871     # . prologue
 5872     55/push-ebp
 5873     89/<- %ebp 4/r32/esp
 5874     # . save registers
 5875     56/push-esi
 5876     # esi = T->fields
 5877     8b/-> *(ebp+8) 6/r32/esi
 5878     8b/-> *(esi+4) 6/r32/esi  # Typeinfo-fields
 5879     # esi = get-or-insert(T->fields, f)
 5880     (leaky-get-or-insert-slice %esi *(ebp+0xc) *Typeinfo-fields-row-size)  # => eax
 5881     89/<- %esi 0/r32/eax
 5882     # if typeinfo-entry doesn't exist, allocate it
 5883     {
 5884       81 7/subop/compare *esi 0/imm32  # output var
 5885       75/jump-if-!= break/disp8
 5886       (allocate Heap *Typeinfo-entry-size)  # => eax
 5887       (zero-out %eax *Typeinfo-entry-size)
 5888       89/<- *esi 0/r32/eax
 5889     }
 5890     # eax = T->fields[f]->entry
 5891     8b/-> *esi 0/r32/eax
 5892 $find-or-create-typeinfo-fields:end:
 5893     # . restore registers
 5894     5e/pop-to-esi
 5895     # . epilogue
 5896     89/<- %esp 5/r32/ebp
 5897     5d/pop-to-ebp
 5898     c3/return
 5899 
 5900 populate-mu-type:  # in: (addr stream byte), t: (handle typeinfo)
 5901     # pseudocode:
 5902     #   var line: (stream byte 512)
 5903     #   curr-index = 0
 5904     #   while true
 5905     #     clear-stream(line)
 5906     #     read-line-buffered(in, line)
 5907     #     if line->write == 0
 5908     #       abort
 5909     #     word-slice = next-mu-token(line)
 5910     #     if slice-empty?(word-slice)               # end of line
 5911     #       continue
 5912     #     if slice-equal?(word-slice, "}")
 5913     #       break
 5914     #     var v: (handle var) = parse-var-with-type(word-slice, line)
 5915     #     var r: (handle typeinfo-fields) = find-or-create-typeinfo-fields(t, word-slice/v->name)
 5916     #     TODO: ensure that r->first is null
 5917     #     r->index = curr-index
 5918     #     curr-index++
 5919     #     r->input-var = v
 5920     #     if r->output-var == 0
 5921     #       r->output-var = new literal
 5922     #     TODO: ensure nothing else in line
 5923     # t->total-size-in-bytes = -2 (not yet initialized)
 5924     #
 5925     # . prologue
 5926     55/push-ebp
 5927     89/<- %ebp 4/r32/esp
 5928     # var curr-index: int at *(ebp-4)
 5929     68/push 0/imm32
 5930     # . save registers
 5931     50/push-eax
 5932     51/push-ecx
 5933     52/push-edx
 5934     53/push-ebx
 5935     56/push-esi
 5936     57/push-edi
 5937     # edi = t
 5938     8b/-> *(ebp+0xc) 7/r32/edi
 5939     # var line/ecx: (stream byte 512)
 5940     81 5/subop/subtract %esp 0x200/imm32
 5941     68/push 0x200/imm32/length
 5942     68/push 0/imm32/read
 5943     68/push 0/imm32/write
 5944     89/<- %ecx 4/r32/esp
 5945     # var word-slice/edx: slice
 5946     68/push 0/imm32/end
 5947     68/push 0/imm32/start
 5948     89/<- %edx 4/r32/esp
 5949     {
 5950 $populate-mu-type:line-loop:
 5951       (clear-stream %ecx)
 5952       (read-line-buffered *(ebp+8) %ecx)
 5953       # if (line->write == 0) abort
 5954       81 7/subop/compare *ecx 0/imm32
 5955       0f 84/jump-if-= $populate-mu-type:abort/disp32
 5956 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
 5962       (next-mu-token %ecx %edx)
 5963       # if slice-empty?(word-slice) continue
 5964       (slice-empty? %edx)  # => eax
 5965       3d/compare-eax-and 0/imm32
 5966       0f 85/jump-if-!= loop/disp32
 5967       # if slice-equal?(word-slice, "}") break
 5968       (slice-equal? %edx "}")
 5969       3d/compare-eax-and 0/imm32
 5970       0f 85/jump-if-!= break/disp32
 5971 $populate-mu-type:parse-element:
 5972       # var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
 5973       (parse-var-with-type %edx %ecx)  # => eax
 5974       89/<- %esi 0/r32/eax
 5975 $populate-mu-type:create-typeinfo-fields:
 5976       # var r/ebx: (handle typeinfo-entry)
 5977       (find-or-create-typeinfo-fields %edi %edx)  # => eax
 5978       89/<- %ebx 0/r32/eax
 5979 #?       (write-buffered Stderr "var ")
 5980 #?       (write-buffered Stderr *esi)  # Var-name
 5981 #?       (write-buffered Stderr " is at index ")
 5982 #?       (print-int32-buffered Stderr *(ebp-4))
 5983 #?       (write-buffered Stderr Newline)
 5984 #?       (flush Stderr)
 5985       # r->index = curr-index
 5986       8b/-> *(ebp-4) 0/r32/eax
 5987       89/<- *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 5988       # ++curr-index
 5989       ff 0/subop/increment *(ebp-4)
 5990 $populate-mu-type:set-input-type:
 5991       # r->input-var = v
 5992       89/<- *ebx 6/r32/esi  # Typeinfo-entry-input-var
 5993       {
 5994 $populate-mu-type:create-output-type:
 5995         # if (r->output-var == 0) create a new var with some placeholder data
 5996         81 7/subop/compare *(ebx+8) 0/imm32  # Typeinfo-entry-output-var
 5997         75/jump-if-!= break/disp8
 5998         (new-literal Heap %edx)  # => eax
 5999         89/<- *(ebx+8) 0/r32/eax  # Typeinfo-entry-output-var
 6000       }
 6001       e9/jump loop/disp32
 6002     }
 6003 $populate-mu-type:invalidate-total-size-in-bytes:
 6004     # Offsets and total size may not be accurate here since we may not yet
 6005     # have encountered the element types.
 6006     # We'll recompute them separately after parsing the entire program.
 6007     c7 0/subop/copy *(edi+8) -2/imm32/uninitialized  # Typeinfo-total-size-in-bytes
 6008 $populate-mu-type:end:
 6009     # . reclaim locals
 6010     81 0/subop/add %esp 0x214/imm32
 6011     # . restore registers
 6012     5f/pop-to-edi
 6013     5e/pop-to-esi
 6014     5b/pop-to-ebx
 6015     5a/pop-to-edx
 6016     59/pop-to-ecx
 6017     58/pop-to-eax
 6018     # reclaim curr-index
 6019     81 0/subop/add %esp 4/imm32
 6020     # . epilogue
 6021     89/<- %esp 5/r32/ebp
 6022     5d/pop-to-ebp
 6023     c3/return
 6024 
 6025 $populate-mu-type:abort:
 6026     # error("unexpected top-level command: " word-slice "\n")
 6027     (write-buffered Stderr "incomplete type definition '")
 6028     (type-name *edi)  # Typeinfo-id => eax
 6029     (write-buffered Stderr %eax)
 6030     (write-buffered Stderr "\n")
 6031     (flush Stderr)
 6032     # . syscall(exit, 1)
 6033     bb/copy-to-ebx  1/imm32
 6034     b8/copy-to-eax  1/imm32/exit
 6035     cd/syscall  0x80/imm8
 6036     # never gets here
 6037 
 6038 type-name:  # index: int -> result/eax: (addr array byte)
 6039     # . prologue
 6040     55/push-ebp
 6041     89/<- %ebp 4/r32/esp
 6042     #
 6043     (index Type-id *(ebp+8))
 6044 $type-name:end:
 6045     # . epilogue
 6046     89/<- %esp 5/r32/ebp
 6047     5d/pop-to-ebp
 6048     c3/return
 6049 
 6050 index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
 6051     # . prologue
 6052     55/push-ebp
 6053     89/<- %ebp 4/r32/esp
 6054     # . save registers
 6055     56/push-esi
 6056     # TODO: bounds-check index
 6057     # esi = arr
 6058     8b/-> *(ebp+8) 6/r32/esi
 6059     # eax = index
 6060     8b/-> *(ebp+0xc) 0/r32/eax
 6061     # eax = *(arr + 12 + index)
 6062     8b/-> *(esi+eax+0xc) 0/r32/eax
 6063 $index:end:
 6064     # . restore registers
 6065     5e/pop-to-esi
 6066     # . epilogue
 6067     89/<- %esp 5/r32/ebp
 6068     5d/pop-to-ebp
 6069     c3/return
 6070 
 6071 #######################################################
 6072 # Compute type sizes
 6073 #######################################################
 6074 
 6075 # Compute the sizes of all user-defined types.
 6076 # We'll need the sizes of their elements, which may be other user-defined
 6077 # types, which we will compute as needed.
 6078 
 6079 # Initially, all user-defined types have their sizes set to -2 (invalid)
 6080 populate-mu-type-sizes:
 6081     # . prologue
 6082     55/push-ebp
 6083     89/<- %ebp 4/r32/esp
 6084     # . save registers
 6085     51/push-ecx
 6086 $populate-mu-type-sizes:total-sizes:
 6087     # var curr/ecx: (handle typeinfo) = *Program->types
 6088     8b/-> *_Program-types 1/r32/ecx
 6089     {
 6090       # if (curr == null) break
 6091       81 7/subop/compare %ecx 0/imm32
 6092       74/jump-if-= break/disp8
 6093       (populate-mu-type-sizes-in-type %ecx)
 6094       # curr = curr->next
 6095       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 6096       eb/jump loop/disp8
 6097     }
 6098 $populate-mu-type-sizes:offsets:
 6099     # var curr/ecx: (handle typeinfo) = *Program->types
 6100     8b/-> *_Program-types 1/r32/ecx
 6101     {
 6102       # if (curr == null) break
 6103       81 7/subop/compare %ecx 0/imm32
 6104       74/jump-if-= break/disp8
 6105       (populate-mu-type-offsets %ecx)
 6106       # curr = curr->next
 6107       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 6108       eb/jump loop/disp8
 6109     }
 6110 $populate-mu-type-sizes:end:
 6111     # . restore registers
 6112     59/pop-to-ecx
 6113     # . epilogue
 6114     89/<- %esp 5/r32/ebp
 6115     5d/pop-to-ebp
 6116     c3/return
 6117 
 6118 # compute sizes of all fields, recursing as necessary
 6119 # sum up all their sizes to arrive at total size
 6120 # fields may be out of order, but that doesn't affect the answer
 6121 populate-mu-type-sizes-in-type:  # T: (handle typeinfo)
 6122     # . prologue
 6123     55/push-ebp
 6124     89/<- %ebp 4/r32/esp
 6125     # . save registers
 6126     50/push-eax
 6127     51/push-ecx
 6128     52/push-edx
 6129     56/push-esi
 6130     57/push-edi
 6131     # esi = T
 6132     8b/-> *(ebp+8) 6/r32/esi
 6133     # if T is already computed, return
 6134     81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-total-size-in-bytes
 6135     7d/jump-if->= $populate-mu-type-sizes-in-type:end/disp8
 6136     # if T is being computed, abort
 6137     81 7/subop/compare *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 6138     74/jump-if-= $populate-mu-type-sizes-in-type:abort/disp8
 6139     # tag T (-2 to -1) to avoid infinite recursion
 6140     c7 0/subop/copy *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 6141     # var total-size/edi: int = 0
 6142     bf/copy-to-edi 0/imm32
 6143     # - for every field, if it's a user-defined type, compute its size
 6144     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 6145     8b/-> *(esi+4) 1/r32/ecx  # Typeinfo-fields
 6146     # var table-size/edx: int = table->write
 6147     8b/-> *ecx 2/r32/edx  # stream-write
 6148     # var curr/ecx: (addr table_row) = table->data
 6149     8d/copy-address *(ecx+0xc) 1/r32/ecx
 6150     # var max/edx: (addr table_row) = table->data + table->write
 6151     8d/copy-address *(ecx+edx) 2/r32/edx
 6152     {
 6153 $populate-mu-type-sizes-in-type:loop:
 6154       # if (curr >= max) break
 6155       39/compare %ecx 2/r32/edx
 6156       73/jump-if-addr>= break/disp8
 6157       # var t/eax: (handle typeinfo-entry) = curr->value
 6158       8b/-> *(ecx+4) 0/r32/eax
 6159       # compute size of t
 6160       (compute-size-of-var *eax)  # Typeinfo-entry-input-var => eax
 6161       # result += eax
 6162       01/add-to %edi 0/r32/eax
 6163       # curr += row-size
 6164       81 0/subop/add %ecx 8/imm32
 6165       #
 6166       eb/jump loop/disp8
 6167     }
 6168     # - save result
 6169     89/<- *(esi+8) 7/r32/edi  # Typeinfo-total-size-in-bytes
 6170 $populate-mu-type-sizes-in-type:end:
 6171     # . restore registers
 6172     5f/pop-to-edi
 6173     5e/pop-to-esi
 6174     5a/pop-to-edx
 6175     59/pop-to-ecx
 6176     58/pop-to-eax
 6177     # . epilogue
 6178     89/<- %esp 5/r32/ebp
 6179     5d/pop-to-ebp
 6180     c3/return
 6181 
 6182 $populate-mu-type-sizes-in-type:abort:
 6183     (write-buffered Stderr "cycle in type definitions\n")
 6184     (flush Stderr)
 6185     # . syscall(exit, 1)
 6186     bb/copy-to-ebx  1/imm32
 6187     b8/copy-to-eax  1/imm32/exit
 6188     cd/syscall  0x80/imm8
 6189     # never gets here
 6190 
 6191 # Analogous to size-of, except we need to compute what size-of can just read
 6192 # off the right data structures.
 6193 compute-size-of-var:  # in: (handle var) -> result/eax: int
 6194     # . prologue
 6195     55/push-ebp
 6196     89/<- %ebp 4/r32/esp
 6197     # . push registers
 6198     51/push-ecx
 6199     # var t/ecx: (handle tree type-id) = v->type
 6200     8b/-> *(ebp+8) 1/r32/ecx
 6201     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6202     # if (t->left >= *Max-type-id) t = t->left
 6203     {
 6204       8b/-> *Max-type-id 0/r32/eax
 6205       39/compare *ecx 0/r32/eax  # Tree-left
 6206       72/jump-if-addr< break/disp8
 6207       8b/-> *ecx 1/r32/ecx  # Tree-left
 6208     }
 6209     (compute-size-of-type-id *ecx)  # Atom-left => eax
 6210 $compute-size-of-var:end:
 6211     # . restore registers
 6212     59/pop-to-ecx
 6213     # . epilogue
 6214     89/<- %esp 5/r32/ebp
 6215     5d/pop-to-ebp
 6216     c3/return
 6217 
 6218 compute-size-of-type-id:  # t: type-id -> result/eax: int
 6219     # . prologue
 6220     55/push-ebp
 6221     89/<- %ebp 4/r32/esp
 6222     #
 6223     8b/-> *(ebp+8) 0/r32/eax
 6224     # if v is a literal, return 0
 6225     3d/compare-eax-and 0/imm32
 6226     74/jump-if-= $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
 6227     # if v has a user-defined type, compute its size
 6228     # TODO: support non-atom type
 6229     (find-typeinfo %eax)  # => eax
 6230     {
 6231       3d/compare-eax-and 0/imm32
 6232       74/jump-if-= break/disp8
 6233 $compute-size-of-type-id:user-defined:
 6234       (populate-mu-type-sizes %eax)
 6235       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6236       eb/jump $compute-size-of-type-id:end/disp8
 6237     }
 6238     # otherwise return the word size
 6239     b8/copy-to-eax 4/imm32
 6240 $compute-size-of-type-id:end:
 6241     # . epilogue
 6242     89/<- %esp 5/r32/ebp
 6243     5d/pop-to-ebp
 6244     c3/return
 6245 
 6246 # at this point we have total sizes for all user-defined types
 6247 # compute offsets for each element
 6248 # complication: fields may be out of order
 6249 populate-mu-type-offsets:  # in: (handle typeinfo)
 6250     # . prologue
 6251     55/push-ebp
 6252     89/<- %ebp 4/r32/esp
 6253     # . save registers
 6254     50/push-eax
 6255     51/push-ecx
 6256     52/push-edx
 6257     53/push-ebx
 6258     56/push-esi
 6259     57/push-edi
 6260     # var curr-offset/edi: int = 0
 6261     bf/copy-to-edi 0/imm32
 6262     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 6263     8b/-> *(ebp+8) 1/r32/ecx
 6264     8b/-> *(ecx+4) 1/r32/ecx  # Typeinfo-fields
 6265     # var num-elems/edx: int = table->write / 8
 6266     8b/-> *ecx 2/r32/edx  # stream-write
 6267     c1 5/subop/shift-right-logical  %edx 3/imm8
 6268     # var i/ebx: int = 0
 6269     bb/copy-to-ebx 0/imm32
 6270     {
 6271 $populate-mu-type-offsets:loop:
 6272       39/compare %ebx 2/r32/edx
 6273       7d/jump-if->= break/disp8
 6274       # var v/esi: (handle typeinfo-entry)
 6275       (locate-typeinfo-entry-with-index %ecx %ebx)  # => eax
 6276       89/<- %esi 0/r32/eax
 6277       # v->output-var->offset = curr-offset
 6278       8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 6279       89/<- *(eax+0xc) 7/r32/edi  # Var-offset
 6280       # curr-offset += size-of(v->input-var)
 6281       8b/-> *esi 0/r32/eax  # Typeinfo-entry-input-var
 6282       (size-of %eax)  # => eax
 6283       01/add-to %edi 0/r32/eax
 6284       # ++i
 6285       43/increment-ebx
 6286       eb/jump loop/disp8
 6287     }
 6288 $populate-mu-type-offsets:end:
 6289     # . restore registers
 6290     5f/pop-to-edi
 6291     5e/pop-to-esi
 6292     5b/pop-to-ebx
 6293     5a/pop-to-edx
 6294     59/pop-to-ecx
 6295     58/pop-to-eax
 6296     # . epilogue
 6297     89/<- %esp 5/r32/ebp
 6298     5d/pop-to-ebp
 6299     c3/return
 6300 
 6301 locate-typeinfo-entry-with-index:  # table: (handle table string_key (handle typeinfo-entry)), idx: int -> result/eax: (handle typeinfo-entry)
 6302     # . prologue
 6303     55/push-ebp
 6304     89/<- %ebp 4/r32/esp
 6305     # . save registers
 6306     51/push-ecx
 6307     52/push-edx
 6308     53/push-ebx
 6309     56/push-esi
 6310     57/push-edi
 6311     # esi = table
 6312     8b/-> *(ebp+8) 6/r32/esi
 6313     # var curr/ecx: (addr string_key) = table->data
 6314     8d/copy-address *(esi+0xc) 1/r32/ecx
 6315     # var max/edx: (addr byte) = &table->data[table->write]
 6316     8b/-> *esi 2/r32/edx
 6317     8d/copy-address *(ecx+edx) 2/r32/edx
 6318     {
 6319 $locate-typeinfo-entry-with-index:loop:
 6320       39/compare %ecx 2/r32/edx
 6321       73/jump-if-addr>= $locate-typeinfo-entry-with-index:abort/disp8
 6322       # var v/ebx: (handle typeinfo-entry)
 6323       8b/-> *(ecx+4) 3/r32/ebx
 6324       # if (v->index == idx) return v
 6325       8b/-> *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 6326       39/compare *(ebp+0xc) 0/r32/eax
 6327       89/<- %eax 3/r32/ebx
 6328       74/jump-if-= break/disp8
 6329       # curr += 8
 6330       81 0/subop/add %ecx 8/imm32
 6331       eb/jump loop/disp8
 6332     }
 6333 $locate-typeinfo-entry-with-index:end:
 6334     # . restore registers
 6335     5f/pop-to-edi
 6336     5e/pop-to-esi
 6337     5b/pop-to-ebx
 6338     5a/pop-to-edx
 6339     59/pop-to-ecx
 6340     # . epilogue
 6341     89/<- %esp 5/r32/ebp
 6342     5d/pop-to-ebp
 6343     c3/return
 6344 
 6345 $locate-typeinfo-entry-with-index:abort:
 6346     (write-buffered Stderr "overflowing typeinfo-entry->index ")
 6347     (print-int32-buffered Stderr %ecx)
 6348     (write-buffered Stderr "\n")
 6349     (flush Stderr)
 6350     # . syscall(exit, 1)
 6351     bb/copy-to-ebx  1/imm32
 6352     b8/copy-to-eax  1/imm32/exit
 6353     cd/syscall  0x80/imm8
 6354     # never gets here
 6355 
 6356 #######################################################
 6357 # Type-checking
 6358 #######################################################
 6359 
 6360 check-mu-types:
 6361     # . prologue
 6362     55/push-ebp
 6363     89/<- %ebp 4/r32/esp
 6364     #
 6365 $check-mu-types:end:
 6366     # . epilogue
 6367     89/<- %esp 5/r32/ebp
 6368     5d/pop-to-ebp
 6369     c3/return
 6370 
 6371 size-of:  # v: (handle var) -> result/eax: int
 6372     # . prologue
 6373     55/push-ebp
 6374     89/<- %ebp 4/r32/esp
 6375     # . save registers
 6376     51/push-ecx
 6377     # var t/ecx: (handle tree type-id) = v->type
 6378     8b/-> *(ebp+8) 1/r32/ecx
 6379     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6380     # if is-mu-array?(t) return size-of-array(t)
 6381     {
 6382       (is-mu-array? %ecx)  # => eax
 6383       3d/compare-eax-and 0/imm32/false
 6384       74/jump-if-= break/disp8
 6385       (size-of-array %ecx)  # => eax
 6386       eb/jump $size-of:end/disp8
 6387     }
 6388     # if (t->left >= *Max-type-id) t = t->left
 6389     {
 6390       8b/-> *Max-type-id 0/r32/eax
 6391       39/compare *ecx 0/r32/eax  # Tree-left
 6392       72/jump-if-addr< break/disp8
 6393       8b/-> *ecx 1/r32/ecx  # Tree-left
 6394     }
 6395     (size-of-type-id *ecx)  # Atom-left => eax
 6396 $size-of:end:
 6397     # . restore registers
 6398     59/pop-to-ecx
 6399     # . epilogue
 6400     89/<- %esp 5/r32/ebp
 6401     5d/pop-to-ebp
 6402     c3/return
 6403 
 6404 is-mu-array?:  # t: (handle tree type-id) -> result/eax: boolean
 6405     # . prologue
 6406     55/push-ebp
 6407     89/<- %ebp 4/r32/esp
 6408     # . save registers
 6409     51/push-ecx
 6410     # ecx = t->left
 6411     8b/-> *(ebp+8) 1/r32/ecx
 6412     8b/-> *ecx 1/r32/ecx  # Tree-left
 6413     # if t is an atomic type, return false
 6414     3b/compare 1/r32/ecx *Max-type-id
 6415     b8/copy-to-eax 0/imm32/false
 6416     72/jump-if-addr< $is-mu-array?:end/disp8
 6417     # return ecx->value == array
 6418     81 7/subop/compare *ecx 3/imm32/array-type-id  # Atom-value
 6419     0f 94/set-if-= %al
 6420     81 4/subop/and %eax 0xff/imm32
 6421 $is-mu-array?:end:
 6422     # . restore registers
 6423     59/pop-to-ecx
 6424     # . epilogue
 6425     89/<- %esp 5/r32/ebp
 6426     5d/pop-to-ebp
 6427     c3/return
 6428 
 6429 size-of-array:  # a: (handle tree type-id) -> result/eax: int
 6430     # . prologue
 6431     55/push-ebp
 6432     89/<- %ebp 4/r32/esp
 6433     # . save registers
 6434     51/push-ecx
 6435     52/push-edx
 6436     #
 6437     8b/-> *(ebp+8) 1/r32/ecx
 6438     # TODO: assert that a->left is 'array'
 6439     8b/-> *(ecx+4) 1/r32/ecx  # Tree-right
 6440     # var elem-type/edx: type-id = a->right->value
 6441     8b/-> *ecx 2/r32/edx  # Atom-value
 6442     8b/-> *edx 2/r32/edx  # Atom-value
 6443     # var array-size/ecx: int = a->right->right->left->value
 6444     8b/-> *(ecx+4) 1/r32/ecx  # Tree-right
 6445     8b/-> *ecx 1/r32/ecx  # Tree-left
 6446     8b/-> *ecx 1/r32/ecx  # Atom-value
 6447     # return array-size * size-of(elem-type)
 6448     (size-of-type-id %edx)  # => eax
 6449     f7 4/subop/multiply-into-eax %ecx
 6450     05/add-to-eax 4/imm32  # for array length
 6451 $size-of-array:end:
 6452     # . restore registers
 6453     5a/pop-to-edx
 6454     59/pop-to-ecx
 6455     # . epilogue
 6456     89/<- %esp 5/r32/ebp
 6457     5d/pop-to-ebp
 6458     c3/return
 6459 
 6460 size-of-type-id:  # t: type-id -> result/eax: int
 6461     # . prologue
 6462     55/push-ebp
 6463     89/<- %ebp 4/r32/esp
 6464     #
 6465     8b/-> *(ebp+8) 0/r32/eax
 6466     # if v is a literal, return 0
 6467     3d/compare-eax-and 0/imm32
 6468     74/jump-if-= $size-of-type-id:end/disp8  # eax changes type from type-id to int
 6469     # if v has a user-defined type, return its size
 6470     # TODO: support non-atom type
 6471     (find-typeinfo %eax)  # => eax
 6472     {
 6473       3d/compare-eax-and 0/imm32
 6474       74/jump-if-= break/disp8
 6475 $size-of-type-id:user-defined:
 6476       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6477       eb/jump $size-of-type-id:end/disp8
 6478     }
 6479     # otherwise return the word size
 6480     b8/copy-to-eax 4/imm32
 6481 $size-of-type-id:end:
 6482     # . epilogue
 6483     89/<- %esp 5/r32/ebp
 6484     5d/pop-to-ebp
 6485     c3/return
 6486 
 6487 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
 6488     # . prologue
 6489     55/push-ebp
 6490     89/<- %ebp 4/r32/esp
 6491     # . save registers
 6492     51/push-ecx
 6493     52/push-edx
 6494     # ecx = a
 6495     8b/-> *(ebp+8) 1/r32/ecx
 6496     # edx = b
 6497     8b/-> *(ebp+0xc) 2/r32/edx
 6498     # if (a == b) return true
 6499     8b/-> %ecx 0/r32/eax  # Var-type
 6500     39/compare %edx 0/r32/eax  # Var-type
 6501     b8/copy-to-eax 1/imm32/true
 6502     74/jump-if-= $type-equal?:end/disp8
 6503     # if (a < MAX_TYPE_ID) return false
 6504     81 7/subop/compare %ecx 0x10000/imm32
 6505     b8/copy-to-eax 0/imm32/false
 6506     72/jump-if-addr< $type-equal?:end/disp8
 6507     # if (b < MAX_TYPE_ID) return false
 6508     81 7/subop/compare %edx 0x10000/imm32
 6509     b8/copy-to-eax 0/imm32/false
 6510     72/jump-if-addr< $type-equal?:end/disp8
 6511     # if (!type-equal?(a->left, b->left)) return false
 6512     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
 6513     3d/compare-eax-and 0/imm32/false
 6514     74/jump-if-= $type-equal?:end/disp8
 6515     # return type-equal?(a->right, b->right)
 6516     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
 6517 $type-equal?:end:
 6518     # . restore registers
 6519     5a/pop-to-edx
 6520     59/pop-to-ecx
 6521     # . epilogue
 6522     89/<- %esp 5/r32/ebp
 6523     5d/pop-to-ebp
 6524     c3/return
 6525 
 6526 #######################################################
 6527 # Code-generation
 6528 #######################################################
 6529 
 6530 == data
 6531 
 6532 Curr-block-depth:  # (addr int)
 6533     0/imm32
 6534 Curr-local-stack-offset:  # (addr int)
 6535     0/imm32
 6536 
 6537 == code
 6538 
 6539 emit-subx:  # out: (addr buffered-file)
 6540     # . prologue
 6541     55/push-ebp
 6542     89/<- %ebp 4/r32/esp
 6543     # . save registers
 6544     50/push-eax
 6545     51/push-ecx
 6546     57/push-edi
 6547     # edi = out
 6548     8b/-> *(ebp+8) 7/r32/edi
 6549     # var curr/ecx: (handle function) = *Program->functions
 6550     8b/-> *_Program-functions 1/r32/ecx
 6551     {
 6552       # if (curr == null) break
 6553       81 7/subop/compare %ecx 0/imm32
 6554       0f 84/jump-if-= break/disp32
 6555       (emit-subx-function %edi %ecx)
 6556       # curr = curr->next
 6557       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 6558       e9/jump loop/disp32
 6559     }
 6560 $emit-subx:end:
 6561     # . restore registers
 6562     5f/pop-to-edi
 6563     59/pop-to-ecx
 6564     58/pop-to-eax
 6565     # . epilogue
 6566     89/<- %esp 5/r32/ebp
 6567     5d/pop-to-ebp
 6568     c3/return
 6569 
 6570 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
 6571     # . prologue
 6572     55/push-ebp
 6573     89/<- %ebp 4/r32/esp
 6574     # some preprocessing
 6575     (populate-mu-type-offsets-in-inouts *(ebp+0xc))
 6576     # . save registers
 6577     50/push-eax
 6578     51/push-ecx
 6579     52/push-edx
 6580     57/push-edi
 6581     # edi = out
 6582     8b/-> *(ebp+8) 7/r32/edi
 6583     # ecx = f
 6584     8b/-> *(ebp+0xc) 1/r32/ecx
 6585     # var vars/edx: (stack (addr var) 256)
 6586     81 5/subop/subtract %esp 0x400/imm32
 6587     68/push 0x400/imm32/length
 6588     68/push 0/imm32/top
 6589     89/<- %edx 4/r32/esp
 6590     #
 6591     (write-buffered %edi *ecx)
 6592     (write-buffered %edi ":\n")
 6593     # initialize some global state
 6594     c7 0/subop/copy *Curr-block-depth 1/imm32
 6595     c7 0/subop/copy *Curr-local-stack-offset 0/imm32
 6596     #
 6597     (emit-subx-prologue %edi)
 6598     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
 6599     (emit-subx-epilogue %edi)
 6600     # TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
 6601     # been cleaned up
 6602 $emit-subx-function:end:
 6603     # . reclaim locals
 6604     81 0/subop/add %esp 408/imm32
 6605     # . restore registers
 6606     5f/pop-to-edi
 6607     5a/pop-to-edx
 6608     59/pop-to-ecx
 6609     58/pop-to-eax
 6610     # . epilogue
 6611     89/<- %esp 5/r32/ebp
 6612     5d/pop-to-ebp
 6613     c3/return
 6614 
 6615 populate-mu-type-offsets-in-inouts:  # f: (handle function)
 6616     # . prologue
 6617     55/push-ebp
 6618     89/<- %ebp 4/r32/esp
 6619     # . save registers
 6620     50/push-eax
 6621     51/push-ecx
 6622     52/push-edx
 6623     53/push-ebx
 6624     57/push-edi
 6625     # var next-offset/edx: int = 8
 6626     ba/copy-to-edx 8/imm32
 6627     # var curr/ecx: (handle list var) = f->inouts
 6628     8b/-> *(ebp+8) 1/r32/ecx
 6629     8b/-> *(ecx+8) 1/r32/ecx  # Function-inouts
 6630     {
 6631 $populate-mu-type-offsets-in-inouts:loop:
 6632       81 7/subop/compare %ecx 0/imm32
 6633       74/jump-if-= break/disp8
 6634       # var v/ebx: (handle var) = curr->value
 6635       8b/-> *ecx 3/r32/ebx  # List-value
 6636       # v->offset = next-offset
 6637       89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
 6638       # next-offset += size-of(v)
 6639       (size-of %ebx)  # => eax
 6640       01/add %edx 0/r32/eax
 6641       # curr = curr->next
 6642       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 6643       eb/jump loop/disp8
 6644     }
 6645 $populate-mu-type-offsets-in-inouts:end:
 6646     # . restore registers
 6647     5f/pop-to-edi
 6648     5b/pop-to-ebx
 6649     5a/pop-to-edx
 6650     59/pop-to-ecx
 6651     58/pop-to-eax
 6652     # . epilogue
 6653     89/<- %esp 5/r32/ebp
 6654     5d/pop-to-ebp
 6655     c3/return
 6656 
 6657 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
 6658     # . prologue
 6659     55/push-ebp
 6660     89/<- %ebp 4/r32/esp
 6661     # . save registers
 6662     50/push-eax
 6663     51/push-ecx
 6664     52/push-edx
 6665     53/push-ebx
 6666     56/push-esi
 6667     # esi = stmts
 6668     8b/-> *(ebp+0xc) 6/r32/esi
 6669     # var var-seen?/edx: boolean <- copy false
 6670     ba/copy-to-edx 0/imm32/false
 6671     #
 6672     {
 6673 $emit-subx-stmt-list:loop:
 6674       81 7/subop/compare %esi 0/imm32
 6675       0f 84/jump-if-= break/disp32
 6676       # var curr-stmt/ecx = stmts->value
 6677       8b/-> *esi 1/r32/ecx  # List-value
 6678       {
 6679 $emit-subx-stmt-list:check-for-block:
 6680         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
 6681         75/jump-if-!= break/disp8
 6682 $emit-subx-stmt-list:block:
 6683         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
 6684       }
 6685       {
 6686 $emit-subx-stmt-list:check-for-stmt:
 6687         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
 6688         0f 85/jump-if-!= break/disp32
 6689 $emit-subx-stmt-list:stmt1:
 6690         {
 6691           (is-mu-branch? %ecx)  # => eax
 6692           3d/compare-eax-and 0/imm32/false
 6693           0f 84/jump-if-= break/disp32
 6694 $emit-subx-stmt-list:branch-stmt:
 6695           # if !var-seen? break
 6696           81 7/subop/compare %edx 0/imm32/false
 6697           0f 84/jump-if-= break/disp32
 6698 $emit-subx-stmt-list:branch-stmt-and-var-seen:
 6699 +-- 26 lines: # unconditional loops -----------------------------------------------------------------------------------------------------------------------------------------------------
 6725 +-- 15 lines: # unconditional breaks ----------------------------------------------------------------------------------------------------------------------------------------------------
 6740 +-- 37 lines: # simple conditional branches without a target ----------------------------------------------------------------------------------------------------------------------------
 6777 +-- 19 lines: # conditional branches with an explicit target ----------------------------------------------------------------------------------------------------------------------------
 6796         }
 6797 $emit-subx-stmt-list:1-to-1:
 6798         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
 6799       }
 6800       {
 6801 $emit-subx-stmt-list:check-for-var-def:
 6802         81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
 6803         75/jump-if-!= break/disp8
 6804 $emit-subx-stmt-list:var-def:
 6805         (emit-subx-var-def *(ebp+8) %ecx)
 6806         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
 6807         # var-seen? = true
 6808         ba/copy-to-edx 1/imm32/true
 6809       }
 6810       {
 6811 $emit-subx-stmt-list:check-for-reg-var-def:
 6812         81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
 6813         0f 85/jump-if-!= break/disp32
 6814 $emit-subx-stmt-list:reg-var-def:
 6815         # TODO: ensure that there's exactly one output
 6816         (compute-reg-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10))
 6817         # register variable definition
 6818         (push *(ebp+0x10) %eax)
 6819         # emit the instruction as usual
 6820         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
 6821         # var-seen? = true
 6822         ba/copy-to-edx 1/imm32/true
 6823       }
 6824 $emit-subx-stmt-list:continue:
 6825       # TODO: raise an error on unrecognized Stmt-tag
 6826       8b/-> *(esi+4) 6/r32/esi  # List-next
 6827       e9/jump loop/disp32
 6828     }
 6829 $emit-subx-stmt-list:emit-cleanup:
 6830     (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
 6831 $emit-subx-stmt-list:cleanup:
 6832     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
 6833 $emit-subx-stmt-list:end:
 6834     # . restore registers
 6835     5e/pop-to-esi
 6836     5b/pop-to-ebx
 6837     5a/pop-to-edx
 6838     59/pop-to-ecx
 6839     58/pop-to-eax
 6840     # . epilogue
 6841     89/<- %esp 5/r32/ebp
 6842     5d/pop-to-ebp
 6843     c3/return
 6844 
 6845 compute-reg-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (handle reg-var-def), vars: (addr stack (handle var)) -> result/eax: (handle var)
 6846     # . prologue
 6847     55/push-ebp
 6848     89/<- %ebp 4/r32/esp
 6849     # . save registers
 6850     51/push-ecx
 6851     # ecx = stmt
 6852     8b/-> *(ebp+0xc) 1/r32/ecx
 6853     # var output/ecx: (handle var) = curr-stmt->outputs->value
 6854     8b/-> *(ecx+0xc) 1/r32/ecx  # Regvardef-inouts
 6855     8b/-> *ecx 1/r32/ecx  # List-value
 6856     # v->block-depth = *Curr-block-depth
 6857     8b/-> *Curr-block-depth 0/r32/eax
 6858     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 6859     # var reg/eax: (handle array byte) = output->register
 6860     8b/-> *(ecx+0x10) 0/r32/eax  # Var-register
 6861     # ensure that output is in a register
 6862     3d/compare-eax-and 0/imm32
 6863     0f 84/jump-if-= $compute-reg-and-maybe-emit-spill:abort/disp32
 6864     # if already-spilled-this-block?(reg, vars) return
 6865     (already-spilled-this-block? %ecx *(ebp+0x10))  # => eax
 6866     3d/compare-eax-and 0/imm32/false
 6867     75/jump-if-!= $compute-reg-and-maybe-emit-spill:end/disp8
 6868     # TODO: assert(size-of(output) == 4)
 6869     # *Curr-local-stack-offset -= 4
 6870     81 5/subop/subtract *Curr-local-stack-offset 4/imm32
 6871     # emit spill
 6872     (emit-indent *(ebp+8) *Curr-block-depth)
 6873     (write-buffered *(ebp+8) "ff 6/subop/push %")
 6874     (write-buffered *(ebp+8) *(ecx+0x10))  # Var-register
 6875     (write-buffered *(ebp+8) Newline)
 6876 $compute-reg-and-maybe-emit-spill:end:
 6877     # return output
 6878     89/<- %eax 1/r32/ecx
 6879     # . restore registers
 6880     59/pop-to-ecx
 6881     # . epilogue
 6882     89/<- %esp 5/r32/ebp
 6883     5d/pop-to-ebp
 6884     c3/return
 6885 
 6886 $compute-reg-and-maybe-emit-spill:abort:
 6887     # error("var '" var->name "' initialized from an instruction must live in a register\n")
 6888     (write-buffered Stderr "var '")
 6889     (write-buffered Stderr *eax)  # Var-name
 6890     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
 6891     (flush Stderr)
 6892     # . syscall(exit, 1)
 6893     bb/copy-to-ebx  1/imm32
 6894     b8/copy-to-eax  1/imm32/exit
 6895     cd/syscall  0x80/imm8
 6896     # never gets here
 6897 
 6898 emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
 6899     # . prologue
 6900     55/push-ebp
 6901     89/<- %ebp 4/r32/esp
 6902     # . save registers
 6903     50/push-eax
 6904     51/push-ecx
 6905     52/push-edx
 6906     # ecx = stmt
 6907     8b/-> *(ebp+0xc) 1/r32/ecx
 6908     # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
 6909     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 6910     8b/-> *edx 2/r32/edx  # Stmt-var-value
 6911     8b/-> *edx 2/r32/edx  # Var-name
 6912     # clean up until target block
 6913     (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
 6914     # emit jump to target block
 6915     (emit-indent *(ebp+8) *Curr-block-depth)
 6916     (write-buffered *(ebp+8) "e9/jump ")
 6917     (write-buffered *(ebp+8) %edx)
 6918     (string-starts-with? *(ecx+4) "break")
 6919     3d/compare-eax-and 0/imm32/false
 6920     {
 6921       74/jump-if-= break/disp8
 6922       (write-buffered *(ebp+8) ":break/disp32\n")
 6923     }
 6924     3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
 6925     {
 6926       75/jump-if-!= break/disp8
 6927       (write-buffered *(ebp+8) ":loop/disp32\n")
 6928     }
 6929 $emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
 6930     # . restore registers
 6931     5a/pop-to-edx
 6932     59/pop-to-ecx
 6933     58/pop-to-eax
 6934     # . epilogue
 6935     89/<- %esp 5/r32/ebp
 6936     5d/pop-to-ebp
 6937     c3/return
 6938 
 6939 is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
 6940     # . prologue
 6941     55/push-ebp
 6942     89/<- %ebp 4/r32/esp
 6943     # . save registers
 6944     51/push-ecx
 6945     # ecx = stmt
 6946     8b/-> *(ebp+8) 1/r32/ecx
 6947     # if (stmt->operation starts with "loop") return true
 6948     (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
 6949     3d/compare-eax-and 0/imm32/false
 6950     75/jump-if-not-equal $is-mu-branch?:end/disp8
 6951     # otherwise return (stmt->operation starts with "break")
 6952     (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
 6953 $is-mu-branch?:end:
 6954     # . restore registers
 6955     59/pop-to-ecx
 6956     # . epilogue
 6957     89/<- %esp 5/r32/ebp
 6958     5d/pop-to-ebp
 6959     c3/return
 6960 
 6961 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
 6962     # . prologue
 6963     55/push-ebp
 6964     89/<- %ebp 4/r32/esp
 6965     # . save registers
 6966     50/push-eax
 6967     # eax = stmt
 6968     8b/-> *(ebp+0xc) 0/r32/eax
 6969     #
 6970     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
 6971     (emit-indent *(ebp+8) *Curr-block-depth)
 6972     (write-buffered *(ebp+8) *eax)
 6973     (write-buffered *(ebp+8) " break/disp32\n")
 6974 $emit-reverse-break:end:
 6975     # . restore registers
 6976     58/pop-to-eax
 6977     # . epilogue
 6978     89/<- %esp 5/r32/ebp
 6979     5d/pop-to-ebp
 6980     c3/return
 6981 
 6982 == data
 6983 
 6984 Reverse-branch:  # (table string string)
 6985   # a table is a stream
 6986   0xa0/imm32/write
 6987   0/imm32/read
 6988   0xa0/imm32/length
 6989   # data
 6990   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
 6991   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
 6992   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
 6993   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
 6994   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
 6995   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
 6996   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
 6997   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
 6998   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
 6999   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
 7000   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
 7001   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
 7002   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
 7003   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
 7004   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
 7005   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
 7006   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
 7007   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
 7008   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
 7009   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
 7010 
 7011 == code
 7012 
 7013 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
 7014     # . prologue
 7015     55/push-ebp
 7016     89/<- %ebp 4/r32/esp
 7017     # . save registers
 7018     50/push-eax
 7019     51/push-ecx
 7020     52/push-edx
 7021     53/push-ebx
 7022     # ecx = vars
 7023     8b/-> *(ebp+0xc) 1/r32/ecx
 7024     # var eax: int = vars->top
 7025     8b/-> *ecx 0/r32/eax
 7026     # var min/ecx: (address (handle var)) = vars->data
 7027     81 0/subop/add %ecx 8/imm32
 7028     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 7029     81 5/subop/subtract %eax 4/imm32
 7030     8d/copy-address *(ecx+eax) 0/r32/eax
 7031     # edx = depth
 7032     8b/-> *(ebp+0x10) 2/r32/edx
 7033     {
 7034 $emit-unconditional-jump-to-depth:loop:
 7035       # if (curr < min) break
 7036       39/compare %eax 1/r32/ecx
 7037       0f 82/jump-if-addr< break/disp32
 7038       # var v/ebx: (handle var) = *curr
 7039       8b/-> *eax 3/r32/ebx
 7040       # if (v->block-depth < until-block-depth) break
 7041       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7042       0f 8c/jump-if-< break/disp32
 7043       {
 7044 $emit-unconditional-jump-to-depth:check:
 7045         # if v->block-depth != until-block-depth, continue
 7046         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7047         0f 85/jump-if-!= break/disp32
 7048 $emit-unconditional-jump-to-depth:depth-found:
 7049         # if v is not a literal, continue
 7050         # . var eax: int = size-of(v)
 7051         50/push-eax
 7052         (size-of %ebx)  # => eax
 7053         # . if (eax != 0) continue
 7054         3d/compare-eax-and 0/imm32
 7055         58/pop-to-eax
 7056         #
 7057         0f 85/jump-if-!= break/disp32
 7058 $emit-unconditional-jump-to-depth:label-found:
 7059         # emit unconditional jump, then return
 7060         (emit-indent *(ebp+8) *Curr-block-depth)
 7061         (write-buffered *(ebp+8) "e9/jump ")
 7062         (write-buffered *(ebp+8) *ebx)  # Var-name
 7063         (write-buffered *(ebp+8) ":")
 7064         (write-buffered *(ebp+8) *(ebp+0x14))
 7065         (write-buffered *(ebp+8) "/disp32\n")
 7066         eb/jump $emit-unconditional-jump-to-depth:end/disp8
 7067       }
 7068       # curr -= 4
 7069       2d/subtract-from-eax 4/imm32
 7070       e9/jump loop/disp32
 7071     }
 7072     # TODO: error if no label at 'depth' was found
 7073 $emit-unconditional-jump-to-depth:end:
 7074     # . restore registers
 7075     5b/pop-to-ebx
 7076     5a/pop-to-edx
 7077     59/pop-to-ecx
 7078     58/pop-to-eax
 7079     # . epilogue
 7080     89/<- %esp 5/r32/ebp
 7081     5d/pop-to-ebp
 7082     c3/return
 7083 
 7084 # emit clean-up code for 'vars' until some block depth
 7085 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 7086 emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
 7087     # . prologue
 7088     55/push-ebp
 7089     89/<- %ebp 4/r32/esp
 7090     # . save registers
 7091     50/push-eax
 7092     51/push-ecx
 7093     52/push-edx
 7094     53/push-ebx
 7095     # ecx = vars
 7096     8b/-> *(ebp+0xc) 1/r32/ecx
 7097     # var eax: int = vars->top
 7098     8b/-> *ecx 0/r32/eax
 7099     # var min/ecx: (address (handle var)) = vars->data
 7100     81 0/subop/add %ecx 8/imm32
 7101     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 7102     81 5/subop/subtract %eax 4/imm32
 7103     8d/copy-address *(ecx+eax) 0/r32/eax
 7104     # edx = until-block-depth
 7105     8b/-> *(ebp+0x10) 2/r32/edx
 7106     {
 7107 $emit-cleanup-code-until-depth:loop:
 7108       # if (curr < min) break
 7109       39/compare %eax 1/r32/ecx
 7110       0f 82/jump-if-addr< break/disp32
 7111       # var v/ebx: (handle var) = *curr
 7112       8b/-> *eax 3/r32/ebx
 7113       # if (v->block-depth < until-block-depth) break
 7114       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7115       0f 8c/jump-if-< break/disp32
 7116       # if v is in a register
 7117       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 7118       {
 7119         0f 84/jump-if-= break/disp32
 7120         50/push-eax
 7121         {
 7122 $emit-cleanup-code-until-depth:check-for-previous-spill:
 7123           (same-register-spilled-before? %ebx *(ebp+0xc) %eax)  # => eax
 7124           3d/compare-eax-and 0/imm32/false
 7125           0f 85/jump-if-!= break/disp32
 7126 $emit-cleanup-code-until-depth:reclaim-var-in-register:
 7127           (emit-indent *(ebp+8) *Curr-block-depth)
 7128           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 7129           (write-buffered *(ebp+8) *(ebx+0x10))
 7130           (write-buffered *(ebp+8) Newline)
 7131         }
 7132         58/pop-to-eax
 7133         eb/jump $emit-cleanup-code-until-depth:continue/disp8
 7134       }
 7135       # otherwise v is on the stack
 7136       {
 7137         75/jump-if-!= break/disp8
 7138 $emit-cleanup-code-until-depth:reclaim-var-on-stack:
 7139         50/push-eax
 7140         (size-of %ebx)  # => eax
 7141         # don't emit code for labels
 7142         3d/compare-eax-and 0/imm32
 7143         74/jump-if-= break/disp8
 7144         #
 7145         (emit-indent *(ebp+8) *Curr-block-depth)
 7146         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 7147         (print-int32-buffered *(ebp+8) %eax)
 7148         (write-buffered *(ebp+8) "/imm32\n")
 7149         58/pop-to-eax
 7150       }
 7151 $emit-cleanup-code-until-depth:continue:
 7152       # curr -= 4
 7153       2d/subtract-from-eax 4/imm32
 7154       e9/jump loop/disp32
 7155     }
 7156 $emit-cleanup-code-until-depth:end:
 7157     # . restore registers
 7158     5b/pop-to-ebx
 7159     5a/pop-to-edx
 7160     59/pop-to-ecx
 7161     58/pop-to-eax
 7162     # . epilogue
 7163     89/<- %esp 5/r32/ebp
 7164     5d/pop-to-ebp
 7165     c3/return
 7166 
 7167 # emit clean-up code for 'vars' until a given label is encountered
 7168 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 7169 emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
 7170     # . prologue
 7171     55/push-ebp
 7172     89/<- %ebp 4/r32/esp
 7173     # . save registers
 7174     50/push-eax
 7175     51/push-ecx
 7176     52/push-edx
 7177     53/push-ebx
 7178     # ecx = vars
 7179     8b/-> *(ebp+0xc) 1/r32/ecx
 7180     # var eax: int = vars->top
 7181     8b/-> *ecx 0/r32/eax
 7182     # var min/ecx: (address (handle var)) = vars->data
 7183     81 0/subop/add %ecx 8/imm32
 7184     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 7185     81 5/subop/subtract %eax 4/imm32
 7186     8d/copy-address *(ecx+eax) 2/r32/edx
 7187     {
 7188 $emit-cleanup-code-until-target:loop:
 7189       # if (curr < min) break
 7190       39/compare %edx 1/r32/ecx
 7191       0f 82/jump-if-addr< break/disp32
 7192       # var v/ebx: (handle var) = *curr
 7193       8b/-> *edx 3/r32/ebx
 7194       # if (v->name == until-block-label) break
 7195       (string-equal? *ebx *(ebp+0x10))  # => eax
 7196       3d/compare-eax-and 0/imm32/false
 7197       0f 85/jump-if-!= break/disp32
 7198       # if v is in a register
 7199       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 7200       {
 7201         74/jump-if-= break/disp8
 7202         50/push-eax
 7203         {
 7204 $emit-cleanup-code-until-target:check-for-previous-spill:
 7205           (same-register-spilled-before? %ebx *(ebp+0xc) %edx)  # => eax
 7206           3d/compare-eax-and 0/imm32/false
 7207           75/jump-if-!= break/disp8
 7208 $emit-cleanup-code-until-target:reclaim-var-in-register:
 7209           (emit-indent *(ebp+8) *Curr-block-depth)
 7210           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 7211           (write-buffered *(ebp+8) *(ebx+0x10))
 7212           (write-buffered *(ebp+8) Newline)
 7213         }
 7214         58/pop-to-eax
 7215         eb/jump $emit-cleanup-code-until-target:continue/disp8
 7216       }
 7217       # otherwise v is on the stack
 7218       {
 7219         75/jump-if-!= break/disp8
 7220 $emit-cleanup-code-until-target:reclaim-var-on-stack:
 7221         (size-of %ebx)  # => eax
 7222         # don't emit code for labels
 7223         3d/compare-eax-and 0/imm32
 7224         74/jump-if-= break/disp8
 7225         #
 7226         (emit-indent *(ebp+8) *Curr-block-depth)
 7227         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 7228         (print-int32-buffered *(ebp+8) %eax)
 7229         (write-buffered *(ebp+8) "/imm32\n")
 7230       }
 7231 $emit-cleanup-code-until-target:continue:
 7232       # curr -= 4
 7233       81 5/subop/subtract %edx 4/imm32
 7234       e9/jump loop/disp32
 7235     }
 7236 $emit-cleanup-code-until-target:end:
 7237     # . restore registers
 7238     5b/pop-to-ebx
 7239     5a/pop-to-edx
 7240     59/pop-to-ecx
 7241     58/pop-to-eax
 7242     # . epilogue
 7243     89/<- %esp 5/r32/ebp
 7244     5d/pop-to-ebp
 7245     c3/return
 7246 
 7247 # is there already a var with the same block-depth and register as 'v' on the 'vars' stack?
 7248 # v is guaranteed not to be within vars
 7249 already-spilled-this-block?:  # v: (handle var), vars: (addr stack (handle var)) -> result/eax: boolean
 7250     # . prologue
 7251     55/push-ebp
 7252     89/<- %ebp 4/r32/esp
 7253     # . save registers
 7254     51/push-ecx
 7255     52/push-edx
 7256     53/push-ebx
 7257     56/push-esi
 7258     57/push-edi
 7259     # ecx = vars
 7260     8b/-> *(ebp+0xc) 1/r32/ecx
 7261     # var eax: int = vars->top
 7262     8b/-> *ecx 0/r32/eax
 7263     # var min/ecx: (address (handle var)) = vars->data
 7264     81 0/subop/add %ecx 8/imm32
 7265     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 7266     81 5/subop/subtract %eax 4/imm32
 7267     8d/copy-address *(ecx+eax) 2/r32/edx
 7268     # var depth/ebx: int = v->block-depth
 7269     8b/-> *(ebp+8) 3/r32/ebx
 7270     8b/-> *(ebx+8) 3/r32/ebx  # Var-block-depth
 7271     # var needle/esi: (handle array byte) = v->register
 7272     8b/-> *(ebp+8) 6/r32/esi
 7273     8b/-> *(esi+0x10) 6/r32/esi  # Var-register
 7274     {
 7275 $already-spilled-this-block?:loop:
 7276       # if (curr < min) break
 7277       39/compare %edx 1/r32/ecx
 7278       0f 82/jump-if-addr< break/disp32
 7279       # var cand/edi: (handle var) = *curr
 7280       8b/-> *edx 7/r32/edi
 7281       # if (cand->block-depth < depth) break
 7282       39/compare *(edi+8) 3/r32/ebx  # Var-block-depth
 7283       0f 8c/jump-if-< break/disp32
 7284       # var cand-reg/edi: (handle array byte) = cand->reg
 7285       8b/-> *(edi+0x10) 7/r32/edi
 7286       # if (cand-reg == null) continue
 7287       {
 7288 $already-spilled-this-block?:check-reg:
 7289         81 7/subop/compare %edi 0/imm32
 7290         74/jump-if-= break/disp8
 7291         # if (cand-reg == needle) return true
 7292         (string-equal? %esi %edi)  # => eax
 7293         3d/compare-eax-and 0/imm32/false
 7294         74/jump-if-= break/disp8
 7295         b8/copy-to-eax 1/imm32/true
 7296         eb/jump $already-spilled-this-block?:end/disp8
 7297       }
 7298 $already-spilled-this-block?:continue:
 7299       # curr -= 4
 7300       81 5/subop/subtract %edx 4/imm32
 7301       e9/jump loop/disp32
 7302     }
 7303     # return false
 7304     b8/copy-to-eax 0/imm32/false
 7305 $already-spilled-this-block?:end:
 7306     # . restore registers
 7307     5f/pop-to-edi
 7308     5e/pop-to-esi
 7309     5b/pop-to-ebx
 7310     5a/pop-to-edx
 7311     59/pop-to-ecx
 7312     # . epilogue
 7313     89/<- %esp 5/r32/ebp
 7314     5d/pop-to-ebp
 7315     c3/return
 7316 
 7317 # is there a var before 'v' with the same block-depth and register on the 'vars' stack?
 7318 # v is guaranteed to be within vars
 7319 # 'start' is provided as an optimization, a pointer within vars
 7320 # *start == v
 7321 same-register-spilled-before?:  # v: (handle var), vars: (addr stack (handle var)), start: (addr (handle var)) -> result/eax: boolean
 7322     # . prologue
 7323     55/push-ebp
 7324     89/<- %ebp 4/r32/esp
 7325     # . save registers
 7326     51/push-ecx
 7327     52/push-edx
 7328     53/push-ebx
 7329     56/push-esi
 7330     57/push-edi
 7331     # ecx = v
 7332     8b/-> *(ebp+8) 1/r32/ecx
 7333     # var reg/edx: (handle array byte) = v->register
 7334     8b/-> *(ecx+0x10) 2/r32/edx  # Var-register
 7335     # var depth/ebx: int = v->block-depth
 7336     8b/-> *(ecx+8) 3/r32/ebx  # Var-block-depth
 7337     # var min/ecx: (address (handle var)) = vars->data
 7338     8b/-> *(ebp+0xc) 1/r32/ecx
 7339     81 0/subop/add %ecx 8/imm32
 7340     # TODO: check that start >= min and start < &vars->data[top]
 7341     # TODO: check that *start == v
 7342     # var curr/esi: (address (handle var)) = start
 7343     8b/-> *(ebp+0x10) 6/r32/esi
 7344     # curr -= 4
 7345     81 5/subop/subtract %esi 4/imm32
 7346     {
 7347 $same-register-spilled-before?:loop:
 7348       # if (curr < min) break
 7349       39/compare %esi 1/r32/ecx
 7350       0f 82/jump-if-addr< break/disp32
 7351       # var x/eax: (handle var) = *curr
 7352       8b/-> *esi 0/r32/eax
 7353       # if (x->block-depth < depth) break
 7354       39/compare *(eax+8) 3/r32/ebx  # Var-block-depth
 7355       0f 8c/jump-if-< break/disp32
 7356       # if (x->register == 0) continue
 7357       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7358       74/jump-if-= $same-register-spilled-before?:continue/disp8
 7359       # if (x->register == reg) return true
 7360       (string-equal? *(eax+0x10) %edx)  # Var-register => eax
 7361       3d/compare-eax-and 0/imm32/false
 7362       75/jump-if-!= $same-register-spilled-before?:end/disp8
 7363 $same-register-spilled-before?:continue:
 7364       # curr -= 4
 7365       81 5/subop/subtract %esi 4/imm32
 7366       e9/jump loop/disp32
 7367     }
 7368 $same-register-spilled-before?:false:
 7369     b8/copy-to-eax 0/imm32/false
 7370 $same-register-spilled-before?:end:
 7371     # . restore registers
 7372     5f/pop-to-edi
 7373     5e/pop-to-esi
 7374     5b/pop-to-ebx
 7375     5a/pop-to-edx
 7376     59/pop-to-ecx
 7377     # . epilogue
 7378     89/<- %esp 5/r32/ebp
 7379     5d/pop-to-ebp
 7380     c3/return
 7381 
 7382 # clean up global state for 'vars' until some block depth
 7383 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
 7384     # . prologue
 7385     55/push-ebp
 7386     89/<- %ebp 4/r32/esp
 7387     # . save registers
 7388     50/push-eax
 7389     51/push-ecx
 7390     56/push-esi
 7391     # esi = vars
 7392     8b/-> *(ebp+8) 6/r32/esi
 7393     # ecx = until-block-depth
 7394     8b/-> *(ebp+0xc) 1/r32/ecx
 7395     {
 7396 $clean-up-blocks:reclaim-loop:
 7397       # if (vars->top <= 0) break
 7398       81 7/subop/compare *esi 0/imm32  # Stack-top
 7399       7e/jump-if-<= break/disp8
 7400       # var v/eax: (handle var) = top(vars)
 7401       (top %esi)  # => eax
 7402       # if (v->block-depth < until-block-depth) break
 7403       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
 7404       7c/jump-if-< break/disp8
 7405       # if v is on the stack, update Curr-local-stack-offset
 7406       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7407       {
 7408         75/jump-if-!= break/disp8
 7409 $clean-up-blocks:reclaim-var-on-stack:
 7410         (size-of %eax)  # => eax
 7411         01/add *Curr-local-stack-offset 0/r32/eax
 7412       }
 7413       (pop %esi)
 7414       e9/jump loop/disp32
 7415     }
 7416 $clean-up-blocks:end:
 7417     # . restore registers
 7418     5e/pop-to-esi
 7419     59/pop-to-ecx
 7420     58/pop-to-eax
 7421     # . epilogue
 7422     89/<- %esp 5/r32/ebp
 7423     5d/pop-to-ebp
 7424     c3/return
 7425 
 7426 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle stmt)
 7427     # . prologue
 7428     55/push-ebp
 7429     89/<- %ebp 4/r32/esp
 7430     # . save registers
 7431     50/push-eax
 7432     51/push-ecx
 7433     52/push-edx
 7434     # eax = stmt
 7435     8b/-> *(ebp+0xc) 0/r32/eax
 7436     # var v/ecx: (handle var)
 7437     8b/-> *(eax+4) 1/r32/ecx  # Vardef-var
 7438     # v->block-depth = *Curr-block-depth
 7439     8b/-> *Curr-block-depth 0/r32/eax
 7440     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 7441     # var n/edx: int = size-of(stmt->var)
 7442     (size-of %ecx)  # => eax
 7443     89/<- %edx 0/r32/eax
 7444     # *Curr-local-stack-offset -= n
 7445     29/subtract-from *Curr-local-stack-offset 2/r32/edx
 7446     # v->offset = *Curr-local-stack-offset
 7447     8b/-> *Curr-local-stack-offset 0/r32/eax
 7448     89/<- *(ecx+0xc) 0/r32/eax  # Var-offset
 7449     # if v is an array, do something special
 7450     {
 7451       (is-mu-array? *(ecx+4))  # Var-type => eax
 7452       3d/compare-eax-and 0/imm32/false
 7453       0f 84/jump-if-= break/disp32
 7454       # var array-size-without-length/edx: int = n-4
 7455       81 5/subop/subtract %edx 4/imm32
 7456       (emit-indent *(ebp+8) *Curr-block-depth)
 7457       (write-buffered *(ebp+8) "(push-n-zero-bytes ")
 7458       (print-int32-buffered *(ebp+8) %edx)
 7459       (write-buffered *(ebp+8) ")\n")
 7460       (emit-indent *(ebp+8) *Curr-block-depth)
 7461       (write-buffered *(ebp+8) "68/push ")
 7462       (print-int32-buffered *(ebp+8) %edx)
 7463       (write-buffered *(ebp+8) "/imm32\n")
 7464       eb/jump $emit-subx-var-def:end/disp8
 7465     }
 7466     # while n > 0
 7467     {
 7468       81 7/subop/compare %edx 0/imm32
 7469       7e/jump-if-<= break/disp8
 7470       (emit-indent *(ebp+8) *Curr-block-depth)
 7471       (write-buffered *(ebp+8) "68/push 0/imm32\n")
 7472       # n -= 4
 7473       81 5/subop/subtract %edx 4/imm32
 7474       #
 7475       eb/jump loop/disp8
 7476     }
 7477 $emit-subx-var-def:end:
 7478     # . restore registers
 7479     5a/pop-to-edx
 7480     59/pop-to-ecx
 7481     58/pop-to-eax
 7482     # . epilogue
 7483     89/<- %esp 5/r32/ebp
 7484     5d/pop-to-ebp
 7485     c3/return
 7486 
 7487 emit-subx-stmt:  # out: (addr buffered-file), stmt: (handle stmt), primitives: (handle primitive), functions: (handle function)
 7488     # . prologue
 7489     55/push-ebp
 7490     89/<- %ebp 4/r32/esp
 7491     # . save registers
 7492     50/push-eax
 7493     51/push-ecx
 7494     # - some special-case primitives that don't actually use the 'primitives' data structure
 7495     # ecx = stmt
 7496     8b/-> *(ebp+0xc) 1/r32/ecx
 7497     # array length
 7498     {
 7499       # if (!string-equal?(stmt->operation, "length")) break
 7500       (string-equal? *(ecx+4) "length")  # Stmt1-operation => eax
 7501       3d/compare-eax-and 0/imm32
 7502       0f 84/jump-if-= break/disp32
 7503       (translate-mu-length-stmt *(ebp+8) *(ebp+0xc))
 7504       e9/jump $emit-subx-stmt:end/disp32
 7505     }
 7506     # index into array
 7507     {
 7508       # if (!string-equal?(var->operation, "index")) break
 7509       (string-equal? *(ecx+4) "index")  # Stmt1-operation => eax
 7510       3d/compare-eax-and 0/imm32
 7511       0f 84/jump-if-= break/disp32
 7512       (translate-mu-index-stmt *(ebp+8) *(ebp+0xc))
 7513       e9/jump $emit-subx-stmt:end/disp32
 7514     }
 7515     # compute-offset for index into array
 7516     {
 7517       # if (!string-equal?(var->operation, "compute-offset")) break
 7518       (string-equal? *(ecx+4) "compute-offset")  # Stmt1-operation => eax
 7519       3d/compare-eax-and 0/imm32
 7520       0f 84/jump-if-= break/disp32
 7521       (translate-mu-compute-index-stmt *(ebp+8) *(ebp+0xc))
 7522       e9/jump $emit-subx-stmt:end/disp32
 7523     }
 7524     # get field from record
 7525     {
 7526       # if (!string-equal?(var->operation, "get")) break
 7527       (string-equal? *(ecx+4) "get")  # Stmt1-operation => eax
 7528       3d/compare-eax-and 0/imm32
 7529       0f 84/jump-if-= break/disp32
 7530       (translate-mu-get-stmt *(ebp+8) *(ebp+0xc))
 7531       e9/jump $emit-subx-stmt:end/disp32
 7532     }
 7533     # - if stmt matches a primitive, emit it
 7534     {
 7535 $emit-subx-stmt:check-for-primitive:
 7536       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
 7537       3d/compare-eax-and 0/imm32
 7538       74/jump-if-= break/disp8
 7539 $emit-subx-stmt:primitive:
 7540       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7541       e9/jump $emit-subx-stmt:end/disp32
 7542     }
 7543     # - if stmt matches a function, emit a call to it
 7544     {
 7545 $emit-subx-stmt:check-for-call:
 7546       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
 7547       3d/compare-eax-and 0/imm32
 7548       74/jump-if-= break/disp8
 7549 $emit-subx-stmt:call:
 7550       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7551       e9/jump $emit-subx-stmt:end/disp32
 7552     }
 7553     # otherwise, assume it's a SubX function (TODO: how to type-check?!)
 7554     (emit-hailmary-call *(ebp+8) *(ebp+0xc))
 7555 $emit-subx-stmt:end:
 7556     # . restore registers
 7557     59/pop-to-ecx
 7558     58/pop-to-eax
 7559     # . epilogue
 7560     89/<- %esp 5/r32/ebp
 7561     5d/pop-to-ebp
 7562     c3/return
 7563 
 7564 translate-mu-length-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7565     # . prologue
 7566     55/push-ebp
 7567     89/<- %ebp 4/r32/esp
 7568     # . save registers
 7569     50/push-eax
 7570     51/push-ecx
 7571     # ecx = stmt
 7572     8b/-> *(ebp+0xc) 1/r32/ecx
 7573     #
 7574     (emit-indent *(ebp+8) *Curr-block-depth)
 7575     (write-buffered *(ebp+8) "8b/copy-from *")
 7576     # var base/eax: (handle var) = inouts[0]
 7577     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 7578     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7579     # if base is an (address array ...) in a register
 7580     {
 7581       81 7/subop/compare *(eax+0x10)) 0/imm32  # Var-register
 7582       74/jump-if-= break/disp8
 7583       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 7584       eb/jump $translate-mu-length-stmt:emit-output/disp8
 7585     }
 7586     # otherwise if base is an (array ...) on the stack
 7587     {
 7588       81 7/subop/compare *(eax+0xc)) 0/imm32  # Var-offset
 7589       74/jump-if-= break/disp8
 7590       (write-buffered *(ebp+8) "(ebp+")
 7591       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-offset
 7592       (write-buffered *(ebp+8) ")")
 7593     }
 7594 $translate-mu-length-stmt:emit-output:
 7595     (write-buffered *(ebp+8) " ")
 7596     # outputs[0] "/r32"
 7597     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7598     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7599     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7600     (print-int32-buffered *(ebp+8) *eax)
 7601     (write-buffered *(ebp+8) "/r32\n")
 7602 $translate-mu-length-stmt:end:
 7603     # . restore registers
 7604     59/pop-to-ecx
 7605     58/pop-to-eax
 7606     # . epilogue
 7607     89/<- %esp 5/r32/ebp
 7608     5d/pop-to-ebp
 7609     c3/return
 7610 
 7611 translate-mu-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7612     # . prologue
 7613     55/push-ebp
 7614     89/<- %ebp 4/r32/esp
 7615     # . save registers
 7616     51/push-ecx
 7617     # var base/ecx: (handle var) = stmt->inouts[0]
 7618     8b/-> *(ebp+0xc) 1/r32/ecx
 7619     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 7620     8b/-> *ecx 1/r32/ecx  # Stmt-var-value
 7621     # if (var->register) do one thing
 7622     {
 7623       81 7/subop/compare *(ecx+0x10) 0/imm32  # Var-register
 7624       74/jump-if-= break/disp8
 7625       # TODO: ensure there's no dereference
 7626       (translate-mu-index-stmt-with-array-in-register *(ebp+8) *(ebp+0xc))
 7627       eb/jump $translate-mu-index-stmt:end/disp8
 7628     }
 7629     # if (var->offset) do a different thing
 7630     {
 7631       81 7/subop/compare *(ecx+0xc) 0/imm32  # Var-offset
 7632       74/jump-if-= break/disp8
 7633       # TODO: ensure there's no dereference
 7634       (translate-mu-index-stmt-with-array-on-stack *(ebp+8) *(ebp+0xc))
 7635       eb/jump $translate-mu-index-stmt:end/disp8
 7636     }
 7637 $translate-mu-index-stmt:end:
 7638     # . restore registers
 7639     59/pop-to-ecx
 7640     # . epilogue
 7641     89/<- %esp 5/r32/ebp
 7642     5d/pop-to-ebp
 7643     c3/return
 7644 
 7645 $translate-mu-index-stmt-with-array:error1:
 7646     (write-buffered Stderr "couldn't translate an index instruction. second (index) input must either lie in a register or be a literal\n")
 7647     (flush Stderr)
 7648     # . syscall(exit, 1)
 7649     bb/copy-to-ebx  1/imm32
 7650     b8/copy-to-eax  1/imm32/exit
 7651     cd/syscall  0x80/imm8
 7652     # never gets here
 7653 
 7654 $translate-mu-index-stmt-with-array:error2:
 7655     (write-buffered Stderr "couldn't translate an index instruction. second (index) input when in a register must be an int or offset\n")
 7656     (flush Stderr)
 7657     # . syscall(exit, 1)
 7658     bb/copy-to-ebx  1/imm32
 7659     b8/copy-to-eax  1/imm32/exit
 7660     cd/syscall  0x80/imm8
 7661     # never gets here
 7662 
 7663 translate-mu-index-stmt-with-array-in-register:  # out: (address buffered-file), stmt: (handle stmt)
 7664     # . prologue
 7665     55/push-ebp
 7666     89/<- %ebp 4/r32/esp
 7667     # . save registers
 7668     50/push-eax
 7669     51/push-ecx
 7670     52/push-edx
 7671     53/push-ebx
 7672     #
 7673     (emit-indent *(ebp+8) *Curr-block-depth)
 7674     (write-buffered *(ebp+8) "8d/copy-address *(")
 7675     # TODO: ensure inouts[0] is in a register and not dereferenced
 7676 $translate-mu-index-stmt-with-array-in-register:emit-base:
 7677     # ecx = stmt
 7678     8b/-> *(ebp+0xc) 1/r32/ecx
 7679     # var base/ebx: (handle var) = inouts[0]
 7680     8b/-> *(ecx+8) 3/r32/ebx  # Stmt1-inouts
 7681     8b/-> *ebx 3/r32/ebx  # Stmt-var-value
 7682     # print base->register " + "
 7683     (write-buffered *(ebp+8) *(ebx+0x10))  # Var-register
 7684     #
 7685     (write-buffered *(ebp+8) " + ")
 7686     # var index/edx: (handle var) = inouts[1]
 7687     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7688     8b/-> *(edx+4) 2/r32/edx  # Stmt-var-next
 7689     8b/-> *edx 2/r32/edx  # Stmt-var-value
 7690     # if index->register
 7691     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 7692     {
 7693       0f 84/jump-if-= break/disp32
 7694 $translate-mu-index-stmt-with-array-in-register:emit-register-index:
 7695       # if index is an int
 7696       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 7697       3d/compare-eax-and 0/imm32/false
 7698       {
 7699         0f 84/jump-if-= break/disp32
 7700 $translate-mu-index-stmt-with-array-in-register:emit-int-register-index:
 7701         # print index->register "<<" log2(size-of(element(base->type))) " + 4) "
 7702         # . index->register "<<"
 7703         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7704         (write-buffered *(ebp+8) "<<")
 7705         # . log2(size-of(element(base->type)))
 7706         # TODO: ensure size is a power of 2
 7707         (array-element-type-id %ebx)  # => eax
 7708         (size-of-type-id %eax)  # => eax
 7709         (num-shift-rights %eax)  # => eax
 7710         (print-int32-buffered *(ebp+8) %eax)
 7711         e9/jump $translate-mu-index-stmt-with-array-in-register:emit-register-index-done/disp32
 7712       }
 7713       # if index->type is any other atom, abort
 7714       8b/-> *(edx+4) 0/r32/eax  # Var-type
 7715       8b/-> *eax 0/r32/eax  # Tree-left or Atom-value
 7716       3b/compare 0/r32/eax *Max-type-id
 7717       0f 82/jump-if-addr< $translate-mu-index-stmt-with-array:error2/disp32
 7718       # if index has type (offset ...)
 7719       (is-simple-mu-type? %eax 7)  # offset => eax
 7720       3d/compare-eax-and 0/imm32/false
 7721       {
 7722         0f 84/jump-if-= break/disp32
 7723         # print index->register " + 4) "
 7724 $translate-mu-index-stmt-with-array-in-register:emit-offset-register-index:
 7725         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7726       }
 7727 $translate-mu-index-stmt-with-array-in-register:emit-register-index-done:
 7728       (write-buffered *(ebp+8) " + 4) ")
 7729       e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
 7730     }
 7731     # otherwise if index is a literal
 7732     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 7733     3d/compare-eax-and 0/imm32/false
 7734     {
 7735       0f 84/jump-if-= break/disp32
 7736 $translate-mu-index-stmt-with-array-in-register:emit-literal-index:
 7737       # var index-value/edx: int = parse-hex-int(index->name)
 7738       (parse-hex-int *edx)  # Var-name => eax
 7739       89/<- %edx 0/r32/eax
 7740       # offset = idx-value * size-of(element(base->type))
 7741       (array-element-type-id %ebx)  # => eax
 7742       (size-of-type-id %eax)  # => eax
 7743       f7 4/subop/multiply-into-eax %edx  # clobbers edx
 7744       # offset += 4 for array size
 7745       05/add-to-eax 4/imm32
 7746       # TODO: check edx for overflow
 7747       # print offset
 7748       (print-int32-buffered *(ebp+8) %eax)
 7749       (write-buffered *(ebp+8) ") ")
 7750       e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
 7751     }
 7752     # otherwise abort
 7753     e9/jump $translate-mu-index-stmt-with-array:error1/disp32
 7754 $translate-mu-index-stmt-with-array-in-register:emit-output:
 7755     # outputs[0] "/r32"
 7756     8b/-> *(ebp+0xc) 1/r32/ecx
 7757     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7758     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7759     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7760     (print-int32-buffered *(ebp+8) *eax)
 7761     (write-buffered *(ebp+8) "/r32\n")
 7762 $translate-mu-index-stmt-with-array-in-register:end:
 7763     # . restore registers
 7764     5b/pop-to-ebx
 7765     5a/pop-to-edx
 7766     59/pop-to-ecx
 7767     58/pop-to-eax
 7768     # . epilogue
 7769     89/<- %esp 5/r32/ebp
 7770     5d/pop-to-ebp
 7771     c3/return
 7772 
 7773 translate-mu-index-stmt-with-array-on-stack:  # out: (address buffered-file), stmt: (handle stmt)
 7774     # . prologue
 7775     55/push-ebp
 7776     89/<- %ebp 4/r32/esp
 7777     # . save registers
 7778     50/push-eax
 7779     51/push-ecx
 7780     52/push-edx
 7781     53/push-ebx
 7782     #
 7783     (emit-indent *(ebp+8) *Curr-block-depth)
 7784     (write-buffered *(ebp+8) "8d/copy-address *(ebp + ")
 7785     # var curr/eax = stmt->inouts
 7786     8b/-> *(ebp+0xc) 0/r32/eax
 7787     # var base/ecx: (handle var) = stmt->inouts[0]
 7788     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 7789     8b/-> *eax 1/r32/ecx  # Stmt-var-value
 7790     # curr = curr->next
 7791     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 7792     # var index/edx: (handle var) = stmt->inouts[1]
 7793     8b/-> *eax 2/r32/edx  # Stmt-var-value
 7794     # if index->register
 7795     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 7796     {
 7797       0f 84/jump-if-= break/disp32
 7798 $translate-mu-index-stmt-with-array-on-stack:emit-register-index:
 7799       # if index is an int
 7800       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 7801       3d/compare-eax-and 0/imm32/false
 7802       {
 7803         0f 84/jump-if-= break/disp32
 7804 $translate-mu-index-stmt-with-array-on-stack:emit-int-register-index:
 7805         # print index->register "<<" log2(size-of(element-type(base))) " + " base->offset+4
 7806         # . inouts[1]->register "<<"
 7807         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7808         (write-buffered *(ebp+8) "<<")
 7809         # . log2(size-of(element(base)))
 7810         # TODO: ensure size is a power of 2
 7811         (array-element-type-id %ecx)  # => eax
 7812         (size-of-type-id %eax)  # => eax
 7813         (num-shift-rights %eax)  # => eax
 7814         (print-int32-buffered *(ebp+8) %eax)
 7815         #
 7816         (write-buffered *(ebp+8) " + ")
 7817         #
 7818         8b/-> *(ecx+0xc) 0/r32/eax  # Var-offset
 7819         05/add-to-eax 4/imm32
 7820         (print-int32-buffered *(ebp+8) %eax)
 7821         e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done/disp32
 7822       }
 7823       # if index->type is any other atom, abort
 7824       8b/-> *(edx+4) 0/r32/eax  # Var-type
 7825       8b/-> *eax 0/r32/eax  # Tree-left or Atom-value
 7826       3b/compare 0/r32/eax *Max-type-id
 7827       0f 82/jump-if-addr< $translate-mu-index-stmt-with-array:error2/disp32
 7828       # if index has type (offset ...)
 7829       (is-simple-mu-type? %eax 7)  # offset => eax
 7830       3d/compare-eax-and 0/imm32/false
 7831       {
 7832         0f 84/jump-if-= break/disp32
 7833         # print index->register
 7834 $translate-mu-index-stmt-with-array-on-stack:emit-offset-register-index:
 7835         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7836       }
 7837 $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done:
 7838       (write-buffered *(ebp+8) ") ")
 7839       e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
 7840     }
 7841     # otherwise if index is a literal
 7842     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 7843     3d/compare-eax-and 0/imm32/false
 7844     {
 7845       0f 84/jump-if-= break/disp32
 7846 $translate-mu-index-stmt-with-array-on-stack:emit-literal-index:
 7847       # var idx-value/edx: int = parse-hex-int(index->name)
 7848       (parse-hex-int *edx)  # Var-name => eax
 7849       89/<- %ebx 0/r32/eax
 7850       # offset = idx-value * size-of(element-type(base->type))
 7851       (array-element-type-id %ecx)  # => eax
 7852       (size-of-type-id %eax)  # => eax
 7853       f7 4/subop/multiply-into-eax %ebx  # clobbers edx
 7854       # offset += base->offset
 7855       03/add-to 0/r32/eax *(ecx+0xc)  # Var-offset
 7856       # offset += 4 for array size
 7857       05/add-to-eax 4/imm32
 7858       # TODO: check edx for overflow
 7859       # print offset
 7860       (print-int32-buffered *(ebp+8) %eax)
 7861       (write-buffered *(ebp+8) ") ")
 7862       e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
 7863     }
 7864     # otherwise abort
 7865     e9/jump $translate-mu-index-stmt-with-array:error1/disp32
 7866 $translate-mu-index-stmt-with-array-on-stack:emit-output:
 7867     # outputs[0] "/r32"
 7868     8b/-> *(ebp+0xc) 0/r32/eax
 7869     8b/-> *(eax+0xc) 0/r32/eax  # Stmt1-outputs
 7870     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7871     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7872     (print-int32-buffered *(ebp+8) *eax)
 7873     (write-buffered *(ebp+8) "/r32\n")
 7874 $translate-mu-index-stmt-with-array-on-stack:end:
 7875     # . restore registers
 7876     5b/pop-to-ebx
 7877     5a/pop-to-edx
 7878     59/pop-to-ecx
 7879     58/pop-to-eax
 7880     # . epilogue
 7881     89/<- %esp 5/r32/ebp
 7882     5d/pop-to-ebp
 7883     c3/return
 7884 
 7885 translate-mu-compute-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7886     # . prologue
 7887     55/push-ebp
 7888     89/<- %ebp 4/r32/esp
 7889     # . save registers
 7890     50/push-eax
 7891     51/push-ecx
 7892     52/push-edx
 7893     53/push-ebx
 7894     #
 7895     (emit-indent *(ebp+8) *Curr-block-depth)
 7896     (write-buffered *(ebp+8) "69/multiply ")
 7897 $translate-mu-compute-index-stmt:emit-elem-size:
 7898     # ecx = stmt
 7899     8b/-> *(ebp+0xc) 1/r32/ecx
 7900     # var first-inout/edx: (handle stmt-var) = stmt->inouts[0]
 7901     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7902     # var base/ebx: (handle var)
 7903     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 7904     # print size-of(element(base->type))
 7905     (array-element-type-id %ebx)  # => eax
 7906     (size-of-type-id %eax)  # => eax
 7907     (print-int32-buffered *(ebp+8) %eax)
 7908     (write-buffered *(ebp+8) "/imm32")
 7909 $translate-mu-compute-index-stmt:emit-index:
 7910     (emit-subx-var-as-rm32 *(ebp+8) *(edx+4))  # Stmt-var-next
 7911     (write-buffered *(ebp+8) Space)
 7912 $translate-mu-compute-index-stmt:emit-output:
 7913     # outputs[0] "/r32"
 7914     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7915     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7916     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7917     (print-int32-buffered *(ebp+8) *eax)
 7918     (write-buffered *(ebp+8) "/r32\n")
 7919 $translate-mu-compute-index-stmt:end:
 7920     # . restore registers
 7921     5b/pop-to-ebx
 7922     5a/pop-to-edx
 7923     59/pop-to-ecx
 7924     58/pop-to-eax
 7925     # . epilogue
 7926     89/<- %esp 5/r32/ebp
 7927     5d/pop-to-ebp
 7928     c3/return
 7929 
 7930 translate-mu-get-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7931     # . prologue
 7932     55/push-ebp
 7933     89/<- %ebp 4/r32/esp
 7934     # . save registers
 7935     50/push-eax
 7936     51/push-ecx
 7937     52/push-edx
 7938     #
 7939     (emit-indent *(ebp+8) *Curr-block-depth)
 7940     (write-buffered *(ebp+8) "8d/copy-address ")
 7941     # ecx = stmt
 7942     8b/-> *(ebp+0xc) 1/r32/ecx
 7943     # var offset/edx: int = get offset of stmt
 7944     (mu-get-offset %ecx)  # => eax
 7945     89/<- %edx 0/r32/eax
 7946     # var base/eax: (handle var) = stmt->inouts[0]
 7947     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
 7948     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7949     # if base is in a register
 7950     81 7/subop/compare *(eax+0x10) 0/imm32
 7951     {
 7952       0f 84/jump-if-= break/disp32
 7953 $translate-mu-get-stmt:emit-register-input:
 7954       # "*(" inouts[0]->register " + " offset ")"
 7955       (write-buffered *(ebp+8) "*(")
 7956       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 7957       (write-buffered *(ebp+8) " + ")
 7958       (print-int32-buffered *(ebp+8) %edx)
 7959       (write-buffered *(ebp+8) ") ")
 7960       e9/jump $translate-mu-get-stmt:emit-output/disp32
 7961     }
 7962     # otherwise base is on the stack
 7963     {
 7964 $translate-mu-get-stmt:emit-stack-input:
 7965       # "*(ebp + " inouts[0]->offset + offset ")"
 7966       (write-buffered *(ebp+8) "*(ebp+")
 7967       03/add-from *(eax+0xc) 2/r32/edx  # Var-offset
 7968       (print-int32-buffered *(ebp+8) %edx)
 7969       (write-buffered *(ebp+8) ") ")
 7970       eb/jump $translate-mu-get-stmt:emit-output/disp8
 7971     }
 7972 $translate-mu-get-stmt:emit-output:
 7973     # outputs[0] "/r32"
 7974     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7975     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7976     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7977     (print-int32-buffered *(ebp+8) *eax)
 7978     (write-buffered *(ebp+8) "/r32\n")
 7979 $translate-mu-get-stmt:end:
 7980     # . restore registers
 7981     5a/pop-to-edx
 7982     59/pop-to-ecx
 7983     58/pop-to-eax
 7984     # . epilogue
 7985     89/<- %esp 5/r32/ebp
 7986     5d/pop-to-ebp
 7987     c3/return
 7988 
 7989 array-element-type-id:  # v: (handle var) -> result/eax: type-id
 7990     # precondition: n is positive
 7991     # . prologue
 7992     55/push-ebp
 7993     89/<- %ebp 4/r32/esp
 7994     #
 7995     8b/-> *(ebp+8) 0/r32/eax
 7996     8b/-> *(eax+4) 0/r32/eax  # Var-type
 7997     # TODO: ensure type->left is 'addr'
 7998     8b/-> *(eax+4) 0/r32/eax  # Tree-right
 7999     # TODO: ensure that type->right is non-null
 8000     # TODO: ensure that type->right->left is 'array'
 8001     8b/-> *(eax+4) 0/r32/eax  # Tree-right
 8002     # TODO: ensure that type->right->right is non-null
 8003     8b/-> *eax 0/r32/eax  # Tree-left
 8004     8b/-> *eax 0/r32/eax  # Atom-value
 8005 $array-element-type-id:end:
 8006     # . epilogue
 8007     89/<- %esp 5/r32/ebp
 8008     5d/pop-to-ebp
 8009     c3/return
 8010 
 8011 power-of-2?:  # n: int -> result/eax: boolean
 8012     # precondition: n is positive
 8013     # . prologue
 8014     55/push-ebp
 8015     89/<- %ebp 4/r32/esp
 8016     # var tmp/eax: int = n-1
 8017     8b/-> *(ebp+8) 0/r32/eax
 8018     48/decrement-eax
 8019     # var tmp2/eax: int = n & tmp
 8020     0b/and-> *(ebp+8) 0/r32/eax
 8021     # return (tmp2 == 0)
 8022     3d/compare-eax-and 0/imm32
 8023     0f 94/set-byte-if-= %al
 8024     81 4/subop/and %eax 0xff/imm32
 8025 $power-of-2?:end:
 8026     # . epilogue
 8027     89/<- %esp 5/r32/ebp
 8028     5d/pop-to-ebp
 8029     c3/return
 8030 
 8031 num-shift-rights:  # n: int -> result/eax: int
 8032     # precondition: n is a positive power of 2
 8033     # . prologue
 8034     55/push-ebp
 8035     89/<- %ebp 4/r32/esp
 8036     # . save registers
 8037     51/push-ecx
 8038     # var curr/ecx: int = n
 8039     8b/-> *(ebp+8) 1/r32/ecx
 8040     # result = 0
 8041     b8/copy-to-eax 0/imm32
 8042     {
 8043       # if (curr <= 1) break
 8044       81 7/subop/compare %ecx 1/imm32
 8045       7e/jump-if-<= break/disp8
 8046       40/increment-eax
 8047       c1/shift 5/subop/arithmetic-right %ecx 1/imm8
 8048       eb/jump loop/disp8
 8049     }
 8050 $num-shift-rights:end:
 8051     # . restore registers
 8052     59/pop-to-ecx
 8053     # . epilogue
 8054     89/<- %esp 5/r32/ebp
 8055     5d/pop-to-ebp
 8056     c3/return
 8057 
 8058 mu-get-offset:  # stmt: (handle stmt) -> result/eax: int
 8059     # . prologue
 8060     55/push-ebp
 8061     89/<- %ebp 4/r32/esp
 8062     # var second-inout/eax: (handle stmt-var) = stmt->inouts->next
 8063     8b/-> *(ebp+8) 0/r32/eax
 8064     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 8065     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 8066     # var output-var/eax: (handle var) = second-inout->value
 8067     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8068     # return output-var->offset
 8069     8b/-> *(eax+0xc) 0/r32/eax  # Var-offset
 8070 $emit-get-offset:end:
 8071     # . epilogue
 8072     89/<- %esp 5/r32/ebp
 8073     5d/pop-to-ebp
 8074     c3/return
 8075 
 8076 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
 8077     # . prologue
 8078     55/push-ebp
 8079     89/<- %ebp 4/r32/esp
 8080     # . save registers
 8081     50/push-eax
 8082     51/push-ecx
 8083     56/push-esi
 8084     # esi = block
 8085     8b/-> *(ebp+0xc) 6/r32/esi
 8086     # block->var->block-depth = *Curr-block-depth
 8087     8b/-> *(esi+8) 0/r32/eax  # Block-var
 8088     8b/-> *Curr-block-depth 1/r32/ecx
 8089     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
 8090     # var stmts/eax: (handle list stmt) = block->statements
 8091     8b/-> *(esi+4) 0/r32/eax  # Block-stmts
 8092     #
 8093     {
 8094 $emit-subx-block:check-empty:
 8095       3d/compare-eax-and 0/imm32
 8096       0f 84/jump-if-= break/disp32
 8097       (emit-indent *(ebp+8) *Curr-block-depth)
 8098       (write-buffered *(ebp+8) "{\n")
 8099       # var v/ecx: (handle var)
 8100       8b/-> *(esi+8) 1/r32/ecx  # Block-var
 8101       #
 8102       (write-buffered *(ebp+8) *ecx)  # Var-name
 8103       (write-buffered *(ebp+8) ":loop:\n")
 8104       ff 0/subop/increment *Curr-block-depth
 8105       (push *(ebp+0x10) %ecx)
 8106       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
 8107       (pop *(ebp+0x10))  # => eax
 8108       ff 1/subop/decrement *Curr-block-depth
 8109       (emit-indent *(ebp+8) *Curr-block-depth)
 8110       (write-buffered *(ebp+8) "}\n")
 8111       (write-buffered *(ebp+8) *ecx)  # Var-name
 8112       (write-buffered *(ebp+8) ":break:\n")
 8113     }
 8114 $emit-subx-block:end:
 8115     # . restore registers
 8116     5e/pop-to-esi
 8117     59/pop-to-ecx
 8118     58/pop-to-eax
 8119     # . epilogue
 8120     89/<- %esp 5/r32/ebp
 8121     5d/pop-to-ebp
 8122     c3/return
 8123 
 8124 # Primitives supported
 8125 # For each operation, put variants with hard-coded registers before flexible ones.
 8126 == data
 8127 Primitives:
 8128 # - increment/decrement
 8129 _Primitive-inc-eax:
 8130     # var/eax <- increment => 40/increment-eax
 8131     "increment"/imm32/name
 8132     0/imm32/no-inouts
 8133     Single-int-var-in-eax/imm32/outputs
 8134     "40/increment-eax"/imm32/subx-name
 8135     0/imm32/no-rm32
 8136     0/imm32/no-r32
 8137     0/imm32/no-imm32
 8138     0/imm32/no-disp32
 8139     0/imm32/output-is-write-only
 8140     _Primitive-inc-ecx/imm32/next
 8141 _Primitive-inc-ecx:
 8142     # var/ecx <- increment => 41/increment-ecx
 8143     "increment"/imm32/name
 8144     0/imm32/no-inouts
 8145     Single-int-var-in-ecx/imm32/outputs
 8146     "41/increment-ecx"/imm32/subx-name
 8147     0/imm32/no-rm32
 8148     0/imm32/no-r32
 8149     0/imm32/no-imm32
 8150     0/imm32/no-disp32
 8151     0/imm32/output-is-write-only
 8152     _Primitive-inc-edx/imm32/next
 8153 _Primitive-inc-edx:
 8154     # var/edx <- increment => 42/increment-edx
 8155     "increment"/imm32/name
 8156     0/imm32/no-inouts
 8157     Single-int-var-in-edx/imm32/outputs
 8158     "42/increment-edx"/imm32/subx-name
 8159     0/imm32/no-rm32
 8160     0/imm32/no-r32
 8161     0/imm32/no-imm32
 8162     0/imm32/no-disp32
 8163     0/imm32/output-is-write-only
 8164     _Primitive-inc-ebx/imm32/next
 8165 _Primitive-inc-ebx:
 8166     # var/ebx <- increment => 43/increment-ebx
 8167     "increment"/imm32/name
 8168     0/imm32/no-inouts
 8169     Single-int-var-in-ebx/imm32/outputs
 8170     "43/increment-ebx"/imm32/subx-name
 8171     0/imm32/no-rm32
 8172     0/imm32/no-r32
 8173     0/imm32/no-imm32
 8174     0/imm32/no-disp32
 8175     0/imm32/output-is-write-only
 8176     _Primitive-inc-esi/imm32/next
 8177 _Primitive-inc-esi:
 8178     # var/esi <- increment => 46/increment-esi
 8179     "increment"/imm32/name
 8180     0/imm32/no-inouts
 8181     Single-int-var-in-esi/imm32/outputs
 8182     "46/increment-esi"/imm32/subx-name
 8183     0/imm32/no-rm32
 8184     0/imm32/no-r32
 8185     0/imm32/no-imm32
 8186     0/imm32/no-disp32
 8187     0/imm32/output-is-write-only
 8188     _Primitive-inc-edi/imm32/next
 8189 _Primitive-inc-edi:
 8190     # var/edi <- increment => 47/increment-edi
 8191     "increment"/imm32/name
 8192     0/imm32/no-inouts
 8193     Single-int-var-in-edi/imm32/outputs
 8194     "47/increment-edi"/imm32/subx-name
 8195     0/imm32/no-rm32
 8196     0/imm32/no-r32
 8197     0/imm32/no-imm32
 8198     0/imm32/no-disp32
 8199     0/imm32/output-is-write-only
 8200     _Primitive-dec-eax/imm32/next
 8201 _Primitive-dec-eax:
 8202     # var/eax <- decrement => 48/decrement-eax
 8203     "decrement"/imm32/name
 8204     0/imm32/no-inouts
 8205     Single-int-var-in-eax/imm32/outputs
 8206     "48/decrement-eax"/imm32/subx-name
 8207     0/imm32/no-rm32
 8208     0/imm32/no-r32
 8209     0/imm32/no-imm32
 8210     0/imm32/no-disp32
 8211     0/imm32/output-is-write-only
 8212     _Primitive-dec-ecx/imm32/next
 8213 _Primitive-dec-ecx:
 8214     # var/ecx <- decrement => 49/decrement-ecx
 8215     "decrement"/imm32/name
 8216     0/imm32/no-inouts
 8217     Single-int-var-in-ecx/imm32/outputs
 8218     "49/decrement-ecx"/imm32/subx-name
 8219     0/imm32/no-rm32
 8220     0/imm32/no-r32
 8221     0/imm32/no-imm32
 8222     0/imm32/no-disp32
 8223     0/imm32/output-is-write-only
 8224     _Primitive-dec-edx/imm32/next
 8225 _Primitive-dec-edx:
 8226     # var/edx <- decrement => 4a/decrement-edx
 8227     "decrement"/imm32/name
 8228     0/imm32/no-inouts
 8229     Single-int-var-in-edx/imm32/outputs
 8230     "4a/decrement-edx"/imm32/subx-name
 8231     0/imm32/no-rm32
 8232     0/imm32/no-r32
 8233     0/imm32/no-imm32
 8234     0/imm32/no-disp32
 8235     0/imm32/output-is-write-only
 8236     _Primitive-dec-ebx/imm32/next
 8237 _Primitive-dec-ebx:
 8238     # var/ebx <- decrement => 4b/decrement-ebx
 8239     "decrement"/imm32/name
 8240     0/imm32/no-inouts
 8241     Single-int-var-in-ebx/imm32/outputs
 8242     "4b/decrement-ebx"/imm32/subx-name
 8243     0/imm32/no-rm32
 8244     0/imm32/no-r32
 8245     0/imm32/no-imm32
 8246     0/imm32/no-disp32
 8247     0/imm32/output-is-write-only
 8248     _Primitive-dec-esi/imm32/next
 8249 _Primitive-dec-esi:
 8250     # var/esi <- decrement => 4e/decrement-esi
 8251     "decrement"/imm32/name
 8252     0/imm32/no-inouts
 8253     Single-int-var-in-esi/imm32/outputs
 8254     "4e/decrement-esi"/imm32/subx-name
 8255     0/imm32/no-rm32
 8256     0/imm32/no-r32
 8257     0/imm32/no-imm32
 8258     0/imm32/no-disp32
 8259     0/imm32/output-is-write-only
 8260     _Primitive-dec-edi/imm32/next
 8261 _Primitive-dec-edi:
 8262     # var/edi <- decrement => 4f/decrement-edi
 8263     "decrement"/imm32/name
 8264     0/imm32/no-inouts
 8265     Single-int-var-in-edi/imm32/outputs
 8266     "4f/decrement-edi"/imm32/subx-name
 8267     0/imm32/no-rm32
 8268     0/imm32/no-r32
 8269     0/imm32/no-imm32
 8270     0/imm32/no-disp32
 8271     0/imm32/output-is-write-only
 8272     _Primitive-inc-mem/imm32/next
 8273 _Primitive-inc-mem:
 8274     # increment var => ff 0/subop/increment *(ebp+__)
 8275     "increment"/imm32/name
 8276     Single-int-var-in-mem/imm32/inouts
 8277     0/imm32/no-outputs
 8278     "ff 0/subop/increment"/imm32/subx-name
 8279     1/imm32/rm32-is-first-inout
 8280     0/imm32/no-r32
 8281     0/imm32/no-imm32
 8282     0/imm32/no-disp32
 8283     0/imm32/output-is-write-only
 8284     _Primitive-inc-reg/imm32/next
 8285 _Primitive-inc-reg:
 8286     # var/reg <- increment => ff 0/subop/increment %__
 8287     "increment"/imm32/name
 8288     0/imm32/no-inouts
 8289     Single-int-var-in-some-register/imm32/outputs
 8290     "ff 0/subop/increment"/imm32/subx-name
 8291     3/imm32/rm32-is-first-output
 8292     0/imm32/no-r32
 8293     0/imm32/no-imm32
 8294     0/imm32/no-disp32
 8295     0/imm32/output-is-write-only
 8296     _Primitive-dec-mem/imm32/next
 8297 _Primitive-dec-mem:
 8298     # decrement var => ff 1/subop/decrement *(ebp+__)
 8299     "decrement"/imm32/name
 8300     Single-int-var-in-mem/imm32/inouts
 8301     0/imm32/no-outputs
 8302     "ff 1/subop/decrement"/imm32/subx-name
 8303     1/imm32/rm32-is-first-inout
 8304     0/imm32/no-r32
 8305     0/imm32/no-imm32
 8306     0/imm32/no-disp32
 8307     0/imm32/output-is-write-only
 8308     _Primitive-dec-reg/imm32/next
 8309 _Primitive-dec-reg:
 8310     # var/reg <- decrement => ff 1/subop/decrement %__
 8311     "decrement"/imm32/name
 8312     0/imm32/no-inouts
 8313     Single-int-var-in-some-register/imm32/outputs
 8314     "ff 1/subop/decrement"/imm32/subx-name
 8315     3/imm32/rm32-is-first-output
 8316     0/imm32/no-r32
 8317     0/imm32/no-imm32
 8318     0/imm32/no-disp32
 8319     0/imm32/output-is-write-only
 8320     _Primitive-add-to-eax/imm32/next
 8321 # - add
 8322 _Primitive-add-to-eax:
 8323     # var/eax <- add lit => 05/add-to-eax lit/imm32
 8324     "add"/imm32/name
 8325     Single-lit-var/imm32/inouts
 8326     Single-int-var-in-eax/imm32/outputs
 8327     "05/add-to-eax"/imm32/subx-name
 8328     0/imm32/no-rm32
 8329     0/imm32/no-r32
 8330     1/imm32/imm32-is-first-inout
 8331     0/imm32/no-disp32
 8332     0/imm32/output-is-write-only
 8333     _Primitive-add-reg-to-reg/imm32/next
 8334 _Primitive-add-reg-to-reg:
 8335     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
 8336     "add"/imm32/name
 8337     Single-int-var-in-some-register/imm32/inouts
 8338     Single-int-var-in-some-register/imm32/outputs
 8339     "01/add-to"/imm32/subx-name
 8340     3/imm32/rm32-is-first-output
 8341     1/imm32/r32-is-first-inout
 8342     0/imm32/no-imm32
 8343     0/imm32/no-disp32
 8344     0/imm32/output-is-write-only
 8345     _Primitive-add-reg-to-mem/imm32/next
 8346 _Primitive-add-reg-to-mem:
 8347     # add-to var1 var2/reg => 01/add-to var1 var2/r32
 8348     "add-to"/imm32/name
 8349     Two-args-int-stack-int-reg/imm32/inouts
 8350     0/imm32/outputs
 8351     "01/add-to"/imm32/subx-name
 8352     1/imm32/rm32-is-first-inout
 8353     2/imm32/r32-is-second-inout
 8354     0/imm32/no-imm32
 8355     0/imm32/no-disp32
 8356     0/imm32/output-is-write-only
 8357     _Primitive-add-mem-to-reg/imm32/next
 8358 _Primitive-add-mem-to-reg:
 8359     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
 8360     "add"/imm32/name
 8361     Single-int-var-in-mem/imm32/inouts
 8362     Single-int-var-in-some-register/imm32/outputs
 8363     "03/add"/imm32/subx-name
 8364     1/imm32/rm32-is-first-inout
 8365     3/imm32/r32-is-first-output
 8366     0/imm32/no-imm32
 8367     0/imm32/no-disp32
 8368     0/imm32/output-is-write-only
 8369     _Primitive-add-lit-to-reg/imm32/next
 8370 _Primitive-add-lit-to-reg:
 8371     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
 8372     "add"/imm32/name
 8373     Single-lit-var/imm32/inouts
 8374     Single-int-var-in-some-register/imm32/outputs
 8375     "81 0/subop/add"/imm32/subx-name
 8376     3/imm32/rm32-is-first-output
 8377     0/imm32/no-r32
 8378     1/imm32/imm32-is-first-inout
 8379     0/imm32/no-disp32
 8380     0/imm32/output-is-write-only
 8381     _Primitive-add-lit-to-mem/imm32/next
 8382 _Primitive-add-lit-to-mem:
 8383     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
 8384     "add-to"/imm32/name
 8385     Int-var-and-literal/imm32/inouts
 8386     0/imm32/outputs
 8387     "81 0/subop/add"/imm32/subx-name
 8388     1/imm32/rm32-is-first-inout
 8389     0/imm32/no-r32
 8390     2/imm32/imm32-is-second-inout
 8391     0/imm32/no-disp32
 8392     0/imm32/output-is-write-only
 8393     _Primitive-subtract-from-eax/imm32/next
 8394 # - subtract
 8395 _Primitive-subtract-from-eax:
 8396     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
 8397     "subtract"/imm32/name
 8398     Single-lit-var/imm32/inouts
 8399     Single-int-var-in-eax/imm32/outputs
 8400     "2d/subtract-from-eax"/imm32/subx-name
 8401     0/imm32/no-rm32
 8402     0/imm32/no-r32
 8403     1/imm32/imm32-is-first-inout
 8404     0/imm32/no-disp32
 8405     0/imm32/output-is-write-only
 8406     _Primitive-subtract-reg-from-reg/imm32/next
 8407 _Primitive-subtract-reg-from-reg:
 8408     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
 8409     "subtract"/imm32/name
 8410     Single-int-var-in-some-register/imm32/inouts
 8411     Single-int-var-in-some-register/imm32/outputs
 8412     "29/subtract-from"/imm32/subx-name
 8413     3/imm32/rm32-is-first-output
 8414     1/imm32/r32-is-first-inout
 8415     0/imm32/no-imm32
 8416     0/imm32/no-disp32
 8417     0/imm32/output-is-write-only
 8418     _Primitive-subtract-reg-from-mem/imm32/next
 8419 _Primitive-subtract-reg-from-mem:
 8420     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
 8421     "subtract-from"/imm32/name
 8422     Two-args-int-stack-int-reg/imm32/inouts
 8423     0/imm32/outputs
 8424     "29/subtract-from"/imm32/subx-name
 8425     1/imm32/rm32-is-first-inout
 8426     2/imm32/r32-is-second-inout
 8427     0/imm32/no-imm32
 8428     0/imm32/no-disp32
 8429     0/imm32/output-is-write-only
 8430     _Primitive-subtract-mem-from-reg/imm32/next
 8431 _Primitive-subtract-mem-from-reg:
 8432     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
 8433     "subtract"/imm32/name
 8434     Single-int-var-in-mem/imm32/inouts
 8435     Single-int-var-in-some-register/imm32/outputs
 8436     "2b/subtract"/imm32/subx-name
 8437     1/imm32/rm32-is-first-inout
 8438     3/imm32/r32-is-first-output
 8439     0/imm32/no-imm32
 8440     0/imm32/no-disp32
 8441     0/imm32/output-is-write-only
 8442     _Primitive-subtract-lit-from-reg/imm32/next
 8443 _Primitive-subtract-lit-from-reg:
 8444     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
 8445     "subtract"/imm32/name
 8446     Single-lit-var/imm32/inouts
 8447     Single-int-var-in-some-register/imm32/outputs
 8448     "81 5/subop/subtract"/imm32/subx-name
 8449     3/imm32/rm32-is-first-output
 8450     0/imm32/no-r32
 8451     1/imm32/imm32-is-first-inout
 8452     0/imm32/no-disp32
 8453     0/imm32/output-is-write-only
 8454     _Primitive-subtract-lit-from-mem/imm32/next
 8455 _Primitive-subtract-lit-from-mem:
 8456     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
 8457     "subtract-from"/imm32/name
 8458     Int-var-and-literal/imm32/inouts
 8459     0/imm32/outputs
 8460     "81 5/subop/subtract"/imm32/subx-name
 8461     1/imm32/rm32-is-first-inout
 8462     0/imm32/no-r32
 8463     2/imm32/imm32-is-first-inout
 8464     0/imm32/no-disp32
 8465     0/imm32/output-is-write-only
 8466     _Primitive-and-with-eax/imm32/next
 8467 # - and
 8468 _Primitive-and-with-eax:
 8469     # var/eax <- and lit => 25/and-with-eax lit/imm32
 8470     "and"/imm32/name
 8471     Single-lit-var/imm32/inouts
 8472     Single-int-var-in-eax/imm32/outputs
 8473     "25/and-with-eax"/imm32/subx-name
 8474     0/imm32/no-rm32
 8475     0/imm32/no-r32
 8476     1/imm32/imm32-is-first-inout
 8477     0/imm32/no-disp32
 8478     0/imm32/output-is-write-only
 8479     _Primitive-and-reg-with-reg/imm32/next
 8480 _Primitive-and-reg-with-reg:
 8481     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
 8482     "and"/imm32/name
 8483     Single-int-var-in-some-register/imm32/inouts
 8484     Single-int-var-in-some-register/imm32/outputs
 8485     "21/and-with"/imm32/subx-name
 8486     3/imm32/rm32-is-first-output
 8487     1/imm32/r32-is-first-inout
 8488     0/imm32/no-imm32
 8489     0/imm32/no-disp32
 8490     0/imm32/output-is-write-only
 8491     _Primitive-and-reg-with-mem/imm32/next
 8492 _Primitive-and-reg-with-mem:
 8493     # and-with var1 var2/reg => 21/and-with var1 var2/r32
 8494     "and-with"/imm32/name
 8495     Two-args-int-stack-int-reg/imm32/inouts
 8496     0/imm32/outputs
 8497     "21/and-with"/imm32/subx-name
 8498     1/imm32/rm32-is-first-inout
 8499     2/imm32/r32-is-second-inout
 8500     0/imm32/no-imm32
 8501     0/imm32/no-disp32
 8502     0/imm32/output-is-write-only
 8503     _Primitive-and-mem-with-reg/imm32/next
 8504 _Primitive-and-mem-with-reg:
 8505     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
 8506     "and"/imm32/name
 8507     Single-int-var-in-mem/imm32/inouts
 8508     Single-int-var-in-some-register/imm32/outputs
 8509     "23/and"/imm32/subx-name
 8510     1/imm32/rm32-is-first-inout
 8511     3/imm32/r32-is-first-output
 8512     0/imm32/no-imm32
 8513     0/imm32/no-disp32
 8514     0/imm32/output-is-write-only
 8515     _Primitive-and-lit-with-reg/imm32/next
 8516 _Primitive-and-lit-with-reg:
 8517     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
 8518     "and"/imm32/name
 8519     Single-lit-var/imm32/inouts
 8520     Single-int-var-in-some-register/imm32/outputs
 8521     "81 4/subop/and"/imm32/subx-name
 8522     3/imm32/rm32-is-first-output
 8523     0/imm32/no-r32
 8524     1/imm32/imm32-is-first-inout
 8525     0/imm32/no-disp32
 8526     0/imm32/output-is-write-only
 8527     _Primitive-and-lit-with-mem/imm32/next
 8528 _Primitive-and-lit-with-mem:
 8529     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
 8530     "and-with"/imm32/name
 8531     Int-var-and-literal/imm32/inouts
 8532     0/imm32/outputs
 8533     "81 4/subop/and"/imm32/subx-name
 8534     1/imm32/rm32-is-first-inout
 8535     0/imm32/no-r32
 8536     2/imm32/imm32-is-first-inout
 8537     0/imm32/no-disp32
 8538     0/imm32/output-is-write-only
 8539     _Primitive-or-with-eax/imm32/next
 8540 # - or
 8541 _Primitive-or-with-eax:
 8542     # var/eax <- or lit => 0d/or-with-eax lit/imm32
 8543     "or"/imm32/name
 8544     Single-lit-var/imm32/inouts
 8545     Single-int-var-in-eax/imm32/outputs
 8546     "0d/or-with-eax"/imm32/subx-name
 8547     0/imm32/no-rm32
 8548     0/imm32/no-r32
 8549     1/imm32/imm32-is-first-inout
 8550     0/imm32/no-disp32
 8551     0/imm32/output-is-write-only
 8552     _Primitive-or-reg-with-reg/imm32/next
 8553 _Primitive-or-reg-with-reg:
 8554     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
 8555     "or"/imm32/name
 8556     Single-int-var-in-some-register/imm32/inouts
 8557     Single-int-var-in-some-register/imm32/outputs
 8558     "09/or-with"/imm32/subx-name
 8559     3/imm32/rm32-is-first-output
 8560     1/imm32/r32-is-first-inout
 8561     0/imm32/no-imm32
 8562     0/imm32/no-disp32
 8563     0/imm32/output-is-write-only
 8564     _Primitive-or-reg-with-mem/imm32/next
 8565 _Primitive-or-reg-with-mem:
 8566     # or-with var1 var2/reg => 09/or-with var1 var2/r32
 8567     "or-with"/imm32/name
 8568     Two-args-int-stack-int-reg/imm32/inouts
 8569     0/imm32/outputs
 8570     "09/or-with"/imm32/subx-name
 8571     1/imm32/rm32-is-first-inout
 8572     2/imm32/r32-is-second-inout
 8573     0/imm32/no-imm32
 8574     0/imm32/no-disp32
 8575     0/imm32/output-is-write-only
 8576     _Primitive-or-mem-with-reg/imm32/next
 8577 _Primitive-or-mem-with-reg:
 8578     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
 8579     "or"/imm32/name
 8580     Single-int-var-in-mem/imm32/inouts
 8581     Single-int-var-in-some-register/imm32/outputs
 8582     "0b/or"/imm32/subx-name
 8583     1/imm32/rm32-is-first-inout
 8584     3/imm32/r32-is-first-output
 8585     0/imm32/no-imm32
 8586     0/imm32/no-disp32
 8587     0/imm32/output-is-write-only
 8588     _Primitive-or-lit-with-reg/imm32/next
 8589 _Primitive-or-lit-with-reg:
 8590     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
 8591     "or"/imm32/name
 8592     Single-lit-var/imm32/inouts
 8593     Single-int-var-in-some-register/imm32/outputs
 8594     "81 1/subop/or"/imm32/subx-name
 8595     3/imm32/rm32-is-first-output
 8596     0/imm32/no-r32
 8597     1/imm32/imm32-is-first-inout
 8598     0/imm32/no-disp32
 8599     0/imm32/output-is-write-only
 8600     _Primitive-or-lit-with-mem/imm32/next
 8601 _Primitive-or-lit-with-mem:
 8602     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
 8603     "or-with"/imm32/name
 8604     Int-var-and-literal/imm32/inouts
 8605     0/imm32/outputs
 8606     "81 1/subop/or"/imm32/subx-name
 8607     1/imm32/rm32-is-first-inout
 8608     0/imm32/no-r32
 8609     2/imm32/imm32-is-second-inout
 8610     0/imm32/no-disp32
 8611     0/imm32/output-is-write-only
 8612     _Primitive-xor-with-eax/imm32/next
 8613 # - xor
 8614 _Primitive-xor-with-eax:
 8615     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
 8616     "xor"/imm32/name
 8617     Single-lit-var/imm32/inouts
 8618     Single-int-var-in-eax/imm32/outputs
 8619     "35/xor-with-eax"/imm32/subx-name
 8620     0/imm32/no-rm32
 8621     0/imm32/no-r32
 8622     1/imm32/imm32-is-first-inout
 8623     0/imm32/no-disp32
 8624     0/imm32/output-is-write-only
 8625     _Primitive-xor-reg-with-reg/imm32/next
 8626 _Primitive-xor-reg-with-reg:
 8627     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
 8628     "xor"/imm32/name
 8629     Single-int-var-in-some-register/imm32/inouts
 8630     Single-int-var-in-some-register/imm32/outputs
 8631     "31/xor-with"/imm32/subx-name
 8632     3/imm32/rm32-is-first-output
 8633     1/imm32/r32-is-first-inout
 8634     0/imm32/no-imm32
 8635     0/imm32/no-disp32
 8636     0/imm32/output-is-write-only
 8637     _Primitive-xor-reg-with-mem/imm32/next
 8638 _Primitive-xor-reg-with-mem:
 8639     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
 8640     "xor-with"/imm32/name
 8641     Two-args-int-stack-int-reg/imm32/inouts
 8642     0/imm32/outputs
 8643     "31/xor-with"/imm32/subx-name
 8644     1/imm32/rm32-is-first-inout
 8645     2/imm32/r32-is-second-inout
 8646     0/imm32/no-imm32
 8647     0/imm32/no-disp32
 8648     0/imm32/output-is-write-only
 8649     _Primitive-xor-mem-with-reg/imm32/next
 8650 _Primitive-xor-mem-with-reg:
 8651     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
 8652     "xor"/imm32/name
 8653     Single-int-var-in-mem/imm32/inouts
 8654     Single-int-var-in-some-register/imm32/outputs
 8655     "33/xor"/imm32/subx-name
 8656     1/imm32/rm32-is-first-inout
 8657     3/imm32/r32-is-first-output
 8658     0/imm32/no-imm32
 8659     0/imm32/no-disp32
 8660     0/imm32/output-is-write-only
 8661     _Primitive-xor-lit-with-reg/imm32/next
 8662 _Primitive-xor-lit-with-reg:
 8663     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
 8664     "xor"/imm32/name
 8665     Single-lit-var/imm32/inouts
 8666     Single-int-var-in-some-register/imm32/outputs
 8667     "81 6/subop/xor"/imm32/subx-name
 8668     3/imm32/rm32-is-first-output
 8669     0/imm32/no-r32
 8670     1/imm32/imm32-is-first-inout
 8671     0/imm32/no-disp32
 8672     0/imm32/output-is-write-only
 8673     _Primitive-xor-lit-with-mem/imm32/next
 8674 _Primitive-xor-lit-with-mem:
 8675     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
 8676     "xor-with"/imm32/name
 8677     Int-var-and-literal/imm32/inouts
 8678     0/imm32/outputs
 8679     "81 6/subop/xor"/imm32/subx-name
 8680     1/imm32/rm32-is-first-inout
 8681     0/imm32/no-r32
 8682     2/imm32/imm32-is-first-inout
 8683     0/imm32/no-disp32
 8684     0/imm32/output-is-write-only
 8685     _Primitive-copy-to-eax/imm32/next
 8686 # - copy
 8687 _Primitive-copy-to-eax:
 8688     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
 8689     "copy"/imm32/name
 8690     Single-lit-var/imm32/inouts
 8691     Single-int-var-in-eax/imm32/outputs
 8692     "b8/copy-to-eax"/imm32/subx-name
 8693     0/imm32/no-rm32
 8694     0/imm32/no-r32
 8695     1/imm32/imm32-is-first-inout
 8696     0/imm32/no-disp32
 8697     1/imm32/output-is-write-only
 8698     _Primitive-copy-to-ecx/imm32/next
 8699 _Primitive-copy-to-ecx:
 8700     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
 8701     "copy"/imm32/name
 8702     Single-lit-var/imm32/inouts
 8703     Single-int-var-in-ecx/imm32/outputs
 8704     "b9/copy-to-ecx"/imm32/subx-name
 8705     0/imm32/no-rm32
 8706     0/imm32/no-r32
 8707     1/imm32/imm32-is-first-inout
 8708     0/imm32/no-disp32
 8709     1/imm32/output-is-write-only
 8710     _Primitive-copy-to-edx/imm32/next
 8711 _Primitive-copy-to-edx:
 8712     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
 8713     "copy"/imm32/name
 8714     Single-lit-var/imm32/inouts
 8715     Single-int-var-in-edx/imm32/outputs
 8716     "ba/copy-to-edx"/imm32/subx-name
 8717     0/imm32/no-rm32
 8718     0/imm32/no-r32
 8719     1/imm32/imm32-is-first-inout
 8720     0/imm32/no-disp32
 8721     1/imm32/output-is-write-only
 8722     _Primitive-copy-to-ebx/imm32/next
 8723 _Primitive-copy-to-ebx:
 8724     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
 8725     "copy"/imm32/name
 8726     Single-lit-var/imm32/inouts
 8727     Single-int-var-in-ebx/imm32/outputs
 8728     "bb/copy-to-ebx"/imm32/subx-name
 8729     0/imm32/no-rm32
 8730     0/imm32/no-r32
 8731     1/imm32/imm32-is-first-inout
 8732     0/imm32/no-disp32
 8733     1/imm32/output-is-write-only
 8734     _Primitive-copy-to-esi/imm32/next
 8735 _Primitive-copy-to-esi:
 8736     # var/esi <- copy lit => be/copy-to-esi lit/imm32
 8737     "copy"/imm32/name
 8738     Single-lit-var/imm32/inouts
 8739     Single-int-var-in-esi/imm32/outputs
 8740     "be/copy-to-esi"/imm32/subx-name
 8741     0/imm32/no-rm32
 8742     0/imm32/no-r32
 8743     1/imm32/imm32-is-first-inout
 8744     0/imm32/no-disp32
 8745     1/imm32/output-is-write-only
 8746     _Primitive-copy-to-edi/imm32/next
 8747 _Primitive-copy-to-edi:
 8748     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
 8749     "copy"/imm32/name
 8750     Single-lit-var/imm32/inouts
 8751     Single-int-var-in-edi/imm32/outputs
 8752     "bf/copy-to-edi"/imm32/subx-name
 8753     0/imm32/no-rm32
 8754     0/imm32/no-r32
 8755     1/imm32/imm32-is-first-inout
 8756     0/imm32/no-disp32
 8757     1/imm32/output-is-write-only
 8758     _Primitive-copy-reg-to-reg/imm32/next
 8759 _Primitive-copy-reg-to-reg:
 8760     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
 8761     "copy"/imm32/name
 8762     Single-int-var-in-some-register/imm32/inouts
 8763     Single-int-var-in-some-register/imm32/outputs
 8764     "89/copy-to"/imm32/subx-name
 8765     3/imm32/rm32-is-first-output
 8766     1/imm32/r32-is-first-inout
 8767     0/imm32/no-imm32
 8768     0/imm32/no-disp32
 8769     1/imm32/output-is-write-only
 8770     _Primitive-copy-reg-to-mem/imm32/next
 8771 _Primitive-copy-reg-to-mem:
 8772     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
 8773     "copy-to"/imm32/name
 8774     Two-args-int-stack-int-reg/imm32/inouts
 8775     0/imm32/outputs
 8776     "89/copy-to"/imm32/subx-name
 8777     1/imm32/rm32-is-first-inout
 8778     2/imm32/r32-is-second-inout
 8779     0/imm32/no-imm32
 8780     0/imm32/no-disp32
 8781     1/imm32/output-is-write-only
 8782     _Primitive-copy-mem-to-reg/imm32/next
 8783 _Primitive-copy-mem-to-reg:
 8784     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
 8785     "copy"/imm32/name
 8786     Single-int-var-in-mem/imm32/inouts
 8787     Single-int-var-in-some-register/imm32/outputs
 8788     "8b/copy-from"/imm32/subx-name
 8789     1/imm32/rm32-is-first-inout
 8790     3/imm32/r32-is-first-output
 8791     0/imm32/no-imm32
 8792     0/imm32/no-disp32
 8793     1/imm32/output-is-write-only
 8794     _Primitive-copy-lit-to-reg/imm32/next
 8795 _Primitive-copy-lit-to-reg:
 8796     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
 8797     "copy"/imm32/name
 8798     Single-lit-var/imm32/inouts
 8799     Single-int-var-in-some-register/imm32/outputs
 8800     "c7 0/subop/copy"/imm32/subx-name
 8801     3/imm32/rm32-is-first-output
 8802     0/imm32/no-r32
 8803     1/imm32/imm32-is-first-inout
 8804     0/imm32/no-disp32
 8805     1/imm32/output-is-write-only
 8806     _Primitive-copy-lit-to-mem/imm32/next
 8807 _Primitive-copy-lit-to-mem:
 8808     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
 8809     "copy-to"/imm32/name
 8810     Int-var-and-literal/imm32/inouts
 8811     0/imm32/outputs
 8812     "c7 0/subop/copy"/imm32/subx-name
 8813     1/imm32/rm32-is-first-inout
 8814     0/imm32/no-r32
 8815     2/imm32/imm32-is-first-inout
 8816     0/imm32/no-disp32
 8817     1/imm32/output-is-write-only
 8818     _Primitive-address/imm32/next
 8819 # - address
 8820 _Primitive-address:
 8821     # var1/reg <- address var2 => 8d/copy-address var2/rm32 var1/r32
 8822     "address"/imm32/name
 8823     Single-int-var-in-mem/imm32/inouts
 8824     Single-addr-var-in-some-register/imm32/outputs
 8825     "8d/copy-address"/imm32/subx-name
 8826     1/imm32/rm32-is-first-inout
 8827     3/imm32/r32-is-first-output
 8828     0/imm32/no-imm32
 8829     0/imm32/no-disp32
 8830     1/imm32/output-is-write-only
 8831     _Primitive-compare-mem-with-reg/imm32/next
 8832 # - compare
 8833 _Primitive-compare-mem-with-reg:
 8834     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
 8835     "compare"/imm32/name
 8836     Two-args-int-stack-int-reg/imm32/inouts
 8837     0/imm32/outputs
 8838     "39/compare->"/imm32/subx-name
 8839     1/imm32/rm32-is-first-inout
 8840     2/imm32/r32-is-second-inout
 8841     0/imm32/no-imm32
 8842     0/imm32/no-disp32
 8843     0/imm32/output-is-write-only
 8844     _Primitive-compare-reg-with-mem/imm32/next
 8845 _Primitive-compare-reg-with-mem:
 8846     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
 8847     "compare"/imm32/name
 8848     Two-args-int-reg-int-stack/imm32/inouts
 8849     0/imm32/outputs
 8850     "3b/compare<-"/imm32/subx-name
 8851     2/imm32/rm32-is-second-inout
 8852     1/imm32/r32-is-first-inout
 8853     0/imm32/no-imm32
 8854     0/imm32/no-disp32
 8855     0/imm32/output-is-write-only
 8856     _Primitive-compare-eax-with-literal/imm32/next
 8857 _Primitive-compare-eax-with-literal:
 8858     # compare var1/eax n => 3d/compare-eax-with n/imm32
 8859     "compare"/imm32/name
 8860     Two-args-int-eax-int-literal/imm32/inouts
 8861     0/imm32/outputs
 8862     "3d/compare-eax-with"/imm32/subx-name
 8863     0/imm32/no-rm32
 8864     0/imm32/no-r32
 8865     2/imm32/imm32-is-second-inout
 8866     0/imm32/no-disp32
 8867     0/imm32/output-is-write-only
 8868     _Primitive-compare-reg-with-literal/imm32/next
 8869 _Primitive-compare-reg-with-literal:
 8870     # compare var1/reg n => 81 7/subop/compare %reg n/imm32
 8871     "compare"/imm32/name
 8872     Int-var-in-register-and-literal/imm32/inouts
 8873     0/imm32/outputs
 8874     "81 7/subop/compare"/imm32/subx-name
 8875     1/imm32/rm32-is-first-inout
 8876     0/imm32/no-r32
 8877     2/imm32/imm32-is-second-inout
 8878     0/imm32/no-disp32
 8879     0/imm32/output-is-write-only
 8880     _Primitive-compare-mem-with-literal/imm32/next
 8881 _Primitive-compare-mem-with-literal:
 8882     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
 8883     "compare"/imm32/name
 8884     Int-var-and-literal/imm32/inouts
 8885     0/imm32/outputs
 8886     "81 7/subop/compare"/imm32/subx-name
 8887     1/imm32/rm32-is-first-inout
 8888     0/imm32/no-r32
 8889     2/imm32/imm32-is-second-inout
 8890     0/imm32/no-disp32
 8891     0/imm32/output-is-write-only
 8892     _Primitive-multiply-reg-by-mem/imm32/next
 8893 # - multiply
 8894 _Primitive-multiply-reg-by-mem:
 8895     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
 8896     "multiply"/imm32/name
 8897     Single-int-var-in-mem/imm32/inouts
 8898     Single-int-var-in-some-register/imm32/outputs
 8899     "0f af/multiply"/imm32/subx-name
 8900     1/imm32/rm32-is-first-inout
 8901     3/imm32/r32-is-first-output
 8902     0/imm32/no-imm32
 8903     0/imm32/no-disp32
 8904     0/imm32/output-is-write-only
 8905     _Primitive-break-if-addr</imm32/next
 8906 # - branches
 8907 _Primitive-break-if-addr<:
 8908     "break-if-addr<"/imm32/name
 8909     0/imm32/inouts
 8910     0/imm32/outputs
 8911     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
 8912     0/imm32/no-rm32
 8913     0/imm32/no-r32
 8914     0/imm32/no-imm32
 8915     0/imm32/no-disp32
 8916     0/imm32/no-output
 8917     _Primitive-break-if-addr>=/imm32/next
 8918 _Primitive-break-if-addr>=:
 8919     "break-if-addr>="/imm32/name
 8920     0/imm32/inouts
 8921     0/imm32/outputs
 8922     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
 8923     0/imm32/no-rm32
 8924     0/imm32/no-r32
 8925     0/imm32/no-imm32
 8926     0/imm32/no-disp32
 8927     0/imm32/no-output
 8928     _Primitive-break-if-=/imm32/next
 8929 _Primitive-break-if-=:
 8930     "break-if-="/imm32/name
 8931     0/imm32/inouts
 8932     0/imm32/outputs
 8933     "0f 84/jump-if-= break/disp32"/imm32/subx-name
 8934     0/imm32/no-rm32
 8935     0/imm32/no-r32
 8936     0/imm32/no-imm32
 8937     0/imm32/no-disp32
 8938     0/imm32/no-output
 8939     _Primitive-break-if-!=/imm32/next
 8940 _Primitive-break-if-!=:
 8941     "break-if-!="/imm32/name
 8942     0/imm32/inouts
 8943     0/imm32/outputs
 8944     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
 8945     0/imm32/no-rm32
 8946     0/imm32/no-r32
 8947     0/imm32/no-imm32
 8948     0/imm32/no-disp32
 8949     0/imm32/no-output
 8950     _Primitive-break-if-addr<=/imm32/next
 8951 _Primitive-break-if-addr<=:
 8952     "break-if-addr<="/imm32/name
 8953     0/imm32/inouts
 8954     0/imm32/outputs
 8955     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
 8956     0/imm32/no-rm32
 8957     0/imm32/no-r32
 8958     0/imm32/no-imm32
 8959     0/imm32/no-disp32
 8960     0/imm32/no-output
 8961     _Primitive-break-if-addr>/imm32/next
 8962 _Primitive-break-if-addr>:
 8963     "break-if-addr>"/imm32/name
 8964     0/imm32/inouts
 8965     0/imm32/outputs
 8966     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
 8967     0/imm32/no-rm32
 8968     0/imm32/no-r32
 8969     0/imm32/no-imm32
 8970     0/imm32/no-disp32
 8971     0/imm32/no-output
 8972     _Primitive-break-if-</imm32/next
 8973 _Primitive-break-if-<:
 8974     "break-if-<"/imm32/name
 8975     0/imm32/inouts
 8976     0/imm32/outputs
 8977     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
 8978     0/imm32/no-rm32
 8979     0/imm32/no-r32
 8980     0/imm32/no-imm32
 8981     0/imm32/no-disp32
 8982     0/imm32/no-output
 8983     _Primitive-break-if->=/imm32/next
 8984 _Primitive-break-if->=:
 8985     "break-if->="/imm32/name
 8986     0/imm32/inouts
 8987     0/imm32/outputs
 8988     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
 8989     0/imm32/no-rm32
 8990     0/imm32/no-r32
 8991     0/imm32/no-imm32
 8992     0/imm32/no-disp32
 8993     0/imm32/no-output
 8994     _Primitive-break-if-<=/imm32/next
 8995 _Primitive-break-if-<=:
 8996     "break-if-<="/imm32/name
 8997     0/imm32/inouts
 8998     0/imm32/outputs
 8999     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
 9000     0/imm32/no-rm32
 9001     0/imm32/no-r32
 9002     0/imm32/no-imm32
 9003     0/imm32/no-disp32
 9004     0/imm32/no-output
 9005     _Primitive-break-if->/imm32/next
 9006 _Primitive-break-if->:
 9007     "break-if->"/imm32/name
 9008     0/imm32/inouts
 9009     0/imm32/outputs
 9010     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
 9011     0/imm32/no-rm32
 9012     0/imm32/no-r32
 9013     0/imm32/no-imm32
 9014     0/imm32/no-disp32
 9015     0/imm32/no-output
 9016     _Primitive-break/imm32/next
 9017 _Primitive-break:
 9018     "break"/imm32/name
 9019     0/imm32/inouts
 9020     0/imm32/outputs
 9021     "e9/jump break/disp32"/imm32/subx-name
 9022     0/imm32/no-rm32
 9023     0/imm32/no-r32
 9024     0/imm32/no-imm32
 9025     0/imm32/no-disp32
 9026     0/imm32/no-output
 9027     _Primitive-loop-if-addr</imm32/next
 9028 _Primitive-loop-if-addr<:
 9029     "loop-if-addr<"/imm32/name
 9030     0/imm32/inouts
 9031     0/imm32/outputs
 9032     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
 9033     0/imm32/no-rm32
 9034     0/imm32/no-r32
 9035     0/imm32/no-imm32
 9036     0/imm32/no-disp32
 9037     0/imm32/no-output
 9038     _Primitive-loop-if-addr>=/imm32/next
 9039 _Primitive-loop-if-addr>=:
 9040     "loop-if-addr>="/imm32/name
 9041     0/imm32/inouts
 9042     0/imm32/outputs
 9043     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
 9044     0/imm32/no-rm32
 9045     0/imm32/no-r32
 9046     0/imm32/no-imm32
 9047     0/imm32/no-disp32
 9048     0/imm32/no-output
 9049     _Primitive-loop-if-=/imm32/next
 9050 _Primitive-loop-if-=:
 9051     "loop-if-="/imm32/name
 9052     0/imm32/inouts
 9053     0/imm32/outputs
 9054     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
 9055     0/imm32/no-rm32
 9056     0/imm32/no-r32
 9057     0/imm32/no-imm32
 9058     0/imm32/no-disp32
 9059     0/imm32/no-output
 9060     _Primitive-loop-if-!=/imm32/next
 9061 _Primitive-loop-if-!=:
 9062     "loop-if-!="/imm32/name
 9063     0/imm32/inouts
 9064     0/imm32/outputs
 9065     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
 9066     0/imm32/no-rm32
 9067     0/imm32/no-r32
 9068     0/imm32/no-imm32
 9069     0/imm32/no-disp32
 9070     0/imm32/no-output
 9071     _Primitive-loop-if-addr<=/imm32/next
 9072 _Primitive-loop-if-addr<=:
 9073     "loop-if-addr<="/imm32/name
 9074     0/imm32/inouts
 9075     0/imm32/outputs
 9076     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
 9077     0/imm32/no-rm32
 9078     0/imm32/no-r32
 9079     0/imm32/no-imm32
 9080     0/imm32/no-disp32
 9081     0/imm32/no-output
 9082     _Primitive-loop-if-addr>/imm32/next
 9083 _Primitive-loop-if-addr>:
 9084     "loop-if-addr>"/imm32/name
 9085     0/imm32/inouts
 9086     0/imm32/outputs
 9087     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
 9088     0/imm32/no-rm32
 9089     0/imm32/no-r32
 9090     0/imm32/no-imm32
 9091     0/imm32/no-disp32
 9092     0/imm32/no-output
 9093     _Primitive-loop-if-</imm32/next
 9094 _Primitive-loop-if-<:
 9095     "loop-if-<"/imm32/name
 9096     0/imm32/inouts
 9097     0/imm32/outputs
 9098     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
 9099     0/imm32/no-rm32
 9100     0/imm32/no-r32
 9101     0/imm32/no-imm32
 9102     0/imm32/no-disp32
 9103     0/imm32/no-output
 9104     _Primitive-loop-if->=/imm32/next
 9105 _Primitive-loop-if->=:
 9106     "loop-if->="/imm32/name
 9107     0/imm32/inouts
 9108     0/imm32/outputs
 9109     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
 9110     0/imm32/no-rm32
 9111     0/imm32/no-r32
 9112     0/imm32/no-imm32
 9113     0/imm32/no-disp32
 9114     0/imm32/no-output
 9115     _Primitive-loop-if-<=/imm32/next
 9116 _Primitive-loop-if-<=:
 9117     "loop-if-<="/imm32/name
 9118     0/imm32/inouts
 9119     0/imm32/outputs
 9120     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
 9121     0/imm32/no-rm32
 9122     0/imm32/no-r32
 9123     0/imm32/no-imm32
 9124     0/imm32/no-disp32
 9125     0/imm32/no-output
 9126     _Primitive-loop-if->/imm32/next
 9127 _Primitive-loop-if->:
 9128     "loop-if->"/imm32/name
 9129     0/imm32/inouts
 9130     0/imm32/outputs
 9131     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
 9132     0/imm32/no-rm32
 9133     0/imm32/no-r32
 9134     0/imm32/no-imm32
 9135     0/imm32/no-disp32
 9136     0/imm32/no-output
 9137     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
 9138 _Primitive-loop:
 9139     "loop"/imm32/name
 9140     0/imm32/inouts
 9141     0/imm32/outputs
 9142     "e9/jump loop/disp32"/imm32/subx-name
 9143     0/imm32/no-rm32
 9144     0/imm32/no-r32
 9145     0/imm32/no-imm32
 9146     0/imm32/no-disp32
 9147     0/imm32/no-output
 9148     _Primitive-break-if-addr<-named/imm32/next
 9149 # - branches to named blocks
 9150 _Primitive-break-if-addr<-named:
 9151     "break-if-addr<"/imm32/name
 9152     Single-lit-var/imm32/inouts
 9153     0/imm32/outputs
 9154     "0f 82/jump-if-addr<"/imm32/subx-name
 9155     0/imm32/no-rm32
 9156     0/imm32/no-r32
 9157     0/imm32/no-imm32
 9158     1/imm32/disp32-is-first-inout
 9159     0/imm32/no-output
 9160     _Primitive-break-if-addr>=-named/imm32/next
 9161 _Primitive-break-if-addr>=-named:
 9162     "break-if-addr>="/imm32/name
 9163     Single-lit-var/imm32/inouts
 9164     0/imm32/outputs
 9165     "0f 83/jump-if-addr>="/imm32/subx-name
 9166     0/imm32/no-rm32
 9167     0/imm32/no-r32
 9168     0/imm32/no-imm32
 9169     1/imm32/disp32-is-first-inout
 9170     0/imm32/no-output
 9171     _Primitive-break-if-=-named/imm32/next
 9172 _Primitive-break-if-=-named:
 9173     "break-if-="/imm32/name
 9174     Single-lit-var/imm32/inouts
 9175     0/imm32/outputs
 9176     "0f 84/jump-if-="/imm32/subx-name
 9177     0/imm32/no-rm32
 9178     0/imm32/no-r32
 9179     0/imm32/no-imm32
 9180     1/imm32/disp32-is-first-inout
 9181     0/imm32/no-output
 9182     _Primitive-break-if-!=-named/imm32/next
 9183 _Primitive-break-if-!=-named:
 9184     "break-if-!="/imm32/name
 9185     Single-lit-var/imm32/inouts
 9186     0/imm32/outputs
 9187     "0f 85/jump-if-!="/imm32/subx-name
 9188     0/imm32/no-rm32
 9189     0/imm32/no-r32
 9190     0/imm32/no-imm32
 9191     1/imm32/disp32-is-first-inout
 9192     0/imm32/no-output
 9193     _Primitive-break-if-addr<=-named/imm32/next
 9194 _Primitive-break-if-addr<=-named:
 9195     "break-if-addr<="/imm32/name
 9196     Single-lit-var/imm32/inouts
 9197     0/imm32/outputs
 9198     "0f 86/jump-if-addr<="/imm32/subx-name
 9199     0/imm32/no-rm32
 9200     0/imm32/no-r32
 9201     0/imm32/no-imm32
 9202     1/imm32/disp32-is-first-inout
 9203     0/imm32/no-output
 9204     _Primitive-break-if-addr>-named/imm32/next
 9205 _Primitive-break-if-addr>-named:
 9206     "break-if-addr>"/imm32/name
 9207     Single-lit-var/imm32/inouts
 9208     0/imm32/outputs
 9209     "0f 87/jump-if-addr>"/imm32/subx-name
 9210     0/imm32/no-rm32
 9211     0/imm32/no-r32
 9212     0/imm32/no-imm32
 9213     1/imm32/disp32-is-first-inout
 9214     0/imm32/no-output
 9215     _Primitive-break-if-<-named/imm32/next
 9216 _Primitive-break-if-<-named:
 9217     "break-if-<"/imm32/name
 9218     Single-lit-var/imm32/inouts
 9219     0/imm32/outputs
 9220     "0f 8c/jump-if-<"/imm32/subx-name
 9221     0/imm32/no-rm32
 9222     0/imm32/no-r32
 9223     0/imm32/no-imm32
 9224     1/imm32/disp32-is-first-inout
 9225     0/imm32/no-output
 9226     _Primitive-break-if->=-named/imm32/next
 9227 _Primitive-break-if->=-named:
 9228     "break-if->="/imm32/name
 9229     Single-lit-var/imm32/inouts
 9230     0/imm32/outputs
 9231     "0f 8d/jump-if->="/imm32/subx-name
 9232     0/imm32/no-rm32
 9233     0/imm32/no-r32
 9234     0/imm32/no-imm32
 9235     1/imm32/disp32-is-first-inout
 9236     0/imm32/no-output
 9237     _Primitive-break-if-<=-named/imm32/next
 9238 _Primitive-break-if-<=-named:
 9239     "break-if-<="/imm32/name
 9240     Single-lit-var/imm32/inouts
 9241     0/imm32/outputs
 9242     "0f 8e/jump-if-<="/imm32/subx-name
 9243     0/imm32/no-rm32
 9244     0/imm32/no-r32
 9245     0/imm32/no-imm32
 9246     1/imm32/disp32-is-first-inout
 9247     0/imm32/no-output
 9248     _Primitive-break-if->-named/imm32/next
 9249 _Primitive-break-if->-named:
 9250     "break-if->"/imm32/name
 9251     Single-lit-var/imm32/inouts
 9252     0/imm32/outputs
 9253     "0f 8f/jump-if->"/imm32/subx-name
 9254     0/imm32/no-rm32
 9255     0/imm32/no-r32
 9256     0/imm32/no-imm32
 9257     1/imm32/disp32-is-first-inout
 9258     0/imm32/no-output
 9259     _Primitive-break-named/imm32/next
 9260 _Primitive-break-named:
 9261     "break"/imm32/name
 9262     Single-lit-var/imm32/inouts
 9263     0/imm32/outputs
 9264     "e9/jump"/imm32/subx-name
 9265     0/imm32/no-rm32
 9266     0/imm32/no-r32
 9267     0/imm32/no-imm32
 9268     1/imm32/disp32-is-first-inout
 9269     0/imm32/no-output
 9270     _Primitive-loop-if-addr<-named/imm32/next
 9271 _Primitive-loop-if-addr<-named:
 9272     "loop-if-addr<"/imm32/name
 9273     Single-lit-var/imm32/inouts
 9274     0/imm32/outputs
 9275     "0f 82/jump-if-addr<"/imm32/subx-name
 9276     0/imm32/no-rm32
 9277     0/imm32/no-r32
 9278     0/imm32/no-imm32
 9279     1/imm32/disp32-is-first-inout
 9280     0/imm32/no-output
 9281     _Primitive-loop-if-addr>=-named/imm32/next
 9282 _Primitive-loop-if-addr>=-named:
 9283     "loop-if-addr>="/imm32/name
 9284     Single-lit-var/imm32/inouts
 9285     0/imm32/outputs
 9286     "0f 83/jump-if-addr>="/imm32/subx-name
 9287     0/imm32/no-rm32
 9288     0/imm32/no-r32
 9289     0/imm32/no-imm32
 9290     1/imm32/disp32-is-first-inout
 9291     0/imm32/no-output
 9292     _Primitive-loop-if-=-named/imm32/next
 9293 _Primitive-loop-if-=-named:
 9294     "loop-if-="/imm32/name
 9295     Single-lit-var/imm32/inouts
 9296     0/imm32/outputs
 9297     "0f 84/jump-if-="/imm32/subx-name
 9298     0/imm32/no-rm32
 9299     0/imm32/no-r32
 9300     0/imm32/no-imm32
 9301     1/imm32/disp32-is-first-inout
 9302     0/imm32/no-output
 9303     _Primitive-loop-if-!=-named/imm32/next
 9304 _Primitive-loop-if-!=-named:
 9305     "loop-if-!="/imm32/name
 9306     Single-lit-var/imm32/inouts
 9307     0/imm32/outputs
 9308     "0f 85/jump-if-!="/imm32/subx-name
 9309     0/imm32/no-rm32
 9310     0/imm32/no-r32
 9311     0/imm32/no-imm32
 9312     1/imm32/disp32-is-first-inout
 9313     0/imm32/no-output
 9314     _Primitive-loop-if-addr<=-named/imm32/next
 9315 _Primitive-loop-if-addr<=-named:
 9316     "loop-if-addr<="/imm32/name
 9317     Single-lit-var/imm32/inouts
 9318     0/imm32/outputs
 9319     "0f 86/jump-if-addr<="/imm32/subx-name
 9320     0/imm32/no-rm32
 9321     0/imm32/no-r32
 9322     0/imm32/no-imm32
 9323     1/imm32/disp32-is-first-inout
 9324     0/imm32/no-output
 9325     _Primitive-loop-if-addr>-named/imm32/next
 9326 _Primitive-loop-if-addr>-named:
 9327     "loop-if-addr>"/imm32/name
 9328     Single-lit-var/imm32/inouts
 9329     0/imm32/outputs
 9330     "0f 87/jump-if-addr>"/imm32/subx-name
 9331     0/imm32/no-rm32
 9332     0/imm32/no-r32
 9333     0/imm32/no-imm32
 9334     1/imm32/disp32-is-first-inout
 9335     0/imm32/no-output
 9336     _Primitive-loop-if-<-named/imm32/next
 9337 _Primitive-loop-if-<-named:
 9338     "loop-if-<"/imm32/name
 9339     Single-lit-var/imm32/inouts
 9340     0/imm32/outputs
 9341     "0f 8c/jump-if-<"/imm32/subx-name
 9342     0/imm32/no-rm32
 9343     0/imm32/no-r32
 9344     0/imm32/no-imm32
 9345     1/imm32/disp32-is-first-inout
 9346     0/imm32/no-output
 9347     _Primitive-loop-if->=-named/imm32/next
 9348 _Primitive-loop-if->=-named:
 9349     "loop-if->="/imm32/name
 9350     Single-lit-var/imm32/inouts
 9351     0/imm32/outputs
 9352     "0f 8d/jump-if->="/imm32/subx-name
 9353     0/imm32/no-rm32
 9354     0/imm32/no-r32
 9355     0/imm32/no-imm32
 9356     1/imm32/disp32-is-first-inout
 9357     0/imm32/no-output
 9358     _Primitive-loop-if-<=-named/imm32/next
 9359 _Primitive-loop-if-<=-named:
 9360     "loop-if-<="/imm32/name
 9361     Single-lit-var/imm32/inouts
 9362     0/imm32/outputs
 9363     "0f 8e/jump-if-<="/imm32/subx-name
 9364     0/imm32/no-rm32
 9365     0/imm32/no-r32
 9366     0/imm32/no-imm32
 9367     1/imm32/disp32-is-first-inout
 9368     0/imm32/no-output
 9369     _Primitive-loop-if->-named/imm32/next
 9370 _Primitive-loop-if->-named:
 9371     "loop-if->"/imm32/name
 9372     Single-lit-var/imm32/inouts
 9373     0/imm32/outputs
 9374     "0f 8f/jump-if->"/imm32/subx-name
 9375     0/imm32/no-rm32
 9376     0/imm32/no-r32
 9377     0/imm32/no-imm32
 9378     1/imm32/disp32-is-first-inout
 9379     0/imm32/no-output
 9380     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
 9381 _Primitive-loop-named:
 9382     "loop"/imm32/name
 9383     Single-lit-var/imm32/inouts
 9384     0/imm32/outputs
 9385     "e9/jump"/imm32/subx-name
 9386     0/imm32/no-rm32
 9387     0/imm32/no-r32
 9388     0/imm32/no-imm32
 9389     1/imm32/disp32-is-first-inout
 9390     0/imm32/no-output
 9391     0/imm32/next
 9392 
 9393 Single-int-var-in-mem:
 9394     Int-var-in-mem/imm32
 9395     0/imm32/next
 9396 
 9397 Int-var-in-mem:
 9398     "arg1"/imm32/name
 9399     Type-int/imm32
 9400     1/imm32/some-block-depth
 9401     1/imm32/some-stack-offset
 9402     0/imm32/no-register
 9403 
 9404 Two-args-int-stack-int-reg:
 9405     Int-var-in-mem/imm32
 9406     Single-int-var-in-some-register/imm32/next
 9407 
 9408 Two-args-int-reg-int-stack:
 9409     Int-var-in-some-register/imm32
 9410     Single-int-var-in-mem/imm32/next
 9411 
 9412 Two-args-int-eax-int-literal:
 9413     Int-var-in-eax/imm32
 9414     Single-lit-var/imm32/next
 9415 
 9416 Int-var-and-literal:
 9417     Int-var-in-mem/imm32
 9418     Single-lit-var/imm32/next
 9419 
 9420 Int-var-in-register-and-literal:
 9421     Int-var-in-some-register/imm32
 9422     Single-lit-var/imm32/next
 9423 
 9424 Single-int-var-in-some-register:
 9425     Int-var-in-some-register/imm32
 9426     0/imm32/next
 9427 
 9428 Single-addr-var-in-some-register:
 9429     Addr-var-in-some-register/imm32
 9430     0/imm32/next
 9431 
 9432 Int-var-in-some-register:
 9433     "arg1"/imm32/name
 9434     Type-int/imm32
 9435     1/imm32/some-block-depth
 9436     0/imm32/no-stack-offset
 9437     Any-register/imm32
 9438 
 9439 Addr-var-in-some-register:
 9440     "arg1"/imm32/name
 9441     Type-addr/imm32
 9442     1/imm32/some-block-depth
 9443     0/imm32/no-stack-offset
 9444     Any-register/imm32
 9445 
 9446 Single-int-var-in-eax:
 9447     Int-var-in-eax/imm32
 9448     0/imm32/next
 9449 
 9450 Int-var-in-eax:
 9451     "arg1"/imm32/name
 9452     Type-int/imm32
 9453     1/imm32/some-block-depth
 9454     0/imm32/no-stack-offset
 9455     "eax"/imm32/register
 9456 
 9457 Single-int-var-in-ecx:
 9458     Int-var-in-ecx/imm32
 9459     0/imm32/next
 9460 
 9461 Int-var-in-ecx:
 9462     "arg1"/imm32/name
 9463     Type-int/imm32
 9464     1/imm32/some-block-depth
 9465     0/imm32/no-stack-offset
 9466     "ecx"/imm32/register
 9467 
 9468 Single-int-var-in-edx:
 9469     Int-var-in-edx/imm32
 9470     0/imm32/next
 9471 
 9472 Int-var-in-edx:
 9473     "arg1"/imm32/name
 9474     Type-int/imm32
 9475     1/imm32/some-block-depth
 9476     0/imm32/no-stack-offset
 9477     "edx"/imm32/register
 9478 
 9479 Single-int-var-in-ebx:
 9480     Int-var-in-ebx/imm32
 9481     0/imm32/next
 9482 
 9483 Int-var-in-ebx:
 9484     "arg1"/imm32/name
 9485     Type-int/imm32
 9486     1/imm32/some-block-depth
 9487     0/imm32/no-stack-offset
 9488     "ebx"/imm32/register
 9489 
 9490 Single-int-var-in-esi:
 9491     Int-var-in-esi/imm32
 9492     0/imm32/next
 9493 
 9494 Int-var-in-esi:
 9495     "arg1"/imm32/name
 9496     Type-int/imm32
 9497     1/imm32/some-block-depth
 9498     0/imm32/no-stack-offset
 9499     "esi"/imm32/register
 9500 
 9501 Single-int-var-in-edi:
 9502     Int-var-in-edi/imm32
 9503     0/imm32/next
 9504 
 9505 Int-var-in-edi:
 9506     "arg1"/imm32/name
 9507     Type-int/imm32
 9508     1/imm32/some-block-depth
 9509     0/imm32/no-stack-offset
 9510     "edi"/imm32/register
 9511 
 9512 Single-lit-var:
 9513     Lit-var/imm32
 9514     0/imm32/next
 9515 
 9516 Lit-var:
 9517     "literal"/imm32/name
 9518     Type-literal/imm32
 9519     1/imm32/some-block-depth
 9520     0/imm32/no-stack-offset
 9521     0/imm32/no-register
 9522 
 9523 Type-int:
 9524     1/imm32/left/int
 9525     0/imm32/right/null
 9526 
 9527 Type-literal:
 9528     0/imm32/left/literal
 9529     0/imm32/right/null
 9530 
 9531 Type-addr:
 9532     2/imm32/left/addr
 9533     0/imm32/right/null
 9534 
 9535 == code
 9536 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle stmt), primitive: (handle function)
 9537     # . prologue
 9538     55/push-ebp
 9539     89/<- %ebp 4/r32/esp
 9540     # . save registers
 9541     50/push-eax
 9542     51/push-ecx
 9543     # ecx = primitive
 9544     8b/-> *(ebp+0x10) 1/r32/ecx
 9545     # emit primitive name
 9546     (emit-indent *(ebp+8) *Curr-block-depth)
 9547     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
 9548     # emit rm32 if necessary
 9549     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
 9550     # emit r32 if necessary
 9551     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
 9552     # emit imm32 if necessary
 9553     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
 9554     # emit disp32 if necessary
 9555     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
 9556     (write-buffered *(ebp+8) Newline)
 9557 $emit-subx-primitive:end:
 9558     # . restore registers
 9559     59/pop-to-ecx
 9560     58/pop-to-eax
 9561     # . epilogue
 9562     89/<- %esp 5/r32/ebp
 9563     5d/pop-to-ebp
 9564     c3/return
 9565 
 9566 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9567     # . prologue
 9568     55/push-ebp
 9569     89/<- %ebp 4/r32/esp
 9570     # . save registers
 9571     50/push-eax
 9572     # if (l == 0) return
 9573     81 7/subop/compare *(ebp+0xc) 0/imm32
 9574     74/jump-if-= $emit-subx-rm32:end/disp8
 9575     # var v/eax: (handle var)
 9576     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9577     (emit-subx-var-as-rm32 *(ebp+8) %eax)
 9578 $emit-subx-rm32:end:
 9579     # . restore registers
 9580     58/pop-to-eax
 9581     # . epilogue
 9582     89/<- %esp 5/r32/ebp
 9583     5d/pop-to-ebp
 9584     c3/return
 9585 
 9586 get-stmt-operand-from-arg-location:  # stmt: (handle stmt), l: arg-location -> var/eax: (handle stmt-var)
 9587     # . prologue
 9588     55/push-ebp
 9589     89/<- %ebp 4/r32/esp
 9590     # . save registers
 9591     51/push-ecx
 9592     # eax = l
 9593     8b/-> *(ebp+0xc) 0/r32/eax
 9594     # ecx = stmt
 9595     8b/-> *(ebp+8) 1/r32/ecx
 9596     # if (l == 1) return stmt->inouts
 9597     {
 9598       3d/compare-eax-and 1/imm32
 9599       75/jump-if-!= break/disp8
 9600 $get-stmt-operand-from-arg-location:1:
 9601       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9602       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9603     }
 9604     # if (l == 2) return stmt->inouts->next
 9605     {
 9606       3d/compare-eax-and 2/imm32
 9607       75/jump-if-!= break/disp8
 9608 $get-stmt-operand-from-arg-location:2:
 9609       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9610       8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 9611       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9612     }
 9613     # if (l == 3) return stmt->outputs
 9614     {
 9615       3d/compare-eax-and 3/imm32
 9616       75/jump-if-!= break/disp8
 9617 $get-stmt-operand-from-arg-location:3:
 9618       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 9619       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9620     }
 9621     # abort
 9622     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
 9623 $get-stmt-operand-from-arg-location:end:
 9624     # . restore registers
 9625     59/pop-to-ecx
 9626     # . epilogue
 9627     89/<- %esp 5/r32/ebp
 9628     5d/pop-to-ebp
 9629     c3/return
 9630 
 9631 $get-stmt-operand-from-arg-location:abort:
 9632     # error("invalid arg-location " eax)
 9633     (write-buffered Stderr "invalid arg-location ")
 9634     (print-int32-buffered Stderr %eax)
 9635     (write-buffered Stderr Newline)
 9636     (flush Stderr)
 9637     # . syscall(exit, 1)
 9638     bb/copy-to-ebx  1/imm32
 9639     b8/copy-to-eax  1/imm32/exit
 9640     cd/syscall  0x80/imm8
 9641     # never gets here
 9642 
 9643 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9644     # . prologue
 9645     55/push-ebp
 9646     89/<- %ebp 4/r32/esp
 9647     # . save registers
 9648     50/push-eax
 9649     51/push-ecx
 9650     # if (location == 0) return
 9651     81 7/subop/compare *(ebp+0xc) 0/imm32
 9652     0f 84/jump-if-= $emit-subx-r32:end/disp32
 9653     # var v/eax: (handle var)
 9654     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9655     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9656     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
 9657     (write-buffered *(ebp+8) Space)
 9658     (print-int32-buffered *(ebp+8) *eax)
 9659     (write-buffered *(ebp+8) "/r32")
 9660 $emit-subx-r32:end:
 9661     # . restore registers
 9662     59/pop-to-ecx
 9663     58/pop-to-eax
 9664     # . epilogue
 9665     89/<- %esp 5/r32/ebp
 9666     5d/pop-to-ebp
 9667     c3/return
 9668 
 9669 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9670     # . prologue
 9671     55/push-ebp
 9672     89/<- %ebp 4/r32/esp
 9673     # . save registers
 9674     50/push-eax
 9675     51/push-ecx
 9676     # if (location == 0) return
 9677     81 7/subop/compare *(ebp+0xc) 0/imm32
 9678     74/jump-if-= $emit-subx-imm32:end/disp8
 9679     # var v/eax: (handle var)
 9680     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9681     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9682     (write-buffered *(ebp+8) Space)
 9683     (write-buffered *(ebp+8) *eax)  # Var-name
 9684     (write-buffered *(ebp+8) "/imm32")
 9685 $emit-subx-imm32:end:
 9686     # . restore registers
 9687     59/pop-to-ecx
 9688     58/pop-to-eax
 9689     # . epilogue
 9690     89/<- %esp 5/r32/ebp
 9691     5d/pop-to-ebp
 9692     c3/return
 9693 
 9694 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9695     # . prologue
 9696     55/push-ebp
 9697     89/<- %ebp 4/r32/esp
 9698     # . save registers
 9699     50/push-eax
 9700     51/push-ecx
 9701     # if (location == 0) return
 9702     81 7/subop/compare *(ebp+0xc) 0/imm32
 9703     0f 84/jump-if-= $emit-subx-disp32:end/disp32
 9704     # var v/eax: (handle var)
 9705     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9706     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9707     (write-buffered *(ebp+8) Space)
 9708     (write-buffered *(ebp+8) *eax)  # Var-name
 9709     # hack: if instruction operation starts with "break", emit ":break"
 9710     # var name/ecx: (addr array byte) = stmt->operation
 9711     8b/-> *(ebp+0x10) 0/r32/eax
 9712     8b/-> *(eax+4) 1/r32/ecx
 9713     {
 9714       (string-starts-with? %ecx "break")  # => eax
 9715       3d/compare-eax-and 0/imm32/false
 9716       74/jump-if-= break/disp8
 9717       (write-buffered *(ebp+8) ":break")
 9718     }
 9719     # hack: if instruction operation starts with "loop", emit ":loop"
 9720     {
 9721       (string-starts-with? %ecx "loop")  # => eax
 9722       3d/compare-eax-and 0/imm32/false
 9723       74/jump-if-= break/disp8
 9724       (write-buffered *(ebp+8) ":loop")
 9725     }
 9726     (write-buffered *(ebp+8) "/disp32")
 9727 $emit-subx-disp32:end:
 9728     # . restore registers
 9729     59/pop-to-ecx
 9730     58/pop-to-eax
 9731     # . epilogue
 9732     89/<- %esp 5/r32/ebp
 9733     5d/pop-to-ebp
 9734     c3/return
 9735 
 9736 emit-subx-call:  # out: (addr buffered-file), stmt: (handle stmt), callee: (handle function)
 9737     # . prologue
 9738     55/push-ebp
 9739     89/<- %ebp 4/r32/esp
 9740     # . save registers
 9741     51/push-ecx
 9742     #
 9743     (emit-indent *(ebp+8) *Curr-block-depth)
 9744     (write-buffered *(ebp+8) "(")
 9745     # - emit function name
 9746     8b/-> *(ebp+0x10) 1/r32/ecx
 9747     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
 9748     # - emit arguments
 9749     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9750     8b/-> *(ebp+0xc) 1/r32/ecx
 9751     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9752     {
 9753       # if (curr == null) break
 9754       81 7/subop/compare %ecx 0/imm32
 9755       74/jump-if-= break/disp8
 9756       #
 9757       (emit-subx-call-operand *(ebp+8) %ecx)
 9758       # curr = curr->next
 9759       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9760       eb/jump loop/disp8
 9761     }
 9762     #
 9763     (write-buffered *(ebp+8) ")\n")
 9764 $emit-subx-call:end:
 9765     # . restore registers
 9766     59/pop-to-ecx
 9767     # . epilogue
 9768     89/<- %esp 5/r32/ebp
 9769     5d/pop-to-ebp
 9770     c3/return
 9771 
 9772 # like a function call, except we have no idea what function it is
 9773 # we hope it's defined in SubX and that the types are ok
 9774 emit-hailmary-call:  # out: (addr buffered-file), stmt: (handle stmt)
 9775     # . prologue
 9776     55/push-ebp
 9777     89/<- %ebp 4/r32/esp
 9778     # . save registers
 9779     51/push-ecx
 9780     #
 9781     (emit-indent *(ebp+8) *Curr-block-depth)
 9782     (write-buffered *(ebp+8) "(")
 9783     # ecx = stmt
 9784     8b/-> *(ebp+0xc) 1/r32/ecx
 9785     # - emit function name
 9786     (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
 9787     # - emit arguments
 9788     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9789     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9790     {
 9791       # if (curr == null) break
 9792       81 7/subop/compare %ecx 0/imm32
 9793       74/jump-if-= break/disp8
 9794       #
 9795       (emit-subx-call-operand *(ebp+8) %ecx)
 9796       # curr = curr->next
 9797       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9798       eb/jump loop/disp8
 9799     }
 9800     #
 9801     (write-buffered *(ebp+8) ")\n")
 9802 $emit-hailmary-call:end:
 9803     # . restore registers
 9804     59/pop-to-ecx
 9805     # . epilogue
 9806     89/<- %esp 5/r32/ebp
 9807     5d/pop-to-ebp
 9808     c3/return
 9809 
 9810 emit-subx-call-operand:  # out: (addr buffered-file), s: (handle stmt-var)
 9811     # shares code with emit-subx-var-as-rm32
 9812     # . prologue
 9813     55/push-ebp
 9814     89/<- %ebp 4/r32/esp
 9815     # . save registers
 9816     50/push-eax
 9817     51/push-ecx
 9818     56/push-esi
 9819     # ecx = s
 9820     8b/-> *(ebp+0xc) 1/r32/ecx
 9821     # var operand/esi: (handle var) = s->value
 9822     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9823     # if (operand->register && s->is-deref?) emit "*__"
 9824     {
 9825 $emit-subx-call-operand:check-for-register-indirect:
 9826       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9827       74/jump-if-= break/disp8
 9828       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9829       74/jump-if-= break/disp8
 9830 $emit-subx-call-operand:register-indirect:
 9831       (write-buffered *(ebp+8) " *")
 9832       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9833       e9/jump $emit-subx-call-operand:end/disp32
 9834     }
 9835     # if (operand->register && !s->is-deref?) emit "%__"
 9836     {
 9837 $emit-subx-call-operand:check-for-register-direct:
 9838       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9839       74/jump-if-= break/disp8
 9840       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9841       75/jump-if-!= break/disp8
 9842 $emit-subx-call-operand:register-direct:
 9843       (write-buffered *(ebp+8) " %")
 9844       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9845       e9/jump $emit-subx-call-operand:end/disp32
 9846     }
 9847     # else if (operand->stack-offset) emit "*(ebp+__)"
 9848     {
 9849       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
 9850       74/jump-if-= break/disp8
 9851 $emit-subx-call-operand:stack:
 9852       (write-buffered *(ebp+8) Space)
 9853       (write-buffered *(ebp+8) "*(ebp+")
 9854       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
 9855       (write-buffered *(ebp+8) ")")
 9856       e9/jump $emit-subx-call-operand:end/disp32
 9857     }
 9858     # else if (operand->type == literal) emit "__"
 9859     {
 9860       8b/-> *(esi+4) 0/r32/eax  # Var-type
 9861       81 7/subop/compare *eax 0/imm32  # Tree-left
 9862       75/jump-if-!= break/disp8
 9863 $emit-subx-call-operand:literal:
 9864       (write-buffered *(ebp+8) Space)
 9865       (write-buffered *(ebp+8) *esi)
 9866     }
 9867 $emit-subx-call-operand:end:
 9868     # . restore registers
 9869     5e/pop-to-esi
 9870     59/pop-to-ecx
 9871     58/pop-to-eax
 9872     # . epilogue
 9873     89/<- %esp 5/r32/ebp
 9874     5d/pop-to-ebp
 9875     c3/return
 9876 
 9877 emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (handle stmt-var)
 9878     # . prologue
 9879     55/push-ebp
 9880     89/<- %ebp 4/r32/esp
 9881     # . save registers
 9882     50/push-eax
 9883     51/push-ecx
 9884     56/push-esi
 9885     # ecx = s
 9886     8b/-> *(ebp+0xc) 1/r32/ecx
 9887     # var operand/esi: (handle var) = s->value
 9888     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9889     # if (operand->register && s->is-deref?) emit "*__"
 9890     {
 9891 $emit-subx-var-as-rm32:check-for-register-indirect:
 9892       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9893       74/jump-if-= break/disp8
 9894       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9895       74/jump-if-= break/disp8
 9896 $emit-subx-var-as-rm32:register-indirect:
 9897       (write-buffered *(ebp+8) " *")
 9898       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9899     }
 9900     # if (operand->register && !s->is-deref?) emit "%__"
 9901     {
 9902 $emit-subx-var-as-rm32:check-for-register-direct:
 9903       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9904       74/jump-if-= break/disp8
 9905       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9906       75/jump-if-!= break/disp8
 9907 $emit-subx-var-as-rm32:register-direct:
 9908       (write-buffered *(ebp+8) " %")
 9909       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9910     }
 9911     # else if (operand->stack-offset) emit "*(ebp+__)"
 9912     {
 9913       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
 9914       74/jump-if-= break/disp8
 9915 $emit-subx-var-as-rm32:stack:
 9916       (write-buffered *(ebp+8) Space)
 9917       (write-buffered *(ebp+8) "*(ebp+")
 9918       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
 9919       (write-buffered *(ebp+8) ")")
 9920     }
 9921 $emit-subx-var-as-rm32:end:
 9922     # . restore registers
 9923     5e/pop-to-esi
 9924     59/pop-to-ecx
 9925     58/pop-to-eax
 9926     # . epilogue
 9927     89/<- %esp 5/r32/ebp
 9928     5d/pop-to-ebp
 9929     c3/return
 9930 
 9931 find-matching-function:  # functions: (addr function), stmt: (handle stmt) -> result/eax: (handle function)
 9932     # . prologue
 9933     55/push-ebp
 9934     89/<- %ebp 4/r32/esp
 9935     # . save registers
 9936     51/push-ecx
 9937     # var curr/ecx: (handle function) = functions
 9938     8b/-> *(ebp+8) 1/r32/ecx
 9939     {
 9940       # if (curr == null) break
 9941       81 7/subop/compare %ecx 0/imm32
 9942       74/jump-if-= break/disp8
 9943       # if match(stmt, curr) return curr
 9944       {
 9945         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
 9946         3d/compare-eax-and 0/imm32/false
 9947         74/jump-if-= break/disp8
 9948         89/<- %eax 1/r32/ecx
 9949         eb/jump $find-matching-function:end/disp8
 9950       }
 9951       # curr = curr->next
 9952       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 9953       eb/jump loop/disp8
 9954     }
 9955     # return null
 9956     b8/copy-to-eax 0/imm32
 9957 $find-matching-function:end:
 9958     # . restore registers
 9959     59/pop-to-ecx
 9960     # . epilogue
 9961     89/<- %esp 5/r32/ebp
 9962     5d/pop-to-ebp
 9963     c3/return
 9964 
 9965 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle stmt) -> result/eax: (handle primitive)
 9966     # . prologue
 9967     55/push-ebp
 9968     89/<- %ebp 4/r32/esp
 9969     # . save registers
 9970     51/push-ecx
 9971     # var curr/ecx: (handle primitive) = primitives
 9972     8b/-> *(ebp+8) 1/r32/ecx
 9973     {
 9974 $find-matching-primitive:loop:
 9975       # if (curr == null) break
 9976       81 7/subop/compare %ecx 0/imm32
 9977       0f 84/jump-if-= break/disp32
 9978 #?       (write-buffered Stderr "prim: ")
 9979 #?       (write-buffered Stderr *ecx)  # Primitive-name
 9980 #?       (write-buffered Stderr " => ")
 9981 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
 9982 #?       (write-buffered Stderr Newline)
 9983 #?       (flush Stderr)
 9984       # if match(curr, stmt) return curr
 9985       {
 9986         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
 9987         3d/compare-eax-and 0/imm32/false
 9988         74/jump-if-= break/disp8
 9989         89/<- %eax 1/r32/ecx
 9990         eb/jump $find-matching-primitive:end/disp8
 9991       }
 9992 $find-matching-primitive:next-primitive:
 9993       # curr = curr->next
 9994       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
 9995       e9/jump loop/disp32
 9996     }
 9997     # return null
 9998     b8/copy-to-eax 0/imm32
 9999 $find-matching-primitive:end:
10000     # . restore registers
10001     59/pop-to-ecx
10002     # . epilogue
10003     89/<- %esp 5/r32/ebp
10004     5d/pop-to-ebp
10005     c3/return
10006 
10007 mu-stmt-matches-function?:  # stmt: (handle stmt), function: (handle function) -> result/eax: boolean
10008     # . prologue
10009     55/push-ebp
10010     89/<- %ebp 4/r32/esp
10011     # . save registers
10012     51/push-ecx
10013     # return function->name == stmt->operation
10014     8b/-> *(ebp+8) 1/r32/ecx
10015     8b/-> *(ebp+0xc) 0/r32/eax
10016     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
10017 $mu-stmt-matches-function?:end:
10018     # . restore registers
10019     59/pop-to-ecx
10020     # . epilogue
10021     89/<- %esp 5/r32/ebp
10022     5d/pop-to-ebp
10023     c3/return
10024 
10025 mu-stmt-matches-primitive?:  # stmt: (handle stmt), primitive: (handle primitive) -> result/eax: boolean
10026     # A mu stmt matches a primitive if the name matches, all the inout vars
10027     # match, and all the output vars match.
10028     # Vars match if types match and registers match.
10029     # In addition, a stmt output matches a primitive's output if types match
10030     # and the primitive has a wildcard register.
10031     # . prologue
10032     55/push-ebp
10033     89/<- %ebp 4/r32/esp
10034     # . save registers
10035     51/push-ecx
10036     52/push-edx
10037     53/push-ebx
10038     56/push-esi
10039     57/push-edi
10040     # ecx = stmt
10041     8b/-> *(ebp+8) 1/r32/ecx
10042     # edx = primitive
10043     8b/-> *(ebp+0xc) 2/r32/edx
10044     {
10045 $mu-stmt-matches-primitive?:check-name:
10046       # if (primitive->name != stmt->operation) return false
10047       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
10048       3d/compare-eax-and 0/imm32/false
10049       75/jump-if-!= break/disp8
10050       b8/copy-to-eax 0/imm32
10051       e9/jump $mu-stmt-matches-primitive?:end/disp32
10052     }
10053 $mu-stmt-matches-primitive?:check-inouts:
10054     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
10055     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
10056     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
10057     {
10058       # if (curr == 0 && curr2 == 0) move on to check outputs
10059       {
10060         81 7/subop/compare %esi 0/imm32
10061         75/jump-if-!= break/disp8
10062 $mu-stmt-matches-primitive?:stmt-inout-is-null:
10063         {
10064           81 7/subop/compare %edi 0/imm32
10065           75/jump-if-!= break/disp8
10066           #
10067           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
10068         }
10069         # return false
10070         b8/copy-to-eax 0/imm32/false
10071         e9/jump $mu-stmt-matches-primitive?:end/disp32
10072       }
10073       # if (curr2 == 0) return false
10074       {
10075         81 7/subop/compare %edi 0/imm32
10076         75/jump-if-!= break/disp8
10077 $mu-stmt-matches-primitive?:prim-inout-is-null:
10078         b8/copy-to-eax 0/imm32/false
10079         e9/jump $mu-stmt-matches-primitive?:end/disp32
10080       }
10081       # if (curr != curr2) return false
10082       {
10083         (operand-matches-primitive? %esi *edi)  # List-value => eax
10084         3d/compare-eax-and 0/imm32/false
10085         75/jump-if-!= break/disp8
10086         b8/copy-to-eax 0/imm32/false
10087         e9/jump $mu-stmt-matches-primitive?:end/disp32
10088       }
10089       # curr=curr->next
10090       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
10091       # curr2=curr2->next
10092       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
10093       eb/jump loop/disp8
10094     }
10095 $mu-stmt-matches-primitive?:check-outputs:
10096     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
10097     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
10098     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
10099     {
10100       # if (curr == 0) return (curr2 == 0)
10101       {
10102 $mu-stmt-matches-primitive?:check-output:
10103         81 7/subop/compare %esi 0/imm32
10104         75/jump-if-!= break/disp8
10105         {
10106           81 7/subop/compare %edi 0/imm32
10107           75/jump-if-!= break/disp8
10108           # return true
10109           b8/copy-to-eax 1/imm32
10110           e9/jump $mu-stmt-matches-primitive?:end/disp32
10111         }
10112         # return false
10113         b8/copy-to-eax 0/imm32
10114         e9/jump $mu-stmt-matches-primitive?:end/disp32
10115       }
10116       # if (curr2 == 0) return false
10117       {
10118         81 7/subop/compare %edi 0/imm32
10119         75/jump-if-!= break/disp8
10120         b8/copy-to-eax 0/imm32
10121         e9/jump $mu-stmt-matches-primitive?:end/disp32
10122       }
10123       # if (curr != curr2) return false
10124       {
10125         (operand-matches-primitive? %esi *edi)  # List-value => eax
10126         3d/compare-eax-and 0/imm32/false
10127         75/jump-if-!= break/disp8
10128         b8/copy-to-eax 0/imm32
10129         e9/jump $mu-stmt-matches-primitive?:end/disp32
10130       }
10131       # curr=curr->next
10132       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
10133       # curr2=curr2->next
10134       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
10135       eb/jump loop/disp8
10136     }
10137 $mu-stmt-matches-primitive?:return-true:
10138     b8/copy-to-eax 1/imm32
10139 $mu-stmt-matches-primitive?:end:
10140     # . restore registers
10141     5f/pop-to-edi
10142     5e/pop-to-esi
10143     5b/pop-to-ebx
10144     5a/pop-to-edx
10145     59/pop-to-ecx
10146     # . epilogue
10147     89/<- %esp 5/r32/ebp
10148     5d/pop-to-ebp
10149     c3/return
10150 
10151 operand-matches-primitive?:  # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
10152     # . prologue
10153     55/push-ebp
10154     89/<- %ebp 4/r32/esp
10155     # . save registers
10156     51/push-ecx
10157     56/push-esi
10158     57/push-edi
10159     # ecx = s
10160     8b/-> *(ebp+8) 1/r32/ecx
10161     # var var/esi : (handle var) = s->value
10162     8b/-> *ecx 6/r32/esi  # Stmt-var-value
10163     # edi = prim-var
10164     8b/-> *(ebp+0xc) 7/r32/edi
10165 $operand-matches-primitive?:check-type:
10166     # if (var->type != prim-var->type) return false
10167     (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
10168     3d/compare-eax-and 0/imm32/false
10169     b8/copy-to-eax 0/imm32/false
10170     0f 84/jump-if-= $operand-matches-primitive?:end/disp32
10171     {
10172 $operand-matches-primitive?:check-register:
10173       # if prim-var is in memory and var is in register but dereference, match
10174       {
10175         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10176         0f 85/jump-if-!= break/disp32
10177         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10178         74/jump-if-= break/disp8
10179         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10180         74/jump-if-= break/disp8
10181         e9/jump $operand-matches-primitive?:return-true/disp32
10182       }
10183       # if prim-var is in register and var is in register but dereference, no match
10184       {
10185         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10186         0f 84/jump-if-= break/disp32
10187         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10188         0f 84/jump-if-= break/disp32
10189         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10190         74/jump-if-= break/disp8
10191         e9/jump $operand-matches-primitive?:return-false/disp32
10192       }
10193       # return false if var->register doesn't match prim-var->register
10194       {
10195         # if register addresses are equal, it's a match
10196         8b/-> *(esi+0x10) 0/r32/eax  # Var-register
10197         39/compare *(edi+0x10) 0/r32/eax  # Var-register
10198         74/jump-if-= break/disp8
10199         # if either address is 0, return false
10200         3d/compare-eax-and 0/imm32
10201         74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
10202         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10203         74/jump-if-=  $operand-matches-primitive?:return-false/disp8
10204         # if prim-var->register is wildcard, it's a match
10205         (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
10206         3d/compare-eax-and 0/imm32/false
10207         75/jump-if-!= break/disp8
10208         # if string contents aren't equal, return false
10209         (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
10210         3d/compare-eax-and 0/imm32/false
10211         74/jump-if-= $operand-matches-primitive?:return-false/disp8
10212       }
10213     }
10214 $operand-matches-primitive?:return-true:
10215     b8/copy-to-eax 1/imm32/true
10216     eb/jump $operand-matches-primitive?:end/disp8
10217 $operand-matches-primitive?:return-false:
10218     b8/copy-to-eax 0/imm32/false
10219 $operand-matches-primitive?:end:
10220     # . restore registers
10221     5f/pop-to-edi
10222     5e/pop-to-esi
10223     59/pop-to-ecx
10224     # . epilogue
10225     89/<- %esp 5/r32/ebp
10226     5d/pop-to-ebp
10227     c3/return
10228 
10229 subx-type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
10230     # . prologue
10231     55/push-ebp
10232     89/<- %ebp 4/r32/esp
10233     # . save registers
10234     51/push-ecx
10235     # var alit/ecx: boolean = is-literal-type?(a)
10236     (is-simple-mu-type? *(ebp+8) 0)  # => eax
10237     89/<- %ecx 0/r32/eax
10238     # var blit/eax: boolean = is-literal-type?(b)
10239     (is-simple-mu-type? *(ebp+0xc) 0)  # => eax
10240     # return alit == blit
10241     39/compare %eax 1/r32/ecx
10242     0f 94/set-byte-if-= %al
10243     81 4/subop/and %eax 0xff/imm32
10244 $subx-type-equal?:end:
10245     # . restore registers
10246     59/pop-to-ecx
10247     # . epilogue
10248     89/<- %esp 5/r32/ebp
10249     5d/pop-to-ebp
10250     c3/return
10251 
10252 is-simple-mu-type?:  # a: (handle tree type-id), n: type-id -> result/eax: boolean
10253     # . prologue
10254     55/push-ebp
10255     89/<- %ebp 4/r32/esp
10256     # . save registers
10257     51/push-ecx
10258     # ecx = n
10259     8b/-> *(ebp+0xc) 1/r32/ecx
10260     # return (a->value == n)
10261     8b/-> *(ebp+8) 0/r32/eax
10262     39/compare *eax 1/r32/ecx  # Atom-type
10263     0f 94/set-byte-if-= %al
10264     81 4/subop/and %eax 0xff/imm32
10265 $is-simple-mu-type?:end:
10266     # . restore registers
10267     59/pop-to-ecx
10268     # . epilogue
10269     89/<- %esp 5/r32/ebp
10270     5d/pop-to-ebp
10271     c3/return
10272 
10273 test-emit-subx-stmt-primitive:
10274     # Primitive operation on a variable on the stack.
10275     #   increment foo
10276     # =>
10277     #   ff 0/subop/increment *(ebp-8)
10278     #
10279     # There's a variable on the var stack as follows:
10280     #   name: 'foo'
10281     #   type: int
10282     #   stack-offset: -8
10283     #
10284     # There's a primitive with this info:
10285     #   name: 'increment'
10286     #   inouts: int/mem
10287     #   value: 'ff 0/subop/increment'
10288     #
10289     # There's nothing in functions.
10290     #
10291     # . prologue
10292     55/push-ebp
10293     89/<- %ebp 4/r32/esp
10294     # setup
10295     (clear-stream _test-output-stream)
10296     (clear-stream $_test-output-buffered-file->buffer)
10297     # var type/ecx: (handle tree type-id) = int
10298     68/push 0/imm32/right/null
10299     68/push 1/imm32/left/int
10300     89/<- %ecx 4/r32/esp
10301     # var var-foo/ecx: var
10302     68/push 0/imm32/no-register
10303     68/push -8/imm32/stack-offset
10304     68/push 1/imm32/block-depth
10305     51/push-ecx
10306     68/push "foo"/imm32
10307     89/<- %ecx 4/r32/esp
10308     # var operand/ebx: (handle stmt-var)
10309     68/push 0/imm32/is-deref:false
10310     68/push 0/imm32/next
10311     51/push-ecx/var-foo
10312     89/<- %ebx 4/r32/esp
10313     # var stmt/esi: statement
10314     68/push 0/imm32/next
10315     68/push 0/imm32/outputs
10316     53/push-ebx/operands
10317     68/push "increment"/imm32/operation
10318     68/push 1/imm32
10319     89/<- %esi 4/r32/esp
10320     # var primitives/ebx: primitive
10321     68/push 0/imm32/next
10322     68/push 0/imm32/output-is-write-only
10323     68/push 0/imm32/no-disp32
10324     68/push 0/imm32/no-imm32
10325     68/push 0/imm32/no-r32
10326     68/push 1/imm32/rm32-is-first-inout
10327     68/push "ff 0/subop/increment"/imm32/subx-name
10328     68/push 0/imm32/outputs
10329     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
10330     68/push "increment"/imm32/name
10331     89/<- %ebx 4/r32/esp
10332     # convert
10333     c7 0/subop/copy *Curr-block-depth 0/imm32
10334     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10335     (flush _test-output-buffered-file)
10336 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10342     # check output
10343     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
10344     # . epilogue
10345     89/<- %esp 5/r32/ebp
10346     5d/pop-to-ebp
10347     c3/return
10348 
10349 test-emit-subx-stmt-primitive-register:
10350     # Primitive operation on a variable in a register.
10351     #   foo <- increment
10352     # =>
10353     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10354     #
10355     # There's a variable on the var stack as follows:
10356     #   name: 'foo'
10357     #   type: int
10358     #   register: 'eax'
10359     #
10360     # There's a primitive with this info:
10361     #   name: 'increment'
10362     #   out: int/reg
10363     #   value: 'ff 0/subop/increment'
10364     #
10365     # There's nothing in functions.
10366     #
10367     # . prologue
10368     55/push-ebp
10369     89/<- %ebp 4/r32/esp
10370     # setup
10371     (clear-stream _test-output-stream)
10372     (clear-stream $_test-output-buffered-file->buffer)
10373     # var type/ecx: (handle tree type-id) = int
10374     68/push 0/imm32/right/null
10375     68/push 1/imm32/left/int
10376     89/<- %ecx 4/r32/esp
10377     # var var-foo/ecx: var in eax
10378     68/push "eax"/imm32/register
10379     68/push 0/imm32/no-stack-offset
10380     68/push 1/imm32/block-depth
10381     51/push-ecx
10382     68/push "foo"/imm32
10383     89/<- %ecx 4/r32/esp
10384     # var operand/ebx: (handle stmt-var)
10385     68/push 0/imm32/is-deref:false
10386     68/push 0/imm32/next
10387     51/push-ecx/var-foo
10388     89/<- %ebx 4/r32/esp
10389     # var stmt/esi: statement
10390     68/push 0/imm32/next
10391     53/push-ebx/outputs
10392     68/push 0/imm32/inouts
10393     68/push "increment"/imm32/operation
10394     68/push 1/imm32
10395     89/<- %esi 4/r32/esp
10396     # var formal-var/ebx: var in any register
10397     68/push Any-register/imm32
10398     68/push 0/imm32/no-stack-offset
10399     68/push 1/imm32/block-depth
10400     ff 6/subop/push *(ecx+4)  # Var-type
10401     68/push "dummy"/imm32
10402     89/<- %ebx 4/r32/esp
10403     # var operand/ebx: (handle stmt-var)
10404     68/push 0/imm32/is-deref:false
10405     68/push 0/imm32/next
10406     53/push-ebx/formal-var
10407     89/<- %ebx 4/r32/esp
10408     # var primitives/ebx: primitive
10409     68/push 0/imm32/next
10410     68/push 0/imm32/output-is-write-only
10411     68/push 0/imm32/no-disp32
10412     68/push 0/imm32/no-imm32
10413     68/push 0/imm32/no-r32
10414     68/push 3/imm32/rm32-in-first-output
10415     68/push "ff 0/subop/increment"/imm32/subx-name
10416     53/push-ebx/outputs
10417     68/push 0/imm32/inouts
10418     68/push "increment"/imm32/name
10419     89/<- %ebx 4/r32/esp
10420     # convert
10421     c7 0/subop/copy *Curr-block-depth 0/imm32
10422     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10423     (flush _test-output-buffered-file)
10424 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10430     # check output
10431     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
10432     # . epilogue
10433     89/<- %esp 5/r32/ebp
10434     5d/pop-to-ebp
10435     c3/return
10436 
10437 test-emit-subx-stmt-select-primitive:
10438     # Select the right primitive between overloads.
10439     #   foo <- increment
10440     # =>
10441     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10442     #
10443     # There's a variable on the var stack as follows:
10444     #   name: 'foo'
10445     #   type: int
10446     #   register: 'eax'
10447     #
10448     # There's two primitives, as follows:
10449     #   - name: 'increment'
10450     #     out: int/reg
10451     #     value: 'ff 0/subop/increment'
10452     #   - name: 'increment'
10453     #     inout: int/mem
10454     #     value: 'ff 0/subop/increment'
10455     #
10456     # There's nothing in functions.
10457     #
10458     # . prologue
10459     55/push-ebp
10460     89/<- %ebp 4/r32/esp
10461     # setup
10462     (clear-stream _test-output-stream)
10463     (clear-stream $_test-output-buffered-file->buffer)
10464     # var type/ecx: (handle tree type-id) = int
10465     68/push 0/imm32/right/null
10466     68/push 1/imm32/left/int
10467     89/<- %ecx 4/r32/esp
10468     # var var-foo/ecx: var in eax
10469     68/push "eax"/imm32/register
10470     68/push 0/imm32/no-stack-offset
10471     68/push 1/imm32/block-depth
10472     51/push-ecx
10473     68/push "foo"/imm32
10474     89/<- %ecx 4/r32/esp
10475     # var real-outputs/edi: (handle stmt-var)
10476     68/push 0/imm32/is-deref:false
10477     68/push 0/imm32/next
10478     51/push-ecx/var-foo
10479     89/<- %edi 4/r32/esp
10480     # var stmt/esi: statement
10481     68/push 0/imm32/next
10482     57/push-edi/outputs
10483     68/push 0/imm32/inouts
10484     68/push "increment"/imm32/operation
10485     68/push 1/imm32
10486     89/<- %esi 4/r32/esp
10487     # var formal-var/ebx: var in any register
10488     68/push Any-register/imm32
10489     68/push 0/imm32/no-stack-offset
10490     68/push 1/imm32/block-depth
10491     ff 6/subop/push *(ecx+4)  # Var-type
10492     68/push "dummy"/imm32
10493     89/<- %ebx 4/r32/esp
10494     # var formal-outputs/ebx: (handle stmt-var)
10495     68/push 0/imm32/is-deref:false
10496     68/push 0/imm32/next
10497     53/push-ebx/formal-var
10498     89/<- %ebx 4/r32/esp
10499     # var primitive1/ebx: primitive
10500     68/push 0/imm32/next
10501     68/push 0/imm32/output-is-write-only
10502     68/push 0/imm32/no-disp32
10503     68/push 0/imm32/no-imm32
10504     68/push 0/imm32/no-r32
10505     68/push 3/imm32/rm32-in-first-output
10506     68/push "ff 0/subop/increment"/imm32/subx-name
10507     53/push-ebx/outputs/formal-outputs
10508     68/push 0/imm32/inouts
10509     68/push "increment"/imm32/name
10510     89/<- %ebx 4/r32/esp
10511     # var primitives/ebx: primitive
10512     53/push-ebx/next
10513     68/push 0/imm32/output-is-write-only
10514     68/push 0/imm32/no-disp32
10515     68/push 0/imm32/no-imm32
10516     68/push 0/imm32/no-r32
10517     68/push 1/imm32/rm32-is-first-inout
10518     68/push "ff 0/subop/increment"/imm32/subx-name
10519     68/push 0/imm32/outputs
10520     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10521     68/push "increment"/imm32/name
10522     89/<- %ebx 4/r32/esp
10523     # convert
10524     c7 0/subop/copy *Curr-block-depth 0/imm32
10525     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10526     (flush _test-output-buffered-file)
10527 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10533     # check output
10534     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
10535     # . epilogue
10536     89/<- %esp 5/r32/ebp
10537     5d/pop-to-ebp
10538     c3/return
10539 
10540 test-emit-subx-stmt-select-primitive-2:
10541     # Select the right primitive between overloads.
10542     #   foo <- increment
10543     # =>
10544     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10545     #
10546     # There's a variable on the var stack as follows:
10547     #   name: 'foo'
10548     #   type: int
10549     #   register: 'eax'
10550     #
10551     # There's two primitives, as follows:
10552     #   - name: 'increment'
10553     #     out: int/reg
10554     #     value: 'ff 0/subop/increment'
10555     #   - name: 'increment'
10556     #     inout: int/mem
10557     #     value: 'ff 0/subop/increment'
10558     #
10559     # There's nothing in functions.
10560     #
10561     # . prologue
10562     55/push-ebp
10563     89/<- %ebp 4/r32/esp
10564     # setup
10565     (clear-stream _test-output-stream)
10566     (clear-stream $_test-output-buffered-file->buffer)
10567     # var type/ecx: (handle tree type-id) = int
10568     68/push 0/imm32/right/null
10569     68/push 1/imm32/left/int
10570     89/<- %ecx 4/r32/esp
10571     # var var-foo/ecx: var in eax
10572     68/push "eax"/imm32/register
10573     68/push 0/imm32/no-stack-offset
10574     68/push 1/imm32/block-depth
10575     51/push-ecx
10576     68/push "foo"/imm32
10577     89/<- %ecx 4/r32/esp
10578     # var inouts/edi: (handle stmt-var)
10579     68/push 0/imm32/is-deref:false
10580     68/push 0/imm32/next
10581     51/push-ecx/var-foo
10582     89/<- %edi 4/r32/esp
10583     # var stmt/esi: statement
10584     68/push 0/imm32/next
10585     68/push 0/imm32/outputs
10586     57/push-edi/inouts
10587     68/push "increment"/imm32/operation
10588     68/push 1/imm32
10589     89/<- %esi 4/r32/esp
10590     # var formal-var/ebx: var in any register
10591     68/push Any-register/imm32
10592     68/push 0/imm32/no-stack-offset
10593     68/push 1/imm32/block-depth
10594     ff 6/subop/push *(ecx+4)  # Var-type
10595     68/push "dummy"/imm32
10596     89/<- %ebx 4/r32/esp
10597     # var operand/ebx: (handle stmt-var)
10598     68/push 0/imm32/is-deref:false
10599     68/push 0/imm32/next
10600     53/push-ebx/formal-var
10601     89/<- %ebx 4/r32/esp
10602     # var primitive1/ebx: primitive
10603     68/push 0/imm32/next
10604     68/push 0/imm32/output-is-write-only
10605     68/push 0/imm32/no-disp32
10606     68/push 0/imm32/no-imm32
10607     68/push 0/imm32/no-r32
10608     68/push 3/imm32/rm32-in-first-output
10609     68/push "ff 0/subop/increment"/imm32/subx-name
10610     53/push-ebx/outputs/formal-outputs
10611     68/push 0/imm32/inouts
10612     68/push "increment"/imm32/name
10613     89/<- %ebx 4/r32/esp
10614     # var primitives/ebx: primitive
10615     53/push-ebx/next
10616     68/push 0/imm32/output-is-write-only
10617     68/push 0/imm32/no-disp32
10618     68/push 0/imm32/no-imm32
10619     68/push 0/imm32/no-r32
10620     68/push 1/imm32/rm32-is-first-inout
10621     68/push "ff 0/subop/increment"/imm32/subx-name
10622     68/push 0/imm32/outputs
10623     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10624     68/push "increment"/imm32/name
10625     89/<- %ebx 4/r32/esp
10626     # convert
10627     c7 0/subop/copy *Curr-block-depth 0/imm32
10628     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
10629     (flush _test-output-buffered-file)
10630 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10636     # check output
10637     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
10638     # . epilogue
10639     89/<- %esp 5/r32/ebp
10640     5d/pop-to-ebp
10641     c3/return
10642 
10643 test-increment-register:
10644     # Select the right register between overloads.
10645     #   foo <- increment
10646     # =>
10647     #   50/increment-eax
10648     #
10649     # There's a variable on the var stack as follows:
10650     #   name: 'foo'
10651     #   type: int
10652     #   register: 'eax'
10653     #
10654     # Primitives are the global definitions.
10655     #
10656     # There are no functions defined.
10657     #
10658     # . prologue
10659     55/push-ebp
10660     89/<- %ebp 4/r32/esp
10661     # setup
10662     (clear-stream _test-output-stream)
10663     (clear-stream $_test-output-buffered-file->buffer)
10664     # var type/ecx: (handle tree type-id) = int
10665     68/push 0/imm32/right/null
10666     68/push 1/imm32/left/int
10667     89/<- %ecx 4/r32/esp
10668     # var var-foo/ecx: var in eax
10669     68/push "eax"/imm32/register
10670     68/push 0/imm32/no-stack-offset
10671     68/push 1/imm32/block-depth
10672     51/push-ecx
10673     68/push "foo"/imm32
10674     89/<- %ecx 4/r32/esp
10675     # var real-outputs/edi: (handle stmt-var)
10676     68/push 0/imm32/is-deref:false
10677     68/push 0/imm32/next
10678     51/push-ecx/var-foo
10679     89/<- %edi 4/r32/esp
10680     # var stmt/esi: statement
10681     68/push 0/imm32/next
10682     57/push-edi/outputs
10683     68/push 0/imm32/inouts
10684     68/push "increment"/imm32/operation
10685     68/push 1/imm32/regular-stmt
10686     89/<- %esi 4/r32/esp
10687     # convert
10688     c7 0/subop/copy *Curr-block-depth 0/imm32
10689     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10690     (flush _test-output-buffered-file)
10691 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10697     # check output
10698     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
10699     # . epilogue
10700     89/<- %esp 5/r32/ebp
10701     5d/pop-to-ebp
10702     c3/return
10703 
10704 test-increment-var:
10705     # Select the right primitive between overloads.
10706     #   foo <- increment
10707     # =>
10708     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10709     #
10710     # There's a variable on the var stack as follows:
10711     #   name: 'foo'
10712     #   type: int
10713     #   register: 'eax'
10714     #
10715     # Primitives are the global definitions.
10716     #
10717     # There are no functions defined.
10718     #
10719     # . prologue
10720     55/push-ebp
10721     89/<- %ebp 4/r32/esp
10722     # setup
10723     (clear-stream _test-output-stream)
10724     (clear-stream $_test-output-buffered-file->buffer)
10725     # var type/ecx: (handle tree type-id) = int
10726     68/push 0/imm32/right/null
10727     68/push 1/imm32/left/int
10728     89/<- %ecx 4/r32/esp
10729     # var var-foo/ecx: var in eax
10730     68/push "eax"/imm32/register
10731     68/push 0/imm32/no-stack-offset
10732     68/push 1/imm32/block-depth
10733     51/push-ecx
10734     68/push "foo"/imm32
10735     89/<- %ecx 4/r32/esp
10736     # var inouts/edi: (handle stmt-var)
10737     68/push 0/imm32/is-deref:false
10738     68/push 0/imm32/next
10739     51/push-ecx/var-foo
10740     89/<- %edi 4/r32/esp
10741     # var stmt/esi: statement
10742     68/push 0/imm32/next
10743     57/push-edi/outputs
10744     68/push 0/imm32/inouts
10745     68/push "increment"/imm32/operation
10746     68/push 1/imm32
10747     89/<- %esi 4/r32/esp
10748     # convert
10749     c7 0/subop/copy *Curr-block-depth 0/imm32
10750     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10751     (flush _test-output-buffered-file)
10752 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10758     # check output
10759     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
10760     # . epilogue
10761     89/<- %esp 5/r32/ebp
10762     5d/pop-to-ebp
10763     c3/return
10764 
10765 test-add-reg-to-reg:
10766     #   var1/reg <- add var2/reg
10767     # =>
10768     #   01/add %var1 var2
10769     #
10770     # . prologue
10771     55/push-ebp
10772     89/<- %ebp 4/r32/esp
10773     # setup
10774     (clear-stream _test-output-stream)
10775     (clear-stream $_test-output-buffered-file->buffer)
10776     # var type/ecx: (handle tree type-id) = int
10777     68/push 0/imm32/right/null
10778     68/push 1/imm32/left/int
10779     89/<- %ecx 4/r32/esp
10780     # var var-var1/ecx: var in eax
10781     68/push "eax"/imm32/register
10782     68/push 0/imm32/no-stack-offset
10783     68/push 1/imm32/block-depth
10784     51/push-ecx
10785     68/push "var1"/imm32
10786     89/<- %ecx 4/r32/esp
10787     # var var-var2/edx: var in ecx
10788     68/push "ecx"/imm32/register
10789     68/push 0/imm32/no-stack-offset
10790     68/push 1/imm32/block-depth
10791     ff 6/subop/push *(ecx+4)  # Var-type
10792     68/push "var2"/imm32
10793     89/<- %edx 4/r32/esp
10794     # var inouts/esi: (handle stmt-var) = [var2]
10795     68/push 0/imm32/is-deref:false
10796     68/push 0/imm32/next
10797     52/push-edx/var-var2
10798     89/<- %esi 4/r32/esp
10799     # var outputs/edi: (handle stmt-var) = [var1, var2]
10800     68/push 0/imm32/is-deref:false
10801     68/push 0/imm32/next
10802     51/push-ecx/var-var1
10803     89/<- %edi 4/r32/esp
10804     # var stmt/esi: statement
10805     68/push 0/imm32/next
10806     57/push-edi/outputs
10807     56/push-esi/inouts
10808     68/push "add"/imm32/operation
10809     68/push 1/imm32
10810     89/<- %esi 4/r32/esp
10811     # convert
10812     c7 0/subop/copy *Curr-block-depth 0/imm32
10813     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10814     (flush _test-output-buffered-file)
10815 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10821     # check output
10822     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
10823     # . epilogue
10824     89/<- %esp 5/r32/ebp
10825     5d/pop-to-ebp
10826     c3/return
10827 
10828 test-add-reg-to-mem:
10829     #   add-to var1 var2/reg
10830     # =>
10831     #   01/add *(ebp+__) var2
10832     #
10833     # . prologue
10834     55/push-ebp
10835     89/<- %ebp 4/r32/esp
10836     # setup
10837     (clear-stream _test-output-stream)
10838     (clear-stream $_test-output-buffered-file->buffer)
10839     # var type/ecx: (handle tree type-id) = int
10840     68/push 0/imm32/right/null
10841     68/push 1/imm32/left/int
10842     89/<- %ecx 4/r32/esp
10843     # var var-var1/ecx: var
10844     68/push 0/imm32/no-register
10845     68/push 8/imm32/stack-offset
10846     68/push 1/imm32/block-depth
10847     51/push-ecx
10848     68/push "var1"/imm32
10849     89/<- %ecx 4/r32/esp
10850     # var var-var2/edx: var in ecx
10851     68/push "ecx"/imm32/register
10852     68/push 0/imm32/no-stack-offset
10853     68/push 1/imm32/block-depth
10854     ff 6/subop/push *(ecx+4)  # Var-type
10855     68/push "var2"/imm32
10856     89/<- %edx 4/r32/esp
10857     # var inouts/esi: (handle stmt-var) = [var2]
10858     68/push 0/imm32/is-deref:false
10859     68/push 0/imm32/next
10860     52/push-edx/var-var2
10861     89/<- %esi 4/r32/esp
10862     # var inouts = (handle stmt-var) = [var1, var2]
10863     56/push-esi/next
10864     51/push-ecx/var-var1
10865     89/<- %esi 4/r32/esp
10866     # var stmt/esi: statement
10867     68/push 0/imm32/next
10868     68/push 0/imm32/outputs
10869     56/push-esi/inouts
10870     68/push "add-to"/imm32/operation
10871     68/push 1/imm32
10872     89/<- %esi 4/r32/esp
10873     # convert
10874     c7 0/subop/copy *Curr-block-depth 0/imm32
10875     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10876     (flush _test-output-buffered-file)
10877 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10883     # check output
10884     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
10885     # . epilogue
10886     89/<- %esp 5/r32/ebp
10887     5d/pop-to-ebp
10888     c3/return
10889 
10890 test-add-mem-to-reg:
10891     #   var1/reg <- add var2
10892     # =>
10893     #   03/add *(ebp+__) var1
10894     #
10895     # . prologue
10896     55/push-ebp
10897     89/<- %ebp 4/r32/esp
10898     # setup
10899     (clear-stream _test-output-stream)
10900     (clear-stream $_test-output-buffered-file->buffer)
10901     # var type/ecx: (handle tree type-id) = int
10902     68/push 0/imm32/right/null
10903     68/push 1/imm32/left/int
10904     89/<- %ecx 4/r32/esp
10905     # var var-var1/ecx: var in eax
10906     68/push "eax"/imm32/register
10907     68/push 0/imm32/no-stack-offset
10908     68/push 1/imm32/block-depth
10909     51/push-ecx
10910     68/push "var1"/imm32
10911     89/<- %ecx 4/r32/esp
10912     # var var-var2/edx: var
10913     68/push 0/imm32/no-register
10914     68/push 8/imm32/stack-offset
10915     68/push 1/imm32/block-depth
10916     ff 6/subop/push *(ecx+4)  # Var-type
10917     68/push "var2"/imm32
10918     89/<- %edx 4/r32/esp
10919     # var inouts/esi: (handle stmt-var) = [var2]
10920     68/push 0/imm32/is-deref:false
10921     68/push 0/imm32/next
10922     52/push-edx/var-var2
10923     89/<- %esi 4/r32/esp
10924     # var outputs/edi = (handle stmt-var) = [var1]
10925     68/push 0/imm32/is-deref:false
10926     68/push 0/imm32/next
10927     51/push-ecx/var-var1
10928     89/<- %edi 4/r32/esp
10929     # var stmt/esi: statement
10930     68/push 0/imm32/next
10931     57/push-edi/outputs
10932     56/push-esi/inouts
10933     68/push "add"/imm32/operation
10934     68/push 1/imm32
10935     89/<- %esi 4/r32/esp
10936     # convert
10937     c7 0/subop/copy *Curr-block-depth 0/imm32
10938     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
10939     (flush _test-output-buffered-file)
10940 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
10946     # check output
10947     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
10948     # . epilogue
10949     89/<- %esp 5/r32/ebp
10950     5d/pop-to-ebp
10951     c3/return
10952 
10953 test-add-literal-to-eax:
10954     #   var1/eax <- add 0x34
10955     # =>
10956     #   05/add-to-eax 0x34/imm32
10957     #
10958     # . prologue
10959     55/push-ebp
10960     89/<- %ebp 4/r32/esp
10961     # setup
10962     (clear-stream _test-output-stream)
10963     (clear-stream $_test-output-buffered-file->buffer)
10964     # var type/ecx: (handle tree type-id) = int
10965     68/push 0/imm32/right/null
10966     68/push 1/imm32/left/int
10967     89/<- %ecx 4/r32/esp
10968     # var var-var1/ecx: var in eax
10969     68/push "eax"/imm32/register
10970     68/push 0/imm32/no-stack-offset
10971     68/push 1/imm32/block-depth
10972     51/push-ecx
10973     68/push "var1"/imm32
10974     89/<- %ecx 4/r32/esp
10975     # var type/edx: (handle tree type-id) = literal
10976     68/push 0/imm32/right/null
10977     68/push 0/imm32/left/literal
10978     89/<- %edx 4/r32/esp
10979     # var var-var2/edx: var literal
10980     68/push 0/imm32/no-register
10981     68/push 0/imm32/no-stack-offset
10982     68/push 1/imm32/block-depth
10983     52/push-edx
10984     68/push "0x34"/imm32
10985     89/<- %edx 4/r32/esp
10986     # var inouts/esi: (handle stmt-var) = [var2]
10987     68/push 0/imm32/is-deref:false
10988     68/push 0/imm32/next
10989     52/push-edx/var-var2
10990     89/<- %esi 4/r32/esp
10991     # var outputs/edi: (handle stmt-var) = [var1]
10992     68/push 0/imm32/is-deref:false
10993     68/push 0/imm32/next
10994     51/push-ecx/var-var1
10995     89/<- %edi 4/r32/esp
10996     # var stmt/esi: statement
10997     68/push 0/imm32/next
10998     57/push-edi/outputs
10999     56/push-esi/inouts
11000     68/push "add"/imm32/operation
11001     68/push 1/imm32
11002     89/<- %esi 4/r32/esp
11003     # convert
11004     c7 0/subop/copy *Curr-block-depth 0/imm32
11005     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11006     (flush _test-output-buffered-file)
11007 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11013     # check output
11014     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
11015     # . epilogue
11016     89/<- %esp 5/r32/ebp
11017     5d/pop-to-ebp
11018     c3/return
11019 
11020 test-add-literal-to-reg:
11021     #   var1/ecx <- add 0x34
11022     # =>
11023     #   81 0/subop/add %ecx 0x34/imm32
11024     #
11025     # . prologue
11026     55/push-ebp
11027     89/<- %ebp 4/r32/esp
11028     # setup
11029     (clear-stream _test-output-stream)
11030     (clear-stream $_test-output-buffered-file->buffer)
11031     # var type/ecx: (handle tree type-id) = int
11032     68/push 0/imm32/right/null
11033     68/push 1/imm32/left/int
11034     89/<- %ecx 4/r32/esp
11035     # var var-var1/ecx: var in ecx
11036     68/push "ecx"/imm32/register
11037     68/push 0/imm32/no-stack-offset
11038     68/push 1/imm32/block-depth
11039     51/push-ecx
11040     68/push "var1"/imm32
11041     89/<- %ecx 4/r32/esp
11042     # var type/edx: (handle tree type-id) = literal
11043     68/push 0/imm32/right/null
11044     68/push 0/imm32/left/literal
11045     89/<- %edx 4/r32/esp
11046     # var var-var2/edx: var literal
11047     68/push 0/imm32/no-register
11048     68/push 0/imm32/no-stack-offset
11049     68/push 1/imm32/block-depth
11050     52/push-edx
11051     68/push "0x34"/imm32
11052     89/<- %edx 4/r32/esp
11053     # var inouts/esi: (handle stmt-var) = [var2]
11054     68/push 0/imm32/is-deref:false
11055     68/push 0/imm32/next
11056     52/push-edx/var-var2
11057     89/<- %esi 4/r32/esp
11058     # var outputs/edi: (handle stmt-var) = [var1]
11059     68/push 0/imm32/is-deref:false
11060     68/push 0/imm32/next
11061     51/push-ecx/var-var1
11062     89/<- %edi 4/r32/esp
11063     # var stmt/esi: statement
11064     68/push 0/imm32/next
11065     57/push-edi/outputs
11066     56/push-esi/inouts
11067     68/push "add"/imm32/operation
11068     68/push 1/imm32
11069     89/<- %esi 4/r32/esp
11070     # convert
11071     c7 0/subop/copy *Curr-block-depth 0/imm32
11072     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11073     (flush _test-output-buffered-file)
11074 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11080     # check output
11081     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
11082     # . epilogue
11083     89/<- %esp 5/r32/ebp
11084     5d/pop-to-ebp
11085     c3/return
11086 
11087 test-add-literal-to-mem:
11088     #   add-to var1, 0x34
11089     # =>
11090     #   81 0/subop/add %eax 0x34/imm32
11091     #
11092     # . prologue
11093     55/push-ebp
11094     89/<- %ebp 4/r32/esp
11095     # setup
11096     (clear-stream _test-output-stream)
11097     (clear-stream $_test-output-buffered-file->buffer)
11098     # var type/ecx: (handle tree type-id) = int
11099     68/push 0/imm32/right/null
11100     68/push 1/imm32/left/int
11101     89/<- %ecx 4/r32/esp
11102     # var var-var1/ecx: var
11103     68/push 0/imm32/no-register
11104     68/push 8/imm32/stack-offset
11105     68/push 1/imm32/block-depth
11106     51/push-ecx
11107     68/push "var1"/imm32
11108     89/<- %ecx 4/r32/esp
11109     # var type/edx: (handle tree type-id) = literal
11110     68/push 0/imm32/right/null
11111     68/push 0/imm32/left/literal
11112     89/<- %edx 4/r32/esp
11113     # var var-var2/edx: var literal
11114     68/push 0/imm32/no-register
11115     68/push 0/imm32/no-stack-offset
11116     68/push 1/imm32/block-depth
11117     52/push-edx
11118     68/push "0x34"/imm32
11119     89/<- %edx 4/r32/esp
11120     # var inouts/esi: (handle stmt-var) = [var2]
11121     68/push 0/imm32/is-deref:false
11122     68/push 0/imm32/next
11123     52/push-edx/var-var2
11124     89/<- %esi 4/r32/esp
11125     # var inouts = (handle stmt-var) = [var1, var2]
11126     68/push 0/imm32/is-deref:false
11127     56/push-esi/next
11128     51/push-ecx/var-var1
11129     89/<- %esi 4/r32/esp
11130     # var stmt/esi: statement
11131     68/push 0/imm32/next
11132     68/push 0/imm32/outputs
11133     56/push-esi/inouts
11134     68/push "add-to"/imm32/operation
11135     68/push 1/imm32
11136     89/<- %esi 4/r32/esp
11137     # convert
11138     c7 0/subop/copy *Curr-block-depth 0/imm32
11139     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11140     (flush _test-output-buffered-file)
11141 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11147     # check output
11148     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
11149     # . epilogue
11150     89/<- %esp 5/r32/ebp
11151     5d/pop-to-ebp
11152     c3/return
11153 
11154 test-compare-mem-with-reg:
11155     #   compare var1, var2/eax
11156     # =>
11157     #   39/compare *(ebp+___) 0/r32/eax
11158     #
11159     # . prologue
11160     55/push-ebp
11161     89/<- %ebp 4/r32/esp
11162     # setup
11163     (clear-stream _test-output-stream)
11164     (clear-stream $_test-output-buffered-file->buffer)
11165     # var type/ecx: (handle tree type-id) = int
11166     68/push 0/imm32/right/null
11167     68/push 1/imm32/left/int
11168     89/<- %ecx 4/r32/esp
11169     # var var-var2/ecx: var in eax
11170     68/push "eax"/imm32/register
11171     68/push 0/imm32/no-stack-offset
11172     68/push 1/imm32/block-depth
11173     51/push-ecx
11174     68/push "var2"/imm32
11175     89/<- %ecx 4/r32/esp
11176     # var var-var1/edx: var
11177     68/push 0/imm32/no-register
11178     68/push 8/imm32/stack-offset
11179     68/push 1/imm32/block-depth
11180     ff 6/subop/push *(ecx+4)  # Var-type
11181     68/push "var1"/imm32
11182     89/<- %edx 4/r32/esp
11183     # var inouts/esi: (handle stmt-var) = [var2]
11184     68/push 0/imm32/is-deref:false
11185     68/push 0/imm32/next
11186     51/push-ecx/var-var2
11187     89/<- %esi 4/r32/esp
11188     # inouts = [var1, var2]
11189     68/push 0/imm32/is-deref:false
11190     56/push-esi
11191     52/push-edx/var-var1
11192     89/<- %esi 4/r32/esp
11193     # var stmt/esi: statement
11194     68/push 0/imm32/next
11195     68/push 0/imm32/outputs
11196     56/push-esi/inouts
11197     68/push "compare"/imm32/operation
11198     68/push 1/imm32
11199     89/<- %esi 4/r32/esp
11200     # convert
11201     c7 0/subop/copy *Curr-block-depth 0/imm32
11202     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11203     (flush _test-output-buffered-file)
11204 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11210     # check output
11211     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
11212     # . epilogue
11213     89/<- %esp 5/r32/ebp
11214     5d/pop-to-ebp
11215     c3/return
11216 
11217 test-compare-reg-with-mem:
11218     #   compare var1/eax, var2
11219     # =>
11220     #   3b/compare *(ebp+___) 0/r32/eax
11221     #
11222     # . prologue
11223     55/push-ebp
11224     89/<- %ebp 4/r32/esp
11225     # setup
11226     (clear-stream _test-output-stream)
11227     (clear-stream $_test-output-buffered-file->buffer)
11228     # var type/ecx: (handle tree type-id) = int
11229     68/push 0/imm32/right/null
11230     68/push 1/imm32/left/int
11231     89/<- %ecx 4/r32/esp
11232     # var var-var1/ecx: var in eax
11233     68/push "eax"/imm32/register
11234     68/push 0/imm32/no-stack-offset
11235     68/push 1/imm32/block-depth
11236     51/push-ecx
11237     68/push "var1"/imm32
11238     89/<- %ecx 4/r32/esp
11239     # var var-var2/edx: var
11240     68/push 0/imm32/no-register
11241     68/push 8/imm32/stack-offset
11242     68/push 1/imm32/block-depth
11243     ff 6/subop/push *(ecx+4)  # Var-type
11244     68/push "var2"/imm32
11245     89/<- %edx 4/r32/esp
11246     # var inouts/esi: (handle stmt-var) = [var2]
11247     68/push 0/imm32/is-deref:false
11248     68/push 0/imm32/next
11249     52/push-edx/var-var2
11250     89/<- %esi 4/r32/esp
11251     # inouts = [var1, var2]
11252     68/push 0/imm32/is-deref:false
11253     56/push-esi
11254     51/push-ecx/var-var1
11255     89/<- %esi 4/r32/esp
11256     # var stmt/esi: statement
11257     68/push 0/imm32/next
11258     68/push 0/imm32/outputs
11259     56/push-esi/inouts
11260     68/push "compare"/imm32/operation
11261     68/push 1/imm32
11262     89/<- %esi 4/r32/esp
11263     # convert
11264     c7 0/subop/copy *Curr-block-depth 0/imm32
11265     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11266     (flush _test-output-buffered-file)
11267 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11273     # check output
11274     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
11275     # . epilogue
11276     89/<- %esp 5/r32/ebp
11277     5d/pop-to-ebp
11278     c3/return
11279 
11280 test-compare-mem-with-literal:
11281     #   compare var1, 0x34
11282     # =>
11283     #   81 7/subop/compare *(ebp+___) 0x34/imm32
11284     #
11285     # . prologue
11286     55/push-ebp
11287     89/<- %ebp 4/r32/esp
11288     # setup
11289     (clear-stream _test-output-stream)
11290     (clear-stream $_test-output-buffered-file->buffer)
11291     # var type/ecx: (handle tree type-id) = int
11292     68/push 0/imm32/right/null
11293     68/push 1/imm32/left/int
11294     89/<- %ecx 4/r32/esp
11295     # var var-var1/ecx: var
11296     68/push 0/imm32/no-register
11297     68/push 8/imm32/stack-offset
11298     68/push 1/imm32/block-depth
11299     51/push-ecx
11300     68/push "var1"/imm32
11301     89/<- %ecx 4/r32/esp
11302     # var type/edx: (handle tree type-id) = literal
11303     68/push 0/imm32/right/null
11304     68/push 0/imm32/left/literal
11305     89/<- %edx 4/r32/esp
11306     # var var-var2/edx: var literal
11307     68/push 0/imm32/no-register
11308     68/push 0/imm32/no-stack-offset
11309     68/push 1/imm32/block-depth
11310     52/push-edx
11311     68/push "0x34"/imm32
11312     89/<- %edx 4/r32/esp
11313     # var inouts/esi: (handle stmt-var) = [var2]
11314     68/push 0/imm32/is-deref:false
11315     68/push 0/imm32/next
11316     52/push-edx/var-var2
11317     89/<- %esi 4/r32/esp
11318     # inouts = [var1, var2]
11319     68/push 0/imm32/is-deref:false
11320     56/push-esi/next
11321     51/push-ecx/var-var1
11322     89/<- %esi 4/r32/esp
11323     # var stmt/esi: statement
11324     68/push 0/imm32/next
11325     68/push 0/imm32/outputs
11326     56/push-esi/inouts
11327     68/push "compare"/imm32/operation
11328     68/push 1/imm32
11329     89/<- %esi 4/r32/esp
11330     # convert
11331     c7 0/subop/copy *Curr-block-depth 0/imm32
11332     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11333     (flush _test-output-buffered-file)
11334 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11340     # check output
11341     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
11342     # . epilogue
11343     89/<- %esp 5/r32/ebp
11344     5d/pop-to-ebp
11345     c3/return
11346 
11347 test-compare-eax-with-literal:
11348     #   compare var1/eax 0x34
11349     # =>
11350     #   3d/compare-eax-with 0x34/imm32
11351     #
11352     # . prologue
11353     55/push-ebp
11354     89/<- %ebp 4/r32/esp
11355     # setup
11356     (clear-stream _test-output-stream)
11357     (clear-stream $_test-output-buffered-file->buffer)
11358     # var type/ecx: (handle tree type-id) = int
11359     68/push 0/imm32/right/null
11360     68/push 1/imm32/left/int
11361     89/<- %ecx 4/r32/esp
11362     # var var-var1/ecx: var in eax
11363     68/push "eax"/imm32/register
11364     68/push 0/imm32/no-stack-offset
11365     68/push 1/imm32/block-depth
11366     51/push-ecx
11367     68/push "var1"/imm32
11368     89/<- %ecx 4/r32/esp
11369     # var type/edx: (handle tree type-id) = literal
11370     68/push 0/imm32/right/null
11371     68/push 0/imm32/left/literal
11372     89/<- %edx 4/r32/esp
11373     # var var-var2/edx: var literal
11374     68/push 0/imm32/no-register
11375     68/push 0/imm32/no-stack-offset
11376     68/push 1/imm32/block-depth
11377     52/push-edx
11378     68/push "0x34"/imm32
11379     89/<- %edx 4/r32/esp
11380     # var inouts/esi: (handle stmt-var) = [var2]
11381     68/push 0/imm32/is-deref:false
11382     68/push 0/imm32/next
11383     52/push-edx/var-var2
11384     89/<- %esi 4/r32/esp
11385     # inouts = [var1, var2]
11386     68/push 0/imm32/is-deref:false
11387     56/push-esi/next
11388     51/push-ecx/var-var1
11389     89/<- %esi 4/r32/esp
11390     # var stmt/esi: statement
11391     68/push 0/imm32/next
11392     68/push 0/imm32/outputs
11393     56/push-esi/inouts
11394     68/push "compare"/imm32/operation
11395     68/push 1/imm32/regular-stmt
11396     89/<- %esi 4/r32/esp
11397     # convert
11398     c7 0/subop/copy *Curr-block-depth 0/imm32
11399     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
11400     (flush _test-output-buffered-file)
11401 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11407     # check output
11408     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
11409     # . epilogue
11410     89/<- %esp 5/r32/ebp
11411     5d/pop-to-ebp
11412     c3/return
11413 
11414 test-compare-reg-with-literal:
11415     #   compare var1/ecx 0x34
11416     # =>
11417     #   81 7/subop/compare %ecx 0x34/imm32
11418     #
11419     # . prologue
11420     55/push-ebp
11421     89/<- %ebp 4/r32/esp
11422     # setup
11423     (clear-stream _test-output-stream)
11424     (clear-stream $_test-output-buffered-file->buffer)
11425     # var type/ecx: (handle tree type-id) = int
11426     68/push 0/imm32/right/null
11427     68/push 1/imm32/left/int
11428     89/<- %ecx 4/r32/esp
11429     # var var-var1/ecx: var in ecx
11430     68/push "ecx"/imm32/register
11431     68/push 0/imm32/no-stack-offset
11432     68/push 1/imm32/block-depth
11433     51/push-ecx
11434     68/push "var1"/imm32
11435     89/<- %ecx 4/r32/esp
11436     # var type/edx: (handle tree type-id) = literal
11437     68/push 0/imm32/right/null
11438     68/push 0/imm32/left/literal
11439     89/<- %edx 4/r32/esp
11440     # var var-var2/edx: var literal
11441     68/push 0/imm32/no-register
11442     68/push 0/imm32/no-stack-offset
11443     68/push 1/imm32/block-depth
11444     52/push-edx
11445     68/push "0x34"/imm32
11446     89/<- %edx 4/r32/esp
11447     # var inouts/esi: (handle stmt-var) = [var2]
11448     68/push 0/imm32/is-deref:false
11449     68/push 0/imm32/next
11450     52/push-edx/var-var2
11451     89/<- %esi 4/r32/esp
11452     # inouts = [var1, var2]
11453     68/push 0/imm32/is-deref:false
11454     56/push-esi/next
11455     51/push-ecx/var-var1
11456     89/<- %esi 4/r32/esp
11457     # var stmt/esi: statement
11458     68/push 0/imm32/next
11459     68/push 0/imm32/outputs
11460     56/push-esi/inouts
11461     68/push "compare"/imm32/operation
11462     68/push 1/imm32/regular-stmt
11463     89/<- %esi 4/r32/esp
11464     # convert
11465     c7 0/subop/copy *Curr-block-depth 0/imm32
11466     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
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 "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
11476     # . epilogue
11477     89/<- %esp 5/r32/ebp
11478     5d/pop-to-ebp
11479     c3/return
11480 
11481 test-emit-subx-stmt-function-call:
11482     # Call a function on a variable on the stack.
11483     #   f foo
11484     # =>
11485     #   (f2 *(ebp-8))
11486     # (Changing the function name supports overloading in general, but here it
11487     # just serves to help disambiguate things.)
11488     #
11489     # There's a variable on the var stack as follows:
11490     #   name: 'foo'
11491     #   type: int
11492     #   stack-offset: -8
11493     #
11494     # There's nothing in primitives.
11495     #
11496     # There's a function with this info:
11497     #   name: 'f'
11498     #   inout: int/mem
11499     #   value: 'f2'
11500     #
11501     # . prologue
11502     55/push-ebp
11503     89/<- %ebp 4/r32/esp
11504     # setup
11505     (clear-stream _test-output-stream)
11506     (clear-stream $_test-output-buffered-file->buffer)
11507     # var type/ecx: (handle tree type-id) = int
11508     68/push 0/imm32/right/null
11509     68/push 1/imm32/left/int
11510     89/<- %ecx 4/r32/esp
11511     # var var-foo/ecx: var
11512     68/push 0/imm32/no-register
11513     68/push -8/imm32/stack-offset
11514     68/push 0/imm32/block-depth
11515     51/push-ecx
11516     68/push "foo"/imm32
11517     89/<- %ecx 4/r32/esp
11518     # var inouts/esi: (handle stmt-var)
11519     68/push 0/imm32/is-deref:false
11520     68/push 0/imm32/next
11521     51/push-ecx/var-foo
11522     89/<- %esi 4/r32/esp
11523     # var stmt/esi: statement
11524     68/push 0/imm32/next
11525     68/push 0/imm32/outputs
11526     56/push-esi/inouts
11527     68/push "f"/imm32/operation
11528     68/push 1/imm32
11529     89/<- %esi 4/r32/esp
11530     # var functions/ebx: function
11531     68/push 0/imm32/next
11532     68/push 0/imm32/body
11533     68/push 0/imm32/outputs
11534     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
11535     68/push "f2"/imm32/subx-name
11536     68/push "f"/imm32/name
11537     89/<- %ebx 4/r32/esp
11538     # convert
11539     c7 0/subop/copy *Curr-block-depth 0/imm32
11540     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
11541     (flush _test-output-buffered-file)
11542 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11548     # check output
11549     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
11550     # . epilogue
11551     89/<- %esp 5/r32/ebp
11552     5d/pop-to-ebp
11553     c3/return
11554 
11555 test-emit-subx-stmt-function-call-with-literal-arg:
11556     # Call a function on a literal.
11557     #   f 34
11558     # =>
11559     #   (f2 34)
11560     #
11561     # . prologue
11562     55/push-ebp
11563     89/<- %ebp 4/r32/esp
11564     # setup
11565     (clear-stream _test-output-stream)
11566     (clear-stream $_test-output-buffered-file->buffer)
11567     # var type/ecx: (handle tree type-id) = literal
11568     68/push 0/imm32/right/null
11569     68/push 0/imm32/left/literal
11570     89/<- %ecx 4/r32/esp
11571     # var var-foo/ecx: var literal
11572     68/push 0/imm32/no-register
11573     68/push 0/imm32/no-stack-offset
11574     68/push 0/imm32/block-depth
11575     51/push-ecx
11576     68/push "34"/imm32
11577     89/<- %ecx 4/r32/esp
11578     # var inouts/esi: (handle stmt-var)
11579     68/push 0/imm32/is-deref:false
11580     68/push 0/imm32/next
11581     51/push-ecx/var-foo
11582     89/<- %esi 4/r32/esp
11583     # var stmt/esi: statement
11584     68/push 0/imm32/next
11585     68/push 0/imm32/outputs
11586     56/push-esi/inouts
11587     68/push "f"/imm32/operation
11588     68/push 1/imm32
11589     89/<- %esi 4/r32/esp
11590     # var functions/ebx: function
11591     68/push 0/imm32/next
11592     68/push 0/imm32/body
11593     68/push 0/imm32/outputs
11594     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
11595     68/push "f2"/imm32/subx-name
11596     68/push "f"/imm32/name
11597     89/<- %ebx 4/r32/esp
11598     # convert
11599     c7 0/subop/copy *Curr-block-depth 0/imm32
11600     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
11601     (flush _test-output-buffered-file)
11602 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
11608     # check output
11609     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
11610     # . epilogue
11611     89/<- %esp 5/r32/ebp
11612     5d/pop-to-ebp
11613     c3/return
11614 
11615 emit-indent:  # out: (addr buffered-file), n: int
11616     # . prologue
11617     55/push-ebp
11618     89/<- %ebp 4/r32/esp
11619     # . save registers
11620     50/push-eax
11621     # var i/eax: int = n
11622     8b/-> *(ebp+0xc) 0/r32/eax
11623     {
11624       # if (i <= 0) break
11625       3d/compare-eax-with 0/imm32
11626       7e/jump-if-<= break/disp8
11627       (write-buffered *(ebp+8) "  ")
11628       48/decrement-eax
11629       eb/jump loop/disp8
11630     }
11631 $emit-indent:end:
11632     # . restore registers
11633     58/pop-to-eax
11634     # . epilogue
11635     89/<- %esp 5/r32/ebp
11636     5d/pop-to-ebp
11637     c3/return
11638 
11639 emit-subx-prologue:  # out: (addr buffered-file)
11640     # . prologue
11641     55/push-ebp
11642     89/<- %ebp 4/r32/esp
11643     #
11644     (write-buffered *(ebp+8) "  # . prologue\n")
11645     (write-buffered *(ebp+8) "  55/push-ebp\n")
11646     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
11647 $emit-subx-prologue:end:
11648     # . epilogue
11649     89/<- %esp 5/r32/ebp
11650     5d/pop-to-ebp
11651     c3/return
11652 
11653 emit-subx-epilogue:  # out: (addr buffered-file)
11654     # . prologue
11655     55/push-ebp
11656     89/<- %ebp 4/r32/esp
11657     #
11658     (write-buffered *(ebp+8) "  # . epilogue\n")
11659     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
11660     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
11661     (write-buffered *(ebp+8) "  c3/return\n")
11662 $emit-subx-epilogue:end:
11663     # . epilogue
11664     89/<- %esp 5/r32/ebp
11665     5d/pop-to-ebp
11666     c3/return