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