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 statement)
 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:  # (handle function)
 244   0/imm32
 245 
 246 Function-name:
 247   0/imm32
 248 Function-subx-name:
 249   4/imm32
 250 Function-inouts:  # (handle list var)
 251   8/imm32
 252 Function-outputs:  # (handle list var)
 253   0xc/imm32
 254 Function-body:  # (handle block)
 255   0x10/imm32
 256 Function-next:  # (handle function)
 257   0x14/imm32
 258 Function-size:  # (addr int)
 259   0x18/imm32/24
 260 
 261 Primitive-name:
 262   0/imm32
 263 Primitive-inouts:  # (handle list var)
 264   4/imm32
 265 Primitive-outputs:  # (handle list var)
 266   8/imm32
 267 Primitive-subx-name:  # (handle array byte)
 268   0xc/imm32
 269 Primitive-subx-rm32:  # enum arg-location
 270   0x10/imm32
 271 Primitive-subx-r32:  # enum arg-location
 272   0x14/imm32
 273 Primitive-subx-imm32:  # enum arg-location
 274   0x18/imm32
 275 Primitive-subx-disp32:  # enum arg-location  -- only for branches
 276   0x1c/imm32
 277 Primitive-output-is-write-only:  # boolean
 278   0x20/imm32
 279 Primitive-next:  # (handle function)
 280   0x24/imm32
 281 Primitive-size:  # (addr int)
 282   0x28/imm32/36
 283 
 284 Stmt-tag:
 285   0/imm32
 286 
 287 Block-statements:  # (handle list statement)
 288   4/imm32
 289 Block-var:  # (handle var)
 290   8/imm32
 291 
 292 Stmt1-operation:  # (handle array byte)
 293   4/imm32
 294 Stmt1-inouts:  # (handle stmt-var)
 295   8/imm32
 296 Stmt1-outputs:  # (handle stmt-var)
 297   0xc/imm32
 298 
 299 Vardef-var:  # (handle var)
 300   4/imm32
 301 
 302 Regvardef-operation:  # (handle array byte)
 303   4/imm32
 304 Regvardef-inouts:  # (handle stmt-var)
 305   8/imm32
 306 Regvardef-outputs:  # (handle stmt-var)  # will have exactly one element
 307   0xc/imm32
 308 
 309 Stmt-size:  # (addr int)
 310   0x10/imm32
 311 
 312 Var-name:
 313   0/imm32
 314 Var-type:
 315   4/imm32
 316 Var-block-depth:
 317   8/imm32
 318 Var-stack-offset:
 319   0xc/imm32
 320 Var-register:
 321   0x10/imm32
 322 Var-size:  # (addr int)
 323   0x14/imm32
 324 
 325 Any-register:  # wildcard
 326   # size
 327   1/imm32
 328   # data
 329   2a/asterisk
 330 
 331 List-value:
 332   0/imm32
 333 List-next:
 334   4/imm32
 335 List-size:  # (addr int)
 336   8/imm32
 337 
 338 # A stmt-var is like a list of vars with call-site specific metadata
 339 Stmt-var-value:  # (handle var)
 340   0/imm32
 341 Stmt-var-next:  # (handle stmt-var)
 342   4/imm32
 343 Stmt-var-is-deref:  # boolean
 344   8/imm32
 345 Stmt-var-size:  # (addr int)
 346   0xc/imm32
 347 
 348 # Types are expressed as trees (s-expressions) of type-ids (ints).
 349 # However, there's no need for singletons, so we can assume (int) == int
 350 #   - if x->right == nil, x is an atom
 351 #   - x->left contains either a pointer to a pair, or an atomic type-id directly.
 352 #     type ids will be less than 0x10000 (MAX_TYPE_ID).
 353 
 354 Tree-left:  # either type-id or (addr tree type-id)
 355   0/imm32
 356 Tree-right:  # (addr tree type-id)
 357   4/imm32
 358 Tree-size:  # (addr int)
 359   8/imm32
 360 
 361 Max-type-id:
 362   0x10000/imm32
 363 
 364 == code
 365 
 366 Entry:
 367     # . prologue
 368     89/<- %ebp 4/r32/esp
 369     (new-segment *Heap-size Heap)
 370     # if (argv[1] == "test') run-tests()
 371     {
 372       # if (argc <= 1) break
 373       81 7/subop/compare *ebp 1/imm32
 374       7e/jump-if-<= break/disp8
 375       # if (argv[1] != "test") break
 376       (kernel-string-equal? *(ebp+8) "test")  # => eax
 377       3d/compare-eax-and 0/imm32/false
 378       74/jump-if-= break/disp8
 379       #
 380       (run-tests)
 381       # syscall(exit, *Num-test-failures)
 382       8b/-> *Num-test-failures 3/r32/ebx
 383       eb/jump $mu-main:end/disp8
 384     }
 385     # otherwise convert Stdin
 386     (convert-mu Stdin Stdout)
 387     (flush Stdout)
 388     # syscall(exit, 0)
 389     bb/copy-to-ebx 0/imm32
 390 $mu-main:end:
 391     b8/copy-to-eax 1/imm32/exit
 392     cd/syscall 0x80/imm8
 393 
 394 convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
 395     # . prologue
 396     55/push-ebp
 397     89/<- %ebp 4/r32/esp
 398     #
 399     (parse-mu *(ebp+8))
 400     (check-mu-types)
 401     (emit-subx *(ebp+0xc))
 402 $convert-mu:end:
 403     # . epilogue
 404     89/<- %esp 5/r32/ebp
 405     5d/pop-to-ebp
 406     c3/return
 407 
 408 test-convert-empty-input:
 409     # empty input => empty output
 410     # . prologue
 411     55/push-ebp
 412     89/<- %ebp 4/r32/esp
 413     # setup
 414     (clear-stream _test-input-stream)
 415     (clear-stream $_test-input-buffered-file->buffer)
 416     (clear-stream _test-output-stream)
 417     (clear-stream $_test-output-buffered-file->buffer)
 418     #
 419     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 420     (flush _test-output-buffered-file)
 421     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 422     # . epilogue
 423     89/<- %esp 5/r32/ebp
 424     5d/pop-to-ebp
 425     c3/return
 426 
 427 test-convert-function-skeleton:
 428     # . prologue
 429     55/push-ebp
 430     89/<- %ebp 4/r32/esp
 431     # setup
 432     (clear-stream _test-input-stream)
 433     (clear-stream $_test-input-buffered-file->buffer)
 434     (clear-stream _test-output-stream)
 435     (clear-stream $_test-output-buffered-file->buffer)
 436     #
 437     (write _test-input-stream "fn foo {\n")
 438     (write _test-input-stream "}\n")
 439     # convert
 440     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 441     (flush _test-output-buffered-file)
 442 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 448     # check output
 449     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
 450     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
 451     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
 452     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 453     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
 454     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 455     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 456     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
 457     # . epilogue
 458     89/<- %esp 5/r32/ebp
 459     5d/pop-to-ebp
 460     c3/return
 461 
 462 test-convert-multiple-function-skeletons:
 463     # . prologue
 464     55/push-ebp
 465     89/<- %ebp 4/r32/esp
 466     # setup
 467     (clear-stream _test-input-stream)
 468     (clear-stream $_test-input-buffered-file->buffer)
 469     (clear-stream _test-output-stream)
 470     (clear-stream $_test-output-buffered-file->buffer)
 471     #
 472     (write _test-input-stream "fn foo {\n")
 473     (write _test-input-stream "}\n")
 474     (write _test-input-stream "fn bar {\n")
 475     (write _test-input-stream "}\n")
 476     # convert
 477     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 478     (flush _test-output-buffered-file)
 479 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 485     # check first function
 486     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
 487     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
 488     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 489     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 490     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 491     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 492     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 493     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
 494     # check second function
 495     (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
 496     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
 497     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 498     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 499     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 500     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 501     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 502     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
 503     # . epilogue
 504     89/<- %esp 5/r32/ebp
 505     5d/pop-to-ebp
 506     c3/return
 507 
 508 test-convert-function-with-arg:
 509     # . prologue
 510     55/push-ebp
 511     89/<- %ebp 4/r32/esp
 512     # setup
 513     (clear-stream _test-input-stream)
 514     (clear-stream $_test-input-buffered-file->buffer)
 515     (clear-stream _test-output-stream)
 516     (clear-stream $_test-output-buffered-file->buffer)
 517     #
 518     (write _test-input-stream "fn foo n: int {\n")
 519     (write _test-input-stream "}\n")
 520     # convert
 521     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 522     (flush _test-output-buffered-file)
 523 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 529     # check output
 530     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
 531     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
 532     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
 533     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
 534     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
 535     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
 536     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
 537     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
 538     # . epilogue
 539     89/<- %esp 5/r32/ebp
 540     5d/pop-to-ebp
 541     c3/return
 542 
 543 test-convert-function-with-arg-and-body:
 544     # . prologue
 545     55/push-ebp
 546     89/<- %ebp 4/r32/esp
 547     # setup
 548     (clear-stream _test-input-stream)
 549     (clear-stream $_test-input-buffered-file->buffer)
 550     (clear-stream _test-output-stream)
 551     (clear-stream $_test-output-buffered-file->buffer)
 552     c7 0/subop/copy *Next-block-index 1/imm32
 553     #
 554     (write _test-input-stream "fn foo n: int {\n")
 555     (write _test-input-stream "  increment n\n")
 556     (write _test-input-stream "}\n")
 557     # convert
 558     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 559     (flush _test-output-buffered-file)
 560 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 566     # check output
 567     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
 568     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
 569     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
 570     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
 571     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
 572     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
 573     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
 574     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
 575     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
 576     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
 577     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
 578     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
 579     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
 580     # . epilogue
 581     89/<- %esp 5/r32/ebp
 582     5d/pop-to-ebp
 583     c3/return
 584 
 585 test-convert-function-distinguishes-args:
 586     # . prologue
 587     55/push-ebp
 588     89/<- %ebp 4/r32/esp
 589     # setup
 590     (clear-stream _test-input-stream)
 591     (clear-stream $_test-input-buffered-file->buffer)
 592     (clear-stream _test-output-stream)
 593     (clear-stream $_test-output-buffered-file->buffer)
 594     c7 0/subop/copy *Next-block-index 1/imm32
 595     #
 596     (write _test-input-stream "fn foo a: int, b: int {\n")
 597     (write _test-input-stream "  increment b\n")
 598     (write _test-input-stream "}\n")
 599     # convert
 600     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 601     (flush _test-output-buffered-file)
 602 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 608     # check output
 609     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
 610     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
 611     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
 612     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
 613     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
 614     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
 615     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
 616     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
 617     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
 618     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
 619     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
 620     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
 621     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
 622     # . epilogue
 623     89/<- %esp 5/r32/ebp
 624     5d/pop-to-ebp
 625     c3/return
 626 
 627 test-convert-function-returns-result:
 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     c7 0/subop/copy *Next-block-index 1/imm32
 637     #
 638     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 639     (write _test-input-stream "  result <- copy a\n")
 640     (write _test-input-stream "  result <- increment\n")
 641     (write _test-input-stream "}\n")
 642     # convert
 643     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 644     (flush _test-output-buffered-file)
 645 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 651     # check output
 652     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-returns-result/0")
 653     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-returns-result/1")
 654     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-returns-result/2")
 655     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
 656     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-returns-result/4")
 657     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-returns-result/5")
 658     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/6")
 659     (check-next-stream-line-equal _test-output-stream "    40/increment-eax"    "F - test-convert-function-returns-result/7")
 660     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-returns-result/8")
 661     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-returns-result/9")
 662     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-returns-result/10")
 663     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/11")
 664     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-returns-result/12")
 665     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-returns-result/13")
 666     # . epilogue
 667     89/<- %esp 5/r32/ebp
 668     5d/pop-to-ebp
 669     c3/return
 670 
 671 test-convert-function-literal-arg:
 672     # . prologue
 673     55/push-ebp
 674     89/<- %ebp 4/r32/esp
 675     # setup
 676     (clear-stream _test-input-stream)
 677     (clear-stream $_test-input-buffered-file->buffer)
 678     (clear-stream _test-output-stream)
 679     (clear-stream $_test-output-buffered-file->buffer)
 680     c7 0/subop/copy *Next-block-index 1/imm32
 681     #
 682     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 683     (write _test-input-stream "  result <- copy a\n")
 684     (write _test-input-stream "  result <- add 1\n")
 685     (write _test-input-stream "}\n")
 686     # convert
 687     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 688     (flush _test-output-buffered-file)
 689 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 695     # check output
 696     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg/0")
 697     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg/1")
 698     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg/2")
 699     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg/3")
 700     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg/4")
 701     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg/5")
 702     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-literal-arg/6")
 703     (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-literal-arg/7")
 704     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg/8")
 705     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg/9")
 706     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg/10")
 707     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg/11")
 708     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg/12")
 709     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg/13")
 710     # . epilogue
 711     89/<- %esp 5/r32/ebp
 712     5d/pop-to-ebp
 713     c3/return
 714 
 715 test-convert-function-literal-arg-2:
 716     # . prologue
 717     55/push-ebp
 718     89/<- %ebp 4/r32/esp
 719     # setup
 720     (clear-stream _test-input-stream)
 721     (clear-stream $_test-input-buffered-file->buffer)
 722     (clear-stream _test-output-stream)
 723     (clear-stream $_test-output-buffered-file->buffer)
 724     c7 0/subop/copy *Next-block-index 1/imm32
 725     #
 726     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
 727     (write _test-input-stream "  result <- copy a\n")
 728     (write _test-input-stream "  result <- add 1\n")
 729     (write _test-input-stream "}\n")
 730     # convert
 731     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 732     (flush _test-output-buffered-file)
 733 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 739     # check output
 740     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg-2/0")
 741     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg-2/1")
 742     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg-2/2")
 743     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg-2/3")
 744     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg-2/4")
 745     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg-2/5")
 746     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-literal-arg-2/6")
 747     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-literal-arg-2/7")
 748     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg-2/8")
 749     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg-2/9")
 750     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg-2/10")
 751     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg-2/11")
 752     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg-2/12")
 753     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg-2/13")
 754     # . epilogue
 755     89/<- %esp 5/r32/ebp
 756     5d/pop-to-ebp
 757     c3/return
 758 
 759 test-convert-function-call-with-literal-arg:
 760     # . prologue
 761     55/push-ebp
 762     89/<- %ebp 4/r32/esp
 763     # setup
 764     (clear-stream _test-input-stream)
 765     (clear-stream $_test-input-buffered-file->buffer)
 766     (clear-stream _test-output-stream)
 767     (clear-stream $_test-output-buffered-file->buffer)
 768     c7 0/subop/copy *Next-block-index 1/imm32
 769     #
 770     (write _test-input-stream "fn main -> result/ebx: int {\n")
 771     (write _test-input-stream "  result <- do-add 3 4\n")
 772     (write _test-input-stream "}\n")
 773     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
 774     (write _test-input-stream "  result <- copy a\n")
 775     (write _test-input-stream "  result <- add b\n")
 776     (write _test-input-stream "}\n")
 777     # convert
 778     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 779     (flush _test-output-buffered-file)
 780 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 786     # check output
 787     (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
 788     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
 789     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
 790     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
 791     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
 792     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
 793     (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/6")
 794     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/7")
 795     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
 796     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/9")
 797     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/10")
 798     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/11")
 799     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/12")
 800     (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/13")
 801     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/14")
 802     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/15")
 803     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/16")
 804     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/17")
 805     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/18")
 806     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/19")
 807     (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/20")
 808     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/21")
 809     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/22")
 810     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/23")
 811     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/24")
 812     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/25")
 813     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/26")
 814     # . epilogue
 815     89/<- %esp 5/r32/ebp
 816     5d/pop-to-ebp
 817     c3/return
 818 
 819 test-convert-function-with-local-var-in-mem:
 820     # . prologue
 821     55/push-ebp
 822     89/<- %ebp 4/r32/esp
 823     # setup
 824     (clear-stream _test-input-stream)
 825     (clear-stream $_test-input-buffered-file->buffer)
 826     (clear-stream _test-output-stream)
 827     (clear-stream $_test-output-buffered-file->buffer)
 828     c7 0/subop/copy *Next-block-index 1/imm32
 829     #
 830     (write _test-input-stream "fn foo {\n")
 831     (write _test-input-stream "  var x: int\n")
 832     (write _test-input-stream "  increment x\n")
 833     (write _test-input-stream "}\n")
 834     # convert
 835     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 836     (flush _test-output-buffered-file)
 837 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 843     # check output
 844     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
 845     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
 846     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
 847     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
 848     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
 849     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
 850     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
 851     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
 852     (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")
 853     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
 854     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
 855     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
 856     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
 857     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
 858     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
 859     # . epilogue
 860     89/<- %esp 5/r32/ebp
 861     5d/pop-to-ebp
 862     c3/return
 863 
 864 test-convert-function-with-local-var-in-reg:
 865     # . prologue
 866     55/push-ebp
 867     89/<- %ebp 4/r32/esp
 868     # setup
 869     (clear-stream _test-input-stream)
 870     (clear-stream $_test-input-buffered-file->buffer)
 871     (clear-stream _test-output-stream)
 872     (clear-stream $_test-output-buffered-file->buffer)
 873     c7 0/subop/copy *Next-block-index 1/imm32
 874     #
 875     (write _test-input-stream "fn foo {\n")
 876     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 877     (write _test-input-stream "  x <- increment\n")
 878     (write _test-input-stream "}\n")
 879     # convert
 880     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 881     (flush _test-output-buffered-file)
 882 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 888     # check output
 889     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
 890     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
 891     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
 892     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
 893     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
 894     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
 895     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
 896     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
 897     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
 898     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
 899     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
 900     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
 901     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
 902     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
 903     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
 904     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
 905     # . epilogue
 906     89/<- %esp 5/r32/ebp
 907     5d/pop-to-ebp
 908     c3/return
 909 
 910 test-convert-function-with-local-var-dereferenced:
 911     # . prologue
 912     55/push-ebp
 913     89/<- %ebp 4/r32/esp
 914     # setup
 915     (clear-stream _test-input-stream)
 916     (clear-stream $_test-input-buffered-file->buffer)
 917     (clear-stream _test-output-stream)
 918     (clear-stream $_test-output-buffered-file->buffer)
 919     c7 0/subop/copy *Next-block-index 1/imm32
 920     #
 921     (write _test-input-stream "fn foo {\n")
 922     (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
 923     (write _test-input-stream "  increment *x\n")
 924     (write _test-input-stream "}\n")
 925     # convert
 926     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 927     (flush _test-output-buffered-file)
 928 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 934     # check output
 935     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
 936     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
 937     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
 938     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
 939     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
 940     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
 941     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
 942     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
 943     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
 944     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
 945     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
 946     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
 947     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
 948     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
 949     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
 950     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
 951     # . epilogue
 952     89/<- %esp 5/r32/ebp
 953     5d/pop-to-ebp
 954     c3/return
 955 
 956 test-convert-compare-register-with-literal:
 957     # . prologue
 958     55/push-ebp
 959     89/<- %ebp 4/r32/esp
 960     # setup
 961     (clear-stream _test-input-stream)
 962     (clear-stream $_test-input-buffered-file->buffer)
 963     (clear-stream _test-output-stream)
 964     (clear-stream $_test-output-buffered-file->buffer)
 965     c7 0/subop/copy *Next-block-index 1/imm32
 966     #
 967     (write _test-input-stream "fn foo {\n")
 968     (write _test-input-stream "  var x/ecx: int <- copy 0\n")
 969     (write _test-input-stream "  compare x, 0\n")
 970     (write _test-input-stream "}\n")
 971     # convert
 972     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 973     (flush _test-output-buffered-file)
 974 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 980     # check output
 981     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
 982     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
 983     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
 984     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
 985     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
 986     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
 987     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 988     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
 989     (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
 990     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 991     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
 992     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
 993     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
 994     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
 995     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
 996     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
 997     # . epilogue
 998     89/<- %esp 5/r32/ebp
 999     5d/pop-to-ebp
1000     c3/return
1001 
1002 test-convert-function-with-local-var-in-block:
1003     # . prologue
1004     55/push-ebp
1005     89/<- %ebp 4/r32/esp
1006     # setup
1007     (clear-stream _test-input-stream)
1008     (clear-stream $_test-input-buffered-file->buffer)
1009     (clear-stream _test-output-stream)
1010     (clear-stream $_test-output-buffered-file->buffer)
1011     c7 0/subop/copy *Next-block-index 1/imm32
1012     #
1013     (write _test-input-stream "fn foo {\n")
1014     (write _test-input-stream "  {\n")
1015     (write _test-input-stream "    var x: int\n")
1016     (write _test-input-stream "    increment x\n")
1017     (write _test-input-stream "  }\n")
1018     (write _test-input-stream "}\n")
1019     # convert
1020     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1021     (flush _test-output-buffered-file)
1022 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1028     # check output
1029     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
1030     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
1031     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
1032     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
1033     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
1034     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
1035     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
1036     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
1037     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
1038     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
1039     (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")
1040     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
1041     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
1042     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
1043     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
1044     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
1045     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
1046     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
1047     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
1048     # . epilogue
1049     89/<- %esp 5/r32/ebp
1050     5d/pop-to-ebp
1051     c3/return
1052 
1053 test-convert-function-with-local-var-in-named-block:
1054     # . prologue
1055     55/push-ebp
1056     89/<- %ebp 4/r32/esp
1057     # setup
1058     (clear-stream _test-input-stream)
1059     (clear-stream $_test-input-buffered-file->buffer)
1060     (clear-stream _test-output-stream)
1061     (clear-stream $_test-output-buffered-file->buffer)
1062     c7 0/subop/copy *Next-block-index 1/imm32
1063     #
1064     (write _test-input-stream "fn foo {\n")
1065     (write _test-input-stream "  $bar: {\n")
1066     (write _test-input-stream "    var x: int\n")
1067     (write _test-input-stream "    increment x\n")
1068     (write _test-input-stream "  }\n")
1069     (write _test-input-stream "}\n")
1070     # convert
1071     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1072     (flush _test-output-buffered-file)
1073 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1079     # check output
1080     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
1081     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
1082     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
1083     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
1084     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
1085     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
1086     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
1087     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
1088     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
1089     (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")
1090     (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")
1091     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
1092     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
1093     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
1094     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
1095     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
1096     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
1097     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
1098     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
1099     # . epilogue
1100     89/<- %esp 5/r32/ebp
1101     5d/pop-to-ebp
1102     c3/return
1103 
1104 test-convert-function-with-branches-in-block:
1105     # . prologue
1106     55/push-ebp
1107     89/<- %ebp 4/r32/esp
1108     # setup
1109     (clear-stream _test-input-stream)
1110     (clear-stream $_test-input-buffered-file->buffer)
1111     (clear-stream _test-output-stream)
1112     (clear-stream $_test-output-buffered-file->buffer)
1113     c7 0/subop/copy *Next-block-index 1/imm32
1114     #
1115     (write _test-input-stream "fn foo x: int {\n")
1116     (write _test-input-stream "  {\n")
1117     (write _test-input-stream "    break-if->=\n")
1118     (write _test-input-stream "    loop-if-addr<\n")
1119     (write _test-input-stream "    increment x\n")
1120     (write _test-input-stream "    loop\n")
1121     (write _test-input-stream "  }\n")
1122     (write _test-input-stream "}\n")
1123     # convert
1124     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1125     (flush _test-output-buffered-file)
1126 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1132     # check output
1133     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
1134     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
1135     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
1136     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
1137     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
1138     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
1139     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
1140     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
1141     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
1142     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
1143     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
1144     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
1145     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
1146     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
1147     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
1148     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
1149     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
1150     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
1151     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
1152     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
1153     # . epilogue
1154     89/<- %esp 5/r32/ebp
1155     5d/pop-to-ebp
1156     c3/return
1157 
1158 test-convert-function-with-branches-in-named-block:
1159     # . prologue
1160     55/push-ebp
1161     89/<- %ebp 4/r32/esp
1162     # setup
1163     (clear-stream _test-input-stream)
1164     (clear-stream $_test-input-buffered-file->buffer)
1165     (clear-stream _test-output-stream)
1166     (clear-stream $_test-output-buffered-file->buffer)
1167     c7 0/subop/copy *Next-block-index 1/imm32
1168     #
1169     (write _test-input-stream "fn foo x: int {\n")
1170     (write _test-input-stream "  $bar: {\n")
1171     (write _test-input-stream "    break-if->= $bar\n")
1172     (write _test-input-stream "    loop-if-addr< $bar\n")
1173     (write _test-input-stream "    increment x\n")
1174     (write _test-input-stream "    loop\n")
1175     (write _test-input-stream "  }\n")
1176     (write _test-input-stream "}\n")
1177     # convert
1178     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1179     (flush _test-output-buffered-file)
1180 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1186     # check output
1187     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
1188     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
1189     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
1190     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
1191     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
1192     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
1193     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
1194     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
1195     (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")
1196     (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")
1197     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
1198     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
1199     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
1200     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
1201     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
1202     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
1203     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
1204     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
1205     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
1206     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
1207     # . epilogue
1208     89/<- %esp 5/r32/ebp
1209     5d/pop-to-ebp
1210     c3/return
1211 
1212 test-convert-function-with-var-in-nested-block:
1213     # . prologue
1214     55/push-ebp
1215     89/<- %ebp 4/r32/esp
1216     # setup
1217     (clear-stream _test-input-stream)
1218     (clear-stream $_test-input-buffered-file->buffer)
1219     (clear-stream _test-output-stream)
1220     (clear-stream $_test-output-buffered-file->buffer)
1221     c7 0/subop/copy *Next-block-index 1/imm32
1222     #
1223     (write _test-input-stream "fn foo x: int {\n")
1224     (write _test-input-stream "  {\n")
1225     (write _test-input-stream "    {\n")
1226     (write _test-input-stream "      var x: int\n")
1227     (write _test-input-stream "      increment x\n")
1228     (write _test-input-stream "    }\n")
1229     (write _test-input-stream "  }\n")
1230     (write _test-input-stream "}\n")
1231     # convert
1232     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1233     (flush _test-output-buffered-file)
1234 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1240     # check output
1241     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
1242     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
1243     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
1244     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
1245     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
1246     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
1247     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
1248     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
1249     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
1250     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
1251     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
1252     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
1253     (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")
1254     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
1255     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
1256     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
1257     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
1258     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
1259     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
1260     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
1261     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
1262     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
1263     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
1264     # . epilogue
1265     89/<- %esp 5/r32/ebp
1266     5d/pop-to-ebp
1267     c3/return
1268 
1269 test-convert-function-with-multiple-vars-in-nested-blocks:
1270     # . prologue
1271     55/push-ebp
1272     89/<- %ebp 4/r32/esp
1273     # setup
1274     (clear-stream _test-input-stream)
1275     (clear-stream $_test-input-buffered-file->buffer)
1276     (clear-stream _test-output-stream)
1277     (clear-stream $_test-output-buffered-file->buffer)
1278     c7 0/subop/copy *Next-block-index 1/imm32
1279     #
1280     (write _test-input-stream "fn foo x: int {\n")
1281     (write _test-input-stream "  {\n")
1282     (write _test-input-stream "    var x/eax: int <- copy 0\n")
1283     (write _test-input-stream "    {\n")
1284     (write _test-input-stream "      var y: int\n")
1285     (write _test-input-stream "      x <- add y\n")
1286     (write _test-input-stream "    }\n")
1287     (write _test-input-stream "  }\n")
1288     (write _test-input-stream "}\n")
1289     # convert
1290     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1291     (flush _test-output-buffered-file)
1292 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1298     # check output
1299     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
1300     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
1301     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
1302     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
1303     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
1304     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
1305     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
1306     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
1307     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
1308     (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")
1309     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
1310     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
1311     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
1312     (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")
1313     (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")
1314     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
1315     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
1316     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
1317     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
1318     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
1319     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
1320     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
1321     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
1322     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
1323     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
1324     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
1325     # . epilogue
1326     89/<- %esp 5/r32/ebp
1327     5d/pop-to-ebp
1328     c3/return
1329 
1330 test-convert-function-with-branches-and-local-vars:
1331     # A conditional 'break' after a 'var' in a block is converted into a
1332     # nested block that performs all necessary cleanup before jumping. This
1333     # results in some ugly code duplication.
1334     # . prologue
1335     55/push-ebp
1336     89/<- %ebp 4/r32/esp
1337     # setup
1338     (clear-stream _test-input-stream)
1339     (clear-stream $_test-input-buffered-file->buffer)
1340     (clear-stream _test-output-stream)
1341     (clear-stream $_test-output-buffered-file->buffer)
1342     c7 0/subop/copy *Next-block-index 1/imm32
1343     #
1344     (write _test-input-stream "fn foo {\n")
1345     (write _test-input-stream "  {\n")
1346     (write _test-input-stream "    var x: int\n")
1347     (write _test-input-stream "    break-if->=\n")
1348     (write _test-input-stream "    increment x\n")
1349     (write _test-input-stream "  }\n")
1350     (write _test-input-stream "}\n")
1351     # convert
1352     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1353     (flush _test-output-buffered-file)
1354 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1360     # check output
1361     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
1362     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
1363     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
1364     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
1365     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
1366     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
1367     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
1368     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
1369     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
1370     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
1371     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
1372     (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")
1373     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
1374     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
1375     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
1376     (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")
1377     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
1378     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
1379     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
1380     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
1381     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
1382     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
1383     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
1384     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
1385     # . epilogue
1386     89/<- %esp 5/r32/ebp
1387     5d/pop-to-ebp
1388     c3/return
1389 
1390 test-convert-function-with-conditional-loops-and-local-vars:
1391     # A conditional 'loop' after a 'var' in a block is converted into a nested
1392     # block that performs all necessary cleanup before jumping. This results
1393     # in some ugly code duplication.
1394     # . prologue
1395     55/push-ebp
1396     89/<- %ebp 4/r32/esp
1397     # setup
1398     (clear-stream _test-input-stream)
1399     (clear-stream $_test-input-buffered-file->buffer)
1400     (clear-stream _test-output-stream)
1401     (clear-stream $_test-output-buffered-file->buffer)
1402     c7 0/subop/copy *Next-block-index 1/imm32
1403     #
1404     (write _test-input-stream "fn foo {\n")
1405     (write _test-input-stream "  {\n")
1406     (write _test-input-stream "    var x: int\n")
1407     (write _test-input-stream "    loop-if->=\n")
1408     (write _test-input-stream "    increment x\n")
1409     (write _test-input-stream "  }\n")
1410     (write _test-input-stream "}\n")
1411     # convert
1412     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1413     (flush _test-output-buffered-file)
1414 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1420     # check output
1421     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
1422     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
1423     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
1424     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
1425     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
1426     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
1427     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
1428     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
1429     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
1430     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
1431     (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")
1432     (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")
1433     (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")
1434     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
1435     (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")
1436     (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")
1437     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
1438     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
1439     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
1440     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
1441     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
1442     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
1443     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
1444     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
1445     # . epilogue
1446     89/<- %esp 5/r32/ebp
1447     5d/pop-to-ebp
1448     c3/return
1449 
1450 test-convert-function-with-unconditional-loops-and-local-vars:
1451     # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
1452     # regular block cleanup. Any instructions after 'loop' are dead and
1453     # therefore skipped.
1454     # . prologue
1455     55/push-ebp
1456     89/<- %ebp 4/r32/esp
1457     # setup
1458     (clear-stream _test-input-stream)
1459     (clear-stream $_test-input-buffered-file->buffer)
1460     (clear-stream _test-output-stream)
1461     (clear-stream $_test-output-buffered-file->buffer)
1462     c7 0/subop/copy *Next-block-index 1/imm32
1463     #
1464     (write _test-input-stream "fn foo {\n")
1465     (write _test-input-stream "  {\n")
1466     (write _test-input-stream "    var x: int\n")
1467     (write _test-input-stream "    loop\n")
1468     (write _test-input-stream "    increment x\n")
1469     (write _test-input-stream "  }\n")
1470     (write _test-input-stream "}\n")
1471     # convert
1472     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1473     (flush _test-output-buffered-file)
1474 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1480     # check output
1481     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
1482     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
1483     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
1484     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
1485     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
1486     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
1487     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
1488     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
1489     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
1490     (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")
1491     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
1492     # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
1493     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
1494     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
1495     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
1496     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
1497     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
1498     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
1499     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
1500     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
1501     # . epilogue
1502     89/<- %esp 5/r32/ebp
1503     5d/pop-to-ebp
1504     c3/return
1505 
1506 test-convert-function-with-branches-and-loops-and-local-vars:
1507     # . prologue
1508     55/push-ebp
1509     89/<- %ebp 4/r32/esp
1510     # setup
1511     (clear-stream _test-input-stream)
1512     (clear-stream $_test-input-buffered-file->buffer)
1513     (clear-stream _test-output-stream)
1514     (clear-stream $_test-output-buffered-file->buffer)
1515     c7 0/subop/copy *Next-block-index 1/imm32
1516     #
1517     (write _test-input-stream "fn foo {\n")
1518     (write _test-input-stream "  {\n")
1519     (write _test-input-stream "    var x: int\n")
1520     (write _test-input-stream "    break-if->=\n")
1521     (write _test-input-stream "    increment x\n")
1522     (write _test-input-stream "    loop\n")
1523     (write _test-input-stream "  }\n")
1524     (write _test-input-stream "}\n")
1525     # convert
1526     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1527     (flush _test-output-buffered-file)
1528 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1534     # check output
1535     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
1536     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
1537     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
1538     (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")
1539     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
1540     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
1541     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
1542     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
1543     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
1544     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
1545     (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")
1546     (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")
1547     (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")
1548     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
1549     (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")
1550     (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")
1551     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
1552     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
1553     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
1554     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
1555     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
1556     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
1557     (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")
1558     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
1559     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
1560     # . epilogue
1561     89/<- %esp 5/r32/ebp
1562     5d/pop-to-ebp
1563     c3/return
1564 
1565 test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
1566     # . prologue
1567     55/push-ebp
1568     89/<- %ebp 4/r32/esp
1569     # setup
1570     (clear-stream _test-input-stream)
1571     (clear-stream $_test-input-buffered-file->buffer)
1572     (clear-stream _test-output-stream)
1573     (clear-stream $_test-output-buffered-file->buffer)
1574     c7 0/subop/copy *Next-block-index 1/imm32
1575     #
1576     (write _test-input-stream "fn foo {\n")
1577     (write _test-input-stream "  a: {\n")
1578     (write _test-input-stream "    var x: int\n")
1579     (write _test-input-stream "    {\n")
1580     (write _test-input-stream "      var y: int\n")
1581     (write _test-input-stream "      break-if->= a\n")
1582     (write _test-input-stream "      increment x\n")
1583     (write _test-input-stream "      loop\n")
1584     (write _test-input-stream "    }\n")
1585     (write _test-input-stream "  }\n")
1586     (write _test-input-stream "}\n")
1587     # convert
1588     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1589     (flush _test-output-buffered-file)
1590 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1596     # check output
1597     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
1598     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
1599     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
1600     (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")
1601     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
1602     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
1603     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
1604     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
1605     (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")
1606     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
1607     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
1608     (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")
1609     (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
1610     (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")
1611     (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")
1612     (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")
1613     (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")
1614     (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
1615     (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")
1616     (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")
1617     (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")
1618     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
1619     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
1620     (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")
1621     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
1622     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
1623     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
1624     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
1625     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
1626     (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")
1627     (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")
1628     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
1629     # . epilogue
1630     89/<- %esp 5/r32/ebp
1631     5d/pop-to-ebp
1632     c3/return
1633 
1634 test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
1635     # . prologue
1636     55/push-ebp
1637     89/<- %ebp 4/r32/esp
1638     # setup
1639     (clear-stream _test-input-stream)
1640     (clear-stream $_test-input-buffered-file->buffer)
1641     (clear-stream _test-output-stream)
1642     (clear-stream $_test-output-buffered-file->buffer)
1643     c7 0/subop/copy *Next-block-index 1/imm32
1644     #
1645     (write _test-input-stream "fn foo {\n")
1646     (write _test-input-stream "  a: {\n")
1647     (write _test-input-stream "    var x: int\n")
1648     (write _test-input-stream "    {\n")
1649     (write _test-input-stream "      var y: int\n")
1650     (write _test-input-stream "      break a\n")
1651     (write _test-input-stream "      increment x\n")
1652     (write _test-input-stream "    }\n")
1653     (write _test-input-stream "  }\n")
1654     (write _test-input-stream "}\n")
1655     # convert
1656     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1657     (flush _test-output-buffered-file)
1658 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1664     # check output
1665     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
1666     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
1667     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
1668     (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")
1669     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
1670     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
1671     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
1672     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
1673     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
1674     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
1675     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
1676     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
1677     (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")
1678     (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")
1679     (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")
1680     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
1681     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
1682     (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")
1683     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
1684     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
1685     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
1686     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
1687     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
1688     (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")
1689     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
1690     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
1691     # . epilogue
1692     89/<- %esp 5/r32/ebp
1693     5d/pop-to-ebp
1694     c3/return
1695 
1696 test-convert-function-with-unconditional-break-and-local-vars:
1697     # . prologue
1698     55/push-ebp
1699     89/<- %ebp 4/r32/esp
1700     # setup
1701     (clear-stream _test-input-stream)
1702     (clear-stream $_test-input-buffered-file->buffer)
1703     (clear-stream _test-output-stream)
1704     (clear-stream $_test-output-buffered-file->buffer)
1705     c7 0/subop/copy *Next-block-index 1/imm32
1706     #
1707     (write _test-input-stream "fn foo {\n")
1708     (write _test-input-stream "  {\n")
1709     (write _test-input-stream "    var x: int\n")
1710     (write _test-input-stream "    {\n")
1711     (write _test-input-stream "      var y: int\n")
1712     (write _test-input-stream "      break\n")
1713     (write _test-input-stream "      increment x\n")
1714     (write _test-input-stream "    }\n")
1715     (write _test-input-stream "  }\n")
1716     (write _test-input-stream "}\n")
1717     # convert
1718     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1719     (flush _test-output-buffered-file)
1720 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1726     # check output
1727     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
1728     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
1729     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
1730     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
1731     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
1732     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
1733     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
1734     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
1735     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
1736     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
1737     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
1738     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
1739     (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")
1740     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
1741     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
1742     (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")
1743     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
1744     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
1745     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
1746     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
1747     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
1748     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
1749     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
1750     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
1751     # . epilogue
1752     89/<- %esp 5/r32/ebp
1753     5d/pop-to-ebp
1754     c3/return
1755 
1756 test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
1757     # . prologue
1758     55/push-ebp
1759     89/<- %ebp 4/r32/esp
1760     # setup
1761     (clear-stream _test-input-stream)
1762     (clear-stream $_test-input-buffered-file->buffer)
1763     (clear-stream _test-output-stream)
1764     (clear-stream $_test-output-buffered-file->buffer)
1765     c7 0/subop/copy *Next-block-index 1/imm32
1766     #
1767     (write _test-input-stream "fn foo {\n")
1768     (write _test-input-stream "  a: {\n")
1769     (write _test-input-stream "    var x: int\n")
1770     (write _test-input-stream "    {\n")
1771     (write _test-input-stream "      var y: int\n")
1772     (write _test-input-stream "      loop a\n")
1773     (write _test-input-stream "      increment x\n")
1774     (write _test-input-stream "    }\n")
1775     (write _test-input-stream "  }\n")
1776     (write _test-input-stream "}\n")
1777     # convert
1778     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1779     (flush _test-output-buffered-file)
1780 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1786     # check output
1787     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
1788     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
1789     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
1790     (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")
1791     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
1792     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
1793     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
1794     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
1795     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
1796     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
1797     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
1798     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
1799     (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")
1800     (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")
1801     (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")
1802     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
1803     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
1804     (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")
1805     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
1806     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
1807     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
1808     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
1809     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
1810     (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")
1811     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
1812     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
1813     # . epilogue
1814     89/<- %esp 5/r32/ebp
1815     5d/pop-to-ebp
1816     c3/return
1817 
1818 test-convert-length-of-array:
1819     # . prologue
1820     55/push-ebp
1821     89/<- %ebp 4/r32/esp
1822     # setup
1823     (clear-stream _test-input-stream)
1824     (clear-stream $_test-input-buffered-file->buffer)
1825     (clear-stream _test-output-stream)
1826     (clear-stream $_test-output-buffered-file->buffer)
1827     c7 0/subop/copy *Next-block-index 1/imm32
1828     #
1829     (write _test-input-stream "fn foo a: (addr array int) {\n")
1830     (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
1831     (write _test-input-stream "  var c/eax: int <- length b\n")
1832     (write _test-input-stream "}\n")
1833     # convert
1834     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1835     (flush _test-output-buffered-file)
1836 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1842     # check output
1843     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
1844     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
1845     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
1846     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
1847     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
1848     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
1849     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
1850     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
1851     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/8")
1852     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
1853     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/10")
1854     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
1855     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
1856     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
1857     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
1858     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
1859     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
1860     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
1861     # . epilogue
1862     89/<- %esp 5/r32/ebp
1863     5d/pop-to-ebp
1864     c3/return
1865 
1866 #######################################################
1867 # Parsing
1868 #######################################################
1869 
1870 parse-mu:  # in: (addr buffered-file)
1871     # pseudocode
1872     #   var curr-function: (addr (handle function)) = Program
1873     #   var line: (stream byte 512)
1874     #   var word-slice: slice
1875     #   while true                                  # line loop
1876     #     clear-stream(line)
1877     #     read-line-buffered(in, line)
1878     #     if (line->write == 0) break               # end of file
1879     #     word-slice = next-mu-token(line)
1880     #     if slice-empty?(word-slice)               # end of line
1881     #       continue
1882     #     else if slice-starts-with?(word-slice, "#")  # comment
1883     #       continue                                # end of line
1884     #     else if slice-equal(word-slice, "fn")
1885     #       var new-function: (handle function) = allocate(function)
1886     #       var vars: (stack (addr var) 256)
1887     #       populate-mu-function-header(in, new-function, vars)
1888     #       populate-mu-function-body(in, new-function, vars)
1889     #       assert(vars->top == 0)
1890     #       *curr-function = new-function
1891     #       curr-function = &new-function->next
1892     #     else
1893     #       abort()
1894     #
1895     # . prologue
1896     55/push-ebp
1897     89/<- %ebp 4/r32/esp
1898     # . save registers
1899     50/push-eax
1900     51/push-ecx
1901     52/push-edx
1902     53/push-ebx
1903     57/push-edi
1904     # var line/ecx: (stream byte 512)
1905     81 5/subop/subtract %esp 0x200/imm32
1906     68/push 0x200/imm32/length
1907     68/push 0/imm32/read
1908     68/push 0/imm32/write
1909     89/<- %ecx 4/r32/esp
1910     # var word-slice/edx: slice
1911     68/push 0/imm32/end
1912     68/push 0/imm32/start
1913     89/<- %edx 4/r32/esp
1914     # var curr-function/edi: (addr (handle function)) = Program
1915     bf/copy-to-edi Program/imm32
1916     # var vars/ebx: (stack (addr var) 256)
1917     81 5/subop/subtract %esp 0x400/imm32
1918     68/push 0x400/imm32/length
1919     68/push 0/imm32/top
1920     89/<- %ebx 4/r32/esp
1921     {
1922 $parse-mu:line-loop:
1923       (clear-stream %ecx)
1924       (read-line-buffered *(ebp+8) %ecx)
1925       # if (line->write == 0) break
1926       81 7/subop/compare *ecx 0/imm32
1927       0f 84/jump-if-= break/disp32
1928 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
1934       (next-mu-token %ecx %edx)
1935       # if slice-empty?(word-slice) continue
1936       (slice-empty? %edx)
1937       3d/compare-eax-and 0/imm32/false
1938       0f 85/jump-if-!= loop/disp32
1939       # if (*word-slice->start == "#") continue
1940       # . eax = *word-slice->start
1941       8b/-> *edx 0/r32/eax
1942       8a/copy-byte *eax 0/r32/AL
1943       81 4/subop/and %eax 0xff/imm32
1944       # . if (eax == '#') continue
1945       3d/compare-eax-and 0x23/imm32/hash
1946       0f 84/jump-if-= loop/disp32
1947       # if (slice-equal?(word-slice, "fn")) parse a function
1948       {
1949 $parse-mu:fn:
1950         (slice-equal? %edx "fn")
1951         3d/compare-eax-and 0/imm32/false
1952         0f 84/jump-if-= break/disp32
1953         # var new-function/eax: (handle function) = populate-mu-function(in, new-function, vars)
1954         (allocate Heap *Function-size)  # => eax
1955         (zero-out %eax *Function-size)
1956         (clear-stack %ebx)
1957         (populate-mu-function-header %ecx %eax %ebx)
1958         (populate-mu-function-body *(ebp+8) %eax %ebx)
1959         # *curr-function = new-function
1960         89/<- *edi 0/r32/eax
1961         # curr-function = &new-function->next
1962         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
1963         e9/jump $parse-mu:line-loop/disp32
1964       }
1965       # otherwise abort
1966       e9/jump $parse-mu:error1/disp32
1967     } # end line loop
1968 $parse-mu:end:
1969     # . reclaim locals
1970     81 0/subop/add %esp 0x630/imm32
1971     # . restore registers
1972     5f/pop-to-edi
1973     5b/pop-to-ebx
1974     5a/pop-to-edx
1975     59/pop-to-ecx
1976     58/pop-to-eax
1977     # . epilogue
1978     89/<- %esp 5/r32/ebp
1979     5d/pop-to-ebp
1980     c3/return
1981 
1982 $parse-mu:error1:
1983     # error("unexpected top-level command: " word-slice "\n")
1984     (write-buffered Stderr "unexpected top-level command: ")
1985     (write-slice-buffered Stderr %edx)
1986     (write-buffered Stderr "\n")
1987     (flush Stderr)
1988     # . syscall(exit, 1)
1989     bb/copy-to-ebx  1/imm32
1990     b8/copy-to-eax  1/imm32/exit
1991     cd/syscall  0x80/imm8
1992     # never gets here
1993 
1994 $parse-mu:error2:
1995     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
1996     (print-int32-buffered Stderr *ebx)
1997     (write-buffered Stderr " vars not reclaimed after fn '")
1998     (write-slice-buffered Stderr *eax)  # Function-name
1999     (write-buffered Stderr "'\n")
2000     (flush Stderr)
2001     # . syscall(exit, 1)
2002     bb/copy-to-ebx  1/imm32
2003     b8/copy-to-eax  1/imm32/exit
2004     cd/syscall  0x80/imm8
2005     # never gets here
2006 
2007 # scenarios considered:
2008 # ✗ fn foo  # no block
2009 # ✓ fn foo {
2010 # ✗ fn foo { {
2011 # ✗ fn foo { }
2012 # ✗ fn foo { } {
2013 # ✗ fn foo x {
2014 # ✗ fn foo x: {
2015 # ✓ fn foo x: int {
2016 # ✓ fn foo x: int {
2017 # ✓ fn foo x: int -> y/eax: int {
2018 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
2019     # pseudocode:
2020     #   var name: slice
2021     #   next-mu-token(first-line, name)
2022     #   assert(name not in '{' '}' '->')
2023     #   out->name = slice-to-string(name)
2024     #   var next-offset: int = 8
2025     #   ## inouts
2026     #   while true
2027     #     ## name
2028     #     name = next-mu-token(first-line)
2029     #     if (name == '{') goto done
2030     #     if (name == '->') break
2031     #     assert(name != '}')
2032     #     var v: (handle var) = parse-var-with-type(name, first-line)
2033     #     assert(v->register == null)
2034     #     v->stack-offset = next-offset
2035     #     next-offset += size-of(v)
2036     #     # v->block-depth is implicitly 0
2037     #     out->inouts = append(out->inouts, v)
2038     #     push(vars, v)
2039     #   ## outputs
2040     #   while true
2041     #     ## name
2042     #     name = next-mu-token(first-line)
2043     #     assert(name not in '{' '}' '->')
2044     #     var v: (handle var) = parse-var-with-type(name, first-line)
2045     #     assert(v->register != null)
2046     #     out->outputs = append(out->outputs, v)
2047     #   done:
2048     #
2049     # . prologue
2050     55/push-ebp
2051     89/<- %ebp 4/r32/esp
2052     # . save registers
2053     50/push-eax
2054     51/push-ecx
2055     52/push-edx
2056     53/push-ebx
2057     57/push-edi
2058     # edi = out
2059     8b/-> *(ebp+0xc) 7/r32/edi
2060     # var word-slice/ecx: slice
2061     68/push 0/imm32/end
2062     68/push 0/imm32/start
2063     89/<- %ecx 4/r32/esp
2064     # var next-offset/edx = 8
2065     ba/copy-to-edx 8/imm32
2066     # read function name
2067     (next-mu-token *(ebp+8) %ecx)
2068     # error checking
2069     # TODO: error if name starts with 'break' or 'loop'
2070     # if (word-slice == '{') abort
2071     (slice-equal? %ecx "{")   # => eax
2072     3d/compare-eax-and 0/imm32/false
2073     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2074     # if (word-slice == '->') abort
2075     (slice-equal? %ecx "->")   # => eax
2076     3d/compare-eax-and 0/imm32/false
2077     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2078     # if (word-slice == '}') abort
2079     (slice-equal? %ecx "}")   # => eax
2080     3d/compare-eax-and 0/imm32/false
2081     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2082     # save function name
2083     (slice-to-string Heap %ecx)  # => eax
2084     89/<- *edi 0/r32/eax  # Function-name
2085     # initialize default subx-name as well
2086     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
2087     # save function inouts
2088     {
2089 $populate-mu-function-header:check-for-inout:
2090       (next-mu-token *(ebp+8) %ecx)
2091       # if (word-slice == '{') goto done
2092       (slice-equal? %ecx "{")   # => eax
2093       3d/compare-eax-and 0/imm32/false
2094       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
2095       # if (word-slice == '->') break
2096       (slice-equal? %ecx "->")   # => eax
2097       3d/compare-eax-and 0/imm32/false
2098       0f 85/jump-if-!= break/disp32
2099       # if (word-slice == '}') abort
2100       (slice-equal? %ecx "}")   # => eax
2101       3d/compare-eax-and 0/imm32/false
2102       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2103       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
2104       (parse-var-with-type %ecx *(ebp+8))  # => eax
2105       89/<- %ebx 0/r32/eax
2106       # assert(v->register == null)
2107       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
2108       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
2109       # v->stack-offset = next-offset
2110       89/<- *(ebx+0xc) 2/r32/edx  # Var-stack-offset
2111       # next-offset += size-of(v)
2112       (size-of %ebx)  # => eax
2113       01/add %edx 0/r32/eax
2114       # v->block-depth is implicitly 0
2115       #
2116       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
2117       89/<- *(edi+8) 0/r32/eax  # Function-inouts
2118       (push *(ebp+0x10) %ebx)
2119       #
2120       e9/jump loop/disp32
2121     }
2122     # save function outputs
2123     {
2124 $populate-mu-function-header:check-for-out:
2125       (next-mu-token *(ebp+8) %ecx)
2126       # if (word-slice == '{') break
2127       (slice-equal? %ecx "{")   # => eax
2128       3d/compare-eax-and 0/imm32/false
2129       0f 85/jump-if-!= break/disp32
2130       # if (word-slice == '->') abort
2131       (slice-equal? %ecx "->")   # => eax
2132       3d/compare-eax-and 0/imm32/false
2133       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2134       # if (word-slice == '}') abort
2135       (slice-equal? %ecx "}")   # => eax
2136       3d/compare-eax-and 0/imm32/false
2137       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2138       #
2139       (parse-var-with-type %ecx *(ebp+8))  # => eax
2140       89/<- %ebx 0/r32/eax
2141       # assert(var->register != null)
2142       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
2143       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
2144       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
2145       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
2146       e9/jump loop/disp32
2147     }
2148 $populate-mu-function-header:done:
2149     (check-no-tokens-left *(ebp+8))
2150 $populate-mu-function-header:end:
2151     # . reclaim locals
2152     81 0/subop/add %esp 8/imm32
2153     # . restore registers
2154     5f/pop-to-edi
2155     5b/pop-to-ebx
2156     5a/pop-to-edx
2157     59/pop-to-ecx
2158     58/pop-to-eax
2159     # . epilogue
2160     89/<- %esp 5/r32/ebp
2161     5d/pop-to-ebp
2162     c3/return
2163 
2164 $populate-mu-function-header:error1:
2165     # error("function header not in form 'fn <name> {'")
2166     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
2167     (flush Stderr)
2168     (rewind-stream *(ebp+8))
2169     (write-stream 2 *(ebp+8))
2170     (write-buffered Stderr "'\n")
2171     (flush Stderr)
2172     # . syscall(exit, 1)
2173     bb/copy-to-ebx  1/imm32
2174     b8/copy-to-eax  1/imm32/exit
2175     cd/syscall  0x80/imm8
2176     # never gets here
2177 
2178 $populate-mu-function-header:error2:
2179     # error("function input '" var "' cannot be in a register")
2180     (write-buffered Stderr "function input '")
2181     (write-buffered Stderr *ebx)  # Var-name
2182     (write-buffered Stderr "' cannot be in a register")
2183     (flush Stderr)
2184     # . syscall(exit, 1)
2185     bb/copy-to-ebx  1/imm32
2186     b8/copy-to-eax  1/imm32/exit
2187     cd/syscall  0x80/imm8
2188     # never gets here
2189 
2190 $populate-mu-function-header:error3:
2191     # error("function input '" var "' must be in a register")
2192     (write-buffered Stderr "function input '")
2193     (write-buffered Stderr *eax)  # Var-name
2194     (write-buffered Stderr " must be in a register'")
2195     (flush Stderr)
2196     (rewind-stream *(ebp+8))
2197     (write-stream 2 *(ebp+8))
2198     (write-buffered Stderr "'\n")
2199     (flush Stderr)
2200     # . syscall(exit, 1)
2201     bb/copy-to-ebx  1/imm32
2202     b8/copy-to-eax  1/imm32/exit
2203     cd/syscall  0x80/imm8
2204     # never gets here
2205 
2206 test-function-header-with-arg:
2207     # . prologue
2208     55/push-ebp
2209     89/<- %ebp 4/r32/esp
2210     # setup
2211     (clear-stream _test-input-stream)
2212     (write _test-input-stream "foo n: int {\n")
2213     # var result/ecx: function
2214     2b/subtract-> *Function-size 4/r32/esp
2215     89/<- %ecx 4/r32/esp
2216     (zero-out %ecx *Function-size)
2217     # var vars/ebx: (stack (addr var) 16)
2218     81 5/subop/subtract %esp 0x10/imm32
2219     68/push 0x10/imm32/length
2220     68/push 0/imm32/top
2221     89/<- %ebx 4/r32/esp
2222     # convert
2223     (populate-mu-function-header _test-input-stream %ecx %ebx)
2224     # check result
2225     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
2226     # edx: (handle list var) = result->inouts
2227     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
2228     # ebx: (handle var) = result->inouts->value
2229     8b/-> *edx 3/r32/ebx  # List-value
2230     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
2231     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2232     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
2233     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
2234     # . epilogue
2235     89/<- %esp 5/r32/ebp
2236     5d/pop-to-ebp
2237     c3/return
2238 
2239 test-function-header-with-multiple-args:
2240     # . prologue
2241     55/push-ebp
2242     89/<- %ebp 4/r32/esp
2243     # setup
2244     (clear-stream _test-input-stream)
2245     (write _test-input-stream "foo a: int, b: int c: int {\n")
2246     # result/ecx: (handle function)
2247     2b/subtract-> *Function-size 4/r32/esp
2248     89/<- %ecx 4/r32/esp
2249     (zero-out %ecx *Function-size)
2250     # var vars/ebx: (stack (addr var) 16)
2251     81 5/subop/subtract %esp 0x10/imm32
2252     68/push 0x10/imm32/length
2253     68/push 0/imm32/top
2254     89/<- %ebx 4/r32/esp
2255     # convert
2256     (populate-mu-function-header _test-input-stream %ecx %ebx)
2257     # check result
2258     (check-strings-equal *ecx "foo")  # Function-name
2259     # edx: (handle list var) = result->inouts
2260     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
2261 $test-function-header-with-multiple-args:inout0:
2262     # ebx: (handle var) = result->inouts->value
2263     8b/-> *edx 3/r32/ebx  # List-value
2264     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
2265     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2266     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
2267     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
2268     # edx = result->inouts->next
2269     8b/-> *(edx+4) 2/r32/edx  # List-next
2270 $test-function-header-with-multiple-args:inout1:
2271     # ebx = result->inouts->next->value
2272     8b/-> *edx 3/r32/ebx  # List-value
2273     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
2274     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2275     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
2276     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
2277     # edx = result->inouts->next->next
2278     8b/-> *(edx+4) 2/r32/edx  # List-next
2279 $test-function-header-with-multiple-args:inout2:
2280     # ebx = result->inouts->next->next->value
2281     8b/-> *edx 3/r32/ebx  # List-value
2282     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
2283     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2284     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
2285     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
2286     # . epilogue
2287     89/<- %esp 5/r32/ebp
2288     5d/pop-to-ebp
2289     c3/return
2290 
2291 test-function-with-multiple-args-and-outputs:
2292     # . prologue
2293     55/push-ebp
2294     89/<- %ebp 4/r32/esp
2295     # setup
2296     (clear-stream _test-input-stream)
2297     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
2298     # result/ecx: (handle function)
2299     2b/subtract-> *Function-size 4/r32/esp
2300     89/<- %ecx 4/r32/esp
2301     (zero-out %ecx *Function-size)
2302     # var vars/ebx: (stack (addr var) 16)
2303     81 5/subop/subtract %esp 0x10/imm32
2304     68/push 0x10/imm32/length
2305     68/push 0/imm32/top
2306     89/<- %ebx 4/r32/esp
2307     # convert
2308     (populate-mu-function-header _test-input-stream %ecx %ebx)
2309     # check result
2310     (check-strings-equal *ecx "foo")  # Function-name
2311     # edx: (handle list var) = result->inouts
2312     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
2313     # ebx: (handle var) = result->inouts->value
2314     8b/-> *edx 3/r32/ebx  # List-value
2315     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
2316     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2317     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
2318     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
2319     # edx = result->inouts->next
2320     8b/-> *(edx+4) 2/r32/edx  # List-next
2321     # ebx = result->inouts->next->value
2322     8b/-> *edx 3/r32/ebx  # List-value
2323     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
2324     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2325     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
2326     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
2327     # edx = result->inouts->next->next
2328     8b/-> *(edx+4) 2/r32/edx  # List-next
2329     # ebx = result->inouts->next->next->value
2330     8b/-> *edx 3/r32/ebx  # List-value
2331     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
2332     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2333     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
2334     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
2335     # edx: (handle list var) = result->outputs
2336     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
2337     # ebx: (handle var) = result->outputs->value
2338     8b/-> *edx 3/r32/ebx  # List-value
2339     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
2340     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
2341     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2342     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
2343     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
2344     # edx = result->outputs->next
2345     8b/-> *(edx+4) 2/r32/edx  # List-next
2346     # ebx = result->outputs->next->value
2347     8b/-> *edx 3/r32/ebx  # List-value
2348     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
2349     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
2350     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2351     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
2352     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
2353     # . epilogue
2354     89/<- %esp 5/r32/ebp
2355     5d/pop-to-ebp
2356     c3/return
2357 
2358 # format for variables with types
2359 #   x: int
2360 #   x: int,
2361 #   x/eax: int
2362 #   x/eax: int,
2363 # ignores at most one trailing comma
2364 # WARNING: modifies name
2365 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
2366     # pseudocode:
2367     #   var v: (handle var) = allocate(Heap, Var-size)
2368     #   var s: slice
2369     #   if (!slice-ends-with(name, ":"))
2370     #     abort
2371     #   --name->end to skip ':'
2372     #   next-token-from-slice(name->start, name->end, '/', s)
2373     #   v->name = slice-to-string(s)
2374     #   ## register
2375     #   next-token-from-slice(s->end, name->end, '/', s)
2376     #   if (!slice-empty?(s))
2377     #     v->register = slice-to-string(s)
2378     #   ## type
2379     #   var type: (handle tree type-id) = parse-type(first-line)
2380     #   v->type = type
2381     #   return v
2382     #
2383     # . prologue
2384     55/push-ebp
2385     89/<- %ebp 4/r32/esp
2386     # . save registers
2387     51/push-ecx
2388     52/push-edx
2389     53/push-ebx
2390     56/push-esi
2391     57/push-edi
2392     # esi = name
2393     8b/-> *(ebp+8) 6/r32/esi
2394     # if (!slice-ends-with?(name, ":")) abort
2395     8b/-> *(esi+4) 1/r32/ecx  # Slice-end
2396     49/decrement-ecx
2397     8a/copy-byte *ecx 1/r32/CL
2398     81 4/subop/and %ecx 0xff/imm32
2399     81 7/subop/compare %ecx 0x3a/imm32/colon
2400     0f 85/jump-if-!= $parse-var-with-type:abort/disp32
2401     # --name->end to skip ':'
2402     ff 1/subop/decrement *(esi+4)
2403     # var result/edi: (handle var) = allocate(Heap, Var-size)
2404     (allocate Heap *Var-size)  # => eax
2405     (zero-out %eax *Var-size)
2406     89/<- %edi 0/r32/eax
2407     # var s/ecx: slice
2408     68/push 0/imm32/end
2409     68/push 0/imm32/start
2410     89/<- %ecx 4/r32/esp
2411 $parse-var-with-type:save-name:
2412     # save v->name
2413     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
2414     # . end/edx = s->end
2415     8b/-> *(ecx+4) 2/r32/edx
2416 $parse-var-with-type:write-name:
2417     (slice-to-string Heap %ecx)  # => eax
2418     89/<- *edi 0/r32/eax  # Var-name
2419     # save v->register
2420 $parse-var-with-type:save-register:
2421     (next-token-from-slice %edx *(esi+4) 0x2f %ecx)  # end, name->end, '/'
2422     # if (!slice-empty?(s)) v->register = slice-to-string(s)
2423     {
2424 $parse-var-with-type:write-register:
2425       (slice-empty? %ecx)  # => eax
2426       3d/compare-eax-and 0/imm32/false
2427       75/jump-if-!= break/disp8
2428       (slice-to-string Heap %ecx)
2429       89/<- *(edi+0x10) 0/r32/eax  # Var-register
2430     }
2431 $parse-var-with-type:save-type:
2432     (parse-type Heap *(ebp+0xc))  # => eax
2433     89/<- *(edi+4) 0/r32/eax  # Var-type
2434 $parse-var-with-type:end:
2435     # return result
2436     89/<- %eax 7/r32/edi
2437     # . reclaim locals
2438     81 0/subop/add %esp 8/imm32
2439     # . restore registers
2440     5f/pop-to-edi
2441     5e/pop-to-esi
2442     5b/pop-to-ebx
2443     5a/pop-to-edx
2444     59/pop-to-ecx
2445     # . epilogue
2446     89/<- %esp 5/r32/ebp
2447     5d/pop-to-ebp
2448     c3/return
2449 
2450 $parse-var-with-type:abort:
2451     # error("var should have form 'name: type' in '" line "'\n")
2452     (write-buffered Stderr "var should have form 'name: type' in '")
2453     (flush Stderr)
2454     (rewind-stream *(ebp+0xc))
2455     (write-stream 2 *(ebp+0xc))
2456     (write-buffered Stderr "'\n")
2457     (flush Stderr)
2458     # . syscall(exit, 1)
2459     bb/copy-to-ebx  1/imm32
2460     b8/copy-to-eax  1/imm32/exit
2461     cd/syscall  0x80/imm8
2462     # never gets here
2463 
2464 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2465     # pseudocode:
2466     #   var s: slice = next-mu-token(in)
2467     #   assert s != ""
2468     #   assert s != "->"
2469     #   assert s != "{"
2470     #   assert s != "}"
2471     #   if s == ")"
2472     #     return 0
2473     #   result = allocate(Tree)
2474     #   zero-out(result, *Tree-size)
2475     #   if s != "("
2476     #     result->left = pos-slice(Type-id, s)
2477     #     return
2478     #   result->left = parse-type(ad, in)
2479     #   result->right = parse-type-tree(ad, in)
2480     #
2481     # . prologue
2482     55/push-ebp
2483     89/<- %ebp 4/r32/esp
2484     # . save registers
2485     51/push-ecx
2486     52/push-edx
2487     # var s/ecx: slice
2488     68/push 0/imm32
2489     68/push 0/imm32
2490     89/<- %ecx 4/r32/esp
2491     # s = next-mu-token(in)
2492     (next-mu-token *(ebp+0xc) %ecx)
2493 #?     (write-buffered Stderr "tok: ")
2494 #?     (write-slice-buffered Stderr %ecx)
2495 #?     (write-buffered Stderr "$\n")
2496 #?     (flush Stderr)
2497     # assert s != ""
2498     (slice-equal? %ecx "")
2499     3d/compare-eax-and 0/imm32/false
2500     0f 85/jump-if-!= $parse-type:abort/disp32
2501     # assert s != "{"
2502     (slice-equal? %ecx "{")
2503     3d/compare-eax-and 0/imm32/false
2504     0f 85/jump-if-!= $parse-type:abort/disp32
2505     # assert s != "}"
2506     (slice-equal? %ecx "}")
2507     3d/compare-eax-and 0/imm32/false
2508     0f 85/jump-if-!= $parse-type:abort/disp32
2509     # assert s != "->"
2510     (slice-equal? %ecx "->")
2511     3d/compare-eax-and 0/imm32/false
2512     0f 85/jump-if-!= $parse-type:abort/disp32
2513     # if (s == ")") return 0
2514     (slice-equal? %ecx ")")
2515     3d/compare-eax-and 0/imm32/false
2516     b8/copy-to-eax 0/imm32
2517     0f 85/jump-if-!= $parse-type:end/disp32
2518     # var result/edx: (handle tree type-id)
2519     (allocate *(ebp+8) *Tree-size)  # => eax
2520     (zero-out %eax *Tree-size)
2521     89/<- %edx 0/r32/eax
2522     {
2523       # if (s != "(") break
2524       (slice-equal? %ecx "(")
2525       3d/compare-eax-and 0/imm32/false
2526       75/jump-if-!= break/disp8
2527       # result->left = pos-slice(Type-id, s)
2528       (pos-slice Type-id %ecx)
2529 #?       (write-buffered Stderr "=> {")
2530 #?       (print-int32-buffered Stderr %eax)
2531 #?       (write-buffered Stderr ", 0}\n")
2532 #?       (flush Stderr)
2533       89/<- *edx 0/r32/eax  # Tree-left
2534       e9/jump $parse-type:return-edx/disp32
2535     }
2536     # otherwise s == "("
2537     # result->left = parse-type(ad, in)
2538     (parse-type *(ebp+8) *(ebp+0xc))
2539 #?     (write-buffered Stderr "=> {")
2540 #?     (print-int32-buffered Stderr %eax)
2541     89/<- *edx 0/r32/eax  # Tree-left
2542     # result->right = parse-type-tree(ad, in)
2543     (parse-type-tree *(ebp+8) *(ebp+0xc))
2544 #?     (write-buffered Stderr Space)
2545 #?     (print-int32-buffered Stderr %eax)
2546 #?     (write-buffered Stderr "}\n")
2547 #?     (flush Stderr)
2548     89/<- *(edx+4) 0/r32/eax  # Tree-right
2549 $parse-type:return-edx:
2550     89/<- %eax 2/r32/edx
2551 $parse-type:end:
2552     # . reclaim locals
2553     81 0/subop/add %esp 8/imm32
2554     # . restore registers
2555     5a/pop-to-edx
2556     59/pop-to-ecx
2557     # . epilogue
2558     89/<- %esp 5/r32/ebp
2559     5d/pop-to-ebp
2560     c3/return
2561 
2562 $parse-type:abort:
2563     # error("unexpected token when parsing type: '" s "'\n")
2564     (write-buffered Stderr "unexpected token when parsing type: '")
2565     (write-slice-buffered Stderr %ecx)
2566     (write-buffered Stderr "'\n")
2567     (flush Stderr)
2568     # . syscall(exit, 1)
2569     bb/copy-to-ebx  1/imm32
2570     b8/copy-to-eax  1/imm32/exit
2571     cd/syscall  0x80/imm8
2572     # never gets here
2573 
2574 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2575     # pseudocode:
2576     #   var tmp: (handle tree type-id) = parse-type(ad, in)
2577     #   if tmp == 0
2578     #     return 0
2579     #   result = allocate(Tree)
2580     #   zero-out(result, *Tree-size)
2581     #   result->left = tmp
2582     #   result->right = parse-type-tree(ad, in)
2583     #
2584     # . prologue
2585     55/push-ebp
2586     89/<- %ebp 4/r32/esp
2587     # . save registers
2588     51/push-ecx
2589     52/push-edx
2590     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
2591     (parse-type *(ebp+8) *(ebp+0xc))
2592     # if (tmp == 0) return tmp
2593     3d/compare-eax-and 0/imm32
2594     74/jump-if-= $parse-type-tree:end/disp8
2595     # var tmp2/ecx = tmp
2596     89/<- %ecx 0/r32/eax
2597     # var result/edx: (handle tree type-id)
2598     (allocate *(ebp+8) *Tree-size)  # => eax
2599     (zero-out %eax *Tree-size)
2600     89/<- %edx 0/r32/eax
2601     # result->left = tmp2
2602     89/<- *edx 1/r32/ecx  # Tree-left
2603     # result->right = parse-type-tree(ad, in)
2604     (parse-type-tree *(ebp+8) *(ebp+0xc))
2605     89/<- *(edx+4) 0/r32/eax  # Tree-right
2606 $parse-type-tree:return-edx:
2607     89/<- %eax 2/r32/edx
2608 $parse-type-tree:end:
2609     # . restore registers
2610     5a/pop-to-edx
2611     59/pop-to-ecx
2612     # . epilogue
2613     89/<- %esp 5/r32/ebp
2614     5d/pop-to-ebp
2615     c3/return
2616 
2617 next-mu-token:  # in: (addr stream byte), out: (addr slice)
2618     # pseudocode:
2619     # start:
2620     #   skip-chars-matching-whitespace(in)
2621     #   if in->read >= in->write              # end of in
2622     #     out = {0, 0}
2623     #     return
2624     #   out->start = &in->data[in->read]
2625     #   var curr-byte/eax: byte = in->data[in->read]
2626     #   if curr->byte == ','                  # comment token
2627     #     ++in->read
2628     #     goto start
2629     #   if curr-byte == '#'                   # comment
2630     #     goto done                             # treat as eof
2631     #   if curr-byte == '"'                   # string literal
2632     #     skip-string(in)
2633     #     goto done                           # no metadata
2634     #   if curr-byte == '('
2635     #     ++in->read
2636     #     goto done
2637     #   if curr-byte == ')'
2638     #     ++in->read
2639     #     goto done
2640     #   # read a word
2641     #   while true
2642     #     if in->read >= in->write
2643     #       break
2644     #     curr-byte = in->data[in->read]
2645     #     if curr-byte == ' '
2646     #       break
2647     #     if curr-byte == '\r'
2648     #       break
2649     #     if curr-byte == '\n'
2650     #       break
2651     #     if curr-byte == '('
2652     #       break
2653     #     if curr-byte == ')'
2654     #       break
2655     #     if curr-byte == ','
2656     #       break
2657     #     ++in->read
2658     # done:
2659     #   out->end = &in->data[in->read]
2660     #
2661     # . prologue
2662     55/push-ebp
2663     89/<- %ebp 4/r32/esp
2664     # . save registers
2665     50/push-eax
2666     51/push-ecx
2667     56/push-esi
2668     57/push-edi
2669     # esi = in
2670     8b/-> *(ebp+8) 6/r32/esi
2671     # edi = out
2672     8b/-> *(ebp+0xc) 7/r32/edi
2673 $next-mu-token:start:
2674     (skip-chars-matching-whitespace %esi)
2675 $next-mu-token:check0:
2676     # if (in->read >= in->write) return out = {0, 0}
2677     # . ecx = in->read
2678     8b/-> *(esi+4) 1/r32/ecx
2679     # . if (ecx >= in->write) return out = {0, 0}
2680     3b/compare 1/r32/ecx *esi
2681     c7 0/subop/copy *edi 0/imm32
2682     c7 0/subop/copy *(edi+4) 0/imm32
2683     0f 8d/jump-if->= $next-mu-token:end/disp32
2684     # out->start = &in->data[in->read]
2685     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2686     89/<- *edi 0/r32/eax
2687     # var curr-byte/eax: byte = in->data[in->read]
2688     31/xor %eax 0/r32/eax
2689     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2690     {
2691 $next-mu-token:check-for-comma:
2692       # if (curr-byte != ',') break
2693       3d/compare-eax-and 0x2c/imm32/comma
2694       75/jump-if-!= break/disp8
2695       # ++in->read
2696       ff 0/subop/increment *(esi+4)
2697       # restart
2698       e9/jump $next-mu-token:start/disp32
2699     }
2700     {
2701 $next-mu-token:check-for-comment:
2702       # if (curr-byte != '#') break
2703       3d/compare-eax-and 0x23/imm32/pound
2704       75/jump-if-!= break/disp8
2705       # return eof
2706       e9/jump $next-mu-token:done/disp32
2707     }
2708     {
2709 $next-mu-token:check-for-string-literal:
2710       # if (curr-byte != '"') break
2711       3d/compare-eax-and 0x22/imm32/dquote
2712       75/jump-if-!= break/disp8
2713       (skip-string %esi)
2714       # return
2715       e9/jump $next-mu-token:done/disp32
2716     }
2717     {
2718 $next-mu-token:check-for-open-paren:
2719       # if (curr-byte != '(') break
2720       3d/compare-eax-and 0x28/imm32/open-paren
2721       75/jump-if-!= break/disp8
2722       # ++in->read
2723       ff 0/subop/increment *(esi+4)
2724       # return
2725       e9/jump $next-mu-token:done/disp32
2726     }
2727     {
2728 $next-mu-token:check-for-close-paren:
2729       # if (curr-byte != ')') break
2730       3d/compare-eax-and 0x29/imm32/close-paren
2731       75/jump-if-!= break/disp8
2732       # ++in->read
2733       ff 0/subop/increment *(esi+4)
2734       # return
2735       e9/jump $next-mu-token:done/disp32
2736     }
2737     {
2738 $next-mu-token:regular-word-without-metadata:
2739       # if (in->read >= in->write) break
2740       # . ecx = in->read
2741       8b/-> *(esi+4) 1/r32/ecx
2742       # . if (ecx >= in->write) break
2743       3b/compare *esi 1/r32/ecx
2744       7d/jump-if->= break/disp8
2745       # var c/eax: byte = in->data[in->read]
2746       31/xor %eax 0/r32/eax
2747       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2748       # if (c == ' ') break
2749       3d/compare-eax-and 0x20/imm32/space
2750       74/jump-if-= break/disp8
2751       # if (c == '\r') break
2752       3d/compare-eax-and 0xd/imm32/carriage-return
2753       74/jump-if-= break/disp8
2754       # if (c == '\n') break
2755       3d/compare-eax-and 0xa/imm32/newline
2756       74/jump-if-= break/disp8
2757       # if (c == '(') break
2758       3d/compare-eax-and 0x28/imm32/open-paren
2759       0f 84/jump-if-= break/disp32
2760       # if (c == ')') break
2761       3d/compare-eax-and 0x29/imm32/close-paren
2762       0f 84/jump-if-= break/disp32
2763       # if (c == ',') break
2764       3d/compare-eax-and 0x2c/imm32/comma
2765       0f 84/jump-if-= break/disp32
2766       # ++in->read
2767       ff 0/subop/increment *(esi+4)
2768       #
2769       e9/jump loop/disp32
2770     }
2771 $next-mu-token:done:
2772     # out->end = &in->data[in->read]
2773     8b/-> *(esi+4) 1/r32/ecx
2774     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2775     89/<- *(edi+4) 0/r32/eax
2776 $next-mu-token:end:
2777     # . restore registers
2778     5f/pop-to-edi
2779     5e/pop-to-esi
2780     59/pop-to-ecx
2781     58/pop-to-eax
2782     # . epilogue
2783     89/<- %esp 5/r32/ebp
2784     5d/pop-to-ebp
2785     c3/return
2786 
2787 # return the index in an array of strings matching 's'
2788 # index is denominated in elements, not bytes
2789 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
2790     # . prologue
2791     55/push-ebp
2792     89/<- %ebp 4/r32/esp
2793     # . save registers
2794     51/push-ecx
2795     52/push-edx
2796     53/push-ebx
2797     56/push-esi
2798 #?     (write-buffered Stderr "pos-slice: ")
2799 #?     (write-slice-buffered Stderr *(ebp+0xc))
2800 #?     (write-buffered Stderr "\n")
2801 #?     (flush Stderr)
2802     # esi = arr
2803     8b/-> *(ebp+8) 6/r32/esi
2804     # var index/ecx: int = 0
2805     b9/copy-to-ecx 0/imm32
2806     # var curr/edx: (addr (addr array byte)) = arr->data
2807     8d/copy-address *(esi+0xc) 2/r32/edx
2808     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
2809     8b/-> *esi 3/r32/ebx
2810     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
2811     {
2812 #?       (write-buffered Stderr "  ")
2813 #?       (print-int32-buffered Stderr %ecx)
2814 #?       (write-buffered Stderr "\n")
2815 #?       (flush Stderr)
2816       # if (curr >= max) return -1
2817       39/compare %edx 3/r32/ebx
2818       b8/copy-to-eax -1/imm32
2819       73/jump-if-addr>= $pos-slice:end/disp8
2820       # if (slice-equal?(s, *curr)) break
2821       (slice-equal? *(ebp+0xc) *edx)  # => eax
2822       3d/compare-eax-and 0/imm32/false
2823       75/jump-if-!= break/disp8
2824       # ++index
2825       41/increment-ecx
2826       # curr += 4
2827       81 0/subop/add %edx 4/imm32
2828       #
2829       eb/jump loop/disp8
2830     }
2831     # return index
2832     89/<- %eax 1/r32/ecx
2833 $pos-slice:end:
2834 #?     (write-buffered Stderr "=> ")
2835 #?     (print-int32-buffered Stderr %eax)
2836 #?     (write-buffered Stderr "\n")
2837     # . restore registers
2838     5e/pop-to-esi
2839     5b/pop-to-ebx
2840     5a/pop-to-edx
2841     59/pop-to-ecx
2842     # . epilogue
2843     89/<- %esp 5/r32/ebp
2844     5d/pop-to-ebp
2845     c3/return
2846 
2847 == data
2848 
2849 Type-id:  # (stream (address array byte))
2850   0x18/imm32/write
2851   0/imm32/read
2852   0x100/imm32/length
2853   # data
2854   "literal"/imm32  # 0
2855   "int"/imm32  # 1
2856   "addr"/imm32  # 2
2857   "array"/imm32  # 3
2858   "handle"/imm32  # 4
2859   "bool"/imm32  # 5
2860   0/imm32
2861   0/imm32
2862   # 0x20
2863   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2864   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2865   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2866   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2867   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2868   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2869   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2870 
2871 == code
2872 
2873 test-parse-var-with-type:
2874     # . prologue
2875     55/push-ebp
2876     89/<- %ebp 4/r32/esp
2877     # (eax..ecx) = "x:"
2878     b8/copy-to-eax "x:"/imm32
2879     8b/-> *eax 1/r32/ecx
2880     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2881     05/add-to-eax 4/imm32
2882     # var slice/ecx: slice = {eax, ecx}
2883     51/push-ecx
2884     50/push-eax
2885     89/<- %ecx 4/r32/esp
2886     # _test-input-stream contains "int"
2887     (clear-stream _test-input-stream)
2888     (write _test-input-stream "int")
2889     #
2890     (parse-var-with-type %ecx _test-input-stream)
2891     8b/-> *eax 2/r32/edx  # Var-name
2892     (check-strings-equal %edx "x" "F - test-var-with-type/name")
2893     8b/-> *(eax+4) 2/r32/edx  # Var-type
2894     (check-ints-equal *edx 1 "F - test-var-with-type/type")
2895     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
2896     # . epilogue
2897     89/<- %esp 5/r32/ebp
2898     5d/pop-to-ebp
2899     c3/return
2900 
2901 test-parse-var-with-type-and-register:
2902     # . prologue
2903     55/push-ebp
2904     89/<- %ebp 4/r32/esp
2905     # (eax..ecx) = "x/eax:"
2906     b8/copy-to-eax "x/eax:"/imm32
2907     8b/-> *eax 1/r32/ecx
2908     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2909     05/add-to-eax 4/imm32
2910     # var slice/ecx: slice = {eax, ecx}
2911     51/push-ecx
2912     50/push-eax
2913     89/<- %ecx 4/r32/esp
2914     # _test-input-stream contains "int"
2915     (clear-stream _test-input-stream)
2916     (write _test-input-stream "int")
2917     #
2918     (parse-var-with-type %ecx _test-input-stream)
2919     8b/-> *eax 2/r32/edx  # Var-name
2920     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
2921     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2922     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
2923     8b/-> *(eax+4) 2/r32/edx  # Var-type
2924     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
2925     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
2926     # . epilogue
2927     89/<- %esp 5/r32/ebp
2928     5d/pop-to-ebp
2929     c3/return
2930 
2931 test-parse-var-with-trailing-characters:
2932     # . prologue
2933     55/push-ebp
2934     89/<- %ebp 4/r32/esp
2935     # (eax..ecx) = "x:"
2936     b8/copy-to-eax "x:"/imm32
2937     8b/-> *eax 1/r32/ecx
2938     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2939     05/add-to-eax 4/imm32
2940     # var slice/ecx: slice = {eax, ecx}
2941     51/push-ecx
2942     50/push-eax
2943     89/<- %ecx 4/r32/esp
2944     # _test-input-stream contains "int,"
2945     (clear-stream _test-input-stream)
2946     (write _test-input-stream "int,")
2947     #
2948     (parse-var-with-type %ecx _test-input-stream)
2949     8b/-> *eax 2/r32/edx  # Var-name
2950     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
2951     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2952     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
2953     8b/-> *(eax+4) 2/r32/edx  # Var-type
2954     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
2955     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
2956     # . epilogue
2957     89/<- %esp 5/r32/ebp
2958     5d/pop-to-ebp
2959     c3/return
2960 
2961 test-parse-var-with-register-and-trailing-characters:
2962     # . prologue
2963     55/push-ebp
2964     89/<- %ebp 4/r32/esp
2965     # (eax..ecx) = "x/eax:"
2966     b8/copy-to-eax "x/eax:"/imm32
2967     8b/-> *eax 1/r32/ecx
2968     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2969     05/add-to-eax 4/imm32
2970     # var slice/ecx: slice = {eax, ecx}
2971     51/push-ecx
2972     50/push-eax
2973     89/<- %ecx 4/r32/esp
2974     # _test-input-stream contains "int,"
2975     (clear-stream _test-input-stream)
2976     (write _test-input-stream "int,")
2977     #
2978     (parse-var-with-type %ecx _test-input-stream)
2979     8b/-> *eax 2/r32/edx  # Var-name
2980     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
2981     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2982     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
2983     8b/-> *(eax+4) 2/r32/edx  # Var-type
2984     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
2985     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
2986     # . epilogue
2987     89/<- %esp 5/r32/ebp
2988     5d/pop-to-ebp
2989     c3/return
2990 
2991 test-parse-var-with-compound-type:
2992     # . prologue
2993     55/push-ebp
2994     89/<- %ebp 4/r32/esp
2995     # (eax..ecx) = "x:"
2996     b8/copy-to-eax "x:"/imm32
2997     8b/-> *eax 1/r32/ecx
2998     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2999     05/add-to-eax 4/imm32
3000     # var slice/ecx: slice = {eax, ecx}
3001     51/push-ecx
3002     50/push-eax
3003     89/<- %ecx 4/r32/esp
3004     # _test-input-stream contains "(addr int)"
3005     (clear-stream _test-input-stream)
3006     (write _test-input-stream "(addr int)")
3007     #
3008     (parse-var-with-type %ecx _test-input-stream)
3009     8b/-> *eax 2/r32/edx  # Var-name
3010     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
3011     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
3012     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
3013     # var type/edx: (handle tree type-id) = var->type
3014     8b/-> *(eax+4) 2/r32/edx  # Var-type
3015     # type->left == atom(addr)
3016     8b/-> *edx 0/r32/eax  # Atom-value
3017     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
3018     # type->right->left == atom(int)
3019     8b/-> *(edx+4) 2/r32/edx  # Tree-right
3020     8b/-> *edx 0/r32/eax  # Tree-left
3021     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
3022     # type->right->right == null
3023     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
3024     # . epilogue
3025     89/<- %esp 5/r32/ebp
3026     5d/pop-to-ebp
3027     c3/return
3028 
3029 # identifier starts with a letter or '$' or '_'
3030 # no constraints at the moment on later letters
3031 # all we really want to do so far is exclude '{', '}' and '->'
3032 is-identifier?:  # in: (addr slice) -> result/eax: boolean
3033     # . prologue
3034     55/push-ebp
3035     89/<- %ebp 4/r32/esp
3036     # if (slice-empty?(in)) return false
3037     (slice-empty? *(ebp+8))  # => eax
3038     3d/compare-eax-and 0/imm32/false
3039     75/jump-if-!= $is-identifier?:false/disp8
3040     # var c/eax: byte = *in->start
3041     8b/-> *(ebp+8) 0/r32/eax
3042     8b/-> *eax 0/r32/eax
3043     8a/copy-byte *eax 0/r32/AL
3044     81 4/subop/and %eax 0xff/imm32
3045     # if (c == '$') return true
3046     3d/compare-eax-and 0x24/imm32/$
3047     74/jump-if-= $is-identifier?:true/disp8
3048     # if (c == '_') return true
3049     3d/compare-eax-and 0x5f/imm32/_
3050     74/jump-if-= $is-identifier?:true/disp8
3051     # drop case
3052     25/and-eax-with 0x5f/imm32
3053     # if (c < 'A') return false
3054     3d/compare-eax-and 0x41/imm32/A
3055     7c/jump-if-< $is-identifier?:false/disp8
3056     # if (c > 'Z') return false
3057     3d/compare-eax-and 0x5a/imm32/Z
3058     7f/jump-if-> $is-identifier?:false/disp8
3059     # otherwise return true
3060 $is-identifier?:true:
3061     b8/copy-to-eax 1/imm32/true
3062     eb/jump $is-identifier?:end/disp8
3063 $is-identifier?:false:
3064     b8/copy-to-eax 0/imm32/false
3065 $is-identifier?:end:
3066     # . epilogue
3067     89/<- %esp 5/r32/ebp
3068     5d/pop-to-ebp
3069     c3/return
3070 
3071 test-is-identifier-dollar:
3072     # . prologue
3073     55/push-ebp
3074     89/<- %ebp 4/r32/esp
3075     # (eax..ecx) = "$a"
3076     b8/copy-to-eax "$a"/imm32
3077     8b/-> *eax 1/r32/ecx
3078     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3079     05/add-to-eax 4/imm32
3080     # var slice/ecx: slice = {eax, ecx}
3081     51/push-ecx
3082     50/push-eax
3083     89/<- %ecx 4/r32/esp
3084     #
3085     (is-identifier? %ecx)
3086     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
3087     # . epilogue
3088     89/<- %esp 5/r32/ebp
3089     5d/pop-to-ebp
3090     c3/return
3091 
3092 test-is-identifier-underscore:
3093     # . prologue
3094     55/push-ebp
3095     89/<- %ebp 4/r32/esp
3096     # (eax..ecx) = "_a"
3097     b8/copy-to-eax "_a"/imm32
3098     8b/-> *eax 1/r32/ecx
3099     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3100     05/add-to-eax 4/imm32
3101     # var slice/ecx: slice = {eax, ecx}
3102     51/push-ecx
3103     50/push-eax
3104     89/<- %ecx 4/r32/esp
3105     #
3106     (is-identifier? %ecx)
3107     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
3108     # . epilogue
3109     89/<- %esp 5/r32/ebp
3110     5d/pop-to-ebp
3111     c3/return
3112 
3113 test-is-identifier-a:
3114     # . prologue
3115     55/push-ebp
3116     89/<- %ebp 4/r32/esp
3117     # (eax..ecx) = "a$"
3118     b8/copy-to-eax "a$"/imm32
3119     8b/-> *eax 1/r32/ecx
3120     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3121     05/add-to-eax 4/imm32
3122     # var slice/ecx: slice = {eax, ecx}
3123     51/push-ecx
3124     50/push-eax
3125     89/<- %ecx 4/r32/esp
3126     #
3127     (is-identifier? %ecx)
3128     (check-ints-equal %eax 1 "F - test-is-identifier-a")
3129     # . epilogue
3130     89/<- %esp 5/r32/ebp
3131     5d/pop-to-ebp
3132     c3/return
3133 
3134 test-is-identifier-z:
3135     # . prologue
3136     55/push-ebp
3137     89/<- %ebp 4/r32/esp
3138     # (eax..ecx) = "z$"
3139     b8/copy-to-eax "z$"/imm32
3140     8b/-> *eax 1/r32/ecx
3141     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3142     05/add-to-eax 4/imm32
3143     # var slice/ecx: slice = {eax, ecx}
3144     51/push-ecx
3145     50/push-eax
3146     89/<- %ecx 4/r32/esp
3147     #
3148     (is-identifier? %ecx)
3149     (check-ints-equal %eax 1 "F - test-is-identifier-z")
3150     # . epilogue
3151     89/<- %esp 5/r32/ebp
3152     5d/pop-to-ebp
3153     c3/return
3154 
3155 test-is-identifier-A:
3156     # . prologue
3157     55/push-ebp
3158     89/<- %ebp 4/r32/esp
3159     # (eax..ecx) = "A$"
3160     b8/copy-to-eax "A$"/imm32
3161     8b/-> *eax 1/r32/ecx
3162     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3163     05/add-to-eax 4/imm32
3164     # var slice/ecx: slice = {eax, ecx}
3165     51/push-ecx
3166     50/push-eax
3167     89/<- %ecx 4/r32/esp
3168     #
3169     (is-identifier? %ecx)
3170     (check-ints-equal %eax 1 "F - test-is-identifier-A")
3171     # . epilogue
3172     89/<- %esp 5/r32/ebp
3173     5d/pop-to-ebp
3174     c3/return
3175 
3176 test-is-identifier-Z:
3177     # . prologue
3178     55/push-ebp
3179     89/<- %ebp 4/r32/esp
3180     # (eax..ecx) = "Z$"
3181     b8/copy-to-eax "Z$"/imm32
3182     8b/-> *eax 1/r32/ecx
3183     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3184     05/add-to-eax 4/imm32
3185     # var slice/ecx: slice = {eax, ecx}
3186     51/push-ecx
3187     50/push-eax
3188     89/<- %ecx 4/r32/esp
3189     #
3190     (is-identifier? %ecx)
3191     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
3192     # . epilogue
3193     89/<- %esp 5/r32/ebp
3194     5d/pop-to-ebp
3195     c3/return
3196 
3197 test-is-identifier-@:
3198     # character before 'A' is invalid
3199     # . prologue
3200     55/push-ebp
3201     89/<- %ebp 4/r32/esp
3202     # (eax..ecx) = "@a"
3203     b8/copy-to-eax "@a"/imm32
3204     8b/-> *eax 1/r32/ecx
3205     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3206     05/add-to-eax 4/imm32
3207     # var slice/ecx: slice = {eax, ecx}
3208     51/push-ecx
3209     50/push-eax
3210     89/<- %ecx 4/r32/esp
3211     #
3212     (is-identifier? %ecx)
3213     (check-ints-equal %eax 0 "F - test-is-identifier-@")
3214     # . epilogue
3215     89/<- %esp 5/r32/ebp
3216     5d/pop-to-ebp
3217     c3/return
3218 
3219 test-is-identifier-square-bracket:
3220     # character after 'Z' is invalid
3221     # . prologue
3222     55/push-ebp
3223     89/<- %ebp 4/r32/esp
3224     # (eax..ecx) = "[a"
3225     b8/copy-to-eax "[a"/imm32
3226     8b/-> *eax 1/r32/ecx
3227     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3228     05/add-to-eax 4/imm32
3229     # var slice/ecx: slice = {eax, ecx}
3230     51/push-ecx
3231     50/push-eax
3232     89/<- %ecx 4/r32/esp
3233     #
3234     (is-identifier? %ecx)
3235     (check-ints-equal %eax 0 "F - test-is-identifier-@")
3236     # . epilogue
3237     89/<- %esp 5/r32/ebp
3238     5d/pop-to-ebp
3239     c3/return
3240 
3241 test-is-identifier-backtick:
3242     # character before 'a' is invalid
3243     # . prologue
3244     55/push-ebp
3245     89/<- %ebp 4/r32/esp
3246     # (eax..ecx) = "`a"
3247     b8/copy-to-eax "`a"/imm32
3248     8b/-> *eax 1/r32/ecx
3249     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3250     05/add-to-eax 4/imm32
3251     # var slice/ecx: slice = {eax, ecx}
3252     51/push-ecx
3253     50/push-eax
3254     89/<- %ecx 4/r32/esp
3255     #
3256     (is-identifier? %ecx)
3257     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
3258     # . epilogue
3259     89/<- %esp 5/r32/ebp
3260     5d/pop-to-ebp
3261     c3/return
3262 
3263 test-is-identifier-curly-brace-open:
3264     # character after 'z' is invalid; also used for blocks
3265     # . prologue
3266     55/push-ebp
3267     89/<- %ebp 4/r32/esp
3268     # (eax..ecx) = "{a"
3269     b8/copy-to-eax "{a"/imm32
3270     8b/-> *eax 1/r32/ecx
3271     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3272     05/add-to-eax 4/imm32
3273     # var slice/ecx: slice = {eax, ecx}
3274     51/push-ecx
3275     50/push-eax
3276     89/<- %ecx 4/r32/esp
3277     #
3278     (is-identifier? %ecx)
3279     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
3280     # . epilogue
3281     89/<- %esp 5/r32/ebp
3282     5d/pop-to-ebp
3283     c3/return
3284 
3285 test-is-identifier-curly-brace-close:
3286     # . prologue
3287     55/push-ebp
3288     89/<- %ebp 4/r32/esp
3289     # (eax..ecx) = "}a"
3290     b8/copy-to-eax "}a"/imm32
3291     8b/-> *eax 1/r32/ecx
3292     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3293     05/add-to-eax 4/imm32
3294     # var slice/ecx: slice = {eax, ecx}
3295     51/push-ecx
3296     50/push-eax
3297     89/<- %ecx 4/r32/esp
3298     #
3299     (is-identifier? %ecx)
3300     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
3301     # . epilogue
3302     89/<- %esp 5/r32/ebp
3303     5d/pop-to-ebp
3304     c3/return
3305 
3306 test-is-identifier-hyphen:
3307     # disallow leading '-' since '->' has special meaning
3308     # . prologue
3309     55/push-ebp
3310     89/<- %ebp 4/r32/esp
3311     # (eax..ecx) = "-a"
3312     b8/copy-to-eax "-a"/imm32
3313     8b/-> *eax 1/r32/ecx
3314     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3315     05/add-to-eax 4/imm32
3316     # var slice/ecx: slice = {eax, ecx}
3317     51/push-ecx
3318     50/push-eax
3319     89/<- %ecx 4/r32/esp
3320     #
3321     (is-identifier? %ecx)
3322     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
3323     # . epilogue
3324     89/<- %esp 5/r32/ebp
3325     5d/pop-to-ebp
3326     c3/return
3327 
3328 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
3329     # . prologue
3330     55/push-ebp
3331     89/<- %ebp 4/r32/esp
3332     # . save registers
3333     50/push-eax
3334     56/push-esi
3335     57/push-edi
3336     # esi = in
3337     8b/-> *(ebp+8) 6/r32/esi
3338     # edi = out
3339     8b/-> *(ebp+0xc) 7/r32/edi
3340     # initialize some global state
3341     c7 0/subop/copy *Curr-block-depth 1/imm32
3342     c7 0/subop/copy *Next-local-stack-offset -4/imm32
3343     # var eax: (handle block) = parse-mu-block(in, vars, fn)
3344     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
3345     # out->body = eax
3346     89/<- *(edi+0x10) 0/r32/eax  # Function-body
3347 $populate-mu-function-body:end:
3348     # . restore registers
3349     5f/pop-to-edi
3350     5e/pop-to-esi
3351     58/pop-to-eax
3352     # . epilogue
3353     89/<- %esp 5/r32/ebp
3354     5d/pop-to-ebp
3355     c3/return
3356 
3357 == data
3358 
3359 # Global state added to each var record when parsing a function
3360 
3361 Curr-block-depth:  # (addr int)
3362     0/imm32
3363 Next-local-stack-offset:  # (addr int)
3364     -4/imm32
3365 
3366 Next-block-index:  # (addr int)
3367     1/imm32
3368 
3369 == code
3370 
3371 # parses a block, assuming that the leading '{' has already been read by the caller
3372 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
3373     # pseudocode:
3374     #   var line: (stream byte 512)
3375     #   var word-slice: slice
3376     #   increment *Curr-block-depth
3377     #   result/eax = allocate(Heap, Stmt-size)
3378     #   result->tag = 0/block
3379     #   result->name = some unique name
3380     #   while true                                  # line loop
3381     #     clear-stream(line)
3382     #     read-line-buffered(in, line)
3383     #     if (line->write == 0) break               # end of file
3384     #     word-slice = next-mu-token(line)
3385     #     if slice-empty?(word-slice)               # end of line
3386     #       continue
3387     #     else if slice-starts-with?(word-slice, "#")
3388     #       continue
3389     #     else if slice-equal?(word-slice, "{")
3390     #       assert(no-tokens-in(line))
3391     #       block = parse-mu-block(in, vars, fn)
3392     #       append-to-block(result, block)
3393     #     else if slice-equal?(word-slice, "}")
3394     #       break
3395     #     else if slice-ends-with?(word-slice, ":")
3396     #       # TODO: error-check the rest of 'line'
3397     #       --word-slice->end to skip ':'
3398     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
3399     #       append-to-block(result, named-block)
3400     #     else if slice-equal?(word-slice, "var")
3401     #       var-def = parse-mu-var-def(line, vars)
3402     #       append-to-block(result, var-def)
3403     #     else
3404     #       stmt = parse-mu-stmt(line, vars, fn)
3405     #       append-to-block(result, stmt)
3406     #   decrement *Curr-block-depth
3407     #   return result
3408     #
3409     # . prologue
3410     55/push-ebp
3411     89/<- %ebp 4/r32/esp
3412     # . save registers
3413     51/push-ecx
3414     52/push-edx
3415     53/push-ebx
3416     57/push-edi
3417     # var line/ecx: (stream byte 512)
3418     81 5/subop/subtract %esp 0x200/imm32
3419     68/push 0x200/imm32/length
3420     68/push 0/imm32/read
3421     68/push 0/imm32/write
3422     89/<- %ecx 4/r32/esp
3423     # var word-slice/edx: slice
3424     68/push 0/imm32/end
3425     68/push 0/imm32/start
3426     89/<- %edx 4/r32/esp
3427     # edi = result
3428     (allocate Heap *Stmt-size)  # => eax
3429     (zero-out %eax *Stmt-size)
3430     89/<- %edi 0/r32/eax
3431     # set result->tag
3432     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
3433     # set result->var
3434     (new-block-name *(ebp+0x10))  # => eax
3435     89/<- *(edi+8) 0/r32/eax  # Block-var
3436     # push result->var to vars
3437     (push *(ebp+0xc) %eax)
3438     # increment *Curr-block-depth
3439     ff 0/subop/increment *Curr-block-depth
3440     {
3441 $parse-mu-block:line-loop:
3442       # line = read-line-buffered(in)
3443       (clear-stream %ecx)
3444       (read-line-buffered *(ebp+8) %ecx)
3445 #?       (write-buffered Stderr "line: ")
3446 #?       (write-stream-data Stderr %ecx)
3447 #?       (write-buffered Stderr Newline)
3448 #?       (flush Stderr)
3449       # if (line->write == 0) break
3450       81 7/subop/compare *ecx 0/imm32
3451       0f 84/jump-if-= break/disp32
3452       # word-slice = next-mu-token(line)
3453       (next-mu-token %ecx %edx)
3454 #?       (write-buffered Stderr "word: ")
3455 #?       (write-slice-buffered Stderr %edx)
3456 #?       (write-buffered Stderr Newline)
3457 #?       (flush Stderr)
3458       # if slice-empty?(word-slice) continue
3459       (slice-empty? %edx)
3460       3d/compare-eax-and 0/imm32/false
3461       0f 85/jump-if-!= loop/disp32
3462       # if (slice-starts-with?(word-slice, '#') continue
3463       # . eax = *word-slice->start
3464       8b/-> *edx 0/r32/eax
3465       8a/copy-byte *eax 0/r32/AL
3466       81 4/subop/and %eax 0xff/imm32
3467       # . if (eax == '#') continue
3468       3d/compare-eax-and 0x23/imm32/hash
3469       0f 84/jump-if-= loop/disp32
3470       # if slice-equal?(word-slice, "{")
3471       {
3472 $parse-mu-block:check-for-block:
3473         (slice-equal? %edx "{")
3474         3d/compare-eax-and 0/imm32/false
3475         74/jump-if-= break/disp8
3476         (check-no-tokens-left %ecx)
3477         # parse new block and append
3478         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3479         (append-to-block Heap %edi %eax)
3480         e9/jump $parse-mu-block:line-loop/disp32
3481       }
3482       # if slice-equal?(word-slice, "}") break
3483 $parse-mu-block:check-for-end:
3484       (slice-equal? %edx "}")
3485       3d/compare-eax-and 0/imm32/false
3486       0f 85/jump-if-!= break/disp32
3487       # if slice-ends-with?(word-slice, ":") parse named block and append
3488       {
3489 $parse-mu-block:check-for-named-block:
3490         # . eax = *(word-slice->end-1)
3491         8b/-> *(edx+4) 0/r32/eax
3492         48/decrement-eax
3493         8a/copy-byte *eax 0/r32/AL
3494         81 4/subop/and %eax 0xff/imm32
3495         # . if (eax != ':') break
3496         3d/compare-eax-and 0x3a/imm32/colon
3497         0f 85/jump-if-!= break/disp32
3498         # TODO: error-check the rest of 'line'
3499         #
3500         # skip ':'
3501         ff 1/subop/decrement *(edx+4)  # Slice-end
3502         #
3503         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3504         (append-to-block Heap %edi %eax)
3505         e9/jump $parse-mu-block:line-loop/disp32
3506       }
3507       # if slice-equal?(word-slice, "var")
3508       {
3509 $parse-mu-block:check-for-var:
3510         (slice-equal? %edx "var")
3511         3d/compare-eax-and 0/imm32/false
3512         74/jump-if-= break/disp8
3513         #
3514         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
3515         (append-to-block Heap %edi %eax)
3516         e9/jump $parse-mu-block:line-loop/disp32
3517       }
3518 $parse-mu-block:regular-stmt:
3519       # otherwise
3520       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3521       (append-to-block Heap %edi %eax)
3522       e9/jump loop/disp32
3523     } # end line loop
3524     # decrement *Curr-block-depth
3525     ff 1/subop/decrement *Curr-block-depth
3526     #
3527     (pop *(ebp+0xc))  # => eax
3528     # return result
3529     89/<- %eax 7/r32/edi
3530 $parse-mu-block:end:
3531     # . reclaim locals
3532     81 0/subop/add %esp 0x214/imm32
3533     # . restore registers
3534     5f/pop-to-edi
3535     5b/pop-to-ebx
3536     5a/pop-to-edx
3537     59/pop-to-ecx
3538     # . epilogue
3539     89/<- %esp 5/r32/ebp
3540     5d/pop-to-ebp
3541     c3/return
3542 
3543 $parse-mu-block:abort:
3544     # error("'{' or '}' should be on its own line, but got '")
3545     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3546     (rewind-stream %ecx)
3547     (write-stream 2 %ecx)
3548     (write-buffered Stderr "'\n")
3549     (flush Stderr)
3550     # . syscall(exit, 1)
3551     bb/copy-to-ebx  1/imm32
3552     b8/copy-to-eax  1/imm32/exit
3553     cd/syscall  0x80/imm8
3554     # never gets here
3555 
3556 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
3557     # . prologue
3558     55/push-ebp
3559     89/<- %ebp 4/r32/esp
3560     # . save registers
3561     51/push-ecx
3562     52/push-edx
3563     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
3564     8b/-> *(ebp+8) 0/r32/eax
3565     8b/-> *eax 0/r32/eax  # Function-name
3566     8b/-> *eax 0/r32/eax  # String-length
3567     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
3568     89/<- %ecx 0/r32/eax
3569     # var name/edx: (stream byte n)
3570     29/subtract %esp 1/r32/ecx
3571     ff 6/subop/push %ecx
3572     68/push 0/imm32/read
3573     68/push 0/imm32/write
3574     89/<- %edx 4/r32/esp
3575     (clear-stream %edx)
3576     # eax = fn->name
3577     8b/-> *(ebp+8) 0/r32/eax
3578     8b/-> *eax 0/r32/eax  # Function-name
3579     # construct result using Next-block-index (and increment it)
3580     (write %edx "$")
3581     (write %edx %eax)
3582     (write %edx ":")
3583     (print-int32 %edx *Next-block-index)
3584     ff 0/subop/increment *Next-block-index
3585     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
3586     # . eax = name->write
3587     8b/-> *edx 0/r32/eax
3588     # . edx = name->data
3589     8d/copy-address *(edx+0xc) 2/r32/edx
3590     # . eax = name->write + name->data
3591     01/add %eax 2/r32/edx
3592     # . push {edx, eax}
3593     ff 6/subop/push %eax
3594     ff 6/subop/push %edx
3595     89/<- %eax 4/r32/esp
3596     # var final-name/edx: (addr array byte) = slice-to-string(s)
3597     (slice-to-string Heap %eax)  # => eax
3598     89/<- %edx 0/r32/eax
3599     # set result->var
3600     # . var type/eax: (handle tree type-id) = literal
3601     (allocate Heap *Tree-size)  # => eax
3602     (zero-out %eax *Tree-size)  # default type is 'literal'
3603     # . var result/eax: (handle var) = new-var(final-name, type)
3604     (new-var Heap %edx %eax *Curr-block-depth 0 0)  # => eax
3605 $new-block-name:end:
3606     # . reclaim locals
3607     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
3608     81 0/subop/add %ecx 8/imm32  # slice
3609     01/add %esp 1/r32/ecx
3610     # . restore registers
3611     5a/pop-to-edx
3612     59/pop-to-ecx
3613     # . epilogue
3614     89/<- %esp 5/r32/ebp
3615     5d/pop-to-ebp
3616     c3/return
3617 
3618 check-no-tokens-left:  # line: (addr stream byte)
3619     # . prologue
3620     55/push-ebp
3621     89/<- %ebp 4/r32/esp
3622     # . save registers
3623     50/push-eax
3624     51/push-ecx
3625     # var s/ecx: slice
3626     68/push 0/imm32/end
3627     68/push 0/imm32/start
3628     89/<- %ecx 4/r32/esp
3629     #
3630     (next-mu-token *(ebp+8) %ecx)
3631     # if slice-empty?(s) return
3632     (slice-empty? %ecx)
3633     3d/compare-eax-and 0/imm32/false
3634     75/jump-if-!= $check-no-tokens-left:end/disp8
3635     # if (slice-starts-with?(s, '#') return
3636     # . eax = *s->start
3637     8b/-> *edx 0/r32/eax
3638     8a/copy-byte *eax 0/r32/AL
3639     81 4/subop/and %eax 0xff/imm32
3640     # . if (eax == '#') continue
3641     3d/compare-eax-and 0x23/imm32/hash
3642     74/jump-if-= $check-no-tokens-left:end/disp8
3643     # abort
3644     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3645     (rewind-stream %ecx)
3646     (write-stream 2 %ecx)
3647     (write-buffered Stderr "'\n")
3648     (flush Stderr)
3649     # . syscall(exit, 1)
3650     bb/copy-to-ebx  1/imm32
3651     b8/copy-to-eax  1/imm32/exit
3652     cd/syscall  0x80/imm8
3653     # never gets here
3654 $check-no-tokens-left:end:
3655     # . reclaim locals
3656     81 0/subop/add %esp 8/imm32
3657     # . restore registers
3658     59/pop-to-ecx
3659     58/pop-to-eax
3660     # . epilogue
3661     89/<- %esp 5/r32/ebp
3662     5d/pop-to-ebp
3663     c3/return
3664 
3665 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3666     # pseudocode:
3667     #   var s: (addr array byte) = slice-to-string(name)
3668     #   var v: (handle var) = new-var(s, 0)
3669     #   v->block-depth = *Curr-block-depth  # containing block depth
3670     #   push(vars, v)
3671     #   result = parse-mu-block(in, vars, fn)
3672     #   pop(vars)
3673     #   result->name = s
3674     #   return result
3675     #
3676     # . prologue
3677     55/push-ebp
3678     89/<- %ebp 4/r32/esp
3679     # . save registers
3680     51/push-ecx
3681     # var s/ecx: (addr array byte) = slice-to-string(name)
3682     (slice-to-string Heap *(ebp+8))  # => eax
3683     89/<- %ecx 0/r32/eax
3684     # var type/eax: (handle tree type-id) = literal
3685     (allocate Heap *Tree-size)  # => eax
3686     (zero-out %eax *Tree-size)  # default type is 'literal'
3687     # var v/ecx: (handle var) = new-var(s, type)
3688     (new-var Heap %ecx %eax *Curr-block-depth 0 0)  # => eax
3689     89/<- %ecx 0/r32/eax
3690     # push(vars, v)
3691     (push *(ebp+0x10) %ecx)
3692     # eax = result
3693     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
3694     # pop the var
3695     50/push-eax
3696     (pop *(ebp+0x10))  # => eax
3697     58/pop-to-eax
3698     # result->tag = named-block
3699     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
3700     # result->var = v
3701     89/<- *(eax+8) 1/r32/ecx  # Block-var
3702 $parse-mu-named-block:end:
3703     # . restore registers
3704     59/pop-to-ecx
3705     # . epilogue
3706     89/<- %esp 5/r32/ebp
3707     5d/pop-to-ebp
3708     c3/return
3709 
3710 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
3711     # . prologue
3712     55/push-ebp
3713     89/<- %ebp 4/r32/esp
3714     # . save registers
3715     51/push-ecx
3716     52/push-edx
3717     # var word-slice/ecx: slice
3718     68/push 0/imm32/end
3719     68/push 0/imm32/start
3720     89/<- %ecx 4/r32/esp
3721     # var v/edx: (handle var) = parse-var-with-type(line)
3722     (next-mu-token *(ebp+8) %ecx)
3723     (parse-var-with-type %ecx *(ebp+8))  # => eax
3724     89/<- %edx 0/r32/eax
3725     # v->block-depth = *Curr-block-depth
3726     8b/-> *Curr-block-depth 0/r32/eax
3727     89/<- *(edx+8) 0/r32/eax
3728     #
3729     (push *(ebp+0xc) %edx)
3730     # either v has no register and there's no more to this line
3731     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
3732     3d/compare-eax-and 0/imm32
3733     {
3734       75/jump-if-!= break/disp8
3735       # v->stack-offset = *Next-local-stack-offset
3736       8b/-> *Next-local-stack-offset 0/r32/eax
3737       89/<- *(edx+0xc) 0/r32/eax  # Var-stack-offset
3738       # TODO: ensure that there's nothing else on this line
3739       (new-vardef Heap %edx)  # => eax
3740       eb/jump $parse-mu-var-def:end/disp8
3741     }
3742     # or v has a register and there's more to this line
3743     {
3744       74/jump-if-= break/disp8
3745       # ensure that the next word is '<-'
3746       (next-mu-token *(ebp+8) %ecx)
3747       (slice-equal? %ecx "<-")  # => eax
3748       3d/compare-eax-and 0/imm32/false
3749       74/jump-if-= $parse-mu-var-def:abort/disp8
3750       #
3751       (new-regvardef Heap %edx)  # => eax
3752       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
3753     }
3754 $parse-mu-var-def:end:
3755     # *Next-local-stack-offset -= size-of(v)
3756     50/push-eax
3757     (size-of %edx)  # => eax
3758     29/subtract-from *Next-local-stack-offset 0/r32/eax
3759     58/pop-to-eax
3760     # . reclaim locals
3761     81 0/subop/add %esp 8/imm32
3762     # . restore registers
3763     5a/pop-to-edx
3764     59/pop-to-ecx
3765     # . epilogue
3766     89/<- %esp 5/r32/ebp
3767     5d/pop-to-ebp
3768     c3/return
3769 
3770 $parse-mu-var-def:abort:
3771     (rewind-stream *(ebp+8))
3772     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
3773     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
3774     (flush Stderr)
3775     (write-stream 2 *(ebp+8))
3776     (write-buffered Stderr "'\n")
3777     (flush Stderr)
3778     # . syscall(exit, 1)
3779     bb/copy-to-ebx  1/imm32
3780     b8/copy-to-eax  1/imm32/exit
3781     cd/syscall  0x80/imm8
3782     # never gets here
3783 
3784 test-parse-mu-var-def:
3785     # 'var n: int'
3786     # . prologue
3787     55/push-ebp
3788     89/<- %ebp 4/r32/esp
3789     # setup
3790     (clear-stream _test-input-stream)
3791     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
3792     c7 0/subop/copy *Curr-block-depth 1/imm32
3793     c7 0/subop/copy *Next-local-stack-offset -4/imm32
3794     # var vars/ecx: (stack (addr var) 4)
3795     81 5/subop/subtract %esp 0x10/imm32
3796     68/push 0x10/imm32/length
3797     68/push 0/imm32/top
3798     89/<- %ecx 4/r32/esp
3799     (clear-stack %ecx)
3800     # convert
3801     (parse-mu-var-def _test-input-stream %ecx)  # => eax
3802     # check result
3803     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is vardef
3804     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
3805     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
3806     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
3807     (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
3808     (check-ints-equal *(eax+0xc) -4 "F - test-parse-mu-reg-var-def/output-stack-offset")  # Var-stack-offset
3809     # ensure type is int
3810     8b/-> *(eax+4) 0/r32/eax  # Var-type
3811     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
3812     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
3813     # globals
3814     (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
3815     # . epilogue
3816     89/<- %esp 5/r32/ebp
3817     5d/pop-to-ebp
3818     c3/return
3819 
3820 test-parse-mu-reg-var-def:
3821     # 'var n/eax: int <- copy 0'
3822     # . prologue
3823     55/push-ebp
3824     89/<- %ebp 4/r32/esp
3825     # setup
3826     (clear-stream _test-input-stream)
3827     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
3828     c7 0/subop/copy *Curr-block-depth 1/imm32
3829     c7 0/subop/copy *Next-local-stack-offset -4/imm32
3830     # var vars/ecx: (stack (addr var) 4)
3831     81 5/subop/subtract %esp 0x10/imm32
3832     68/push 0x10/imm32/length
3833     68/push 0/imm32/top
3834     89/<- %ecx 4/r32/esp
3835     (clear-stack %ecx)
3836     # convert
3837     (parse-mu-var-def _test-input-stream %ecx)  # => eax
3838     # check result
3839     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is regvardef
3840     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
3841     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
3842     8b/-> *eax 0/r32/eax  # Stmt-var-value
3843     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
3844     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
3845     (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
3846     (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-stack-offset")  # Var-stack-offset
3847     # ensure type is int
3848     8b/-> *(eax+4) 0/r32/eax  # Var-type
3849     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
3850     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
3851     # globals
3852     (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
3853     # . epilogue
3854     89/<- %esp 5/r32/ebp
3855     5d/pop-to-ebp
3856     c3/return
3857 
3858 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3859     # pseudocode:
3860     #   var name: slice
3861     #   result = allocate(Heap, Stmt-size)
3862     #   if stmt-has-outputs?(line)
3863     #     while true
3864     #       name = next-mu-token(line)
3865     #       if (name == '<-') break
3866     #       assert(is-identifier?(name))
3867     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
3868     #       result->outputs = append(result->outputs, v)
3869     #   add-operation-and-inputs-to-stmt(result, line, vars)
3870     #
3871     # . prologue
3872     55/push-ebp
3873     89/<- %ebp 4/r32/esp
3874     # . save registers
3875     51/push-ecx
3876     52/push-edx
3877     57/push-edi
3878     # var name/ecx: slice
3879     68/push 0/imm32/end
3880     68/push 0/imm32/start
3881     89/<- %ecx 4/r32/esp
3882     # var is-deref?/edx: boolean = false
3883     ba/copy-to-edx 0/imm32/false
3884     # result/edi: (handle stmt)
3885     (allocate Heap *Stmt-size)  # => eax
3886     (zero-out %eax *Stmt-size)
3887     89/<- %edi 0/r32/eax
3888     # result->tag = 1/stmt
3889     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
3890     {
3891       (stmt-has-outputs? *(ebp+8))
3892       3d/compare-eax-and 0/imm32/false
3893       0f 84/jump-if-= break/disp32
3894       {
3895 $parse-mu-stmt:read-outputs:
3896         # name = next-mu-token(line)
3897         (next-mu-token *(ebp+8) %ecx)
3898         # if slice-empty?(word-slice) break
3899         (slice-empty? %ecx)  # => eax
3900         3d/compare-eax-and 0/imm32/false
3901         0f 85/jump-if-!= break/disp32
3902         # if (name == "<-") break
3903         (slice-equal? %ecx "<-")  # => eax
3904         3d/compare-eax-and 0/imm32/false
3905         0f 85/jump-if-!= break/disp32
3906         # is-deref? = false
3907         ba/copy-to-edx 0/imm32/false
3908         # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
3909         8b/-> *ecx 0/r32/eax  # Slice-start
3910         8a/copy-byte *eax 0/r32/AL
3911         81 4/subop/and %eax 0xff/imm32
3912         3d/compare-eax-and 0x2a/imm32/asterisk
3913         {
3914           75/jump-if-!= break/disp8
3915           ff 0/subop/increment *ecx
3916           ba/copy-to-edx 1/imm32/true
3917         }
3918         # assert(is-identifier?(name))
3919         (is-identifier? %ecx)  # => eax
3920         3d/compare-eax-and 0/imm32/false
3921         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
3922         # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
3923         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3924         (append-stmt-var Heap %eax *(edi+0xc) %edx)  # Stmt1-outputs => eax
3925         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
3926         e9/jump loop/disp32
3927       }
3928     }
3929     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
3930 $parse-mu-stmt:end:
3931     # return result
3932     89/<- %eax 7/r32/edi
3933     # . reclaim locals
3934     81 0/subop/add %esp 8/imm32
3935     # . restore registers
3936     5f/pop-to-edi
3937     5a/pop-to-edx
3938     59/pop-to-ecx
3939     # . epilogue
3940     89/<- %esp 5/r32/ebp
3941     5d/pop-to-ebp
3942     c3/return
3943 
3944 $parse-mu-stmt:abort:
3945     # error("invalid identifier '" name "'\n")
3946     (write-buffered Stderr "invalid identifier '")
3947     (write-slice-buffered Stderr %ecx)
3948     (write-buffered Stderr "'\n")
3949     (flush Stderr)
3950     # . syscall(exit, 1)
3951     bb/copy-to-ebx  1/imm32
3952     b8/copy-to-eax  1/imm32/exit
3953     cd/syscall  0x80/imm8
3954     # never gets here
3955 
3956 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
3957     # pseudocode:
3958     #   stmt->name = slice-to-string(next-mu-token(line))
3959     #   while true
3960     #     name = next-mu-token(line)
3961     #     v = lookup-var-or-literal(name)
3962     #     stmt->inouts = append(stmt->inouts, v)
3963     #
3964     # . prologue
3965     55/push-ebp
3966     89/<- %ebp 4/r32/esp
3967     # . save registers
3968     50/push-eax
3969     51/push-ecx
3970     52/push-edx
3971     57/push-edi
3972     # edi = stmt
3973     8b/-> *(ebp+8) 7/r32/edi
3974     # var name/ecx: slice
3975     68/push 0/imm32/end
3976     68/push 0/imm32/start
3977     89/<- %ecx 4/r32/esp
3978     # var is-deref?/edx: boolean = false
3979     ba/copy-to-edx 0/imm32/false
3980 $add-operation-and-inputs-to-stmt:read-operation:
3981     (next-mu-token *(ebp+0xc) %ecx)
3982     (slice-to-string Heap %ecx)  # => eax
3983     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
3984     {
3985 $add-operation-and-inputs-to-stmt:read-inouts:
3986       # name = next-mu-token(line)
3987       (next-mu-token *(ebp+0xc) %ecx)
3988       # if slice-empty?(word-slice) break
3989       (slice-empty? %ecx)  # => eax
3990       3d/compare-eax-and 0/imm32/false
3991       0f 85/jump-if-!= break/disp32
3992       # if (name == "<-") abort
3993       (slice-equal? %ecx "<-")
3994       3d/compare-eax-and 0/imm32/false
3995       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
3996       # is-deref? = false
3997       ba/copy-to-edx 0/imm32/false
3998       # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
3999       8b/-> *ecx 0/r32/eax  # Slice-start
4000       8a/copy-byte *eax 0/r32/AL
4001       81 4/subop/and %eax 0xff/imm32
4002       3d/compare-eax-and 0x2a/imm32/asterisk
4003       {
4004         75/jump-if-!= break/disp8
4005 $add-operation-and-inputs-to-stmt:inout-is-deref:
4006         ff 0/subop/increment *ecx
4007         ba/copy-to-edx 1/imm32/true
4008       }
4009       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
4010       (append-stmt-var Heap %eax *(edi+8) %edx)  # Stmt1-inouts or Regvardef-inouts => eax
4011       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
4012       e9/jump loop/disp32
4013     }
4014 $add-operation-and-inputs-to-stmt:end:
4015     # . reclaim locals
4016     81 0/subop/add %esp 8/imm32
4017     # . restore registers
4018     5f/pop-to-edi
4019     5a/pop-to-edx
4020     59/pop-to-ecx
4021     58/pop-to-eax
4022     # . epilogue
4023     89/<- %esp 5/r32/ebp
4024     5d/pop-to-ebp
4025     c3/return
4026 
4027 $add-operation-and-inputs-to-stmt:abort:
4028     # error("invalid statement '" line "'\n")
4029     (rewind-stream *(ebp+8))
4030     (write-buffered Stderr "invalid identifier '")
4031     (flush Stderr)
4032     (write-stream 2 *(ebp+8))
4033     (write-buffered Stderr "'\n")
4034     (flush Stderr)
4035     # . syscall(exit, 1)
4036     bb/copy-to-ebx  1/imm32
4037     b8/copy-to-eax  1/imm32/exit
4038     cd/syscall  0x80/imm8
4039     # never gets here
4040 
4041 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
4042     # . prologue
4043     55/push-ebp
4044     89/<- %ebp 4/r32/esp
4045     # . save registers
4046     51/push-ecx
4047     # var word-slice/ecx: slice
4048     68/push 0/imm32/end
4049     68/push 0/imm32/start
4050     89/<- %ecx 4/r32/esp
4051     # result = false
4052     b8/copy-to-eax 0/imm32/false
4053     (rewind-stream *(ebp+8))
4054     {
4055       (next-mu-token *(ebp+8) %ecx)
4056       # if slice-empty?(word-slice) break
4057       (slice-empty? %ecx)
4058       3d/compare-eax-and 0/imm32/false
4059       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
4060       0f 85/jump-if-!= break/disp32
4061       # if slice-starts-with?(word-slice, '#') break
4062       # . eax = *word-slice->start
4063       8b/-> *ecx 0/r32/eax
4064       8a/copy-byte *eax 0/r32/AL
4065       81 4/subop/and %eax 0xff/imm32
4066       # . if (eax == '#') break
4067       3d/compare-eax-and 0x23/imm32/hash
4068       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
4069       0f 84/jump-if-= break/disp32
4070       # if slice-equal?(word-slice, '<-') return true
4071       (slice-equal? %ecx "<-")
4072       3d/compare-eax-and 0/imm32/false
4073       74/jump-if-= loop/disp8
4074       b8/copy-to-eax 1/imm32/true
4075     }
4076 $stmt-has-outputs:end:
4077     (rewind-stream *(ebp+8))
4078     # . reclaim locals
4079     81 0/subop/add %esp 8/imm32
4080     # . restore registers
4081     59/pop-to-ecx
4082     # . epilogue
4083     89/<- %esp 5/r32/ebp
4084     5d/pop-to-ebp
4085     c3/return
4086 
4087 # if 'name' starts with a digit, create a new literal var for it
4088 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
4089 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
4090     # . prologue
4091     55/push-ebp
4092     89/<- %ebp 4/r32/esp
4093     # . save registers
4094     51/push-ecx
4095     56/push-esi
4096     # esi = name
4097     8b/-> *(ebp+8) 6/r32/esi
4098     # if slice-empty?(name) abort
4099     (slice-empty? %esi)  # => eax
4100     3d/compare-eax-and 0/imm32/false
4101     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
4102     # var c/ecx: byte = *name->start
4103     8b/-> *esi 1/r32/ecx
4104     8a/copy-byte *ecx 1/r32/CL
4105     81 4/subop/and %ecx 0xff/imm32
4106     # if is-decimal-digit?(c) return new var(name)
4107     {
4108       (is-decimal-digit? %ecx)  # => eax
4109       81 7/subop/compare %eax 0/imm32/false
4110       74/jump-if-= break/disp8
4111       (new-literal-integer Heap %esi)  # => eax
4112       eb/jump $lookup-var-or-literal:end/disp8
4113     }
4114     # else if (c == '"') return new var(name)
4115     {
4116       81 7/subop/compare %ecx 0x22/imm32/dquote
4117       75/jump-if-!= break/disp8
4118       (new-literal-string Heap %esi)  # => eax
4119       eb/jump $lookup-var-or-literal:end/disp8
4120     }
4121     # otherwise return lookup-var(name, vars)
4122     {
4123       (lookup-var %esi *(ebp+0xc))  # => eax
4124     }
4125 $lookup-var-or-literal:end:
4126     # . restore registers
4127     5e/pop-to-esi
4128     59/pop-to-ecx
4129     # . epilogue
4130     89/<- %esp 5/r32/ebp
4131     5d/pop-to-ebp
4132     c3/return
4133 
4134 $lookup-var-or-literal:abort:
4135     (write-buffered Stderr "empty variable!")
4136     (flush Stderr)
4137     # . syscall(exit, 1)
4138     bb/copy-to-ebx  1/imm32
4139     b8/copy-to-eax  1/imm32/exit
4140     cd/syscall  0x80/imm8
4141     # never gets here
4142 
4143 # return first 'name' from the top (back) of 'vars' and abort if not found
4144 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
4145     # . prologue
4146     55/push-ebp
4147     89/<- %ebp 4/r32/esp
4148     # var target/eax: (handle array byte) = slice-to-string(name)
4149     (slice-to-string Heap *(ebp+8))  # => eax
4150     #
4151     (lookup-var-helper %eax *(ebp+0xc))  # => eax
4152     # if (result == 0) abort
4153     3d/compare-eax-and 0/imm32
4154     74/jump-if-= $lookup-var:abort/disp8
4155 $lookup-var:end:
4156     # . epilogue
4157     89/<- %esp 5/r32/ebp
4158     5d/pop-to-ebp
4159     c3/return
4160 
4161 $lookup-var:abort:
4162     (write-buffered Stderr "unknown variable '")
4163     (write-slice-buffered Stderr *(ebp+8))
4164     (write-buffered Stderr "'\n")
4165     (flush Stderr)
4166     # . syscall(exit, 1)
4167     bb/copy-to-ebx  1/imm32
4168     b8/copy-to-eax  1/imm32/exit
4169     cd/syscall  0x80/imm8
4170     # never gets here
4171 
4172 # return first 'name' from the top (back) of 'vars', and 0/null if not found
4173 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
4174     # pseudocode:
4175     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
4176     #   var min = vars->data
4177     #   while curr >= min
4178     #     var v: (handle var) = *curr
4179     #     if v->name == name
4180     #       return v
4181     #   return 0
4182     #
4183     # . prologue
4184     55/push-ebp
4185     89/<- %ebp 4/r32/esp
4186     # . save registers
4187     52/push-edx
4188     53/push-ebx
4189     56/push-esi
4190     # esi = vars
4191     8b/-> *(ebp+0xc) 6/r32/esi
4192     # ebx = vars->top
4193     8b/-> *esi 3/r32/ebx
4194     # if (vars->top > vars->length) abort
4195     3b/compare 0/r32/eax *(esi+4)
4196     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
4197     # var min/edx: (addr handle var) = vars->data
4198     8d/copy-address *(esi+8) 2/r32/edx
4199     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
4200     81 5/subop/subtract %ebx 4/imm32
4201     8d/copy-address *(esi+ebx+8) 3/r32/ebx
4202     {
4203       # if (curr < min) return 0
4204       39/compare %ebx 2/r32/edx
4205       b8/copy-to-eax 0/imm32
4206       0f 82/jump-if-addr< break/disp32
4207       # var v/eax: (handle var) = *curr
4208       8b/-> *ebx 0/r32/eax
4209       # if (v->name == name) return v
4210       (string-equal? *eax *(ebp+8))  # Var-name
4211       3d/compare-eax-and 0/imm32/false
4212       8b/-> *ebx 0/r32/eax
4213       75/jump-if-!= break/disp8
4214       # curr -= 4
4215       81 5/subop/subtract %ebx 4/imm32
4216       e9/jump loop/disp32
4217     }
4218 $lookup-var-helper:end:
4219     # . restore registers
4220     5e/pop-to-esi
4221     5b/pop-to-ebx
4222     5a/pop-to-edx
4223     # . epilogue
4224     89/<- %esp 5/r32/ebp
4225     5d/pop-to-ebp
4226     c3/return
4227 
4228 $lookup-var-helper:error1:
4229     (write-buffered Stderr "malformed stack when looking up '")
4230     (write-slice-buffered Stderr *(ebp+8))
4231     (write-buffered Stderr "'\n")
4232     (flush Stderr)
4233     # . syscall(exit, 1)
4234     bb/copy-to-ebx  1/imm32
4235     b8/copy-to-eax  1/imm32/exit
4236     cd/syscall  0x80/imm8
4237     # never gets here
4238 
4239 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
4240 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
4241     # . prologue
4242     55/push-ebp
4243     89/<- %ebp 4/r32/esp
4244     # . save registers
4245     51/push-ecx
4246     # var target/ecx: (handle array byte) = slice-to-string(name)
4247     (slice-to-string Heap *(ebp+8))  # => eax
4248     89/<- %ecx 0/r32/eax
4249     #
4250     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
4251     {
4252       # if (result != 0) return
4253       3d/compare-eax-and 0/imm32
4254       75/jump-if-!= break/disp8
4255       # if name is one of fn's outputs, return it
4256       {
4257         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
4258         3d/compare-eax-and 0/imm32
4259         # otherwise abort
4260         0f 84/jump-if-!= $lookup-var:abort/disp32
4261       }
4262     }
4263 $lookup-or-define-var:end:
4264     # . restore registers
4265     59/pop-to-ecx
4266     # . epilogue
4267     89/<- %esp 5/r32/ebp
4268     5d/pop-to-ebp
4269     c3/return
4270 
4271 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
4272     # . prologue
4273     55/push-ebp
4274     89/<- %ebp 4/r32/esp
4275     # . save registers
4276     51/push-ecx
4277     # var curr/ecx: (handle list var) = fn->outputs
4278     8b/-> *(ebp+8) 1/r32/ecx
4279     8b/-> *(ecx+0xc) 1/r32/ecx
4280     # while curr != null
4281     {
4282       81 7/subop/compare %ecx 0/imm32
4283       74/jump-if-= break/disp8
4284       # var v: (handle var) = *curr
4285       8b/-> *ecx 0/r32/eax  # List-value
4286       # if (curr->name == name) return curr
4287       50/push-eax
4288       (string-equal? *eax *(ebp+0xc))
4289       3d/compare-eax-and 0/imm32/false
4290       58/pop-to-eax
4291       75/jump-if-!= $find-in-function-outputs:end/disp8
4292       # curr = curr->next
4293       8b/-> *(ecx+4) 1/r32/ecx  # List-next
4294       eb/jump loop/disp8
4295     }
4296     b8/copy-to-eax 0/imm32
4297 $find-in-function-outputs:end:
4298     # . restore registers
4299     59/pop-to-ecx
4300     # . epilogue
4301     89/<- %esp 5/r32/ebp
4302     5d/pop-to-ebp
4303     c3/return
4304 
4305 test-parse-mu-stmt:
4306     # . prologue
4307     55/push-ebp
4308     89/<- %ebp 4/r32/esp
4309     # setup
4310     (clear-stream _test-input-stream)
4311     (write _test-input-stream "increment n\n")
4312     # var vars/ecx: (stack (addr var) 4)
4313     81 5/subop/subtract %esp 0x10/imm32
4314     68/push 0x10/imm32/length
4315     68/push 0/imm32/top
4316     89/<- %ecx 4/r32/esp
4317     (clear-stack %ecx)
4318     # var v/edx: var
4319     81 5/subop/subtract %esp 0x14/imm32  # Var-size
4320     89/<- %edx 4/r32/esp
4321     (zero-out %edx 0x14)  # Var-size
4322     # v->name = "n"
4323     c7 0/subop/copy *edx "n"/imm32  # Var-name
4324     #
4325     (push %ecx %edx)
4326     # convert
4327     (parse-mu-stmt _test-input-stream %ecx)  # => eax
4328     # check result
4329     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
4330     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
4331     # edx: (handle list var) = result->inouts
4332     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
4333     # ebx: (handle var) = result->inouts->value
4334     8b/-> *edx 3/r32/ebx  # Stmt-var-value
4335     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
4336     # . epilogue
4337     89/<- %esp 5/r32/ebp
4338     5d/pop-to-ebp
4339     c3/return
4340 
4341 test-parse-mu-stmt-with-comma:
4342     # . prologue
4343     55/push-ebp
4344     89/<- %ebp 4/r32/esp
4345     # setup
4346     (clear-stream _test-input-stream)
4347     (write _test-input-stream "copy-to n, 3\n")
4348     # var vars/ecx: (stack (addr var) 4)
4349     81 5/subop/subtract %esp 0x10/imm32
4350     68/push 0x10/imm32/length
4351     68/push 0/imm32/top
4352     89/<- %ecx 4/r32/esp
4353     (clear-stack %ecx)
4354     # var v/edx: var
4355     81 5/subop/subtract %esp 0x14/imm32  # Var-size
4356     89/<- %edx 4/r32/esp
4357     (zero-out %edx 0x14)  # Var-size
4358     # v->name = "n"
4359     c7 0/subop/copy *edx "n"/imm32  # Var-name
4360     #
4361     (push %ecx %edx)
4362     # convert
4363     (parse-mu-stmt _test-input-stream %ecx)  # => eax
4364     # check result
4365     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
4366     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
4367     # edx: (handle list var) = result->inouts
4368     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
4369     # ebx: (handle var) = result->inouts->value
4370     8b/-> *edx 3/r32/ebx  # Stmt-var-value
4371     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
4372     # . epilogue
4373     89/<- %esp 5/r32/ebp
4374     5d/pop-to-ebp
4375     c3/return
4376 
4377 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)
4378     # . prologue
4379     55/push-ebp
4380     89/<- %ebp 4/r32/esp
4381     # . save registers
4382     51/push-ecx
4383     #
4384     (allocate *(ebp+8) *Function-size)  # => eax
4385     8b/-> *(ebp+0xc) 1/r32/ecx
4386     89/<- *eax 1/r32/ecx  # Function-name
4387     8b/-> *(ebp+0x10) 1/r32/ecx
4388     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
4389     8b/-> *(ebp+0x14) 1/r32/ecx
4390     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
4391     8b/-> *(ebp+0x18) 1/r32/ecx
4392     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
4393     8b/-> *(ebp+0x1c) 1/r32/ecx
4394     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
4395     8b/-> *(ebp+0x20) 1/r32/ecx
4396     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
4397 $new-function:end:
4398     # . restore registers
4399     59/pop-to-ecx
4400     # . epilogue
4401     89/<- %esp 5/r32/ebp
4402     5d/pop-to-ebp
4403     c3/return
4404 
4405 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte), type: (addr tree type-id), block: int, stack-offset: int, register: (addr array byte) -> result/eax: (handle var)
4406     # . prologue
4407     55/push-ebp
4408     89/<- %ebp 4/r32/esp
4409     # . save registers
4410     51/push-ecx
4411     #
4412     (allocate *(ebp+8) *Var-size)  # => eax
4413     8b/-> *(ebp+0xc) 1/r32/ecx
4414     89/<- *eax 1/r32/ecx  # Var-name
4415     8b/-> *(ebp+0x10) 1/r32/ecx
4416     89/<- *(eax+4) 1/r32/ecx  # Var-type
4417     8b/-> *(ebp+0x14) 1/r32/ecx
4418     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
4419     8b/-> *(ebp+0x18) 1/r32/ecx
4420     89/<- *(eax+0xc) 1/r32/ecx  # Var-stack-offset
4421     8b/-> *(ebp+0x1c) 1/r32/ecx
4422     89/<- *(eax+0x10) 1/r32/ecx  # Var-register
4423 $new-var:end:
4424     # . restore registers
4425     59/pop-to-ecx
4426     # . epilogue
4427     89/<- %esp 5/r32/ebp
4428     5d/pop-to-ebp
4429     c3/return
4430 
4431 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4432     # . prologue
4433     55/push-ebp
4434     89/<- %ebp 4/r32/esp
4435     # . save registers
4436     51/push-ecx
4437     # if (!is-hex-int?(name)) abort
4438     (is-hex-int? *(ebp+0xc))  # => eax
4439     3d/compare-eax-and 0/imm32/false
4440     0f 84/jump-if-= $new-literal-integer:abort/disp32
4441     # var s/ecx: (addr array byte)
4442     (slice-to-string Heap *(ebp+0xc))  # => eax
4443     89/<- %ecx 0/r32/eax
4444     # result/ecx = new var(s)
4445     (allocate *(ebp+8) *Var-size)  # => eax
4446     (zero-out %eax *Var-size)
4447     89/<- *eax 1/r32/ecx  # Var-name
4448     89/<- %ecx 0/r32/eax
4449     # result->type = new type()
4450     (allocate *(ebp+8) *Tree-size)  # => eax
4451     (zero-out %eax *Tree-size)  # default type is 'literal'
4452     89/<- *(ecx+4) 0/r32/eax  # Var-type
4453     # move result to eax
4454     89/<- %eax 1/r32/ecx
4455 $new-literal-integer:end:
4456     # . restore registers
4457     59/pop-to-ecx
4458     # . epilogue
4459     89/<- %esp 5/r32/ebp
4460     5d/pop-to-ebp
4461     c3/return
4462 
4463 $new-literal-integer:abort:
4464     (write-buffered Stderr "variable cannot begin with a digit '")
4465     (write-slice-buffered Stderr *(ebp+0xc))
4466     (write-buffered Stderr "'\n")
4467     (flush Stderr)
4468     # . syscall(exit, 1)
4469     bb/copy-to-ebx  1/imm32
4470     b8/copy-to-eax  1/imm32/exit
4471     cd/syscall  0x80/imm8
4472     # never gets here
4473 
4474 new-literal-string:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4475     # . prologue
4476     55/push-ebp
4477     89/<- %ebp 4/r32/esp
4478     # . save registers
4479     51/push-ecx
4480     # var s/ecx: (addr array byte)
4481     (slice-to-string Heap *(ebp+0xc))  # => eax
4482     89/<- %ecx 0/r32/eax
4483     # result/ecx = new var(s)
4484     (allocate *(ebp+8) *Var-size)  # => eax
4485     (zero-out %eax *Var-size)
4486     89/<- *eax 1/r32/ecx  # Var-name
4487     89/<- %ecx 0/r32/eax
4488     # result->type = new type()
4489     (allocate *(ebp+8) *Tree-size)  # => eax
4490     (zero-out %eax *Tree-size)  # default type is 'literal'
4491     89/<- *(ecx+4) 0/r32/eax  # Var-type
4492     # move result to eax
4493     89/<- %eax 1/r32/ecx
4494 $new-literal-string:end:
4495     # . restore registers
4496     59/pop-to-ecx
4497     # . epilogue
4498     89/<- %esp 5/r32/ebp
4499     5d/pop-to-ebp
4500     c3/return
4501 
4502 new-label:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4503     # . prologue
4504     55/push-ebp
4505     89/<- %ebp 4/r32/esp
4506     # . save registers
4507     51/push-ecx
4508     # var s/ecx: (addr array byte)
4509     (slice-to-string Heap *(ebp+0xc))  # => eax
4510     89/<- %ecx 0/r32/eax
4511     #
4512     (allocate *(ebp+8) *Var-size)  # => eax
4513     89/<- *eax 1/r32/ecx  # Var-name
4514     89/<- %ecx 0/r32/eax
4515     (allocate *(ebp+8) *Tree-size)  # => eax
4516     (zero-out %eax *Tree-size)  # labels are literals
4517     89/<- *(ecx+4) 0/r32/eax  # Var-type
4518     89/<- %eax 1/r32/ecx
4519     c7 0/subop/copy *(eax+8) 0/imm32  # Var-block-depth
4520     c7 0/subop/copy *(eax+0xc) 0/imm32  # Var-stack-offset
4521     c7 0/subop/copy *(eax+0x10) 0/imm32  # Var-register
4522 $new-label:end:
4523     # . restore registers
4524     59/pop-to-ecx
4525     # . epilogue
4526     89/<- %esp 5/r32/ebp
4527     5d/pop-to-ebp
4528     c3/return
4529 
4530 new-block:  # ad: (addr allocation-descriptor), data: (handle list statement) -> result/eax: (handle statement)
4531     # . prologue
4532     55/push-ebp
4533     89/<- %ebp 4/r32/esp
4534     # . save registers
4535     51/push-ecx
4536     #
4537     (allocate *(ebp+8) *Stmt-size)  # => eax
4538     (zero-out %eax *Stmt-size)
4539     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
4540     8b/-> *(ebp+0xc) 1/r32/ecx
4541     89/<- *(eax+4) 1/r32/ecx  # Block-statements
4542 $new-block:end:
4543     # . restore registers
4544     59/pop-to-ecx
4545     # . epilogue
4546     89/<- %esp 5/r32/ebp
4547     5d/pop-to-ebp
4548     c3/return
4549 
4550 new-vardef:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement)
4551     # . prologue
4552     55/push-ebp
4553     89/<- %ebp 4/r32/esp
4554     # . save registers
4555     51/push-ecx
4556     #
4557     (allocate *(ebp+8) *Stmt-size)  # => eax
4558     (zero-out %eax *Stmt-size)
4559     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
4560     # result->var = var
4561     8b/-> *(ebp+0xc) 1/r32/ecx
4562     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
4563 $new-vardef:end:
4564     # . restore registers
4565     59/pop-to-ecx
4566     # . epilogue
4567     89/<- %esp 5/r32/ebp
4568     5d/pop-to-ebp
4569     c3/return
4570 
4571 new-regvardef:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement)
4572     # . prologue
4573     55/push-ebp
4574     89/<- %ebp 4/r32/esp
4575     # . save registers
4576     51/push-ecx
4577     57/push-edi
4578     # ecx = var
4579     8b/-> *(ebp+0xc) 1/r32/ecx
4580     # edi = result
4581     (allocate *(ebp+8) *Stmt-size)  # => eax
4582     89/<- %edi 0/r32/eax
4583     (zero-out %edi *Stmt-size)
4584     # set tag
4585     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
4586     # set output
4587     (append-stmt-var Heap %ecx *(edi+0xc) 0)  # Regvardef-outputs => eax
4588     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
4589 $new-regvardef:end:
4590     89/<- %eax 7/r32/edi
4591     # . restore registers
4592     5f/pop-to-edi
4593     59/pop-to-ecx
4594     # . epilogue
4595     89/<- %esp 5/r32/ebp
4596     5d/pop-to-ebp
4597     c3/return
4598 
4599 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
4600     # . prologue
4601     55/push-ebp
4602     89/<- %ebp 4/r32/esp
4603     # . save registers
4604     51/push-ecx
4605     #
4606     (allocate *(ebp+8) *List-size)  # => eax
4607     (zero-out %eax *List-size)
4608     8b/-> *(ebp+0xc) 1/r32/ecx
4609     89/<- *eax 1/r32/ecx  # List-value
4610     # if (list == null) return result
4611     81 7/subop/compare *(ebp+0x10) 0/imm32
4612     74/jump-if-= $append-list:end/disp8
4613     # otherwise append
4614     # var curr/ecx = list
4615     8b/-> *(ebp+0x10) 1/r32/ecx
4616     # while (curr->next != null) curr = curr->next
4617     {
4618       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
4619       74/jump-if-= break/disp8
4620       # curr = curr->next
4621       8b/-> *(ecx+4) 1/r32/ecx
4622       eb/jump loop/disp8
4623     }
4624     # curr->next = result
4625     89/<- *(ecx+4) 0/r32/eax
4626     # return list
4627     8b/-> *(ebp+0x10) 0/r32/eax
4628 $append-list:end:
4629     # . restore registers
4630     59/pop-to-ecx
4631     # . epilogue
4632     89/<- %esp 5/r32/ebp
4633     5d/pop-to-ebp
4634     c3/return
4635 
4636 append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
4637     # . prologue
4638     55/push-ebp
4639     89/<- %ebp 4/r32/esp
4640     # . save registers
4641     51/push-ecx
4642     #
4643     (allocate *(ebp+8) *Stmt-var-size)  # => eax
4644     (zero-out %eax *Stmt-var-size)
4645     8b/-> *(ebp+0xc) 1/r32/ecx
4646     89/<- *eax 1/r32/ecx  # Stmt-var-value
4647     8b/-> *(ebp+0x14) 1/r32/ecx
4648     89/<- *(eax+8) 1/r32/ecx  # Stmt-var-is-deref
4649     # if (list == null) return result
4650     81 7/subop/compare *(ebp+0x10) 0/imm32
4651     74/jump-if-= $append-stmt-var:end/disp8
4652     # otherwise append
4653     # var curr/ecx: (handle stmt-var) = vars
4654     8b/-> *(ebp+0x10) 1/r32/ecx
4655     # while (curr->next != null) curr = curr->next
4656     {
4657       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
4658       74/jump-if-= break/disp8
4659       # curr = curr->next
4660       8b/-> *(ecx+4) 1/r32/ecx
4661       eb/jump loop/disp8
4662     }
4663     # curr->next = result
4664     89/<- *(ecx+4) 0/r32/eax
4665     # return vars
4666     8b/-> *(ebp+0x10) 0/r32/eax
4667 $append-stmt-var:end:
4668     # . restore registers
4669     59/pop-to-ecx
4670     # . epilogue
4671     89/<- %esp 5/r32/ebp
4672     5d/pop-to-ebp
4673     c3/return
4674 
4675 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
4676     # . prologue
4677     55/push-ebp
4678     89/<- %ebp 4/r32/esp
4679     # . save registers
4680     56/push-esi
4681     # esi = block
4682     8b/-> *(ebp+0xc) 6/r32/esi
4683     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-statements
4684     89/<- *(esi+4) 0/r32/eax  # Block-statements
4685 $append-to-block:end:
4686     # . restore registers
4687     5e/pop-to-esi
4688     # . epilogue
4689     89/<- %esp 5/r32/ebp
4690     5d/pop-to-ebp
4691     c3/return
4692 
4693 #######################################################
4694 # Type-checking
4695 #######################################################
4696 
4697 check-mu-types:
4698     # . prologue
4699     55/push-ebp
4700     89/<- %ebp 4/r32/esp
4701     #
4702 $check-mu-types:end:
4703     # . epilogue
4704     89/<- %esp 5/r32/ebp
4705     5d/pop-to-ebp
4706     c3/return
4707 
4708 size-of:  # v: (addr var) -> result/eax: int
4709     # . prologue
4710     55/push-ebp
4711     89/<- %ebp 4/r32/esp
4712     # if v is a literal, return 0
4713     8b/-> *(ebp+8) 0/r32/eax
4714     8b/-> *(eax+4) 0/r32/eax  # Var-type
4715     81 7/subop/compare *eax 0/imm32  # Tree-left
4716     b8/copy-to-eax 0/imm32
4717     74/jump-if-= $size-of:end/disp8
4718     # hard-coded since we only support 'int' types for now
4719     b8/copy-to-eax 4/imm32
4720 $size-of:end:
4721     # . epilogue
4722     89/<- %esp 5/r32/ebp
4723     5d/pop-to-ebp
4724     c3/return
4725 
4726 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
4727     # . prologue
4728     55/push-ebp
4729     89/<- %ebp 4/r32/esp
4730     # . save registers
4731     51/push-ecx
4732     52/push-edx
4733     # ecx = a
4734     8b/-> *(ebp+8) 1/r32/ecx
4735     # edx = b
4736     8b/-> *(ebp+0xc) 2/r32/edx
4737     # if (a == b) return true
4738     8b/-> %ecx 0/r32/eax  # Var-type
4739     39/compare %edx 0/r32/eax  # Var-type
4740     b8/copy-to-eax 1/imm32/true
4741     74/jump-if-= $type-equal?:end/disp8
4742     # if (a < MAX_TYPE_ID) return false
4743     81 7/subop/compare %ecx 0x10000/imm32
4744     b8/copy-to-eax 0/imm32/false
4745     72/jump-if-addr< $type-equal?:end/disp8
4746     # if (b < MAX_TYPE_ID) return false
4747     81 7/subop/compare %edx 0x10000/imm32
4748     b8/copy-to-eax 0/imm32/false
4749     72/jump-if-addr< $type-equal?:end/disp8
4750     # if (!type-equal?(a->left, b->left)) return false
4751     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
4752     3d/compare-eax-and 0/imm32/false
4753     74/jump-if-= $type-equal?:end/disp8
4754     # return type-equal?(a->right, b->right)
4755     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
4756 $type-equal?:end:
4757     # . restore registers
4758     5a/pop-to-edx
4759     59/pop-to-ecx
4760     # . epilogue
4761     89/<- %esp 5/r32/ebp
4762     5d/pop-to-ebp
4763     c3/return
4764 
4765 == data
4766 
4767 # not yet used, but it will be
4768 Type-size:  # (stream int)
4769   0x18/imm32/write
4770   0/imm32/read
4771   0x100/imm32/length
4772   # data
4773   4/imm32  # literal
4774   4/imm32  # int
4775   4/imm32  # addr
4776   0/imm32  # array (logic elsewhere)
4777   8/imm32  # handle (fat pointer)
4778   4/imm32  # bool
4779   0/imm32
4780   0/imm32
4781   # 0x20
4782   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4783   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4784   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4785   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4786   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4787   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4788   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4789 
4790 == code
4791 
4792 #######################################################
4793 # Code-generation
4794 #######################################################
4795 
4796 emit-subx:  # out: (addr buffered-file)
4797     # . prologue
4798     55/push-ebp
4799     89/<- %ebp 4/r32/esp
4800     # . save registers
4801     50/push-eax
4802     51/push-ecx
4803     57/push-edi
4804     # edi = out
4805     8b/-> *(ebp+8) 7/r32/edi
4806     # var curr/ecx: (handle function) = *Program
4807     8b/-> *Program 1/r32/ecx
4808     {
4809       # if (curr == null) break
4810       81 7/subop/compare %ecx 0/imm32
4811       0f 84/jump-if-= break/disp32
4812       (emit-subx-function %edi %ecx)
4813       # curr = curr->next
4814       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
4815       e9/jump loop/disp32
4816     }
4817 $emit-subx:end:
4818     # . restore registers
4819     5f/pop-to-edi
4820     59/pop-to-ecx
4821     58/pop-to-eax
4822     # . epilogue
4823     89/<- %esp 5/r32/ebp
4824     5d/pop-to-ebp
4825     c3/return
4826 
4827 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
4828     # . prologue
4829     55/push-ebp
4830     89/<- %ebp 4/r32/esp
4831     # . save registers
4832     50/push-eax
4833     51/push-ecx
4834     52/push-edx
4835     57/push-edi
4836     # edi = out
4837     8b/-> *(ebp+8) 7/r32/edi
4838     # ecx = f
4839     8b/-> *(ebp+0xc) 1/r32/ecx
4840     # var vars/edx: (stack (addr var) 256)
4841     81 5/subop/subtract %esp 0x400/imm32
4842     68/push 0x400/imm32/length
4843     68/push 0/imm32/top
4844     89/<- %edx 4/r32/esp
4845     #
4846     (write-buffered %edi *ecx)
4847     (write-buffered %edi ":\n")
4848     # Important: each block's depth during code-generation should be identical
4849     # to what it was during parsing.
4850     c7 0/subop/copy *Curr-block-depth 1/imm32
4851     (emit-subx-prologue %edi)
4852     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
4853     (emit-subx-epilogue %edi)
4854 $emit-subx-function:end:
4855     # . reclaim locals
4856     81 0/subop/add %esp 408/imm32
4857     # . restore registers
4858     5f/pop-to-edi
4859     5a/pop-to-edx
4860     59/pop-to-ecx
4861     58/pop-to-eax
4862     # . epilogue
4863     89/<- %esp 5/r32/ebp
4864     5d/pop-to-ebp
4865     c3/return
4866 
4867 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
4868     # . prologue
4869     55/push-ebp
4870     89/<- %ebp 4/r32/esp
4871     # . save registers
4872     50/push-eax
4873     51/push-ecx
4874     52/push-edx
4875     53/push-ebx
4876     56/push-esi
4877     # esi = stmts
4878     8b/-> *(ebp+0xc) 6/r32/esi
4879     # var var-seen?/edx: boolean <- copy false
4880     ba/copy-to-edx 0/imm32/false
4881     #
4882     {
4883 $emit-subx-stmt-list:loop:
4884       81 7/subop/compare %esi 0/imm32
4885       0f 84/jump-if-= break/disp32
4886       # var curr-stmt/ecx = stmts->value
4887       8b/-> *esi 1/r32/ecx  # List-value
4888       {
4889 $emit-subx-stmt-list:check-for-block:
4890         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
4891         75/jump-if-!= break/disp8
4892 $emit-subx-stmt-list:block:
4893         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
4894       }
4895       {
4896 $emit-subx-stmt-list:check-for-stmt:
4897         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
4898         0f 85/jump-if-!= break/disp32
4899 $emit-subx-stmt-list:stmt1:
4900         {
4901           (is-mu-branch? %ecx)  # => eax
4902           3d/compare-eax-and 0/imm32/false
4903           0f 84/jump-if-= break/disp32
4904 $emit-subx-stmt-list:branch-stmt:
4905           # if !var-seen? break
4906           81 7/subop/compare %edx 0/imm32/false
4907           0f 84/jump-if-= break/disp32
4908 $emit-subx-stmt-list:branch-stmt-and-var-seen:
4909 +-- 26 lines: # unconditional loops -----------------------------------------------------------------------------------------------------------------------------------------------------
4935 +-- 15 lines: # unconditional breaks ----------------------------------------------------------------------------------------------------------------------------------------------------
4950 +-- 37 lines: # simple conditional branches without a target ----------------------------------------------------------------------------------------------------------------------------
4987 +-- 19 lines: # conditional branches with an explicit target ----------------------------------------------------------------------------------------------------------------------------
5006         }
5007 $emit-subx-stmt-list:1-to-1:
5008         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
5009       }
5010       {
5011 $emit-subx-stmt-list:check-for-vardef:
5012         81 7/subop/compare *ecx 2/imm32/vardef  # Stmt-tag
5013         75/jump-if-!= break/disp8
5014 $emit-subx-stmt-list:vardef:
5015         (emit-subx-var-def *(ebp+8) %ecx)
5016         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
5017         # var-seen? = true
5018         ba/copy-to-edx 1/imm32/true
5019       }
5020       {
5021 $emit-subx-stmt-list:check-for-regvardef:
5022         81 7/subop/compare *ecx 3/imm32/regvardef  # Stmt-tag
5023         0f 85/jump-if-!= break/disp32
5024 $emit-subx-stmt-list:regvardef:
5025         # TODO: ensure that there's exactly one output
5026         # var output/eax: (handle var) = curr-stmt->outputs->value
5027         8b/-> *(ecx+0xc) 0/r32/eax
5028         8b/-> *eax 0/r32/eax
5029         # ensure that output is in a register
5030         81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
5031         0f 84/jump-if-= $emit-subx-stmt-list:abort-regvardef-without-register/disp32
5032         # emit spill
5033         (emit-indent *(ebp+8) *Curr-block-depth)
5034         (write-buffered *(ebp+8) "ff 6/subop/push %")
5035         (write-buffered *(ebp+8) *(eax+0x10))
5036         (write-buffered *(ebp+8) Newline)
5037         # register variable definition
5038         (push *(ebp+0x10) %eax)
5039         # emit the instruction as usual
5040         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
5041         # var-seen? = true
5042         ba/copy-to-edx 1/imm32/true
5043       }
5044 $emit-subx-stmt-list:continue:
5045       # TODO: raise an error on unrecognized Stmt-tag
5046       8b/-> *(esi+4) 6/r32/esi  # List-next
5047       e9/jump loop/disp32
5048     }
5049 $emit-subx-stmt-list:emit-cleanup:
5050     (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
5051 $emit-subx-stmt-list:cleanup:
5052     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
5053 $emit-subx-stmt-list:end:
5054     # . restore registers
5055     5e/pop-to-esi
5056     5b/pop-to-ebx
5057     5a/pop-to-edx
5058     59/pop-to-ecx
5059     58/pop-to-eax
5060     # . epilogue
5061     89/<- %esp 5/r32/ebp
5062     5d/pop-to-ebp
5063     c3/return
5064 
5065 $emit-subx-stmt-list:abort-regvardef-without-register:
5066     # error("var '" var->name "' initialized from an instruction must live in a register\n")
5067     (write-buffered Stderr "var '")
5068     (write-buffered Stderr *eax)  # Var-name
5069     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
5070     (flush Stderr)
5071     # . syscall(exit, 1)
5072     bb/copy-to-ebx  1/imm32
5073     b8/copy-to-eax  1/imm32/exit
5074     cd/syscall  0x80/imm8
5075     # never gets here
5076 
5077 emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
5078     # . prologue
5079     55/push-ebp
5080     89/<- %ebp 4/r32/esp
5081     # . save registers
5082     50/push-eax
5083     51/push-ecx
5084     52/push-edx
5085     # ecx = stmt
5086     8b/-> *(ebp+0xc) 1/r32/ecx
5087     # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
5088     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
5089     8b/-> *edx 2/r32/edx  # Stmt-var-value
5090     8b/-> *edx 2/r32/edx  # Var-name
5091     # clean up until target block
5092     (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
5093     # emit jump to target block
5094     (emit-indent *(ebp+8) *Curr-block-depth)
5095     (write-buffered *(ebp+8) "e9/jump ")
5096     (write-buffered *(ebp+8) %edx)
5097     (string-starts-with? *(ecx+4) "break")
5098     3d/compare-eax-and 0/imm32/false
5099     {
5100       74/jump-if-= break/disp8
5101       (write-buffered *(ebp+8) ":break/disp32\n")
5102     }
5103     3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
5104     {
5105       75/jump-if-!= break/disp8
5106       (write-buffered *(ebp+8) ":loop/disp32\n")
5107     }
5108 $emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
5109     # . restore registers
5110     5a/pop-to-edx
5111     59/pop-to-ecx
5112     58/pop-to-eax
5113     # . epilogue
5114     89/<- %esp 5/r32/ebp
5115     5d/pop-to-ebp
5116     c3/return
5117 
5118 is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
5119     # . prologue
5120     55/push-ebp
5121     89/<- %ebp 4/r32/esp
5122     # . save registers
5123     51/push-ecx
5124     # ecx = stmt
5125     8b/-> *(ebp+8) 1/r32/ecx
5126     # if (stmt->operation starts with "loop") return true
5127     (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
5128     3d/compare-eax-and 0/imm32/false
5129     75/jump-if-not-equal $is-mu-branch?:end/disp8
5130     # otherwise return (stmt->operation starts with "break")
5131     (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
5132 $is-mu-branch?:end:
5133     # . restore registers
5134     59/pop-to-ecx
5135     # . epilogue
5136     89/<- %esp 5/r32/ebp
5137     5d/pop-to-ebp
5138     c3/return
5139 
5140 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
5141     # . prologue
5142     55/push-ebp
5143     89/<- %ebp 4/r32/esp
5144     # . save registers
5145     50/push-eax
5146     # eax = stmt
5147     8b/-> *(ebp+0xc) 0/r32/eax
5148     #
5149     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
5150     (emit-indent *(ebp+8) *Curr-block-depth)
5151     (write-buffered *(ebp+8) *eax)
5152     (write-buffered *(ebp+8) " break/disp32\n")
5153 $emit-reverse-break:end:
5154     # . restore registers
5155     58/pop-to-eax
5156     # . epilogue
5157     89/<- %esp 5/r32/ebp
5158     5d/pop-to-ebp
5159     c3/return
5160 
5161 == data
5162 
5163 Reverse-branch:  # (table string string)
5164   # a table is a stream
5165   0xa0/imm32/write
5166   0/imm32/read
5167   0xa0/imm32/length
5168   # data
5169   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
5170   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
5171   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
5172   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
5173   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
5174   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
5175   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
5176   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
5177   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
5178   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
5179   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
5180   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
5181   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
5182   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
5183   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
5184   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
5185   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
5186   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
5187   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
5188   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
5189 
5190 == code
5191 
5192 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
5193     # . prologue
5194     55/push-ebp
5195     89/<- %ebp 4/r32/esp
5196     # . save registers
5197     50/push-eax
5198     51/push-ecx
5199     52/push-edx
5200     53/push-ebx
5201     # ecx = vars
5202     8b/-> *(ebp+0xc) 1/r32/ecx
5203     # var eax: int = vars->top
5204     8b/-> *ecx 0/r32/eax
5205     # var min/ecx: (address (handle var)) = vars->data
5206     81 0/subop/add %ecx 8/imm32
5207     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
5208     81 5/subop/subtract %eax 4/imm32
5209     8d/copy-address *(ecx+eax) 0/r32/eax
5210     # edx = depth
5211     8b/-> *(ebp+0x10) 2/r32/edx
5212     {
5213 $emit-unconditional-jump-to-depth:loop:
5214       # if (curr < min) break
5215       39/compare %eax 1/r32/ecx
5216       0f 82/jump-if-addr< break/disp32
5217       # var v/ebx: (handle var) = *curr
5218       8b/-> *eax 3/r32/ebx
5219       # if (v->block-depth < until-block-depth) break
5220       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
5221       0f 8c/jump-if-< break/disp32
5222       {
5223 $emit-unconditional-jump-to-depth:check:
5224         # if v->block-depth != until-block-depth, continue
5225         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
5226         0f 85/jump-if-!= break/disp32
5227 $emit-unconditional-jump-to-depth:depth-found:
5228         # if v is not a literal, continue
5229         # . var eax: int = size-of(v)
5230         50/push-eax
5231         (size-of %ebx)  # => eax
5232         # . if (eax != 0) continue
5233         3d/compare-eax-and 0/imm32
5234         58/pop-to-eax
5235         #
5236         0f 85/jump-if-!= break/disp32
5237 $emit-unconditional-jump-to-depth:label-found:
5238         # emit unconditional jump, then return
5239         (emit-indent *(ebp+8) *Curr-block-depth)
5240         (write-buffered *(ebp+8) "e9/jump ")
5241         (write-buffered *(ebp+8) *ebx)  # Var-name
5242         (write-buffered *(ebp+8) ":")
5243         (write-buffered *(ebp+8) *(ebp+0x14))
5244         (write-buffered *(ebp+8) "/disp32\n")
5245         eb/jump $emit-unconditional-jump-to-depth:end/disp8
5246       }
5247       # curr -= 4
5248       2d/subtract-from-eax 4/imm32
5249       e9/jump loop/disp32
5250     }
5251     # TODO: error if no label at 'depth' was found
5252 $emit-unconditional-jump-to-depth:end:
5253     # . restore registers
5254     5b/pop-to-ebx
5255     5a/pop-to-edx
5256     59/pop-to-ecx
5257     58/pop-to-eax
5258     # . epilogue
5259     89/<- %esp 5/r32/ebp
5260     5d/pop-to-ebp
5261     c3/return
5262 
5263 # emit clean-up code for 'vars' until some block depth
5264 # doesn't actually modify 'vars' so we need traverse manually inside the stack
5265 emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
5266     # . prologue
5267     55/push-ebp
5268     89/<- %ebp 4/r32/esp
5269     # . save registers
5270     50/push-eax
5271     51/push-ecx
5272     52/push-edx
5273     53/push-ebx
5274     # ecx = vars
5275     8b/-> *(ebp+0xc) 1/r32/ecx
5276     # var eax: int = vars->top
5277     8b/-> *ecx 0/r32/eax
5278     # var min/ecx: (address (handle var)) = vars->data
5279     81 0/subop/add %ecx 8/imm32
5280     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
5281     81 5/subop/subtract %eax 4/imm32
5282     8d/copy-address *(ecx+eax) 0/r32/eax
5283     # edx = until-block-depth
5284     8b/-> *(ebp+0x10) 2/r32/edx
5285     {
5286 $emit-cleanup-code-until-depth:loop:
5287       # if (curr < min) break
5288       39/compare %eax 1/r32/ecx
5289       0f 82/jump-if-addr< break/disp32
5290       # var v/ebx: (handle var) = *curr
5291       8b/-> *eax 3/r32/ebx
5292       # if (v->block-depth < until-block-depth) break
5293       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
5294       0f 8c/jump-if-< break/disp32
5295       # if v is in a register
5296       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
5297       {
5298         74/jump-if-= break/disp8
5299 $emit-cleanup-code-until-depth:reclaim-var-in-register:
5300         (emit-indent *(ebp+8) *Curr-block-depth)
5301         (write-buffered *(ebp+8) "8f 0/subop/pop %")
5302         (write-buffered *(ebp+8) *(ebx+0x10))
5303         (write-buffered *(ebp+8) Newline)
5304       }
5305       # otherwise v is on the stack
5306       {
5307         75/jump-if-!= break/disp8
5308 $emit-cleanup-code-until-depth:reclaim-var-on-stack:
5309         50/push-eax
5310         (size-of %ebx)  # => eax
5311         # don't emit code for labels
5312         3d/compare-eax-and 0/imm32
5313         74/jump-if-= break/disp8
5314         #
5315         (emit-indent *(ebp+8) *Curr-block-depth)
5316         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
5317         (print-int32-buffered *(ebp+8) %eax)
5318         (write-buffered *(ebp+8) "/imm32\n")
5319         58/pop-to-eax
5320       }
5321       # curr -= 4
5322       2d/subtract-from-eax 4/imm32
5323       e9/jump loop/disp32
5324     }
5325 $emit-cleanup-code-until-depth:end:
5326     # . restore registers
5327     5b/pop-to-ebx
5328     5a/pop-to-edx
5329     59/pop-to-ecx
5330     58/pop-to-eax
5331     # . epilogue
5332     89/<- %esp 5/r32/ebp
5333     5d/pop-to-ebp
5334     c3/return
5335 
5336 # emit clean-up code for 'vars' until a given label is encountered
5337 # doesn't actually modify 'vars' so we need traverse manually inside the stack
5338 emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
5339     # . prologue
5340     55/push-ebp
5341     89/<- %ebp 4/r32/esp
5342     # . save registers
5343     50/push-eax
5344     51/push-ecx
5345     52/push-edx
5346     53/push-ebx
5347     # ecx = vars
5348     8b/-> *(ebp+0xc) 1/r32/ecx
5349     # var eax: int = vars->top
5350     8b/-> *ecx 0/r32/eax
5351     # var min/ecx: (address (handle var)) = vars->data
5352     81 0/subop/add %ecx 8/imm32
5353     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
5354     81 5/subop/subtract %eax 4/imm32
5355     8d/copy-address *(ecx+eax) 2/r32/edx
5356     {
5357 $emit-cleanup-code-until-target:loop:
5358       # if (curr < min) break
5359       39/compare %edx 1/r32/ecx
5360       0f 82/jump-if-addr< break/disp32
5361       # var v/ebx: (handle var) = *curr
5362       8b/-> *edx 3/r32/ebx
5363       # if (v->name == until-block-label) break
5364       (string-equal? *ebx *(ebp+0x10))  # => eax
5365       3d/compare-eax-and 0/imm32/false
5366       0f 85/jump-if-!= break/disp32
5367       # if v is in a register
5368       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
5369       {
5370         74/jump-if-= break/disp8
5371 $emit-cleanup-code-until-target:reclaim-var-in-register:
5372         (emit-indent *(ebp+8) *Curr-block-depth)
5373         (write-buffered *(ebp+8) "8f 0/subop/pop %")
5374         (write-buffered *(ebp+8) *(ebx+0x10))
5375         (write-buffered *(ebp+8) Newline)
5376       }
5377       # otherwise v is on the stack
5378       {
5379         75/jump-if-!= break/disp8
5380 $emit-cleanup-code-until-target:reclaim-var-on-stack:
5381         (size-of %ebx)  # => eax
5382         # don't emit code for labels
5383         3d/compare-eax-and 0/imm32
5384         74/jump-if-= break/disp8
5385         #
5386         (emit-indent *(ebp+8) *Curr-block-depth)
5387         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
5388         (print-int32-buffered *(ebp+8) %eax)
5389         (write-buffered *(ebp+8) "/imm32\n")
5390       }
5391       # curr -= 4
5392       81 5/subop/subtract %edx 4/imm32
5393       e9/jump loop/disp32
5394     }
5395 $emit-cleanup-code-until-target:end:
5396     # . restore registers
5397     5b/pop-to-ebx
5398     5a/pop-to-edx
5399     59/pop-to-ecx
5400     58/pop-to-eax
5401     # . epilogue
5402     89/<- %esp 5/r32/ebp
5403     5d/pop-to-ebp
5404     c3/return
5405 
5406 # clean up global state for 'vars' until some block depth
5407 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
5408     # . prologue
5409     55/push-ebp
5410     89/<- %ebp 4/r32/esp
5411     # . save registers
5412     50/push-eax
5413     51/push-ecx
5414     56/push-esi
5415     # esi = vars
5416     8b/-> *(ebp+8) 6/r32/esi
5417     # ecx = until-block-depth
5418     8b/-> *(ebp+0xc) 1/r32/ecx
5419     {
5420 $clean-up-blocks:reclaim-loop:
5421       # if (vars->top <= 0) break
5422       81 7/subop/compare *esi 0/imm32  # Stack-top
5423       7e/jump-if-<= break/disp8
5424       # var v/eax: (handle var) = top(vars)
5425       (top %esi)  # => eax
5426       # if (v->block-depth < until-block-depth) break
5427       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
5428       7c/jump-if-< break/disp8
5429       # if v is on the stack, update Next-local-stack-offset
5430       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
5431       {
5432         75/jump-if-!= break/disp8
5433 $clean-up-blocks:reclaim-var-on-stack:
5434         (size-of %eax)  # => eax
5435         01/add *Next-local-stack-offset 0/r32/eax
5436       }
5437       (pop %esi)
5438       e9/jump loop/disp32
5439     }
5440 $clean-up-blocks:end:
5441     # . restore registers
5442     5e/pop-to-esi
5443     59/pop-to-ecx
5444     58/pop-to-eax
5445     # . epilogue
5446     89/<- %esp 5/r32/ebp
5447     5d/pop-to-ebp
5448     c3/return
5449 
5450 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle statement)
5451     # . prologue
5452     55/push-ebp
5453     89/<- %ebp 4/r32/esp
5454     # . save registers
5455     50/push-eax
5456     51/push-ecx
5457     # eax = stmt
5458     8b/-> *(ebp+0xc) 0/r32/eax
5459     # var n/eax: int = size-of(stmt->var)
5460     (size-of *(eax+4))  # Vardef-var => eax
5461     # while n > 0
5462     {
5463       3d/compare-eax-with 0/imm32
5464       7e/jump-if-<= break/disp8
5465       (emit-indent *(ebp+8) *Curr-block-depth)
5466       (write-buffered *(ebp+8) "68/push 0/imm32\n")
5467       # n -= 4
5468       2d/subtract-from-eax 4/imm32
5469       #
5470       eb/jump loop/disp8
5471     }
5472 $emit-subx-var-def:end:
5473     # . restore registers
5474     59/pop-to-ecx
5475     58/pop-to-eax
5476     # . epilogue
5477     89/<- %esp 5/r32/ebp
5478     5d/pop-to-ebp
5479     c3/return
5480 
5481 emit-subx-statement:  # out: (addr buffered-file), stmt: (handle statement), primitives: (handle primitive), functions: (handle function)
5482     # . prologue
5483     55/push-ebp
5484     89/<- %ebp 4/r32/esp
5485     # . save registers
5486     50/push-eax
5487     51/push-ecx
5488     # handle some special cases
5489     # ecx = stmt
5490     8b/-> *(ebp+0xc) 1/r32/ecx
5491 +-- 24 lines: # array length ------------------------------------------------------------------------------------------------------------------------------------------------------------
5515 +-- 32 lines: # array index -------------------------------------------------------------------------------------------------------------------------------------------------------------
5547     # if stmt matches a primitive, emit it
5548     {
5549 $emit-subx-statement:check-for-primitive:
5550       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
5551       3d/compare-eax-and 0/imm32
5552       74/jump-if-= break/disp8
5553 $emit-subx-statement:primitive:
5554       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
5555       e9/jump $emit-subx-statement:end/disp32
5556     }
5557     # else if stmt matches a function, emit a call to it
5558     {
5559 $emit-subx-statement:check-for-call:
5560       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
5561       3d/compare-eax-and 0/imm32
5562       74/jump-if-= break/disp8
5563 $emit-subx-statement:call:
5564       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
5565       e9/jump $emit-subx-statement:end/disp32
5566     }
5567     # else assume it's a SubX function (TODO: how to type-check?!)
5568     (emit-hailmary-call *(ebp+8) *(ebp+0xc))
5569 $emit-subx-statement:end:
5570     # . restore registers
5571     59/pop-to-ecx
5572     58/pop-to-eax
5573     # . epilogue
5574     89/<- %esp 5/r32/ebp
5575     5d/pop-to-ebp
5576     c3/return
5577 
5578 $emit-subx-statement:abort:
5579     # error("couldn't translate '" stmt "'\n")
5580     (write-buffered Stderr "couldn't translate an instruction with operation '")
5581     8b/-> *(ebp+0xc) 0/r32/eax
5582     (write-buffered Stderr *(eax+4))  # Stmt1-operation
5583     (write-buffered Stderr "'\n")
5584     (flush Stderr)
5585     # . syscall(exit, 1)
5586     bb/copy-to-ebx  1/imm32
5587     b8/copy-to-eax  1/imm32/exit
5588     cd/syscall  0x80/imm8
5589     # never gets here
5590 
5591 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
5592     # . prologue
5593     55/push-ebp
5594     89/<- %ebp 4/r32/esp
5595     # . save registers
5596     50/push-eax
5597     51/push-ecx
5598     56/push-esi
5599     # esi = block
5600     8b/-> *(ebp+0xc) 6/r32/esi
5601     # var stmts/eax: (handle list statement) = block->statements
5602     8b/-> *(esi+4) 0/r32/eax  # Block-statements
5603     #
5604     {
5605 $emit-subx-block:check-empty:
5606       81 7/subop/compare %eax 0/imm32
5607       0f 84/jump-if-= break/disp32
5608       (emit-indent *(ebp+8) *Curr-block-depth)
5609       (write-buffered *(ebp+8) "{\n")
5610       # var v/ecx: (addr array byte) = block->var->name
5611       8b/-> *(esi+8) 1/r32/ecx  # Block-var
5612       (write-buffered *(ebp+8) *ecx)  # Var-name
5613       (write-buffered *(ebp+8) ":loop:\n")
5614       ff 0/subop/increment *Curr-block-depth
5615       (push *(ebp+0x10) %ecx)
5616       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
5617       (pop *(ebp+0x10))  # => eax
5618       ff 1/subop/decrement *Curr-block-depth
5619       (emit-indent *(ebp+8) *Curr-block-depth)
5620       (write-buffered *(ebp+8) "}\n")
5621       (write-buffered *(ebp+8) *ecx)  # Var-name
5622       (write-buffered *(ebp+8) ":break:\n")
5623     }
5624 $emit-subx-block:end:
5625     # . restore registers
5626     5e/pop-to-esi
5627     59/pop-to-ecx
5628     58/pop-to-eax
5629     # . epilogue
5630     89/<- %esp 5/r32/ebp
5631     5d/pop-to-ebp
5632     c3/return
5633 
5634 # Primitives supported
5635 # For each operation, put variants with hard-coded registers before flexible ones.
5636 == data
5637 Primitives:
5638 # - increment/decrement
5639 _Primitive-inc-eax:
5640     # var/eax <- increment => 40/increment-eax
5641     "increment"/imm32/name
5642     0/imm32/no-inouts
5643     Single-int-var-in-eax/imm32/outputs
5644     "40/increment-eax"/imm32/subx-name
5645     0/imm32/no-rm32
5646     0/imm32/no-r32
5647     0/imm32/no-imm32
5648     0/imm32/no-disp32
5649     0/imm32/output-is-write-only
5650     _Primitive-inc-ecx/imm32/next
5651 _Primitive-inc-ecx:
5652     # var/ecx <- increment => 41/increment-ecx
5653     "increment"/imm32/name
5654     0/imm32/no-inouts
5655     Single-int-var-in-ecx/imm32/outputs
5656     "41/increment-ecx"/imm32/subx-name
5657     0/imm32/no-rm32
5658     0/imm32/no-r32
5659     0/imm32/no-imm32
5660     0/imm32/no-disp32
5661     0/imm32/output-is-write-only
5662     _Primitive-inc-edx/imm32/next
5663 _Primitive-inc-edx:
5664     # var/edx <- increment => 42/increment-edx
5665     "increment"/imm32/name
5666     0/imm32/no-inouts
5667     Single-int-var-in-edx/imm32/outputs
5668     "42/increment-edx"/imm32/subx-name
5669     0/imm32/no-rm32
5670     0/imm32/no-r32
5671     0/imm32/no-imm32
5672     0/imm32/no-disp32
5673     0/imm32/output-is-write-only
5674     _Primitive-inc-ebx/imm32/next
5675 _Primitive-inc-ebx:
5676     # var/ebx <- increment => 43/increment-ebx
5677     "increment"/imm32/name
5678     0/imm32/no-inouts
5679     Single-int-var-in-ebx/imm32/outputs
5680     "43/increment-ebx"/imm32/subx-name
5681     0/imm32/no-rm32
5682     0/imm32/no-r32
5683     0/imm32/no-imm32
5684     0/imm32/no-disp32
5685     0/imm32/output-is-write-only
5686     _Primitive-inc-esi/imm32/next
5687 _Primitive-inc-esi:
5688     # var/esi <- increment => 46/increment-esi
5689     "increment"/imm32/name
5690     0/imm32/no-inouts
5691     Single-int-var-in-esi/imm32/outputs
5692     "46/increment-esi"/imm32/subx-name
5693     0/imm32/no-rm32
5694     0/imm32/no-r32
5695     0/imm32/no-imm32
5696     0/imm32/no-disp32
5697     0/imm32/output-is-write-only
5698     _Primitive-inc-edi/imm32/next
5699 _Primitive-inc-edi:
5700     # var/edi <- increment => 47/increment-edi
5701     "increment"/imm32/name
5702     0/imm32/no-inouts
5703     Single-int-var-in-edi/imm32/outputs
5704     "47/increment-edi"/imm32/subx-name
5705     0/imm32/no-rm32
5706     0/imm32/no-r32
5707     0/imm32/no-imm32
5708     0/imm32/no-disp32
5709     0/imm32/output-is-write-only
5710     _Primitive-dec-eax/imm32/next
5711 _Primitive-dec-eax:
5712     # var/eax <- decrement => 48/decrement-eax
5713     "decrement"/imm32/name
5714     0/imm32/no-inouts
5715     Single-int-var-in-eax/imm32/outputs
5716     "48/decrement-eax"/imm32/subx-name
5717     0/imm32/no-rm32
5718     0/imm32/no-r32
5719     0/imm32/no-imm32
5720     0/imm32/no-disp32
5721     0/imm32/output-is-write-only
5722     _Primitive-dec-ecx/imm32/next
5723 _Primitive-dec-ecx:
5724     # var/ecx <- decrement => 49/decrement-ecx
5725     "decrement"/imm32/name
5726     0/imm32/no-inouts
5727     Single-int-var-in-ecx/imm32/outputs
5728     "49/decrement-ecx"/imm32/subx-name
5729     0/imm32/no-rm32
5730     0/imm32/no-r32
5731     0/imm32/no-imm32
5732     0/imm32/no-disp32
5733     0/imm32/output-is-write-only
5734     _Primitive-dec-edx/imm32/next
5735 _Primitive-dec-edx:
5736     # var/edx <- decrement => 4a/decrement-edx
5737     "decrement"/imm32/name
5738     0/imm32/no-inouts
5739     Single-int-var-in-edx/imm32/outputs
5740     "4a/decrement-edx"/imm32/subx-name
5741     0/imm32/no-rm32
5742     0/imm32/no-r32
5743     0/imm32/no-imm32
5744     0/imm32/no-disp32
5745     0/imm32/output-is-write-only
5746     _Primitive-dec-ebx/imm32/next
5747 _Primitive-dec-ebx:
5748     # var/ebx <- decrement => 4b/decrement-ebx
5749     "decrement"/imm32/name
5750     0/imm32/no-inouts
5751     Single-int-var-in-ebx/imm32/outputs
5752     "4b/decrement-ebx"/imm32/subx-name
5753     0/imm32/no-rm32
5754     0/imm32/no-r32
5755     0/imm32/no-imm32
5756     0/imm32/no-disp32
5757     0/imm32/output-is-write-only
5758     _Primitive-dec-esi/imm32/next
5759 _Primitive-dec-esi:
5760     # var/esi <- decrement => 4e/decrement-esi
5761     "decrement"/imm32/name
5762     0/imm32/no-inouts
5763     Single-int-var-in-esi/imm32/outputs
5764     "4e/decrement-esi"/imm32/subx-name
5765     0/imm32/no-rm32
5766     0/imm32/no-r32
5767     0/imm32/no-imm32
5768     0/imm32/no-disp32
5769     0/imm32/output-is-write-only
5770     _Primitive-dec-edi/imm32/next
5771 _Primitive-dec-edi:
5772     # var/edi <- decrement => 4f/decrement-edi
5773     "decrement"/imm32/name
5774     0/imm32/no-inouts
5775     Single-int-var-in-edi/imm32/outputs
5776     "4f/decrement-edi"/imm32/subx-name
5777     0/imm32/no-rm32
5778     0/imm32/no-r32
5779     0/imm32/no-imm32
5780     0/imm32/no-disp32
5781     0/imm32/output-is-write-only
5782     _Primitive-inc-mem/imm32/next
5783 _Primitive-inc-mem:
5784     # increment var => ff 0/subop/increment *(ebp+__)
5785     "increment"/imm32/name
5786     Single-int-var-in-mem/imm32/inouts
5787     0/imm32/no-outputs
5788     "ff 0/subop/increment"/imm32/subx-name
5789     1/imm32/rm32-is-first-inout
5790     0/imm32/no-r32
5791     0/imm32/no-imm32
5792     0/imm32/no-disp32
5793     0/imm32/output-is-write-only
5794     _Primitive-inc-reg/imm32/next
5795 _Primitive-inc-reg:
5796     # var/reg <- increment => ff 0/subop/increment %__
5797     "increment"/imm32/name
5798     0/imm32/no-inouts
5799     Single-int-var-in-some-register/imm32/outputs
5800     "ff 0/subop/increment"/imm32/subx-name
5801     3/imm32/rm32-is-first-output
5802     0/imm32/no-r32
5803     0/imm32/no-imm32
5804     0/imm32/no-disp32
5805     0/imm32/output-is-write-only
5806     _Primitive-dec-mem/imm32/next
5807 _Primitive-dec-mem:
5808     # decrement var => ff 1/subop/decrement *(ebp+__)
5809     "decrement"/imm32/name
5810     Single-int-var-in-mem/imm32/inouts
5811     0/imm32/no-outputs
5812     "ff 1/subop/decrement"/imm32/subx-name
5813     1/imm32/rm32-is-first-inout
5814     0/imm32/no-r32
5815     0/imm32/no-imm32
5816     0/imm32/no-disp32
5817     0/imm32/output-is-write-only
5818     _Primitive-dec-reg/imm32/next
5819 _Primitive-dec-reg:
5820     # var/reg <- decrement => ff 1/subop/decrement %__
5821     "decrement"/imm32/name
5822     0/imm32/no-inouts
5823     Single-int-var-in-some-register/imm32/outputs
5824     "ff 1/subop/decrement"/imm32/subx-name
5825     3/imm32/rm32-is-first-output
5826     0/imm32/no-r32
5827     0/imm32/no-imm32
5828     0/imm32/no-disp32
5829     0/imm32/output-is-write-only
5830     _Primitive-add-to-eax/imm32/next
5831 # - add
5832 _Primitive-add-to-eax:
5833     # var/eax <- add lit => 05/add-to-eax lit/imm32
5834     "add"/imm32/name
5835     Single-lit-var/imm32/inouts
5836     Single-int-var-in-eax/imm32/outputs
5837     "05/add-to-eax"/imm32/subx-name
5838     0/imm32/no-rm32
5839     0/imm32/no-r32
5840     1/imm32/imm32-is-first-inout
5841     0/imm32/no-disp32
5842     0/imm32/output-is-write-only
5843     _Primitive-add-reg-to-reg/imm32/next
5844 _Primitive-add-reg-to-reg:
5845     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
5846     "add"/imm32/name
5847     Single-int-var-in-some-register/imm32/inouts
5848     Single-int-var-in-some-register/imm32/outputs
5849     "01/add-to"/imm32/subx-name
5850     3/imm32/rm32-is-first-output
5851     1/imm32/r32-is-first-inout
5852     0/imm32/no-imm32
5853     0/imm32/no-disp32
5854     0/imm32/output-is-write-only
5855     _Primitive-add-reg-to-mem/imm32/next
5856 _Primitive-add-reg-to-mem:
5857     # add-to var1 var2/reg => 01/add-to var1 var2/r32
5858     "add-to"/imm32/name
5859     Two-args-int-stack-int-reg/imm32/inouts
5860     0/imm32/outputs
5861     "01/add-to"/imm32/subx-name
5862     1/imm32/rm32-is-first-inout
5863     2/imm32/r32-is-second-inout
5864     0/imm32/no-imm32
5865     0/imm32/no-disp32
5866     0/imm32/output-is-write-only
5867     _Primitive-add-mem-to-reg/imm32/next
5868 _Primitive-add-mem-to-reg:
5869     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
5870     "add"/imm32/name
5871     Single-int-var-in-mem/imm32/inouts
5872     Single-int-var-in-some-register/imm32/outputs
5873     "03/add"/imm32/subx-name
5874     1/imm32/rm32-is-first-inout
5875     3/imm32/r32-is-first-output
5876     0/imm32/no-imm32
5877     0/imm32/no-disp32
5878     0/imm32/output-is-write-only
5879     _Primitive-add-lit-to-reg/imm32/next
5880 _Primitive-add-lit-to-reg:
5881     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
5882     "add"/imm32/name
5883     Single-lit-var/imm32/inouts
5884     Single-int-var-in-some-register/imm32/outputs
5885     "81 0/subop/add"/imm32/subx-name
5886     3/imm32/rm32-is-first-output
5887     0/imm32/no-r32
5888     1/imm32/imm32-is-first-inout
5889     0/imm32/no-disp32
5890     0/imm32/output-is-write-only
5891     _Primitive-add-lit-to-mem/imm32/next
5892 _Primitive-add-lit-to-mem:
5893     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
5894     "add-to"/imm32/name
5895     Int-var-and-literal/imm32/inouts
5896     0/imm32/outputs
5897     "81 0/subop/add"/imm32/subx-name
5898     1/imm32/rm32-is-first-inout
5899     0/imm32/no-r32
5900     2/imm32/imm32-is-second-inout
5901     0/imm32/no-disp32
5902     0/imm32/output-is-write-only
5903     _Primitive-subtract-from-eax/imm32/next
5904 # - subtract
5905 _Primitive-subtract-from-eax:
5906     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
5907     "subtract"/imm32/name
5908     Single-lit-var/imm32/inouts
5909     Single-int-var-in-eax/imm32/outputs
5910     "2d/subtract-from-eax"/imm32/subx-name
5911     0/imm32/no-rm32
5912     0/imm32/no-r32
5913     1/imm32/imm32-is-first-inout
5914     0/imm32/no-disp32
5915     0/imm32/output-is-write-only
5916     _Primitive-subtract-reg-from-reg/imm32/next
5917 _Primitive-subtract-reg-from-reg:
5918     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
5919     "subtract"/imm32/name
5920     Single-int-var-in-some-register/imm32/inouts
5921     Single-int-var-in-some-register/imm32/outputs
5922     "29/subtract-from"/imm32/subx-name
5923     3/imm32/rm32-is-first-output
5924     1/imm32/r32-is-first-inout
5925     0/imm32/no-imm32
5926     0/imm32/no-disp32
5927     0/imm32/output-is-write-only
5928     _Primitive-subtract-reg-from-mem/imm32/next
5929 _Primitive-subtract-reg-from-mem:
5930     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
5931     "subtract-from"/imm32/name
5932     Two-args-int-stack-int-reg/imm32/inouts
5933     0/imm32/outputs
5934     "29/subtract-from"/imm32/subx-name
5935     1/imm32/rm32-is-first-inout
5936     2/imm32/r32-is-second-inout
5937     0/imm32/no-imm32
5938     0/imm32/no-disp32
5939     0/imm32/output-is-write-only
5940     _Primitive-subtract-mem-from-reg/imm32/next
5941 _Primitive-subtract-mem-from-reg:
5942     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
5943     "subtract"/imm32/name
5944     Single-int-var-in-mem/imm32/inouts
5945     Single-int-var-in-some-register/imm32/outputs
5946     "2b/subtract"/imm32/subx-name
5947     1/imm32/rm32-is-first-inout
5948     3/imm32/r32-is-first-output
5949     0/imm32/no-imm32
5950     0/imm32/no-disp32
5951     0/imm32/output-is-write-only
5952     _Primitive-subtract-lit-from-reg/imm32/next
5953 _Primitive-subtract-lit-from-reg:
5954     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
5955     "subtract"/imm32/name
5956     Single-lit-var/imm32/inouts
5957     Single-int-var-in-some-register/imm32/outputs
5958     "81 5/subop/subtract"/imm32/subx-name
5959     3/imm32/rm32-is-first-output
5960     0/imm32/no-r32
5961     1/imm32/imm32-is-first-inout
5962     0/imm32/no-disp32
5963     0/imm32/output-is-write-only
5964     _Primitive-subtract-lit-from-mem/imm32/next
5965 _Primitive-subtract-lit-from-mem:
5966     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
5967     "subtract-from"/imm32/name
5968     Int-var-and-literal/imm32/inouts
5969     0/imm32/outputs
5970     "81 5/subop/subtract"/imm32/subx-name
5971     1/imm32/rm32-is-first-inout
5972     0/imm32/no-r32
5973     2/imm32/imm32-is-first-inout
5974     0/imm32/no-disp32
5975     0/imm32/output-is-write-only
5976     _Primitive-and-with-eax/imm32/next
5977 # - and
5978 _Primitive-and-with-eax:
5979     # var/eax <- and lit => 25/and-with-eax lit/imm32
5980     "and"/imm32/name
5981     Single-lit-var/imm32/inouts
5982     Single-int-var-in-eax/imm32/outputs
5983     "25/and-with-eax"/imm32/subx-name
5984     0/imm32/no-rm32
5985     0/imm32/no-r32
5986     1/imm32/imm32-is-first-inout
5987     0/imm32/no-disp32
5988     0/imm32/output-is-write-only
5989     _Primitive-and-reg-with-reg/imm32/next
5990 _Primitive-and-reg-with-reg:
5991     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
5992     "and"/imm32/name
5993     Single-int-var-in-some-register/imm32/inouts
5994     Single-int-var-in-some-register/imm32/outputs
5995     "21/and-with"/imm32/subx-name
5996     3/imm32/rm32-is-first-output
5997     1/imm32/r32-is-first-inout
5998     0/imm32/no-imm32
5999     0/imm32/no-disp32
6000     0/imm32/output-is-write-only
6001     _Primitive-and-reg-with-mem/imm32/next
6002 _Primitive-and-reg-with-mem:
6003     # and-with var1 var2/reg => 21/and-with var1 var2/r32
6004     "and-with"/imm32/name
6005     Two-args-int-stack-int-reg/imm32/inouts
6006     0/imm32/outputs
6007     "21/and-with"/imm32/subx-name
6008     1/imm32/rm32-is-first-inout
6009     2/imm32/r32-is-second-inout
6010     0/imm32/no-imm32
6011     0/imm32/no-disp32
6012     0/imm32/output-is-write-only
6013     _Primitive-and-mem-with-reg/imm32/next
6014 _Primitive-and-mem-with-reg:
6015     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
6016     "and"/imm32/name
6017     Single-int-var-in-mem/imm32/inouts
6018     Single-int-var-in-some-register/imm32/outputs
6019     "23/and"/imm32/subx-name
6020     1/imm32/rm32-is-first-inout
6021     3/imm32/r32-is-first-output
6022     0/imm32/no-imm32
6023     0/imm32/no-disp32
6024     0/imm32/output-is-write-only
6025     _Primitive-and-lit-with-reg/imm32/next
6026 _Primitive-and-lit-with-reg:
6027     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
6028     "and"/imm32/name
6029     Single-lit-var/imm32/inouts
6030     Single-int-var-in-some-register/imm32/outputs
6031     "81 4/subop/and"/imm32/subx-name
6032     3/imm32/rm32-is-first-output
6033     0/imm32/no-r32
6034     1/imm32/imm32-is-first-inout
6035     0/imm32/no-disp32
6036     0/imm32/output-is-write-only
6037     _Primitive-and-lit-with-mem/imm32/next
6038 _Primitive-and-lit-with-mem:
6039     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
6040     "and-with"/imm32/name
6041     Int-var-and-literal/imm32/inouts
6042     0/imm32/outputs
6043     "81 4/subop/and"/imm32/subx-name
6044     1/imm32/rm32-is-first-inout
6045     0/imm32/no-r32
6046     2/imm32/imm32-is-first-inout
6047     0/imm32/no-disp32
6048     0/imm32/output-is-write-only
6049     _Primitive-or-with-eax/imm32/next
6050 # - or
6051 _Primitive-or-with-eax:
6052     # var/eax <- or lit => 0d/or-with-eax lit/imm32
6053     "or"/imm32/name
6054     Single-lit-var/imm32/inouts
6055     Single-int-var-in-eax/imm32/outputs
6056     "0d/or-with-eax"/imm32/subx-name
6057     0/imm32/no-rm32
6058     0/imm32/no-r32
6059     1/imm32/imm32-is-first-inout
6060     0/imm32/no-disp32
6061     0/imm32/output-is-write-only
6062     _Primitive-or-reg-with-reg/imm32/next
6063 _Primitive-or-reg-with-reg:
6064     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
6065     "or"/imm32/name
6066     Single-int-var-in-some-register/imm32/inouts
6067     Single-int-var-in-some-register/imm32/outputs
6068     "09/or-with"/imm32/subx-name
6069     3/imm32/rm32-is-first-output
6070     1/imm32/r32-is-first-inout
6071     0/imm32/no-imm32
6072     0/imm32/no-disp32
6073     0/imm32/output-is-write-only
6074     _Primitive-or-reg-with-mem/imm32/next
6075 _Primitive-or-reg-with-mem:
6076     # or-with var1 var2/reg => 09/or-with var1 var2/r32
6077     "or-with"/imm32/name
6078     Two-args-int-stack-int-reg/imm32/inouts
6079     0/imm32/outputs
6080     "09/or-with"/imm32/subx-name
6081     1/imm32/rm32-is-first-inout
6082     2/imm32/r32-is-second-inout
6083     0/imm32/no-imm32
6084     0/imm32/no-disp32
6085     0/imm32/output-is-write-only
6086     _Primitive-or-mem-with-reg/imm32/next
6087 _Primitive-or-mem-with-reg:
6088     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
6089     "or"/imm32/name
6090     Single-int-var-in-mem/imm32/inouts
6091     Single-int-var-in-some-register/imm32/outputs
6092     "0b/or"/imm32/subx-name
6093     1/imm32/rm32-is-first-inout
6094     3/imm32/r32-is-first-output
6095     0/imm32/no-imm32
6096     0/imm32/no-disp32
6097     0/imm32/output-is-write-only
6098     _Primitive-or-lit-with-reg/imm32/next
6099 _Primitive-or-lit-with-reg:
6100     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
6101     "or"/imm32/name
6102     Single-lit-var/imm32/inouts
6103     Single-int-var-in-some-register/imm32/outputs
6104     "81 1/subop/or"/imm32/subx-name
6105     3/imm32/rm32-is-first-output
6106     0/imm32/no-r32
6107     1/imm32/imm32-is-first-inout
6108     0/imm32/no-disp32
6109     0/imm32/output-is-write-only
6110     _Primitive-or-lit-with-mem/imm32/next
6111 _Primitive-or-lit-with-mem:
6112     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
6113     "or-with"/imm32/name
6114     Int-var-and-literal/imm32/inouts
6115     0/imm32/outputs
6116     "81 1/subop/or"/imm32/subx-name
6117     1/imm32/rm32-is-first-inout
6118     0/imm32/no-r32
6119     2/imm32/imm32-is-second-inout
6120     0/imm32/no-disp32
6121     0/imm32/output-is-write-only
6122     _Primitive-xor-with-eax/imm32/next
6123 # - xor
6124 _Primitive-xor-with-eax:
6125     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
6126     "xor"/imm32/name
6127     Single-lit-var/imm32/inouts
6128     Single-int-var-in-eax/imm32/outputs
6129     "35/xor-with-eax"/imm32/subx-name
6130     0/imm32/no-rm32
6131     0/imm32/no-r32
6132     1/imm32/imm32-is-first-inout
6133     0/imm32/no-disp32
6134     0/imm32/output-is-write-only
6135     _Primitive-xor-reg-with-reg/imm32/next
6136 _Primitive-xor-reg-with-reg:
6137     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
6138     "xor"/imm32/name
6139     Single-int-var-in-some-register/imm32/inouts
6140     Single-int-var-in-some-register/imm32/outputs
6141     "31/xor-with"/imm32/subx-name
6142     3/imm32/rm32-is-first-output
6143     1/imm32/r32-is-first-inout
6144     0/imm32/no-imm32
6145     0/imm32/no-disp32
6146     0/imm32/output-is-write-only
6147     _Primitive-xor-reg-with-mem/imm32/next
6148 _Primitive-xor-reg-with-mem:
6149     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
6150     "xor-with"/imm32/name
6151     Two-args-int-stack-int-reg/imm32/inouts
6152     0/imm32/outputs
6153     "31/xor-with"/imm32/subx-name
6154     1/imm32/rm32-is-first-inout
6155     2/imm32/r32-is-second-inout
6156     0/imm32/no-imm32
6157     0/imm32/no-disp32
6158     0/imm32/output-is-write-only
6159     _Primitive-xor-mem-with-reg/imm32/next
6160 _Primitive-xor-mem-with-reg:
6161     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
6162     "xor"/imm32/name
6163     Single-int-var-in-mem/imm32/inouts
6164     Single-int-var-in-some-register/imm32/outputs
6165     "33/xor"/imm32/subx-name
6166     1/imm32/rm32-is-first-inout
6167     3/imm32/r32-is-first-output
6168     0/imm32/no-imm32
6169     0/imm32/no-disp32
6170     0/imm32/output-is-write-only
6171     _Primitive-xor-lit-with-reg/imm32/next
6172 _Primitive-xor-lit-with-reg:
6173     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
6174     "xor"/imm32/name
6175     Single-lit-var/imm32/inouts
6176     Single-int-var-in-some-register/imm32/outputs
6177     "81 6/subop/xor"/imm32/subx-name
6178     3/imm32/rm32-is-first-output
6179     0/imm32/no-r32
6180     1/imm32/imm32-is-first-inout
6181     0/imm32/no-disp32
6182     0/imm32/output-is-write-only
6183     _Primitive-xor-lit-with-mem/imm32/next
6184 _Primitive-xor-lit-with-mem:
6185     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
6186     "xor-with"/imm32/name
6187     Int-var-and-literal/imm32/inouts
6188     0/imm32/outputs
6189     "81 6/subop/xor"/imm32/subx-name
6190     1/imm32/rm32-is-first-inout
6191     0/imm32/no-r32
6192     2/imm32/imm32-is-first-inout
6193     0/imm32/no-disp32
6194     0/imm32/output-is-write-only
6195     _Primitive-copy-to-eax/imm32/next
6196 # - copy
6197 _Primitive-copy-to-eax:
6198     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
6199     "copy"/imm32/name
6200     Single-lit-var/imm32/inouts
6201     Single-int-var-in-eax/imm32/outputs
6202     "b8/copy-to-eax"/imm32/subx-name
6203     0/imm32/no-rm32
6204     0/imm32/no-r32
6205     1/imm32/imm32-is-first-inout
6206     0/imm32/no-disp32
6207     1/imm32/output-is-write-only
6208     _Primitive-copy-to-ecx/imm32/next
6209 _Primitive-copy-to-ecx:
6210     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
6211     "copy"/imm32/name
6212     Single-lit-var/imm32/inouts
6213     Single-int-var-in-ecx/imm32/outputs
6214     "b9/copy-to-ecx"/imm32/subx-name
6215     0/imm32/no-rm32
6216     0/imm32/no-r32
6217     1/imm32/imm32-is-first-inout
6218     0/imm32/no-disp32
6219     1/imm32/output-is-write-only
6220     _Primitive-copy-to-edx/imm32/next
6221 _Primitive-copy-to-edx:
6222     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
6223     "copy"/imm32/name
6224     Single-lit-var/imm32/inouts
6225     Single-int-var-in-edx/imm32/outputs
6226     "ba/copy-to-edx"/imm32/subx-name
6227     0/imm32/no-rm32
6228     0/imm32/no-r32
6229     1/imm32/imm32-is-first-inout
6230     0/imm32/no-disp32
6231     1/imm32/output-is-write-only
6232     _Primitive-copy-to-ebx/imm32/next
6233 _Primitive-copy-to-ebx:
6234     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
6235     "copy"/imm32/name
6236     Single-lit-var/imm32/inouts
6237     Single-int-var-in-ebx/imm32/outputs
6238     "bb/copy-to-ebx"/imm32/subx-name
6239     0/imm32/no-rm32
6240     0/imm32/no-r32
6241     1/imm32/imm32-is-first-inout
6242     0/imm32/no-disp32
6243     1/imm32/output-is-write-only
6244     _Primitive-copy-to-esi/imm32/next
6245 _Primitive-copy-to-esi:
6246     # var/esi <- copy lit => be/copy-to-esi lit/imm32
6247     "copy"/imm32/name
6248     Single-lit-var/imm32/inouts
6249     Single-int-var-in-esi/imm32/outputs
6250     "be/copy-to-esi"/imm32/subx-name
6251     0/imm32/no-rm32
6252     0/imm32/no-r32
6253     1/imm32/imm32-is-first-inout
6254     0/imm32/no-disp32
6255     1/imm32/output-is-write-only
6256     _Primitive-copy-to-edi/imm32/next
6257 _Primitive-copy-to-edi:
6258     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
6259     "copy"/imm32/name
6260     Single-lit-var/imm32/inouts
6261     Single-int-var-in-edi/imm32/outputs
6262     "bf/copy-to-edi"/imm32/subx-name
6263     0/imm32/no-rm32
6264     0/imm32/no-r32
6265     1/imm32/imm32-is-first-inout
6266     0/imm32/no-disp32
6267     1/imm32/output-is-write-only
6268     _Primitive-copy-reg-to-reg/imm32/next
6269 _Primitive-copy-reg-to-reg:
6270     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
6271     "copy"/imm32/name
6272     Single-int-var-in-some-register/imm32/inouts
6273     Single-int-var-in-some-register/imm32/outputs
6274     "89/copy-to"/imm32/subx-name
6275     3/imm32/rm32-is-first-output
6276     1/imm32/r32-is-first-inout
6277     0/imm32/no-imm32
6278     0/imm32/no-disp32
6279     1/imm32/output-is-write-only
6280     _Primitive-copy-reg-to-mem/imm32/next
6281 _Primitive-copy-reg-to-mem:
6282     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
6283     "copy-to"/imm32/name
6284     Two-args-int-stack-int-reg/imm32/inouts
6285     0/imm32/outputs
6286     "89/copy-to"/imm32/subx-name
6287     1/imm32/rm32-is-first-inout
6288     2/imm32/r32-is-second-inout
6289     0/imm32/no-imm32
6290     0/imm32/no-disp32
6291     1/imm32/output-is-write-only
6292     _Primitive-copy-mem-to-reg/imm32/next
6293 _Primitive-copy-mem-to-reg:
6294     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
6295     "copy"/imm32/name
6296     Single-int-var-in-mem/imm32/inouts
6297     Single-int-var-in-some-register/imm32/outputs
6298     "8b/copy-from"/imm32/subx-name
6299     1/imm32/rm32-is-first-inout
6300     3/imm32/r32-is-first-output
6301     0/imm32/no-imm32
6302     0/imm32/no-disp32
6303     1/imm32/output-is-write-only
6304     _Primitive-copy-lit-to-reg/imm32/next
6305 _Primitive-copy-lit-to-reg:
6306     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
6307     "copy"/imm32/name
6308     Single-lit-var/imm32/inouts
6309     Single-int-var-in-some-register/imm32/outputs
6310     "c7 0/subop/copy"/imm32/subx-name
6311     3/imm32/rm32-is-first-output
6312     0/imm32/no-r32
6313     1/imm32/imm32-is-first-inout
6314     0/imm32/no-disp32
6315     1/imm32/output-is-write-only
6316     _Primitive-copy-lit-to-mem/imm32/next
6317 _Primitive-copy-lit-to-mem:
6318     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
6319     "copy-to"/imm32/name
6320     Int-var-and-literal/imm32/inouts
6321     0/imm32/outputs
6322     "c7 0/subop/copy"/imm32/subx-name
6323     1/imm32/rm32-is-first-inout
6324     0/imm32/no-r32
6325     2/imm32/imm32-is-first-inout
6326     0/imm32/no-disp32
6327     1/imm32/output-is-write-only
6328     _Primitive-compare-mem-with-reg/imm32/next
6329 # - compare
6330 _Primitive-compare-mem-with-reg:
6331     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
6332     "compare"/imm32/name
6333     Two-args-int-stack-int-reg/imm32/inouts
6334     0/imm32/outputs
6335     "39/compare->"/imm32/subx-name
6336     1/imm32/rm32-is-first-inout
6337     2/imm32/r32-is-second-inout
6338     0/imm32/no-imm32
6339     0/imm32/no-disp32
6340     0/imm32/output-is-write-only
6341     _Primitive-compare-reg-with-mem/imm32/next
6342 _Primitive-compare-reg-with-mem:
6343     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
6344     "compare"/imm32/name
6345     Two-args-int-reg-int-stack/imm32/inouts
6346     0/imm32/outputs
6347     "3b/compare<-"/imm32/subx-name
6348     2/imm32/rm32-is-second-inout
6349     1/imm32/r32-is-first-inout
6350     0/imm32/no-imm32
6351     0/imm32/no-disp32
6352     0/imm32/output-is-write-only
6353     _Primitive-compare-eax-with-literal/imm32/next
6354 _Primitive-compare-eax-with-literal:
6355     # compare var1/eax n => 3d/compare-eax-with n/imm32
6356     "compare"/imm32/name
6357     Two-args-int-eax-int-literal/imm32/inouts
6358     0/imm32/outputs
6359     "3d/compare-eax-with"/imm32/subx-name
6360     0/imm32/no-rm32
6361     0/imm32/no-r32
6362     2/imm32/imm32-is-second-inout
6363     0/imm32/no-disp32
6364     0/imm32/output-is-write-only
6365     _Primitive-compare-reg-with-literal/imm32/next
6366 _Primitive-compare-reg-with-literal:
6367     # compare var1/reg n => 81 7/subop/compare %reg n/imm32
6368     "compare"/imm32/name
6369     Int-var-in-register-and-literal/imm32/inouts
6370     0/imm32/outputs
6371     "81 7/subop/compare"/imm32/subx-name
6372     1/imm32/rm32-is-first-inout
6373     0/imm32/no-r32
6374     2/imm32/imm32-is-second-inout
6375     0/imm32/no-disp32
6376     0/imm32/output-is-write-only
6377     _Primitive-compare-mem-with-literal/imm32/next
6378 _Primitive-compare-mem-with-literal:
6379     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
6380     "compare"/imm32/name
6381     Int-var-and-literal/imm32/inouts
6382     0/imm32/outputs
6383     "81 7/subop/compare"/imm32/subx-name
6384     1/imm32/rm32-is-first-inout
6385     0/imm32/no-r32
6386     2/imm32/imm32-is-second-inout
6387     0/imm32/no-disp32
6388     0/imm32/output-is-write-only
6389     _Primitive-multiply-reg-by-mem/imm32/next
6390 # - multiply
6391 _Primitive-multiply-reg-by-mem:
6392     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
6393     "multiply"/imm32/name
6394     Single-int-var-in-mem/imm32/inouts
6395     Single-int-var-in-some-register/imm32/outputs
6396     "0f af/multiply"/imm32/subx-name
6397     1/imm32/rm32-is-first-inout
6398     3/imm32/r32-is-first-output
6399     0/imm32/no-imm32
6400     0/imm32/no-disp32
6401     0/imm32/output-is-write-only
6402     _Primitive-break-if-addr</imm32/next
6403 # - branches
6404 _Primitive-break-if-addr<:
6405     "break-if-addr<"/imm32/name
6406     0/imm32/inouts
6407     0/imm32/outputs
6408     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
6409     0/imm32/no-rm32
6410     0/imm32/no-r32
6411     0/imm32/no-imm32
6412     0/imm32/no-disp32
6413     0/imm32/no-output
6414     _Primitive-break-if-addr>=/imm32/next
6415 _Primitive-break-if-addr>=:
6416     "break-if-addr>="/imm32/name
6417     0/imm32/inouts
6418     0/imm32/outputs
6419     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
6420     0/imm32/no-rm32
6421     0/imm32/no-r32
6422     0/imm32/no-imm32
6423     0/imm32/no-disp32
6424     0/imm32/no-output
6425     _Primitive-break-if-=/imm32/next
6426 _Primitive-break-if-=:
6427     "break-if-="/imm32/name
6428     0/imm32/inouts
6429     0/imm32/outputs
6430     "0f 84/jump-if-= break/disp32"/imm32/subx-name
6431     0/imm32/no-rm32
6432     0/imm32/no-r32
6433     0/imm32/no-imm32
6434     0/imm32/no-disp32
6435     0/imm32/no-output
6436     _Primitive-break-if-!=/imm32/next
6437 _Primitive-break-if-!=:
6438     "break-if-!="/imm32/name
6439     0/imm32/inouts
6440     0/imm32/outputs
6441     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
6442     0/imm32/no-rm32
6443     0/imm32/no-r32
6444     0/imm32/no-imm32
6445     0/imm32/no-disp32
6446     0/imm32/no-output
6447     _Primitive-break-if-addr<=/imm32/next
6448 _Primitive-break-if-addr<=:
6449     "break-if-addr<="/imm32/name
6450     0/imm32/inouts
6451     0/imm32/outputs
6452     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
6453     0/imm32/no-rm32
6454     0/imm32/no-r32
6455     0/imm32/no-imm32
6456     0/imm32/no-disp32
6457     0/imm32/no-output
6458     _Primitive-break-if-addr>/imm32/next
6459 _Primitive-break-if-addr>:
6460     "break-if-addr>"/imm32/name
6461     0/imm32/inouts
6462     0/imm32/outputs
6463     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
6464     0/imm32/no-rm32
6465     0/imm32/no-r32
6466     0/imm32/no-imm32
6467     0/imm32/no-disp32
6468     0/imm32/no-output
6469     _Primitive-break-if-</imm32/next
6470 _Primitive-break-if-<:
6471     "break-if-<"/imm32/name
6472     0/imm32/inouts
6473     0/imm32/outputs
6474     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
6475     0/imm32/no-rm32
6476     0/imm32/no-r32
6477     0/imm32/no-imm32
6478     0/imm32/no-disp32
6479     0/imm32/no-output
6480     _Primitive-break-if->=/imm32/next
6481 _Primitive-break-if->=:
6482     "break-if->="/imm32/name
6483     0/imm32/inouts
6484     0/imm32/outputs
6485     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
6486     0/imm32/no-rm32
6487     0/imm32/no-r32
6488     0/imm32/no-imm32
6489     0/imm32/no-disp32
6490     0/imm32/no-output
6491     _Primitive-break-if-<=/imm32/next
6492 _Primitive-break-if-<=:
6493     "break-if-<="/imm32/name
6494     0/imm32/inouts
6495     0/imm32/outputs
6496     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
6497     0/imm32/no-rm32
6498     0/imm32/no-r32
6499     0/imm32/no-imm32
6500     0/imm32/no-disp32
6501     0/imm32/no-output
6502     _Primitive-break-if->/imm32/next
6503 _Primitive-break-if->:
6504     "break-if->"/imm32/name
6505     0/imm32/inouts
6506     0/imm32/outputs
6507     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
6508     0/imm32/no-rm32
6509     0/imm32/no-r32
6510     0/imm32/no-imm32
6511     0/imm32/no-disp32
6512     0/imm32/no-output
6513     _Primitive-break/imm32/next
6514 _Primitive-break:
6515     "break"/imm32/name
6516     0/imm32/inouts
6517     0/imm32/outputs
6518     "e9/jump break/disp32"/imm32/subx-name
6519     0/imm32/no-rm32
6520     0/imm32/no-r32
6521     0/imm32/no-imm32
6522     0/imm32/no-disp32
6523     0/imm32/no-output
6524     _Primitive-loop-if-addr</imm32/next
6525 _Primitive-loop-if-addr<:
6526     "loop-if-addr<"/imm32/name
6527     0/imm32/inouts
6528     0/imm32/outputs
6529     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
6530     0/imm32/no-rm32
6531     0/imm32/no-r32
6532     0/imm32/no-imm32
6533     0/imm32/no-disp32
6534     0/imm32/no-output
6535     _Primitive-loop-if-addr>=/imm32/next
6536 _Primitive-loop-if-addr>=:
6537     "loop-if-addr>="/imm32/name
6538     0/imm32/inouts
6539     0/imm32/outputs
6540     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
6541     0/imm32/no-rm32
6542     0/imm32/no-r32
6543     0/imm32/no-imm32
6544     0/imm32/no-disp32
6545     0/imm32/no-output
6546     _Primitive-loop-if-=/imm32/next
6547 _Primitive-loop-if-=:
6548     "loop-if-="/imm32/name
6549     0/imm32/inouts
6550     0/imm32/outputs
6551     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
6552     0/imm32/no-rm32
6553     0/imm32/no-r32
6554     0/imm32/no-imm32
6555     0/imm32/no-disp32
6556     0/imm32/no-output
6557     _Primitive-loop-if-!=/imm32/next
6558 _Primitive-loop-if-!=:
6559     "loop-if-!="/imm32/name
6560     0/imm32/inouts
6561     0/imm32/outputs
6562     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
6563     0/imm32/no-rm32
6564     0/imm32/no-r32
6565     0/imm32/no-imm32
6566     0/imm32/no-disp32
6567     0/imm32/no-output
6568     _Primitive-loop-if-addr<=/imm32/next
6569 _Primitive-loop-if-addr<=:
6570     "loop-if-addr<="/imm32/name
6571     0/imm32/inouts
6572     0/imm32/outputs
6573     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
6574     0/imm32/no-rm32
6575     0/imm32/no-r32
6576     0/imm32/no-imm32
6577     0/imm32/no-disp32
6578     0/imm32/no-output
6579     _Primitive-loop-if-addr>/imm32/next
6580 _Primitive-loop-if-addr>:
6581     "loop-if-addr>"/imm32/name
6582     0/imm32/inouts
6583     0/imm32/outputs
6584     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
6585     0/imm32/no-rm32
6586     0/imm32/no-r32
6587     0/imm32/no-imm32
6588     0/imm32/no-disp32
6589     0/imm32/no-output
6590     _Primitive-loop-if-</imm32/next
6591 _Primitive-loop-if-<:
6592     "loop-if-<"/imm32/name
6593     0/imm32/inouts
6594     0/imm32/outputs
6595     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
6596     0/imm32/no-rm32
6597     0/imm32/no-r32
6598     0/imm32/no-imm32
6599     0/imm32/no-disp32
6600     0/imm32/no-output
6601     _Primitive-loop-if->=/imm32/next
6602 _Primitive-loop-if->=:
6603     "loop-if->="/imm32/name
6604     0/imm32/inouts
6605     0/imm32/outputs
6606     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
6607     0/imm32/no-rm32
6608     0/imm32/no-r32
6609     0/imm32/no-imm32
6610     0/imm32/no-disp32
6611     0/imm32/no-output
6612     _Primitive-loop-if-<=/imm32/next
6613 _Primitive-loop-if-<=:
6614     "loop-if-<="/imm32/name
6615     0/imm32/inouts
6616     0/imm32/outputs
6617     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
6618     0/imm32/no-rm32
6619     0/imm32/no-r32
6620     0/imm32/no-imm32
6621     0/imm32/no-disp32
6622     0/imm32/no-output
6623     _Primitive-loop-if->/imm32/next
6624 _Primitive-loop-if->:
6625     "loop-if->"/imm32/name
6626     0/imm32/inouts
6627     0/imm32/outputs
6628     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
6629     0/imm32/no-rm32
6630     0/imm32/no-r32
6631     0/imm32/no-imm32
6632     0/imm32/no-disp32
6633     0/imm32/no-output
6634     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
6635 _Primitive-loop:
6636     "loop"/imm32/name
6637     0/imm32/inouts
6638     0/imm32/outputs
6639     "e9/jump loop/disp32"/imm32/subx-name
6640     0/imm32/no-rm32
6641     0/imm32/no-r32
6642     0/imm32/no-imm32
6643     0/imm32/no-disp32
6644     0/imm32/no-output
6645     _Primitive-break-if-addr<-named/imm32/next
6646 # - branches to named blocks
6647 _Primitive-break-if-addr<-named:
6648     "break-if-addr<"/imm32/name
6649     Single-lit-var/imm32/inouts
6650     0/imm32/outputs
6651     "0f 82/jump-if-addr<"/imm32/subx-name
6652     0/imm32/no-rm32
6653     0/imm32/no-r32
6654     0/imm32/no-imm32
6655     1/imm32/disp32-is-first-inout
6656     0/imm32/no-output
6657     _Primitive-break-if-addr>=-named/imm32/next
6658 _Primitive-break-if-addr>=-named:
6659     "break-if-addr>="/imm32/name
6660     Single-lit-var/imm32/inouts
6661     0/imm32/outputs
6662     "0f 83/jump-if-addr>="/imm32/subx-name
6663     0/imm32/no-rm32
6664     0/imm32/no-r32
6665     0/imm32/no-imm32
6666     1/imm32/disp32-is-first-inout
6667     0/imm32/no-output
6668     _Primitive-break-if-=-named/imm32/next
6669 _Primitive-break-if-=-named:
6670     "break-if-="/imm32/name
6671     Single-lit-var/imm32/inouts
6672     0/imm32/outputs
6673     "0f 84/jump-if-="/imm32/subx-name
6674     0/imm32/no-rm32
6675     0/imm32/no-r32
6676     0/imm32/no-imm32
6677     1/imm32/disp32-is-first-inout
6678     0/imm32/no-output
6679     _Primitive-break-if-!=-named/imm32/next
6680 _Primitive-break-if-!=-named:
6681     "break-if-!="/imm32/name
6682     Single-lit-var/imm32/inouts
6683     0/imm32/outputs
6684     "0f 85/jump-if-!="/imm32/subx-name
6685     0/imm32/no-rm32
6686     0/imm32/no-r32
6687     0/imm32/no-imm32
6688     1/imm32/disp32-is-first-inout
6689     0/imm32/no-output
6690     _Primitive-break-if-addr<=-named/imm32/next
6691 _Primitive-break-if-addr<=-named:
6692     "break-if-addr<="/imm32/name
6693     Single-lit-var/imm32/inouts
6694     0/imm32/outputs
6695     "0f 86/jump-if-addr<="/imm32/subx-name
6696     0/imm32/no-rm32
6697     0/imm32/no-r32
6698     0/imm32/no-imm32
6699     1/imm32/disp32-is-first-inout
6700     0/imm32/no-output
6701     _Primitive-break-if-addr>-named/imm32/next
6702 _Primitive-break-if-addr>-named:
6703     "break-if-addr>"/imm32/name
6704     Single-lit-var/imm32/inouts
6705     0/imm32/outputs
6706     "0f 87/jump-if-addr>"/imm32/subx-name
6707     0/imm32/no-rm32
6708     0/imm32/no-r32
6709     0/imm32/no-imm32
6710     1/imm32/disp32-is-first-inout
6711     0/imm32/no-output
6712     _Primitive-break-if-<-named/imm32/next
6713 _Primitive-break-if-<-named:
6714     "break-if-<"/imm32/name
6715     Single-lit-var/imm32/inouts
6716     0/imm32/outputs
6717     "0f 8c/jump-if-<"/imm32/subx-name
6718     0/imm32/no-rm32
6719     0/imm32/no-r32
6720     0/imm32/no-imm32
6721     1/imm32/disp32-is-first-inout
6722     0/imm32/no-output
6723     _Primitive-break-if->=-named/imm32/next
6724 _Primitive-break-if->=-named:
6725     "break-if->="/imm32/name
6726     Single-lit-var/imm32/inouts
6727     0/imm32/outputs
6728     "0f 8d/jump-if->="/imm32/subx-name
6729     0/imm32/no-rm32
6730     0/imm32/no-r32
6731     0/imm32/no-imm32
6732     1/imm32/disp32-is-first-inout
6733     0/imm32/no-output
6734     _Primitive-break-if-<=-named/imm32/next
6735 _Primitive-break-if-<=-named:
6736     "break-if-<="/imm32/name
6737     Single-lit-var/imm32/inouts
6738     0/imm32/outputs
6739     "0f 8e/jump-if-<="/imm32/subx-name
6740     0/imm32/no-rm32
6741     0/imm32/no-r32
6742     0/imm32/no-imm32
6743     1/imm32/disp32-is-first-inout
6744     0/imm32/no-output
6745     _Primitive-break-if->-named/imm32/next
6746 _Primitive-break-if->-named:
6747     "break-if->"/imm32/name
6748     Single-lit-var/imm32/inouts
6749     0/imm32/outputs
6750     "0f 8f/jump-if->"/imm32/subx-name
6751     0/imm32/no-rm32
6752     0/imm32/no-r32
6753     0/imm32/no-imm32
6754     1/imm32/disp32-is-first-inout
6755     0/imm32/no-output
6756     _Primitive-break-named/imm32/next
6757 _Primitive-break-named:
6758     "break"/imm32/name
6759     Single-lit-var/imm32/inouts
6760     0/imm32/outputs
6761     "e9/jump"/imm32/subx-name
6762     0/imm32/no-rm32
6763     0/imm32/no-r32
6764     0/imm32/no-imm32
6765     1/imm32/disp32-is-first-inout
6766     0/imm32/no-output
6767     _Primitive-loop-if-addr<-named/imm32/next
6768 _Primitive-loop-if-addr<-named:
6769     "loop-if-addr<"/imm32/name
6770     Single-lit-var/imm32/inouts
6771     0/imm32/outputs
6772     "0f 82/jump-if-addr<"/imm32/subx-name
6773     0/imm32/no-rm32
6774     0/imm32/no-r32
6775     0/imm32/no-imm32
6776     1/imm32/disp32-is-first-inout
6777     0/imm32/no-output
6778     _Primitive-loop-if-addr>=-named/imm32/next
6779 _Primitive-loop-if-addr>=-named:
6780     "loop-if-addr>="/imm32/name
6781     Single-lit-var/imm32/inouts
6782     0/imm32/outputs
6783     "0f 83/jump-if-addr>="/imm32/subx-name
6784     0/imm32/no-rm32
6785     0/imm32/no-r32
6786     0/imm32/no-imm32
6787     1/imm32/disp32-is-first-inout
6788     0/imm32/no-output
6789     _Primitive-loop-if-=-named/imm32/next
6790 _Primitive-loop-if-=-named:
6791     "loop-if-="/imm32/name
6792     Single-lit-var/imm32/inouts
6793     0/imm32/outputs
6794     "0f 84/jump-if-="/imm32/subx-name
6795     0/imm32/no-rm32
6796     0/imm32/no-r32
6797     0/imm32/no-imm32
6798     1/imm32/disp32-is-first-inout
6799     0/imm32/no-output
6800     _Primitive-loop-if-!=-named/imm32/next
6801 _Primitive-loop-if-!=-named:
6802     "loop-if-!="/imm32/name
6803     Single-lit-var/imm32/inouts
6804     0/imm32/outputs
6805     "0f 85/jump-if-!="/imm32/subx-name
6806     0/imm32/no-rm32
6807     0/imm32/no-r32
6808     0/imm32/no-imm32
6809     1/imm32/disp32-is-first-inout
6810     0/imm32/no-output
6811     _Primitive-loop-if-addr<=-named/imm32/next
6812 _Primitive-loop-if-addr<=-named:
6813     "loop-if-addr<="/imm32/name
6814     Single-lit-var/imm32/inouts
6815     0/imm32/outputs
6816     "0f 86/jump-if-addr<="/imm32/subx-name
6817     0/imm32/no-rm32
6818     0/imm32/no-r32
6819     0/imm32/no-imm32
6820     1/imm32/disp32-is-first-inout
6821     0/imm32/no-output
6822     _Primitive-loop-if-addr>-named/imm32/next
6823 _Primitive-loop-if-addr>-named:
6824     "loop-if-addr>"/imm32/name
6825     Single-lit-var/imm32/inouts
6826     0/imm32/outputs
6827     "0f 87/jump-if-addr>"/imm32/subx-name
6828     0/imm32/no-rm32
6829     0/imm32/no-r32
6830     0/imm32/no-imm32
6831     1/imm32/disp32-is-first-inout
6832     0/imm32/no-output
6833     _Primitive-loop-if-<-named/imm32/next
6834 _Primitive-loop-if-<-named:
6835     "loop-if-<"/imm32/name
6836     Single-lit-var/imm32/inouts
6837     0/imm32/outputs
6838     "0f 8c/jump-if-<"/imm32/subx-name
6839     0/imm32/no-rm32
6840     0/imm32/no-r32
6841     0/imm32/no-imm32
6842     1/imm32/disp32-is-first-inout
6843     0/imm32/no-output
6844     _Primitive-loop-if->=-named/imm32/next
6845 _Primitive-loop-if->=-named:
6846     "loop-if->="/imm32/name
6847     Single-lit-var/imm32/inouts
6848     0/imm32/outputs
6849     "0f 8d/jump-if->="/imm32/subx-name
6850     0/imm32/no-rm32
6851     0/imm32/no-r32
6852     0/imm32/no-imm32
6853     1/imm32/disp32-is-first-inout
6854     0/imm32/no-output
6855     _Primitive-loop-if-<=-named/imm32/next
6856 _Primitive-loop-if-<=-named:
6857     "loop-if-<="/imm32/name
6858     Single-lit-var/imm32/inouts
6859     0/imm32/outputs
6860     "0f 8e/jump-if-<="/imm32/subx-name
6861     0/imm32/no-rm32
6862     0/imm32/no-r32
6863     0/imm32/no-imm32
6864     1/imm32/disp32-is-first-inout
6865     0/imm32/no-output
6866     _Primitive-loop-if->-named/imm32/next
6867 _Primitive-loop-if->-named:
6868     "loop-if->"/imm32/name
6869     Single-lit-var/imm32/inouts
6870     0/imm32/outputs
6871     "0f 8f/jump-if->"/imm32/subx-name
6872     0/imm32/no-rm32
6873     0/imm32/no-r32
6874     0/imm32/no-imm32
6875     1/imm32/disp32-is-first-inout
6876     0/imm32/no-output
6877     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
6878 _Primitive-loop-named:
6879     "loop"/imm32/name
6880     Single-lit-var/imm32/inouts
6881     0/imm32/outputs
6882     "e9/jump"/imm32/subx-name
6883     0/imm32/no-rm32
6884     0/imm32/no-r32
6885     0/imm32/no-imm32
6886     1/imm32/disp32-is-first-inout
6887     0/imm32/no-output
6888     0/imm32/next
6889 
6890 Single-int-var-in-mem:
6891     Int-var-in-mem/imm32
6892     0/imm32/next
6893 
6894 Int-var-in-mem:
6895     "arg1"/imm32/name
6896     Type-int/imm32
6897     1/imm32/some-block-depth
6898     1/imm32/some-stack-offset
6899     0/imm32/no-register
6900 
6901 Two-args-int-stack-int-reg:
6902     Int-var-in-mem/imm32
6903     Single-int-var-in-some-register/imm32/next
6904 
6905 Two-args-int-reg-int-stack:
6906     Int-var-in-some-register/imm32
6907     Single-int-var-in-mem/imm32/next
6908 
6909 Two-args-int-eax-int-literal:
6910     Int-var-in-eax/imm32
6911     Single-lit-var/imm32/next
6912 
6913 Int-var-and-literal:
6914     Int-var-in-mem/imm32
6915     Single-lit-var/imm32/next
6916 
6917 Int-var-in-register-and-literal:
6918     Int-var-in-some-register/imm32
6919     Single-lit-var/imm32/next
6920 
6921 Single-int-var-in-some-register:
6922     Int-var-in-some-register/imm32
6923     0/imm32/next
6924 
6925 Int-var-in-some-register:
6926     "arg1"/imm32/name
6927     Type-int/imm32
6928     1/imm32/some-block-depth
6929     0/imm32/no-stack-offset
6930     Any-register/imm32
6931 
6932 Single-int-var-in-eax:
6933     Int-var-in-eax/imm32
6934     0/imm32/next
6935 
6936 Int-var-in-eax:
6937     "arg1"/imm32/name
6938     Type-int/imm32
6939     1/imm32/some-block-depth
6940     0/imm32/no-stack-offset
6941     "eax"/imm32/register
6942 
6943 Single-int-var-in-ecx:
6944     Int-var-in-ecx/imm32
6945     0/imm32/next
6946 
6947 Int-var-in-ecx:
6948     "arg1"/imm32/name
6949     Type-int/imm32
6950     1/imm32/some-block-depth
6951     0/imm32/no-stack-offset
6952     "ecx"/imm32/register
6953 
6954 Single-int-var-in-edx:
6955     Int-var-in-edx/imm32
6956     0/imm32/next
6957 
6958 Int-var-in-edx:
6959     "arg1"/imm32/name
6960     Type-int/imm32
6961     1/imm32/some-block-depth
6962     0/imm32/no-stack-offset
6963     "edx"/imm32/register
6964 
6965 Single-int-var-in-ebx:
6966     Int-var-in-ebx/imm32
6967     0/imm32/next
6968 
6969 Int-var-in-ebx:
6970     "arg1"/imm32/name
6971     Type-int/imm32
6972     1/imm32/some-block-depth
6973     0/imm32/no-stack-offset
6974     "ebx"/imm32/register
6975 
6976 Single-int-var-in-esi:
6977     Int-var-in-esi/imm32
6978     0/imm32/next
6979 
6980 Int-var-in-esi:
6981     "arg1"/imm32/name
6982     Type-int/imm32
6983     1/imm32/some-block-depth
6984     0/imm32/no-stack-offset
6985     "esi"/imm32/register
6986 
6987 Single-int-var-in-edi:
6988     Int-var-in-edi/imm32
6989     0/imm32/next
6990 
6991 Int-var-in-edi:
6992     "arg1"/imm32/name
6993     Type-int/imm32
6994     1/imm32/some-block-depth
6995     0/imm32/no-stack-offset
6996     "edi"/imm32/register
6997 
6998 Single-lit-var:
6999     Lit-var/imm32
7000     0/imm32/next
7001 
7002 Lit-var:
7003     "literal"/imm32/name
7004     Type-literal/imm32
7005     1/imm32/some-block-depth
7006     0/imm32/no-stack-offset
7007     0/imm32/no-register
7008 
7009 Type-int:
7010     1/imm32/left/int
7011     0/imm32/right/null
7012 
7013 Type-literal:
7014     0/imm32/left/literal
7015     0/imm32/right/null
7016 
7017 == code
7018 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle statement), primitive: (handle function)
7019     # . prologue
7020     55/push-ebp
7021     89/<- %ebp 4/r32/esp
7022     # . save registers
7023     50/push-eax
7024     51/push-ecx
7025     # ecx = primitive
7026     8b/-> *(ebp+0x10) 1/r32/ecx
7027     # emit primitive name
7028     (emit-indent *(ebp+8) *Curr-block-depth)
7029     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
7030     # emit rm32 if necessary
7031     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
7032     # emit r32 if necessary
7033     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
7034     # emit imm32 if necessary
7035     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
7036     # emit disp32 if necessary
7037     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
7038     (write-buffered *(ebp+8) Newline)
7039 $emit-subx-primitive:end:
7040     # . restore registers
7041     59/pop-to-ecx
7042     58/pop-to-eax
7043     # . epilogue
7044     89/<- %esp 5/r32/ebp
7045     5d/pop-to-ebp
7046     c3/return
7047 
7048 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
7049     # . prologue
7050     55/push-ebp
7051     89/<- %ebp 4/r32/esp
7052     # . save registers
7053     50/push-eax
7054     # if (l == 0) return
7055     81 7/subop/compare *(ebp+0xc) 0/imm32
7056     74/jump-if-= $emit-subx-rm32:end/disp8
7057     # var v/eax: (handle var)
7058     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7059     (emit-subx-var-as-rm32 *(ebp+8) %eax)
7060 $emit-subx-rm32:end:
7061     # . restore registers
7062     58/pop-to-eax
7063     # . epilogue
7064     89/<- %esp 5/r32/ebp
7065     5d/pop-to-ebp
7066     c3/return
7067 
7068 get-stmt-operand-from-arg-location:  # stmt: (handle statement), l: arg-location -> var/eax: (handle stmt-var)
7069     # . prologue
7070     55/push-ebp
7071     89/<- %ebp 4/r32/esp
7072     # . save registers
7073     51/push-ecx
7074     # eax = l
7075     8b/-> *(ebp+0xc) 0/r32/eax
7076     # ecx = stmt
7077     8b/-> *(ebp+8) 1/r32/ecx
7078     # if (l == 1) return stmt->inouts
7079     {
7080       3d/compare-eax-and 1/imm32
7081       75/jump-if-!= break/disp8
7082 $get-stmt-operand-from-arg-location:1:
7083       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
7084       eb/jump $get-stmt-operand-from-arg-location:end/disp8
7085     }
7086     # if (l == 2) return stmt->inouts->next
7087     {
7088       3d/compare-eax-and 2/imm32
7089       75/jump-if-!= break/disp8
7090 $get-stmt-operand-from-arg-location:2:
7091       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
7092       8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
7093       eb/jump $get-stmt-operand-from-arg-location:end/disp8
7094     }
7095     # if (l == 3) return stmt->outputs
7096     {
7097       3d/compare-eax-and 3/imm32
7098       75/jump-if-!= break/disp8
7099 $get-stmt-operand-from-arg-location:3:
7100       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
7101       eb/jump $get-stmt-operand-from-arg-location:end/disp8
7102     }
7103     # abort
7104     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
7105 $get-stmt-operand-from-arg-location:end:
7106     # . restore registers
7107     59/pop-to-ecx
7108     # . epilogue
7109     89/<- %esp 5/r32/ebp
7110     5d/pop-to-ebp
7111     c3/return
7112 
7113 $get-stmt-operand-from-arg-location:abort:
7114     # error("invalid arg-location " eax)
7115     (write-buffered Stderr "invalid arg-location ")
7116     (print-int32-buffered Stderr %eax)
7117     (write-buffered Stderr Newline)
7118     (flush Stderr)
7119     # . syscall(exit, 1)
7120     bb/copy-to-ebx  1/imm32
7121     b8/copy-to-eax  1/imm32/exit
7122     cd/syscall  0x80/imm8
7123     # never gets here
7124 
7125 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
7126     # . prologue
7127     55/push-ebp
7128     89/<- %ebp 4/r32/esp
7129     # . save registers
7130     50/push-eax
7131     51/push-ecx
7132     # if (location == 0) return
7133     81 7/subop/compare *(ebp+0xc) 0/imm32
7134     0f 84/jump-if-= $emit-subx-r32:end/disp32
7135     # var v/eax: (handle var)
7136     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7137     8b/-> *eax 0/r32/eax  # Stmt-var-value
7138     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
7139     (write-buffered *(ebp+8) Space)
7140     (print-int32-buffered *(ebp+8) *eax)
7141     (write-buffered *(ebp+8) "/r32")
7142 $emit-subx-r32:end:
7143     # . restore registers
7144     59/pop-to-ecx
7145     58/pop-to-eax
7146     # . epilogue
7147     89/<- %esp 5/r32/ebp
7148     5d/pop-to-ebp
7149     c3/return
7150 
7151 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
7152     # . prologue
7153     55/push-ebp
7154     89/<- %ebp 4/r32/esp
7155     # . save registers
7156     50/push-eax
7157     51/push-ecx
7158     # if (location == 0) return
7159     81 7/subop/compare *(ebp+0xc) 0/imm32
7160     74/jump-if-= $emit-subx-imm32:end/disp8
7161     # var v/eax: (handle var)
7162     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7163     8b/-> *eax 0/r32/eax  # Stmt-var-value
7164     (write-buffered *(ebp+8) Space)
7165     (write-buffered *(ebp+8) *eax)  # Var-name
7166     (write-buffered *(ebp+8) "/imm32")
7167 $emit-subx-imm32:end:
7168     # . restore registers
7169     59/pop-to-ecx
7170     58/pop-to-eax
7171     # . epilogue
7172     89/<- %esp 5/r32/ebp
7173     5d/pop-to-ebp
7174     c3/return
7175 
7176 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
7177     # . prologue
7178     55/push-ebp
7179     89/<- %ebp 4/r32/esp
7180     # . save registers
7181     50/push-eax
7182     51/push-ecx
7183     # if (location == 0) return
7184     81 7/subop/compare *(ebp+0xc) 0/imm32
7185     0f 84/jump-if-= $emit-subx-disp32:end/disp32
7186     # var v/eax: (handle var)
7187     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7188     8b/-> *eax 0/r32/eax  # Stmt-var-value
7189     (write-buffered *(ebp+8) Space)
7190     (write-buffered *(ebp+8) *eax)  # Var-name
7191     # hack: if instruction operation starts with "break", emit ":break"
7192     # var name/ecx: (addr array byte) = stmt->operation
7193     8b/-> *(ebp+0x10) 0/r32/eax
7194     8b/-> *(eax+4) 1/r32/ecx
7195     {
7196       (string-starts-with? %ecx "break")  # => eax
7197       3d/compare-eax-and 0/imm32/false
7198       74/jump-if-= break/disp8
7199       (write-buffered *(ebp+8) ":break")
7200     }
7201     # hack: if instruction operation starts with "loop", emit ":loop"
7202     {
7203       (string-starts-with? %ecx "loop")  # => eax
7204       3d/compare-eax-and 0/imm32/false
7205       74/jump-if-= break/disp8
7206       (write-buffered *(ebp+8) ":loop")
7207     }
7208     (write-buffered *(ebp+8) "/disp32")
7209 $emit-subx-disp32:end:
7210     # . restore registers
7211     59/pop-to-ecx
7212     58/pop-to-eax
7213     # . epilogue
7214     89/<- %esp 5/r32/ebp
7215     5d/pop-to-ebp
7216     c3/return
7217 
7218 emit-subx-call:  # out: (addr buffered-file), stmt: (handle statement), callee: (handle function)
7219     # . prologue
7220     55/push-ebp
7221     89/<- %ebp 4/r32/esp
7222     # . save registers
7223     51/push-ecx
7224     #
7225     (emit-indent *(ebp+8) *Curr-block-depth)
7226     (write-buffered *(ebp+8) "(")
7227     # - emit function name
7228     8b/-> *(ebp+0x10) 1/r32/ecx
7229     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
7230     # - emit arguments
7231     # var curr/ecx: (handle stmt-var) = stmt->inouts
7232     8b/-> *(ebp+0xc) 1/r32/ecx
7233     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
7234     {
7235       # if (curr == null) break
7236       81 7/subop/compare %ecx 0/imm32
7237       74/jump-if-= break/disp8
7238       #
7239       (emit-subx-call-operand *(ebp+8) %ecx)
7240       # curr = curr->next
7241       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
7242       eb/jump loop/disp8
7243     }
7244     #
7245     (write-buffered *(ebp+8) ")\n")
7246 $emit-subx-call:end:
7247     # . restore registers
7248     59/pop-to-ecx
7249     # . epilogue
7250     89/<- %esp 5/r32/ebp
7251     5d/pop-to-ebp
7252     c3/return
7253 
7254 # like a function call, except we have no idea what function it is
7255 # we hope it's defined in SubX and that the types are ok
7256 emit-hailmary-call:  # out: (addr buffered-file), stmt: (handle statement)
7257     # . prologue
7258     55/push-ebp
7259     89/<- %ebp 4/r32/esp
7260     # . save registers
7261     51/push-ecx
7262     #
7263     (emit-indent *(ebp+8) *Curr-block-depth)
7264     (write-buffered *(ebp+8) "(")
7265     # ecx = stmt
7266     8b/-> *(ebp+0xc) 1/r32/ecx
7267     # - emit function name
7268     (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
7269     # - emit arguments
7270     # var curr/ecx: (handle stmt-var) = stmt->inouts
7271     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
7272     {
7273       # if (curr == null) break
7274       81 7/subop/compare %ecx 0/imm32
7275       74/jump-if-= break/disp8
7276       #
7277       (emit-subx-call-operand *(ebp+8) %ecx)
7278       # curr = curr->next
7279       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
7280       eb/jump loop/disp8
7281     }
7282     #
7283     (write-buffered *(ebp+8) ")\n")
7284 $emit-hailmary-call:end:
7285     # . restore registers
7286     59/pop-to-ecx
7287     # . epilogue
7288     89/<- %esp 5/r32/ebp
7289     5d/pop-to-ebp
7290     c3/return
7291 
7292 emit-subx-call-operand:  # out: (addr buffered-file), s: (handle stmt-var)
7293     # shares code with emit-subx-var-as-rm32
7294     # . prologue
7295     55/push-ebp
7296     89/<- %ebp 4/r32/esp
7297     # . save registers
7298     50/push-eax
7299     51/push-ecx
7300     56/push-esi
7301     # ecx = s
7302     8b/-> *(ebp+0xc) 1/r32/ecx
7303     # var operand/esi: (handle var) = s->value
7304     8b/-> *ecx 6/r32/esi  # Stmt-var-value
7305     # if (operand->register && s->is-deref?) emit "*__"
7306     {
7307 $emit-subx-call-operand:check-for-register-indirect:
7308       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7309       74/jump-if-= break/disp8
7310       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7311       74/jump-if-= break/disp8
7312 $emit-subx-call-operand:register-indirect:
7313       (write-buffered *(ebp+8) " *")
7314       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
7315       e9/jump $emit-subx-call-operand:end/disp32
7316     }
7317     # if (operand->register && !s->is-deref?) emit "%__"
7318     {
7319 $emit-subx-call-operand:check-for-register-direct:
7320       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7321       74/jump-if-= break/disp8
7322       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7323       75/jump-if-!= break/disp8
7324 $emit-subx-call-operand:register-direct:
7325       (write-buffered *(ebp+8) " %")
7326       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
7327       e9/jump $emit-subx-call-operand:end/disp32
7328     }
7329     # else if (operand->stack-offset) emit "*(ebp+__)"
7330     {
7331       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-stack-offset
7332       74/jump-if-= break/disp8
7333 $emit-subx-call-operand:stack:
7334       (write-buffered *(ebp+8) Space)
7335       (write-buffered *(ebp+8) "*(ebp+")
7336       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-stack-offset
7337       (write-buffered *(ebp+8) ")")
7338       e9/jump $emit-subx-call-operand:end/disp32
7339     }
7340     # else if (operand->type == literal) emit "__"
7341     {
7342       8b/-> *(esi+4) 0/r32/eax  # Var-type
7343       81 7/subop/compare *eax 0/imm32  # Tree-left
7344       75/jump-if-!= break/disp8
7345 $emit-subx-call-operand:literal:
7346       (write-buffered *(ebp+8) Space)
7347       (write-buffered *(ebp+8) *esi)
7348     }
7349 $emit-subx-call-operand:end:
7350     # . restore registers
7351     5e/pop-to-esi
7352     59/pop-to-ecx
7353     58/pop-to-eax
7354     # . epilogue
7355     89/<- %esp 5/r32/ebp
7356     5d/pop-to-ebp
7357     c3/return
7358 
7359 emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (handle stmt-var)
7360     # . prologue
7361     55/push-ebp
7362     89/<- %ebp 4/r32/esp
7363     # . save registers
7364     50/push-eax
7365     51/push-ecx
7366     56/push-esi
7367     # ecx = s
7368     8b/-> *(ebp+0xc) 1/r32/ecx
7369     # var operand/esi: (handle var) = s->value
7370     8b/-> *ecx 6/r32/esi  # Stmt-var-value
7371     # if (operand->register && s->is-deref?) emit "*__"
7372     {
7373 $emit-subx-var-as-rm32:check-for-register-indirect:
7374       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7375       74/jump-if-= break/disp8
7376       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7377       74/jump-if-= break/disp8
7378 $emit-subx-var-as-rm32:register-indirect:
7379       (write-buffered *(ebp+8) " *")
7380       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
7381     }
7382     # if (operand->register && !s->is-deref?) emit "%__"
7383     {
7384 $emit-subx-var-as-rm32:check-for-register-direct:
7385       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7386       74/jump-if-= break/disp8
7387       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7388       75/jump-if-!= break/disp8
7389 $emit-subx-var-as-rm32:register-direct:
7390       (write-buffered *(ebp+8) " %")
7391       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
7392     }
7393     # else if (operand->stack-offset) emit "*(ebp+__)"
7394     {
7395       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-stack-offset
7396       74/jump-if-= break/disp8
7397 $emit-subx-var-as-rm32:stack:
7398       (write-buffered *(ebp+8) Space)
7399       (write-buffered *(ebp+8) "*(ebp+")
7400       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-stack-offset
7401       (write-buffered *(ebp+8) ")")
7402     }
7403 $emit-subx-var-as-rm32:end:
7404     # . restore registers
7405     5e/pop-to-esi
7406     59/pop-to-ecx
7407     58/pop-to-eax
7408     # . epilogue
7409     89/<- %esp 5/r32/ebp
7410     5d/pop-to-ebp
7411     c3/return
7412 
7413 find-matching-function:  # functions: (addr function), stmt: (handle statement) -> result/eax: (handle function)
7414     # . prologue
7415     55/push-ebp
7416     89/<- %ebp 4/r32/esp
7417     # . save registers
7418     51/push-ecx
7419     # var curr/ecx: (handle function) = functions
7420     8b/-> *(ebp+8) 1/r32/ecx
7421     {
7422       # if (curr == null) break
7423       81 7/subop/compare %ecx 0/imm32
7424       74/jump-if-= break/disp8
7425       # if match(stmt, curr) return curr
7426       {
7427         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
7428         3d/compare-eax-and 0/imm32/false
7429         74/jump-if-= break/disp8
7430         89/<- %eax 1/r32/ecx
7431         eb/jump $find-matching-function:end/disp8
7432       }
7433       # curr = curr->next
7434       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
7435       eb/jump loop/disp8
7436     }
7437     # return null
7438     b8/copy-to-eax 0/imm32
7439 $find-matching-function:end:
7440     # . restore registers
7441     59/pop-to-ecx
7442     # . epilogue
7443     89/<- %esp 5/r32/ebp
7444     5d/pop-to-ebp
7445     c3/return
7446 
7447 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle statement) -> result/eax: (handle primitive)
7448     # . prologue
7449     55/push-ebp
7450     89/<- %ebp 4/r32/esp
7451     # . save registers
7452     51/push-ecx
7453     # var curr/ecx: (handle primitive) = primitives
7454     8b/-> *(ebp+8) 1/r32/ecx
7455     {
7456 $find-matching-primitive:loop:
7457       # if (curr == null) break
7458       81 7/subop/compare %ecx 0/imm32
7459       0f 84/jump-if-= break/disp32
7460 #?       (write-buffered Stderr "prim: ")
7461 #?       (write-buffered Stderr *ecx)  # Primitive-name
7462 #?       (write-buffered Stderr " => ")
7463 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
7464 #?       (write-buffered Stderr Newline)
7465 #?       (flush Stderr)
7466       # if match(curr, stmt) return curr
7467       {
7468         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
7469         3d/compare-eax-and 0/imm32/false
7470         74/jump-if-= break/disp8
7471         89/<- %eax 1/r32/ecx
7472         eb/jump $find-matching-primitive:end/disp8
7473       }
7474 $find-matching-primitive:next-primitive:
7475       # curr = curr->next
7476       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
7477       e9/jump loop/disp32
7478     }
7479     # return null
7480     b8/copy-to-eax 0/imm32
7481 $find-matching-primitive:end:
7482     # . restore registers
7483     59/pop-to-ecx
7484     # . epilogue
7485     89/<- %esp 5/r32/ebp
7486     5d/pop-to-ebp
7487     c3/return
7488 
7489 mu-stmt-matches-function?:  # stmt: (handle statement), function: (handle function) -> result/eax: boolean
7490     # . prologue
7491     55/push-ebp
7492     89/<- %ebp 4/r32/esp
7493     # . save registers
7494     51/push-ecx
7495     # return function->name == stmt->operation
7496     8b/-> *(ebp+8) 1/r32/ecx
7497     8b/-> *(ebp+0xc) 0/r32/eax
7498     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
7499 $mu-stmt-matches-function?:end:
7500     # . restore registers
7501     59/pop-to-ecx
7502     # . epilogue
7503     89/<- %esp 5/r32/ebp
7504     5d/pop-to-ebp
7505     c3/return
7506 
7507 mu-stmt-matches-primitive?:  # stmt: (handle statement), primitive: (handle primitive) -> result/eax: boolean
7508     # A mu stmt matches a primitive if the name matches, all the inout vars
7509     # match, and all the output vars match.
7510     # Vars match if types match and registers match.
7511     # In addition, a stmt output matches a primitive's output if types match
7512     # and the primitive has a wildcard register.
7513     # . prologue
7514     55/push-ebp
7515     89/<- %ebp 4/r32/esp
7516     # . save registers
7517     51/push-ecx
7518     52/push-edx
7519     53/push-ebx
7520     56/push-esi
7521     57/push-edi
7522     # ecx = stmt
7523     8b/-> *(ebp+8) 1/r32/ecx
7524     # edx = primitive
7525     8b/-> *(ebp+0xc) 2/r32/edx
7526     {
7527 $mu-stmt-matches-primitive?:check-name:
7528       # if (primitive->name != stmt->operation) return false
7529       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
7530       3d/compare-eax-and 0/imm32/false
7531       75/jump-if-!= break/disp8
7532       b8/copy-to-eax 0/imm32
7533       e9/jump $mu-stmt-matches-primitive?:end/disp32
7534     }
7535 $mu-stmt-matches-primitive?:check-inouts:
7536     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
7537     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
7538     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
7539     {
7540       # if (curr == 0 && curr2 == 0) move on to check outputs
7541       {
7542         81 7/subop/compare %esi 0/imm32
7543         75/jump-if-!= break/disp8
7544 $mu-stmt-matches-primitive?:stmt-inout-is-null:
7545         {
7546           81 7/subop/compare %edi 0/imm32
7547           75/jump-if-!= break/disp8
7548           #
7549           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
7550         }
7551         # return false
7552         b8/copy-to-eax 0/imm32/false
7553         e9/jump $mu-stmt-matches-primitive?:end/disp32
7554       }
7555       # if (curr2 == 0) return false
7556       {
7557         81 7/subop/compare %edi 0/imm32
7558         75/jump-if-!= break/disp8
7559 $mu-stmt-matches-primitive?:prim-inout-is-null:
7560         b8/copy-to-eax 0/imm32/false
7561         e9/jump $mu-stmt-matches-primitive?:end/disp32
7562       }
7563       # if (curr != curr2) return false
7564       {
7565         (operand-matches-primitive? %esi *edi)  # List-value => eax
7566         3d/compare-eax-and 0/imm32/false
7567         75/jump-if-!= break/disp8
7568         b8/copy-to-eax 0/imm32/false
7569         e9/jump $mu-stmt-matches-primitive?:end/disp32
7570       }
7571       # curr=curr->next
7572       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
7573       # curr2=curr2->next
7574       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
7575       eb/jump loop/disp8
7576     }
7577 $mu-stmt-matches-primitive?:check-outputs:
7578     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
7579     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
7580     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
7581     {
7582       # if (curr == 0) return (curr2 == 0)
7583       {
7584 $mu-stmt-matches-primitive?:check-output:
7585         81 7/subop/compare %esi 0/imm32
7586         75/jump-if-!= break/disp8
7587         {
7588           81 7/subop/compare %edi 0/imm32
7589           75/jump-if-!= break/disp8
7590           # return true
7591           b8/copy-to-eax 1/imm32
7592           e9/jump $mu-stmt-matches-primitive?:end/disp32
7593         }
7594         # return false
7595         b8/copy-to-eax 0/imm32
7596         e9/jump $mu-stmt-matches-primitive?:end/disp32
7597       }
7598       # if (curr2 == 0) return false
7599       {
7600         81 7/subop/compare %edi 0/imm32
7601         75/jump-if-!= break/disp8
7602         b8/copy-to-eax 0/imm32
7603         e9/jump $mu-stmt-matches-primitive?:end/disp32
7604       }
7605       # if (curr != curr2) return false
7606       {
7607         (operand-matches-primitive? %esi *edi)  # List-value => eax
7608         3d/compare-eax-and 0/imm32/false
7609         75/jump-if-!= break/disp8
7610         b8/copy-to-eax 0/imm32
7611         e9/jump $mu-stmt-matches-primitive?:end/disp32
7612       }
7613       # curr=curr->next
7614       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
7615       # curr2=curr2->next
7616       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
7617       eb/jump loop/disp8
7618     }
7619 $mu-stmt-matches-primitive?:return-true:
7620     b8/copy-to-eax 1/imm32
7621 $mu-stmt-matches-primitive?:end:
7622     # . restore registers
7623     5f/pop-to-edi
7624     5e/pop-to-esi
7625     5b/pop-to-ebx
7626     5a/pop-to-edx
7627     59/pop-to-ecx
7628     # . epilogue
7629     89/<- %esp 5/r32/ebp
7630     5d/pop-to-ebp
7631     c3/return
7632 
7633 operand-matches-primitive?:  # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
7634     # . prologue
7635     55/push-ebp
7636     89/<- %ebp 4/r32/esp
7637     # . save registers
7638     51/push-ecx
7639     56/push-esi
7640     57/push-edi
7641     # ecx = s
7642     8b/-> *(ebp+8) 1/r32/ecx
7643     # var var/esi : (handle var) = s->value
7644     8b/-> *ecx 6/r32/esi  # Stmt-var-value
7645     # edi = prim-var
7646     8b/-> *(ebp+0xc) 7/r32/edi
7647 $operand-matches-primitive?:check-type:
7648     # if (var->type != prim-var->type) return false
7649     (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
7650     3d/compare-eax-and 0/imm32/false
7651     b8/copy-to-eax 0/imm32/false
7652     0f 84/jump-if-= $operand-matches-primitive?:end/disp32
7653     {
7654 $operand-matches-primitive?:check-register:
7655       # if prim-var is in memory and var is in register but dereference, match
7656       {
7657         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
7658         0f 85/jump-if-!= break/disp32
7659         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7660         74/jump-if-= break/disp8
7661         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7662         74/jump-if-= break/disp8
7663         e9/jump $operand-matches-primitive?:return-true/disp32
7664       }
7665       # if prim-var is in register and var is in register but dereference, no match
7666       {
7667         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
7668         0f 84/jump-if-= break/disp32
7669         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7670         0f 84/jump-if-= break/disp32
7671         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7672         74/jump-if-= break/disp8
7673         e9/jump $operand-matches-primitive?:return-false/disp32
7674       }
7675       # return false if var->register doesn't match prim-var->register
7676       {
7677         # if register addresses are equal, it's a match
7678         8b/-> *(esi+0x10) 0/r32/eax  # Var-register
7679         39/compare *(edi+0x10) 0/r32/eax  # Var-register
7680         74/jump-if-= break/disp8
7681         # if either address is 0, return false
7682         3d/compare-eax-and 0/imm32
7683         74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
7684         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
7685         74/jump-if-=  $operand-matches-primitive?:return-false/disp8
7686         # if prim-var->register is wildcard, it's a match
7687         (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
7688         3d/compare-eax-and 0/imm32/false
7689         75/jump-if-!= break/disp8
7690         # if string contents aren't equal, return false
7691         (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
7692         3d/compare-eax-and 0/imm32/false
7693         74/jump-if-= $operand-matches-primitive?:return-false/disp8
7694       }
7695     }
7696 $operand-matches-primitive?:return-true:
7697     b8/copy-to-eax 1/imm32/true
7698     eb/jump $operand-matches-primitive?:end/disp8
7699 $operand-matches-primitive?:return-false:
7700     b8/copy-to-eax 0/imm32/false
7701 $operand-matches-primitive?:end:
7702     # . restore registers
7703     5f/pop-to-edi
7704     5e/pop-to-esi
7705     59/pop-to-ecx
7706     # . epilogue
7707     89/<- %esp 5/r32/ebp
7708     5d/pop-to-ebp
7709     c3/return
7710 
7711 subx-type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
7712     # . prologue
7713     55/push-ebp
7714     89/<- %ebp 4/r32/esp
7715     # . save registers
7716     51/push-ecx
7717     # var alit/ecx: boolean = is-literal-type?(a)
7718     (is-literal-type? *(ebp+8))  # => eax
7719     89/<- %ecx 0/r32/eax
7720     # var blit/eax: boolean = is-literal-type?(b)
7721     (is-literal-type? *(ebp+0xc))  # => eax
7722     # return alit == blit
7723     39/compare %eax 1/r32/ecx
7724     74/jump-if-= $subx-type-equal?:true/disp8
7725 $subx-type-equal?:false:
7726     b8/copy-to-eax 0/imm32/false
7727     eb/jump $subx-type-equal?:end/disp8
7728 $subx-type-equal?:true:
7729     b8/copy-to-eax 1/imm32/true
7730 $subx-type-equal?:end:
7731     # . restore registers
7732     59/pop-to-ecx
7733     # . epilogue
7734     89/<- %esp 5/r32/ebp
7735     5d/pop-to-ebp
7736     c3/return
7737 
7738 is-literal-type?:  # a: (handle tree type-id) -> result/eax: boolean
7739     # . prologue
7740     55/push-ebp
7741     89/<- %ebp 4/r32/esp
7742     #
7743     8b/-> *(ebp+8) 0/r32/eax
7744     8b/-> *eax 0/r32/eax  # Atom-value
7745     3d/compare-eax-and 0/imm32/false
7746     74/jump-if-equal $is-literal-type?:end/disp8
7747     b8/copy-to-eax 1/imm32/true
7748 $is-literal-type?:end:
7749     # . epilogue
7750     89/<- %esp 5/r32/ebp
7751     5d/pop-to-ebp
7752     c3/return
7753 
7754 test-emit-subx-statement-primitive:
7755     # Primitive operation on a variable on the stack.
7756     #   increment foo
7757     # =>
7758     #   ff 0/subop/increment *(ebp-8)
7759     #
7760     # There's a variable on the var stack as follows:
7761     #   name: 'foo'
7762     #   type: int
7763     #   stack-offset: -8
7764     #
7765     # There's a primitive with this info:
7766     #   name: 'increment'
7767     #   inouts: int/mem
7768     #   value: 'ff 0/subop/increment'
7769     #
7770     # There's nothing in functions.
7771     #
7772     # . prologue
7773     55/push-ebp
7774     89/<- %ebp 4/r32/esp
7775     # setup
7776     (clear-stream _test-output-stream)
7777     (clear-stream $_test-output-buffered-file->buffer)
7778     # var type/ecx: (handle tree type-id) = int
7779     68/push 0/imm32/right/null
7780     68/push 1/imm32/left/int
7781     89/<- %ecx 4/r32/esp
7782     # var var-foo/ecx: var
7783     68/push 0/imm32/no-register
7784     68/push -8/imm32/stack-offset
7785     68/push 1/imm32/block-depth
7786     51/push-ecx
7787     68/push "foo"/imm32
7788     89/<- %ecx 4/r32/esp
7789     # var operand/ebx: (handle stmt-var)
7790     68/push 0/imm32/is-deref:false
7791     68/push 0/imm32/next
7792     51/push-ecx/var-foo
7793     89/<- %ebx 4/r32/esp
7794     # var stmt/esi: statement
7795     68/push 0/imm32/next
7796     68/push 0/imm32/outputs
7797     53/push-ebx/operands
7798     68/push "increment"/imm32/operation
7799     68/push 1/imm32
7800     89/<- %esi 4/r32/esp
7801     # var primitives/ebx: primitive
7802     68/push 0/imm32/next
7803     68/push 0/imm32/output-is-write-only
7804     68/push 0/imm32/no-disp32
7805     68/push 0/imm32/no-imm32
7806     68/push 0/imm32/no-r32
7807     68/push 1/imm32/rm32-is-first-inout
7808     68/push "ff 0/subop/increment"/imm32/subx-name
7809     68/push 0/imm32/outputs
7810     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
7811     68/push "increment"/imm32/name
7812     89/<- %ebx 4/r32/esp
7813     # convert
7814     c7 0/subop/copy *Curr-block-depth 0/imm32
7815     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
7816     (flush _test-output-buffered-file)
7817 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
7823     # check output
7824     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive")
7825     # . epilogue
7826     89/<- %esp 5/r32/ebp
7827     5d/pop-to-ebp
7828     c3/return
7829 
7830 test-emit-subx-statement-primitive-register:
7831     # Primitive operation on a variable in a register.
7832     #   foo <- increment
7833     # =>
7834     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
7835     #
7836     # There's a variable on the var stack as follows:
7837     #   name: 'foo'
7838     #   type: int
7839     #   register: 'eax'
7840     #
7841     # There's a primitive with this info:
7842     #   name: 'increment'
7843     #   out: int/reg
7844     #   value: 'ff 0/subop/increment'
7845     #
7846     # There's nothing in functions.
7847     #
7848     # . prologue
7849     55/push-ebp
7850     89/<- %ebp 4/r32/esp
7851     # setup
7852     (clear-stream _test-output-stream)
7853     (clear-stream $_test-output-buffered-file->buffer)
7854     # var type/ecx: (handle tree type-id) = int
7855     68/push 0/imm32/right/null
7856     68/push 1/imm32/left/int
7857     89/<- %ecx 4/r32/esp
7858     # var var-foo/ecx: var in eax
7859     68/push "eax"/imm32/register
7860     68/push 0/imm32/no-stack-offset
7861     68/push 1/imm32/block-depth
7862     51/push-ecx
7863     68/push "foo"/imm32
7864     89/<- %ecx 4/r32/esp
7865     # var operand/ebx: (handle stmt-var)
7866     68/push 0/imm32/is-deref:false
7867     68/push 0/imm32/next
7868     51/push-ecx/var-foo
7869     89/<- %ebx 4/r32/esp
7870     # var stmt/esi: statement
7871     68/push 0/imm32/next
7872     53/push-ebx/outputs
7873     68/push 0/imm32/inouts
7874     68/push "increment"/imm32/operation
7875     68/push 1/imm32
7876     89/<- %esi 4/r32/esp
7877     # var formal-var/ebx: var in any register
7878     68/push Any-register/imm32
7879     68/push 0/imm32/no-stack-offset
7880     68/push 1/imm32/block-depth
7881     ff 6/subop/push *(ecx+4)  # Var-type
7882     68/push "dummy"/imm32
7883     89/<- %ebx 4/r32/esp
7884     # var operand/ebx: (handle stmt-var)
7885     68/push 0/imm32/is-deref:false
7886     68/push 0/imm32/next
7887     53/push-ebx/formal-var
7888     89/<- %ebx 4/r32/esp
7889     # var primitives/ebx: primitive
7890     68/push 0/imm32/next
7891     68/push 0/imm32/output-is-write-only
7892     68/push 0/imm32/no-disp32
7893     68/push 0/imm32/no-imm32
7894     68/push 0/imm32/no-r32
7895     68/push 3/imm32/rm32-in-first-output
7896     68/push "ff 0/subop/increment"/imm32/subx-name
7897     53/push-ebx/outputs
7898     68/push 0/imm32/inouts
7899     68/push "increment"/imm32/name
7900     89/<- %ebx 4/r32/esp
7901     # convert
7902     c7 0/subop/copy *Curr-block-depth 0/imm32
7903     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
7904     (flush _test-output-buffered-file)
7905 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
7911     # check output
7912     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register")
7913     # . epilogue
7914     89/<- %esp 5/r32/ebp
7915     5d/pop-to-ebp
7916     c3/return
7917 
7918 test-emit-subx-statement-select-primitive:
7919     # Select the right primitive between overloads.
7920     #   foo <- increment
7921     # =>
7922     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
7923     #
7924     # There's a variable on the var stack as follows:
7925     #   name: 'foo'
7926     #   type: int
7927     #   register: 'eax'
7928     #
7929     # There's two primitives, as follows:
7930     #   - name: 'increment'
7931     #     out: int/reg
7932     #     value: 'ff 0/subop/increment'
7933     #   - name: 'increment'
7934     #     inout: int/mem
7935     #     value: 'ff 0/subop/increment'
7936     #
7937     # There's nothing in functions.
7938     #
7939     # . prologue
7940     55/push-ebp
7941     89/<- %ebp 4/r32/esp
7942     # setup
7943     (clear-stream _test-output-stream)
7944     (clear-stream $_test-output-buffered-file->buffer)
7945     # var type/ecx: (handle tree type-id) = int
7946     68/push 0/imm32/right/null
7947     68/push 1/imm32/left/int
7948     89/<- %ecx 4/r32/esp
7949     # var var-foo/ecx: var in eax
7950     68/push "eax"/imm32/register
7951     68/push 0/imm32/no-stack-offset
7952     68/push 1/imm32/block-depth
7953     51/push-ecx
7954     68/push "foo"/imm32
7955     89/<- %ecx 4/r32/esp
7956     # var real-outputs/edi: (handle stmt-var)
7957     68/push 0/imm32/is-deref:false
7958     68/push 0/imm32/next
7959     51/push-ecx/var-foo
7960     89/<- %edi 4/r32/esp
7961     # var stmt/esi: statement
7962     68/push 0/imm32/next
7963     57/push-edi/outputs
7964     68/push 0/imm32/inouts
7965     68/push "increment"/imm32/operation
7966     68/push 1/imm32
7967     89/<- %esi 4/r32/esp
7968     # var formal-var/ebx: var in any register
7969     68/push Any-register/imm32
7970     68/push 0/imm32/no-stack-offset
7971     68/push 1/imm32/block-depth
7972     ff 6/subop/push *(ecx+4)  # Var-type
7973     68/push "dummy"/imm32
7974     89/<- %ebx 4/r32/esp
7975     # var formal-outputs/ebx: (handle stmt-var)
7976     68/push 0/imm32/is-deref:false
7977     68/push 0/imm32/next
7978     53/push-ebx/formal-var
7979     89/<- %ebx 4/r32/esp
7980     # var primitive1/ebx: primitive
7981     68/push 0/imm32/next
7982     68/push 0/imm32/output-is-write-only
7983     68/push 0/imm32/no-disp32
7984     68/push 0/imm32/no-imm32
7985     68/push 0/imm32/no-r32
7986     68/push 3/imm32/rm32-in-first-output
7987     68/push "ff 0/subop/increment"/imm32/subx-name
7988     53/push-ebx/outputs/formal-outputs
7989     68/push 0/imm32/inouts
7990     68/push "increment"/imm32/name
7991     89/<- %ebx 4/r32/esp
7992     # var primitives/ebx: primitive
7993     53/push-ebx/next
7994     68/push 0/imm32/output-is-write-only
7995     68/push 0/imm32/no-disp32
7996     68/push 0/imm32/no-imm32
7997     68/push 0/imm32/no-r32
7998     68/push 1/imm32/rm32-is-first-inout
7999     68/push "ff 0/subop/increment"/imm32/subx-name
8000     68/push 0/imm32/outputs
8001     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
8002     68/push "increment"/imm32/name
8003     89/<- %ebx 4/r32/esp
8004     # convert
8005     c7 0/subop/copy *Curr-block-depth 0/imm32
8006     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
8007     (flush _test-output-buffered-file)
8008 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8014     # check output
8015     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive")
8016     # . epilogue
8017     89/<- %esp 5/r32/ebp
8018     5d/pop-to-ebp
8019     c3/return
8020 
8021 test-emit-subx-statement-select-primitive-2:
8022     # Select the right primitive between overloads.
8023     #   foo <- increment
8024     # =>
8025     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
8026     #
8027     # There's a variable on the var stack as follows:
8028     #   name: 'foo'
8029     #   type: int
8030     #   register: 'eax'
8031     #
8032     # There's two primitives, as follows:
8033     #   - name: 'increment'
8034     #     out: int/reg
8035     #     value: 'ff 0/subop/increment'
8036     #   - name: 'increment'
8037     #     inout: int/mem
8038     #     value: 'ff 0/subop/increment'
8039     #
8040     # There's nothing in functions.
8041     #
8042     # . prologue
8043     55/push-ebp
8044     89/<- %ebp 4/r32/esp
8045     # setup
8046     (clear-stream _test-output-stream)
8047     (clear-stream $_test-output-buffered-file->buffer)
8048     # var type/ecx: (handle tree type-id) = int
8049     68/push 0/imm32/right/null
8050     68/push 1/imm32/left/int
8051     89/<- %ecx 4/r32/esp
8052     # var var-foo/ecx: var in eax
8053     68/push "eax"/imm32/register
8054     68/push 0/imm32/no-stack-offset
8055     68/push 1/imm32/block-depth
8056     51/push-ecx
8057     68/push "foo"/imm32
8058     89/<- %ecx 4/r32/esp
8059     # var inouts/edi: (handle stmt-var)
8060     68/push 0/imm32/is-deref:false
8061     68/push 0/imm32/next
8062     51/push-ecx/var-foo
8063     89/<- %edi 4/r32/esp
8064     # var stmt/esi: statement
8065     68/push 0/imm32/next
8066     68/push 0/imm32/outputs
8067     57/push-edi/inouts
8068     68/push "increment"/imm32/operation
8069     68/push 1/imm32
8070     89/<- %esi 4/r32/esp
8071     # var formal-var/ebx: var in any register
8072     68/push Any-register/imm32
8073     68/push 0/imm32/no-stack-offset
8074     68/push 1/imm32/block-depth
8075     ff 6/subop/push *(ecx+4)  # Var-type
8076     68/push "dummy"/imm32
8077     89/<- %ebx 4/r32/esp
8078     # var operand/ebx: (handle stmt-var)
8079     68/push 0/imm32/is-deref:false
8080     68/push 0/imm32/next
8081     53/push-ebx/formal-var
8082     89/<- %ebx 4/r32/esp
8083     # var primitive1/ebx: primitive
8084     68/push 0/imm32/next
8085     68/push 0/imm32/output-is-write-only
8086     68/push 0/imm32/no-disp32
8087     68/push 0/imm32/no-imm32
8088     68/push 0/imm32/no-r32
8089     68/push 3/imm32/rm32-in-first-output
8090     68/push "ff 0/subop/increment"/imm32/subx-name
8091     53/push-ebx/outputs/formal-outputs
8092     68/push 0/imm32/inouts
8093     68/push "increment"/imm32/name
8094     89/<- %ebx 4/r32/esp
8095     # var primitives/ebx: primitive
8096     53/push-ebx/next
8097     68/push 0/imm32/output-is-write-only
8098     68/push 0/imm32/no-disp32
8099     68/push 0/imm32/no-imm32
8100     68/push 0/imm32/no-r32
8101     68/push 1/imm32/rm32-is-first-inout
8102     68/push "ff 0/subop/increment"/imm32/subx-name
8103     68/push 0/imm32/outputs
8104     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
8105     68/push "increment"/imm32/name
8106     89/<- %ebx 4/r32/esp
8107     # convert
8108     c7 0/subop/copy *Curr-block-depth 0/imm32
8109     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
8110     (flush _test-output-buffered-file)
8111 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8117     # check output
8118     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2")
8119     # . epilogue
8120     89/<- %esp 5/r32/ebp
8121     5d/pop-to-ebp
8122     c3/return
8123 
8124 test-increment-register:
8125     # Select the right register between overloads.
8126     #   foo <- increment
8127     # =>
8128     #   50/increment-eax
8129     #
8130     # There's a variable on the var stack as follows:
8131     #   name: 'foo'
8132     #   type: int
8133     #   register: 'eax'
8134     #
8135     # Primitives are the global definitions.
8136     #
8137     # There are no functions defined.
8138     #
8139     # . prologue
8140     55/push-ebp
8141     89/<- %ebp 4/r32/esp
8142     # setup
8143     (clear-stream _test-output-stream)
8144     (clear-stream $_test-output-buffered-file->buffer)
8145     # var type/ecx: (handle tree type-id) = int
8146     68/push 0/imm32/right/null
8147     68/push 1/imm32/left/int
8148     89/<- %ecx 4/r32/esp
8149     # var var-foo/ecx: var in eax
8150     68/push "eax"/imm32/register
8151     68/push 0/imm32/no-stack-offset
8152     68/push 1/imm32/block-depth
8153     51/push-ecx
8154     68/push "foo"/imm32
8155     89/<- %ecx 4/r32/esp
8156     # var real-outputs/edi: (handle stmt-var)
8157     68/push 0/imm32/is-deref:false
8158     68/push 0/imm32/next
8159     51/push-ecx/var-foo
8160     89/<- %edi 4/r32/esp
8161     # var stmt/esi: statement
8162     68/push 0/imm32/next
8163     57/push-edi/outputs
8164     68/push 0/imm32/inouts
8165     68/push "increment"/imm32/operation
8166     68/push 1/imm32/regular-statement
8167     89/<- %esi 4/r32/esp
8168     # convert
8169     c7 0/subop/copy *Curr-block-depth 0/imm32
8170     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8171     (flush _test-output-buffered-file)
8172 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8178     # check output
8179     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
8180     # . epilogue
8181     89/<- %esp 5/r32/ebp
8182     5d/pop-to-ebp
8183     c3/return
8184 
8185 test-increment-var:
8186     # Select the right primitive between overloads.
8187     #   foo <- increment
8188     # =>
8189     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
8190     #
8191     # There's a variable on the var stack as follows:
8192     #   name: 'foo'
8193     #   type: int
8194     #   register: 'eax'
8195     #
8196     # Primitives are the global definitions.
8197     #
8198     # There are no functions defined.
8199     #
8200     # . prologue
8201     55/push-ebp
8202     89/<- %ebp 4/r32/esp
8203     # setup
8204     (clear-stream _test-output-stream)
8205     (clear-stream $_test-output-buffered-file->buffer)
8206     # var type/ecx: (handle tree type-id) = int
8207     68/push 0/imm32/right/null
8208     68/push 1/imm32/left/int
8209     89/<- %ecx 4/r32/esp
8210     # var var-foo/ecx: var in eax
8211     68/push "eax"/imm32/register
8212     68/push 0/imm32/no-stack-offset
8213     68/push 1/imm32/block-depth
8214     51/push-ecx
8215     68/push "foo"/imm32
8216     89/<- %ecx 4/r32/esp
8217     # var inouts/edi: (handle stmt-var)
8218     68/push 0/imm32/is-deref:false
8219     68/push 0/imm32/next
8220     51/push-ecx/var-foo
8221     89/<- %edi 4/r32/esp
8222     # var stmt/esi: statement
8223     68/push 0/imm32/next
8224     57/push-edi/outputs
8225     68/push 0/imm32/inouts
8226     68/push "increment"/imm32/operation
8227     68/push 1/imm32
8228     89/<- %esi 4/r32/esp
8229     # convert
8230     c7 0/subop/copy *Curr-block-depth 0/imm32
8231     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8232     (flush _test-output-buffered-file)
8233 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8239     # check output
8240     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
8241     # . epilogue
8242     89/<- %esp 5/r32/ebp
8243     5d/pop-to-ebp
8244     c3/return
8245 
8246 test-add-reg-to-reg:
8247     #   var1/reg <- add var2/reg
8248     # =>
8249     #   01/add %var1 var2
8250     #
8251     # . prologue
8252     55/push-ebp
8253     89/<- %ebp 4/r32/esp
8254     # setup
8255     (clear-stream _test-output-stream)
8256     (clear-stream $_test-output-buffered-file->buffer)
8257     # var type/ecx: (handle tree type-id) = int
8258     68/push 0/imm32/right/null
8259     68/push 1/imm32/left/int
8260     89/<- %ecx 4/r32/esp
8261     # var var-var1/ecx: var in eax
8262     68/push "eax"/imm32/register
8263     68/push 0/imm32/no-stack-offset
8264     68/push 1/imm32/block-depth
8265     51/push-ecx
8266     68/push "var1"/imm32
8267     89/<- %ecx 4/r32/esp
8268     # var var-var2/edx: var in ecx
8269     68/push "ecx"/imm32/register
8270     68/push 0/imm32/no-stack-offset
8271     68/push 1/imm32/block-depth
8272     ff 6/subop/push *(ecx+4)  # Var-type
8273     68/push "var2"/imm32
8274     89/<- %edx 4/r32/esp
8275     # var inouts/esi: (handle stmt-var) = [var2]
8276     68/push 0/imm32/is-deref:false
8277     68/push 0/imm32/next
8278     52/push-edx/var-var2
8279     89/<- %esi 4/r32/esp
8280     # var outputs/edi: (handle stmt-var) = [var1, var2]
8281     68/push 0/imm32/is-deref:false
8282     68/push 0/imm32/next
8283     51/push-ecx/var-var1
8284     89/<- %edi 4/r32/esp
8285     # var stmt/esi: statement
8286     68/push 0/imm32/next
8287     57/push-edi/outputs
8288     56/push-esi/inouts
8289     68/push "add"/imm32/operation
8290     68/push 1/imm32
8291     89/<- %esi 4/r32/esp
8292     # convert
8293     c7 0/subop/copy *Curr-block-depth 0/imm32
8294     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8295     (flush _test-output-buffered-file)
8296 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8302     # check output
8303     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
8304     # . epilogue
8305     89/<- %esp 5/r32/ebp
8306     5d/pop-to-ebp
8307     c3/return
8308 
8309 test-add-reg-to-mem:
8310     #   add-to var1 var2/reg
8311     # =>
8312     #   01/add *(ebp+__) var2
8313     #
8314     # . prologue
8315     55/push-ebp
8316     89/<- %ebp 4/r32/esp
8317     # setup
8318     (clear-stream _test-output-stream)
8319     (clear-stream $_test-output-buffered-file->buffer)
8320     # var type/ecx: (handle tree type-id) = int
8321     68/push 0/imm32/right/null
8322     68/push 1/imm32/left/int
8323     89/<- %ecx 4/r32/esp
8324     # var var-var1/ecx: var
8325     68/push 0/imm32/no-register
8326     68/push 8/imm32/stack-offset
8327     68/push 1/imm32/block-depth
8328     51/push-ecx
8329     68/push "var1"/imm32
8330     89/<- %ecx 4/r32/esp
8331     # var var-var2/edx: var in ecx
8332     68/push "ecx"/imm32/register
8333     68/push 0/imm32/no-stack-offset
8334     68/push 1/imm32/block-depth
8335     ff 6/subop/push *(ecx+4)  # Var-type
8336     68/push "var2"/imm32
8337     89/<- %edx 4/r32/esp
8338     # var inouts/esi: (handle stmt-var) = [var2]
8339     68/push 0/imm32/is-deref:false
8340     68/push 0/imm32/next
8341     52/push-edx/var-var2
8342     89/<- %esi 4/r32/esp
8343     # var inouts = (handle stmt-var) = [var1, var2]
8344     56/push-esi/next
8345     51/push-ecx/var-var1
8346     89/<- %esi 4/r32/esp
8347     # var stmt/esi: statement
8348     68/push 0/imm32/next
8349     68/push 0/imm32/outputs
8350     56/push-esi/inouts
8351     68/push "add-to"/imm32/operation
8352     68/push 1/imm32
8353     89/<- %esi 4/r32/esp
8354     # convert
8355     c7 0/subop/copy *Curr-block-depth 0/imm32
8356     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8357     (flush _test-output-buffered-file)
8358 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8364     # check output
8365     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
8366     # . epilogue
8367     89/<- %esp 5/r32/ebp
8368     5d/pop-to-ebp
8369     c3/return
8370 
8371 test-add-mem-to-reg:
8372     #   var1/reg <- add var2
8373     # =>
8374     #   03/add *(ebp+__) var1
8375     #
8376     # . prologue
8377     55/push-ebp
8378     89/<- %ebp 4/r32/esp
8379     # setup
8380     (clear-stream _test-output-stream)
8381     (clear-stream $_test-output-buffered-file->buffer)
8382     # var type/ecx: (handle tree type-id) = int
8383     68/push 0/imm32/right/null
8384     68/push 1/imm32/left/int
8385     89/<- %ecx 4/r32/esp
8386     # var var-var1/ecx: var in eax
8387     68/push "eax"/imm32/register
8388     68/push 0/imm32/no-stack-offset
8389     68/push 1/imm32/block-depth
8390     51/push-ecx
8391     68/push "var1"/imm32
8392     89/<- %ecx 4/r32/esp
8393     # var var-var2/edx: var
8394     68/push 0/imm32/no-register
8395     68/push 8/imm32/stack-offset
8396     68/push 1/imm32/block-depth
8397     ff 6/subop/push *(ecx+4)  # Var-type
8398     68/push "var2"/imm32
8399     89/<- %edx 4/r32/esp
8400     # var inouts/esi: (handle stmt-var) = [var2]
8401     68/push 0/imm32/is-deref:false
8402     68/push 0/imm32/next
8403     52/push-edx/var-var2
8404     89/<- %esi 4/r32/esp
8405     # var outputs/edi = (handle stmt-var) = [var1]
8406     68/push 0/imm32/is-deref:false
8407     68/push 0/imm32/next
8408     51/push-ecx/var-var1
8409     89/<- %edi 4/r32/esp
8410     # var stmt/esi: statement
8411     68/push 0/imm32/next
8412     57/push-edi/outputs
8413     56/push-esi/inouts
8414     68/push "add"/imm32/operation
8415     68/push 1/imm32
8416     89/<- %esi 4/r32/esp
8417     # convert
8418     c7 0/subop/copy *Curr-block-depth 0/imm32
8419     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8420     (flush _test-output-buffered-file)
8421 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8427     # check output
8428     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
8429     # . epilogue
8430     89/<- %esp 5/r32/ebp
8431     5d/pop-to-ebp
8432     c3/return
8433 
8434 test-add-literal-to-eax:
8435     #   var1/eax <- add 0x34
8436     # =>
8437     #   05/add-to-eax 0x34/imm32
8438     #
8439     # . prologue
8440     55/push-ebp
8441     89/<- %ebp 4/r32/esp
8442     # setup
8443     (clear-stream _test-output-stream)
8444     (clear-stream $_test-output-buffered-file->buffer)
8445     # var type/ecx: (handle tree type-id) = int
8446     68/push 0/imm32/right/null
8447     68/push 1/imm32/left/int
8448     89/<- %ecx 4/r32/esp
8449     # var var-var1/ecx: var in eax
8450     68/push "eax"/imm32/register
8451     68/push 0/imm32/no-stack-offset
8452     68/push 1/imm32/block-depth
8453     51/push-ecx
8454     68/push "var1"/imm32
8455     89/<- %ecx 4/r32/esp
8456     # var type/edx: (handle tree type-id) = literal
8457     68/push 0/imm32/right/null
8458     68/push 0/imm32/left/literal
8459     89/<- %edx 4/r32/esp
8460     # var var-var2/edx: var literal
8461     68/push 0/imm32/no-register
8462     68/push 0/imm32/no-stack-offset
8463     68/push 1/imm32/block-depth
8464     52/push-edx
8465     68/push "0x34"/imm32
8466     89/<- %edx 4/r32/esp
8467     # var inouts/esi: (handle stmt-var) = [var2]
8468     68/push 0/imm32/is-deref:false
8469     68/push 0/imm32/next
8470     52/push-edx/var-var2
8471     89/<- %esi 4/r32/esp
8472     # var outputs/edi: (handle stmt-var) = [var1]
8473     68/push 0/imm32/is-deref:false
8474     68/push 0/imm32/next
8475     51/push-ecx/var-var1
8476     89/<- %edi 4/r32/esp
8477     # var stmt/esi: statement
8478     68/push 0/imm32/next
8479     57/push-edi/outputs
8480     56/push-esi/inouts
8481     68/push "add"/imm32/operation
8482     68/push 1/imm32
8483     89/<- %esi 4/r32/esp
8484     # convert
8485     c7 0/subop/copy *Curr-block-depth 0/imm32
8486     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8487     (flush _test-output-buffered-file)
8488 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8494     # check output
8495     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
8496     # . epilogue
8497     89/<- %esp 5/r32/ebp
8498     5d/pop-to-ebp
8499     c3/return
8500 
8501 test-add-literal-to-reg:
8502     #   var1/ecx <- add 0x34
8503     # =>
8504     #   81 0/subop/add %ecx 0x34/imm32
8505     #
8506     # . prologue
8507     55/push-ebp
8508     89/<- %ebp 4/r32/esp
8509     # setup
8510     (clear-stream _test-output-stream)
8511     (clear-stream $_test-output-buffered-file->buffer)
8512     # var type/ecx: (handle tree type-id) = int
8513     68/push 0/imm32/right/null
8514     68/push 1/imm32/left/int
8515     89/<- %ecx 4/r32/esp
8516     # var var-var1/ecx: var in ecx
8517     68/push "ecx"/imm32/register
8518     68/push 0/imm32/no-stack-offset
8519     68/push 1/imm32/block-depth
8520     51/push-ecx
8521     68/push "var1"/imm32
8522     89/<- %ecx 4/r32/esp
8523     # var type/edx: (handle tree type-id) = literal
8524     68/push 0/imm32/right/null
8525     68/push 0/imm32/left/literal
8526     89/<- %edx 4/r32/esp
8527     # var var-var2/edx: var literal
8528     68/push 0/imm32/no-register
8529     68/push 0/imm32/no-stack-offset
8530     68/push 1/imm32/block-depth
8531     52/push-edx
8532     68/push "0x34"/imm32
8533     89/<- %edx 4/r32/esp
8534     # var inouts/esi: (handle stmt-var) = [var2]
8535     68/push 0/imm32/is-deref:false
8536     68/push 0/imm32/next
8537     52/push-edx/var-var2
8538     89/<- %esi 4/r32/esp
8539     # var outputs/edi: (handle stmt-var) = [var1]
8540     68/push 0/imm32/is-deref:false
8541     68/push 0/imm32/next
8542     51/push-ecx/var-var1
8543     89/<- %edi 4/r32/esp
8544     # var stmt/esi: statement
8545     68/push 0/imm32/next
8546     57/push-edi/outputs
8547     56/push-esi/inouts
8548     68/push "add"/imm32/operation
8549     68/push 1/imm32
8550     89/<- %esi 4/r32/esp
8551     # convert
8552     c7 0/subop/copy *Curr-block-depth 0/imm32
8553     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8554     (flush _test-output-buffered-file)
8555 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8561     # check output
8562     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
8563     # . epilogue
8564     89/<- %esp 5/r32/ebp
8565     5d/pop-to-ebp
8566     c3/return
8567 
8568 test-add-literal-to-mem:
8569     #   add-to var1, 0x34
8570     # =>
8571     #   81 0/subop/add %eax 0x34/imm32
8572     #
8573     # . prologue
8574     55/push-ebp
8575     89/<- %ebp 4/r32/esp
8576     # setup
8577     (clear-stream _test-output-stream)
8578     (clear-stream $_test-output-buffered-file->buffer)
8579     # var type/ecx: (handle tree type-id) = int
8580     68/push 0/imm32/right/null
8581     68/push 1/imm32/left/int
8582     89/<- %ecx 4/r32/esp
8583     # var var-var1/ecx: var
8584     68/push 0/imm32/no-register
8585     68/push 8/imm32/stack-offset
8586     68/push 1/imm32/block-depth
8587     51/push-ecx
8588     68/push "var1"/imm32
8589     89/<- %ecx 4/r32/esp
8590     # var type/edx: (handle tree type-id) = literal
8591     68/push 0/imm32/right/null
8592     68/push 0/imm32/left/literal
8593     89/<- %edx 4/r32/esp
8594     # var var-var2/edx: var literal
8595     68/push 0/imm32/no-register
8596     68/push 0/imm32/no-stack-offset
8597     68/push 1/imm32/block-depth
8598     52/push-edx
8599     68/push "0x34"/imm32
8600     89/<- %edx 4/r32/esp
8601     # var inouts/esi: (handle stmt-var) = [var2]
8602     68/push 0/imm32/is-deref:false
8603     68/push 0/imm32/next
8604     52/push-edx/var-var2
8605     89/<- %esi 4/r32/esp
8606     # var inouts = (handle stmt-var) = [var1, var2]
8607     68/push 0/imm32/is-deref:false
8608     56/push-esi/next
8609     51/push-ecx/var-var1
8610     89/<- %esi 4/r32/esp
8611     # var stmt/esi: statement
8612     68/push 0/imm32/next
8613     68/push 0/imm32/outputs
8614     56/push-esi/inouts
8615     68/push "add-to"/imm32/operation
8616     68/push 1/imm32
8617     89/<- %esi 4/r32/esp
8618     # convert
8619     c7 0/subop/copy *Curr-block-depth 0/imm32
8620     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8621     (flush _test-output-buffered-file)
8622 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8628     # check output
8629     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
8630     # . epilogue
8631     89/<- %esp 5/r32/ebp
8632     5d/pop-to-ebp
8633     c3/return
8634 
8635 test-compare-mem-with-reg:
8636     #   compare var1, var2/eax
8637     # =>
8638     #   39/compare *(ebp+___) 0/r32/eax
8639     #
8640     # . prologue
8641     55/push-ebp
8642     89/<- %ebp 4/r32/esp
8643     # setup
8644     (clear-stream _test-output-stream)
8645     (clear-stream $_test-output-buffered-file->buffer)
8646     # var type/ecx: (handle tree type-id) = int
8647     68/push 0/imm32/right/null
8648     68/push 1/imm32/left/int
8649     89/<- %ecx 4/r32/esp
8650     # var var-var2/ecx: var in eax
8651     68/push "eax"/imm32/register
8652     68/push 0/imm32/no-stack-offset
8653     68/push 1/imm32/block-depth
8654     51/push-ecx
8655     68/push "var2"/imm32
8656     89/<- %ecx 4/r32/esp
8657     # var var-var1/edx: var
8658     68/push 0/imm32/no-register
8659     68/push 8/imm32/stack-offset
8660     68/push 1/imm32/block-depth
8661     ff 6/subop/push *(ecx+4)  # Var-type
8662     68/push "var1"/imm32
8663     89/<- %edx 4/r32/esp
8664     # var inouts/esi: (handle stmt-var) = [var2]
8665     68/push 0/imm32/is-deref:false
8666     68/push 0/imm32/next
8667     51/push-ecx/var-var2
8668     89/<- %esi 4/r32/esp
8669     # inouts = [var1, var2]
8670     68/push 0/imm32/is-deref:false
8671     56/push-esi
8672     52/push-edx/var-var1
8673     89/<- %esi 4/r32/esp
8674     # var stmt/esi: statement
8675     68/push 0/imm32/next
8676     68/push 0/imm32/outputs
8677     56/push-esi/inouts
8678     68/push "compare"/imm32/operation
8679     68/push 1/imm32
8680     89/<- %esi 4/r32/esp
8681     # convert
8682     c7 0/subop/copy *Curr-block-depth 0/imm32
8683     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8684     (flush _test-output-buffered-file)
8685 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8691     # check output
8692     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
8693     # . epilogue
8694     89/<- %esp 5/r32/ebp
8695     5d/pop-to-ebp
8696     c3/return
8697 
8698 test-compare-reg-with-mem:
8699     #   compare var1/eax, var2
8700     # =>
8701     #   3b/compare *(ebp+___) 0/r32/eax
8702     #
8703     # . prologue
8704     55/push-ebp
8705     89/<- %ebp 4/r32/esp
8706     # setup
8707     (clear-stream _test-output-stream)
8708     (clear-stream $_test-output-buffered-file->buffer)
8709     # var type/ecx: (handle tree type-id) = int
8710     68/push 0/imm32/right/null
8711     68/push 1/imm32/left/int
8712     89/<- %ecx 4/r32/esp
8713     # var var-var1/ecx: var in eax
8714     68/push "eax"/imm32/register
8715     68/push 0/imm32/no-stack-offset
8716     68/push 1/imm32/block-depth
8717     51/push-ecx
8718     68/push "var1"/imm32
8719     89/<- %ecx 4/r32/esp
8720     # var var-var2/edx: var
8721     68/push 0/imm32/no-register
8722     68/push 8/imm32/stack-offset
8723     68/push 1/imm32/block-depth
8724     ff 6/subop/push *(ecx+4)  # Var-type
8725     68/push "var2"/imm32
8726     89/<- %edx 4/r32/esp
8727     # var inouts/esi: (handle stmt-var) = [var2]
8728     68/push 0/imm32/is-deref:false
8729     68/push 0/imm32/next
8730     52/push-edx/var-var2
8731     89/<- %esi 4/r32/esp
8732     # inouts = [var1, var2]
8733     68/push 0/imm32/is-deref:false
8734     56/push-esi
8735     51/push-ecx/var-var1
8736     89/<- %esi 4/r32/esp
8737     # var stmt/esi: statement
8738     68/push 0/imm32/next
8739     68/push 0/imm32/outputs
8740     56/push-esi/inouts
8741     68/push "compare"/imm32/operation
8742     68/push 1/imm32
8743     89/<- %esi 4/r32/esp
8744     # convert
8745     c7 0/subop/copy *Curr-block-depth 0/imm32
8746     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8747     (flush _test-output-buffered-file)
8748 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8754     # check output
8755     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
8756     # . epilogue
8757     89/<- %esp 5/r32/ebp
8758     5d/pop-to-ebp
8759     c3/return
8760 
8761 test-compare-mem-with-literal:
8762     #   compare var1, 0x34
8763     # =>
8764     #   81 7/subop/compare *(ebp+___) 0x34/imm32
8765     #
8766     # . prologue
8767     55/push-ebp
8768     89/<- %ebp 4/r32/esp
8769     # setup
8770     (clear-stream _test-output-stream)
8771     (clear-stream $_test-output-buffered-file->buffer)
8772     # var type/ecx: (handle tree type-id) = int
8773     68/push 0/imm32/right/null
8774     68/push 1/imm32/left/int
8775     89/<- %ecx 4/r32/esp
8776     # var var-var1/ecx: var
8777     68/push 0/imm32/no-register
8778     68/push 8/imm32/stack-offset
8779     68/push 1/imm32/block-depth
8780     51/push-ecx
8781     68/push "var1"/imm32
8782     89/<- %ecx 4/r32/esp
8783     # var type/edx: (handle tree type-id) = literal
8784     68/push 0/imm32/right/null
8785     68/push 0/imm32/left/literal
8786     89/<- %edx 4/r32/esp
8787     # var var-var2/edx: var literal
8788     68/push 0/imm32/no-register
8789     68/push 0/imm32/no-stack-offset
8790     68/push 1/imm32/block-depth
8791     52/push-edx
8792     68/push "0x34"/imm32
8793     89/<- %edx 4/r32/esp
8794     # var inouts/esi: (handle stmt-var) = [var2]
8795     68/push 0/imm32/is-deref:false
8796     68/push 0/imm32/next
8797     52/push-edx/var-var2
8798     89/<- %esi 4/r32/esp
8799     # inouts = [var1, var2]
8800     68/push 0/imm32/is-deref:false
8801     56/push-esi/next
8802     51/push-ecx/var-var1
8803     89/<- %esi 4/r32/esp
8804     # var stmt/esi: statement
8805     68/push 0/imm32/next
8806     68/push 0/imm32/outputs
8807     56/push-esi/inouts
8808     68/push "compare"/imm32/operation
8809     68/push 1/imm32
8810     89/<- %esi 4/r32/esp
8811     # convert
8812     c7 0/subop/copy *Curr-block-depth 0/imm32
8813     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8814     (flush _test-output-buffered-file)
8815 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8821     # check output
8822     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
8823     # . epilogue
8824     89/<- %esp 5/r32/ebp
8825     5d/pop-to-ebp
8826     c3/return
8827 
8828 test-compare-eax-with-literal:
8829     #   compare var1/eax 0x34
8830     # =>
8831     #   3d/compare-eax-with 0x34/imm32
8832     #
8833     # . prologue
8834     55/push-ebp
8835     89/<- %ebp 4/r32/esp
8836     # setup
8837     (clear-stream _test-output-stream)
8838     (clear-stream $_test-output-buffered-file->buffer)
8839     # var type/ecx: (handle tree type-id) = int
8840     68/push 0/imm32/right/null
8841     68/push 1/imm32/left/int
8842     89/<- %ecx 4/r32/esp
8843     # var var-var1/ecx: var in eax
8844     68/push "eax"/imm32/register
8845     68/push 0/imm32/no-stack-offset
8846     68/push 1/imm32/block-depth
8847     51/push-ecx
8848     68/push "var1"/imm32
8849     89/<- %ecx 4/r32/esp
8850     # var type/edx: (handle tree type-id) = literal
8851     68/push 0/imm32/right/null
8852     68/push 0/imm32/left/literal
8853     89/<- %edx 4/r32/esp
8854     # var var-var2/edx: var literal
8855     68/push 0/imm32/no-register
8856     68/push 0/imm32/no-stack-offset
8857     68/push 1/imm32/block-depth
8858     52/push-edx
8859     68/push "0x34"/imm32
8860     89/<- %edx 4/r32/esp
8861     # var inouts/esi: (handle stmt-var) = [var2]
8862     68/push 0/imm32/is-deref:false
8863     68/push 0/imm32/next
8864     52/push-edx/var-var2
8865     89/<- %esi 4/r32/esp
8866     # inouts = [var1, var2]
8867     68/push 0/imm32/is-deref:false
8868     56/push-esi/next
8869     51/push-ecx/var-var1
8870     89/<- %esi 4/r32/esp
8871     # var stmt/esi: statement
8872     68/push 0/imm32/next
8873     68/push 0/imm32/outputs
8874     56/push-esi/inouts
8875     68/push "compare"/imm32/operation
8876     68/push 1/imm32/regular-stmt
8877     89/<- %esi 4/r32/esp
8878     # convert
8879     c7 0/subop/copy *Curr-block-depth 0/imm32
8880     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8881     (flush _test-output-buffered-file)
8882 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8888     # check output
8889     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
8890     # . epilogue
8891     89/<- %esp 5/r32/ebp
8892     5d/pop-to-ebp
8893     c3/return
8894 
8895 test-compare-reg-with-literal:
8896     #   compare var1/ecx 0x34
8897     # =>
8898     #   81 7/subop/compare %ecx 0x34/imm32
8899     #
8900     # . prologue
8901     55/push-ebp
8902     89/<- %ebp 4/r32/esp
8903     # setup
8904     (clear-stream _test-output-stream)
8905     (clear-stream $_test-output-buffered-file->buffer)
8906     # var type/ecx: (handle tree type-id) = int
8907     68/push 0/imm32/right/null
8908     68/push 1/imm32/left/int
8909     89/<- %ecx 4/r32/esp
8910     # var var-var1/ecx: var in ecx
8911     68/push "ecx"/imm32/register
8912     68/push 0/imm32/no-stack-offset
8913     68/push 1/imm32/block-depth
8914     51/push-ecx
8915     68/push "var1"/imm32
8916     89/<- %ecx 4/r32/esp
8917     # var type/edx: (handle tree type-id) = literal
8918     68/push 0/imm32/right/null
8919     68/push 0/imm32/left/literal
8920     89/<- %edx 4/r32/esp
8921     # var var-var2/edx: var literal
8922     68/push 0/imm32/no-register
8923     68/push 0/imm32/no-stack-offset
8924     68/push 1/imm32/block-depth
8925     52/push-edx
8926     68/push "0x34"/imm32
8927     89/<- %edx 4/r32/esp
8928     # var inouts/esi: (handle stmt-var) = [var2]
8929     68/push 0/imm32/is-deref:false
8930     68/push 0/imm32/next
8931     52/push-edx/var-var2
8932     89/<- %esi 4/r32/esp
8933     # inouts = [var1, var2]
8934     68/push 0/imm32/is-deref:false
8935     56/push-esi/next
8936     51/push-ecx/var-var1
8937     89/<- %esi 4/r32/esp
8938     # var stmt/esi: statement
8939     68/push 0/imm32/next
8940     68/push 0/imm32/outputs
8941     56/push-esi/inouts
8942     68/push "compare"/imm32/operation
8943     68/push 1/imm32/regular-stmt
8944     89/<- %esi 4/r32/esp
8945     # convert
8946     c7 0/subop/copy *Curr-block-depth 0/imm32
8947     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8948     (flush _test-output-buffered-file)
8949 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8955     # check output
8956     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
8957     # . epilogue
8958     89/<- %esp 5/r32/ebp
8959     5d/pop-to-ebp
8960     c3/return
8961 
8962 test-emit-subx-statement-function-call:
8963     # Call a function on a variable on the stack.
8964     #   f foo
8965     # =>
8966     #   (f2 *(ebp-8))
8967     # (Changing the function name supports overloading in general, but here it
8968     # just serves to help disambiguate things.)
8969     #
8970     # There's a variable on the var stack as follows:
8971     #   name: 'foo'
8972     #   type: int
8973     #   stack-offset: -8
8974     #
8975     # There's nothing in primitives.
8976     #
8977     # There's a function with this info:
8978     #   name: 'f'
8979     #   inout: int/mem
8980     #   value: 'f2'
8981     #
8982     # . prologue
8983     55/push-ebp
8984     89/<- %ebp 4/r32/esp
8985     # setup
8986     (clear-stream _test-output-stream)
8987     (clear-stream $_test-output-buffered-file->buffer)
8988     # var type/ecx: (handle tree type-id) = int
8989     68/push 0/imm32/right/null
8990     68/push 1/imm32/left/int
8991     89/<- %ecx 4/r32/esp
8992     # var var-foo/ecx: var
8993     68/push 0/imm32/no-register
8994     68/push -8/imm32/stack-offset
8995     68/push 0/imm32/block-depth
8996     51/push-ecx
8997     68/push "foo"/imm32
8998     89/<- %ecx 4/r32/esp
8999     # var inouts/esi: (handle stmt-var)
9000     68/push 0/imm32/is-deref:false
9001     68/push 0/imm32/next
9002     51/push-ecx/var-foo
9003     89/<- %esi 4/r32/esp
9004     # var stmt/esi: statement
9005     68/push 0/imm32/next
9006     68/push 0/imm32/outputs
9007     56/push-esi/inouts
9008     68/push "f"/imm32/operation
9009     68/push 1/imm32
9010     89/<- %esi 4/r32/esp
9011     # var functions/ebx: function
9012     68/push 0/imm32/next
9013     68/push 0/imm32/body
9014     68/push 0/imm32/outputs
9015     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
9016     68/push "f2"/imm32/subx-name
9017     68/push "f"/imm32/name
9018     89/<- %ebx 4/r32/esp
9019     # convert
9020     c7 0/subop/copy *Curr-block-depth 0/imm32
9021     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
9022     (flush _test-output-buffered-file)
9023 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9029     # check output
9030     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call")
9031     # . epilogue
9032     89/<- %esp 5/r32/ebp
9033     5d/pop-to-ebp
9034     c3/return
9035 
9036 test-emit-subx-statement-function-call-with-literal-arg:
9037     # Call a function on a literal.
9038     #   f 34
9039     # =>
9040     #   (f2 34)
9041     #
9042     # . prologue
9043     55/push-ebp
9044     89/<- %ebp 4/r32/esp
9045     # setup
9046     (clear-stream _test-output-stream)
9047     (clear-stream $_test-output-buffered-file->buffer)
9048     # var type/ecx: (handle tree type-id) = literal
9049     68/push 0/imm32/right/null
9050     68/push 0/imm32/left/literal
9051     89/<- %ecx 4/r32/esp
9052     # var var-foo/ecx: var literal
9053     68/push 0/imm32/no-register
9054     68/push 0/imm32/no-stack-offset
9055     68/push 0/imm32/block-depth
9056     51/push-ecx
9057     68/push "34"/imm32
9058     89/<- %ecx 4/r32/esp
9059     # var inouts/esi: (handle stmt-var)
9060     68/push 0/imm32/is-deref:false
9061     68/push 0/imm32/next
9062     51/push-ecx/var-foo
9063     89/<- %esi 4/r32/esp
9064     # var stmt/esi: statement
9065     68/push 0/imm32/next
9066     68/push 0/imm32/outputs
9067     56/push-esi/inouts
9068     68/push "f"/imm32/operation
9069     68/push 1/imm32
9070     89/<- %esi 4/r32/esp
9071     # var functions/ebx: function
9072     68/push 0/imm32/next
9073     68/push 0/imm32/body
9074     68/push 0/imm32/outputs
9075     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
9076     68/push "f2"/imm32/subx-name
9077     68/push "f"/imm32/name
9078     89/<- %ebx 4/r32/esp
9079     # convert
9080     c7 0/subop/copy *Curr-block-depth 0/imm32
9081     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
9082     (flush _test-output-buffered-file)
9083 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9089     # check output
9090     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg")
9091     # . epilogue
9092     89/<- %esp 5/r32/ebp
9093     5d/pop-to-ebp
9094     c3/return
9095 
9096 emit-indent:  # out: (addr buffered-file), n: int
9097     # . prologue
9098     55/push-ebp
9099     89/<- %ebp 4/r32/esp
9100     # . save registers
9101     50/push-eax
9102     # var i/eax: int = n
9103     8b/-> *(ebp+0xc) 0/r32/eax
9104     {
9105       # if (i <= 0) break
9106       3d/compare-eax-with 0/imm32
9107       7e/jump-if-<= break/disp8
9108       (write-buffered *(ebp+8) "  ")
9109       48/decrement-eax
9110       eb/jump loop/disp8
9111     }
9112 $emit-indent:end:
9113     # . restore registers
9114     58/pop-to-eax
9115     # . epilogue
9116     89/<- %esp 5/r32/ebp
9117     5d/pop-to-ebp
9118     c3/return
9119 
9120 emit-subx-prologue:  # out: (addr buffered-file)
9121     # . prologue
9122     55/push-ebp
9123     89/<- %ebp 4/r32/esp
9124     #
9125     (write-buffered *(ebp+8) "  # . prologue\n")
9126     (write-buffered *(ebp+8) "  55/push-ebp\n")
9127     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
9128 $emit-subx-prologue:end:
9129     # . epilogue
9130     89/<- %esp 5/r32/ebp
9131     5d/pop-to-ebp
9132     c3/return
9133 
9134 emit-subx-epilogue:  # out: (addr buffered-file)
9135     # . prologue
9136     55/push-ebp
9137     89/<- %ebp 4/r32/esp
9138     #
9139     (write-buffered *(ebp+8) "  # . epilogue\n")
9140     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
9141     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
9142     (write-buffered *(ebp+8) "  c3/return\n")
9143 $emit-subx-epilogue:end:
9144     # . epilogue
9145     89/<- %esp 5/r32/ebp
9146     5d/pop-to-ebp
9147     c3/return