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 # Variables in registers need not have a name; in that case you refer to them
  36 # directly by the register name.
  37 #
  38 # Function inputs are always passed in memory (on the stack), while outputs
  39 # are always returned in registers.
  40 #
  41 # Blocks mostly consist of statements.
  42 #
  43 # Statements mostly consist of a name, optional inputs and optional outputs.
  44 #
  45 # Statement inputs are variables or literals. Variables need to specify type
  46 # (and storage) the first time they're mentioned but not later.
  47 #
  48 # Statement outputs, like function outputs, must be variables in registers.
  49 #
  50 # Statement names must be either primitives or user-defined functions.
  51 #
  52 # Primitives can write to any register.
  53 # User-defined functions only write to hard-coded registers. Outputs of each
  54 # call must have the same registers as in the function definition.
  55 #
  56 # There are some other statement types:
  57 #   - blocks. Multiple statements surrounded by '{...}' and optionally
  58 #     prefixed with a label name and ':'
  59 #       - {
  60 #           ...
  61 #         }
  62 #       - foo: {
  63 #           ...
  64 #         }
  65 #
  66 #   - variable definitions on the stack. E.g.:
  67 #       - var foo: int
  68 #       - var bar: (array int 3)
  69 #     There's no initializer; variables are automatically initialized.
  70 #     The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
  71 #
  72 #   - variables definitions in a register. E.g.:
  73 #       - var foo/eax: int <- add bar 1
  74 #     The initializer is mandatory and must be a valid instruction that writes
  75 #     a single output to the right register. In practice registers will
  76 #     usually be either initialized by primitives or copied from eax.
  77 #       - var eax: int <- foo bar quux
  78 #         var floo/ecx: int <- copy eax
  79 #
  80 # Still todo:
  81 #   global variables
  82 #   heap allocations (planned name: 'handle')
  83 #   user-defined types: 'type' for structs, 'choice' for unions
  84 #   short-lived 'address' type for efficiently writing inside nested structs
  85 #
  86 # We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle'
  87 # and 'address' in comments. Their definitions are in layer 50, but really you
  88 # can ignore the distinctions on a first reading of this program.
  89 #
  90 # Formal types:
  91 #   A program is a linked list of functions
  92 #   A function contains:
  93 #     name: (handle array byte)
  94 #     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
  95 #       data: (handle var)
  96 #       next: (handle list)
  97 #     outputs: linked list of vars
  98 #       data: (handle var)
  99 #       next: (handle list)
 100 #     body: (handle block)
 101 #   A var-type contains:
 102 #     name: (handle array byte)
 103 #     type: (handle tree type-id)
 104 #
 105 #   A statement can be:
 106 #     tag 0: a block
 107 #     tag 1: a simple statement (stmt1)
 108 #     tag 2: a variable defined on the stack
 109 #     tag 3: a variable defined in a register
 110 #
 111 #   A block contains:
 112 #     tag: 0
 113 #     statements: (handle list statement)
 114 #     name: (handle array byte) -- starting with '$'
 115 #
 116 #   A regular statement contains:
 117 #     tag: 1
 118 #     operation: (handle array byte)
 119 #     inouts: (handle list operand)
 120 #     outputs: (handle list var)
 121 #
 122 #   A variable defined on the stack contains:
 123 #     tag: 2
 124 #     name: (handle array byte)
 125 #     type: (handle tree type-id)
 126 #
 127 #   A variable defined in a register contains:
 128 #     tag: 3
 129 #     name: (handle array byte)
 130 #     type: (handle tree type-id)
 131 #     reg: (handle array byte)
 132 
 133 # == Translation: managing the stack
 134 # Now that we know what the language looks like in the large, let's think
 135 # about how translation happens from the bottom up. One crucial piece of the
 136 # puzzle is how Mu will clean up variables defined on the stack for you.
 137 #
 138 # Assume that we maintain a 'functions' list while parsing source code. And a
 139 # 'primitives' list is a global constant. Both these contain enough information
 140 # to perform type-checking on function calls or primitive statements, respectively.
 141 #
 142 # Defining variables pushes them on a stack with the current block depth and
 143 # enough information about their location (stack offset or register).
 144 # Starting a block increments the current block id.
 145 # Each statement now has enough information to emit code for it.
 146 # Ending a block is where the magic happens:
 147 #   pop all variables at the current block depth
 148 #   emit code to restore all register variables introduced at the current depth
 149 #   emit code to clean up all stack variables at the current depth (just increment esp)
 150 #   decrement the current block depth
 151 #
 152 # Formal types:
 153 #   live-vars: stack of vars
 154 #   var:
 155 #     name: (handle array byte)
 156 #     type: (handle tree type-id)
 157 #     block: int
 158 #     stack-offset: int  (added to ebp)
 159 #     register: (handle array byte)
 160 #       either usual register names
 161 #       or '*' to indicate any register
 162 #   At most one of stack-offset or register-index must be non-zero.
 163 #   A register of '*' designates a variable _template_. Only legal in formal
 164 #   parameters for primitives.
 165 
 166 # == Translating a single function call
 167 # This one's easy. Assuming we've already checked things, we just drop the
 168 # outputs (which use hard-coded registers) and emit inputs in a standard format.
 169 #
 170 # out1, out2, out3, ... <- name inout1, inout2, inout3, ...
 171 # =>
 172 # (subx-name inout1 inout2 inout3)
 173 #
 174 # Formal types:
 175 #   functions: linked list of info
 176 #     name: (handle array byte)
 177 #     inouts: linked list of vars
 178 #     outputs: linked list of vars
 179 #     body: block (singleton linked list)
 180 #     subx-name: (handle array byte)
 181 
 182 # == Translating a single primitive instruction
 183 # A second crucial piece of the puzzle is how Mu converts fairly regular
 184 # primitives with their uniform syntax to SubX instructions with their gnarly
 185 # x86 details.
 186 #
 187 # Mu instructions have inputs and outputs. Primitives can have up to 2 of
 188 # them.
 189 # SubX instructions have rm32 and r32 operands.
 190 # The translation between them covers almost all the possibilities.
 191 #   Instructions with 1 inout may turn into ones with 1 rm32
 192 #     (e.g. incrementing a var on the stack)
 193 #   Instructions with 1 output may turn into ones with 1 rm32
 194 #     (e.g. incrementing a var in a register)
 195 #   1 inout and 1 output may turn into 1 rm32 and 1 r32
 196 #     (e.g. adding a var to a reg)
 197 #   2 inouts may turn into 1 rm32 and 1 r32
 198 #     (e.g. adding a reg to a var)
 199 #   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
 200 #     (e.g. adding a constant to a var)
 201 #   1 output and 1 literal may turn into 1 rm32 and 1 imm32
 202 #     (e.g. adding a constant to a reg)
 203 #   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
 204 #     (special-case: divide edx:eax by a var or reg)
 205 # Observations:
 206 #   We always emit rm32. It may be the first inout or the first output.
 207 #   We may emit r32 or imm32 or neither.
 208 #   When we emit r32 it may come from first inout or second inout or first output.
 209 #
 210 # Accordingly, the formal data structure for a primitive looks like this:
 211 #   primitives: linked list of info
 212 #     name: (handle array byte)
 213 #     mu-inouts: linked list of vars to check
 214 #     mu-outputs: linked list of vars to check; at most a singleton
 215 #     subx-name: (handle array byte)
 216 #     subx-rm32: enum arg-location
 217 #     subx-r32: enum arg-location
 218 #     subx-imm32: enum arg-location
 219 #     subx-disp32: enum arg-location
 220 #     output-is-write-only: boolean
 221 #   arg-location: enum
 222 #     0 means none
 223 #     1 means first inout
 224 #     2 means second inout
 225 #     3 means first output
 226 
 227 # == Translating a block
 228 # Emit block name if necessary
 229 # Emit '{'
 230 # When you encounter a statement, emit it as above
 231 # When you encounter a variable declaration
 232 #   emit any code needed for it (bzeros)
 233 #   push it on the var stack
 234 #   update register dict if necessary
 235 # When you encounter '}'
 236 #   While popping variables off the var stack until block id changes
 237 #     Emit code needed to clean up the stack
 238 #       either increment esp
 239 #       or pop into appropriate register
 240 
 241 # The rest is straightforward.
 242 
 243 == data
 244 
 245 Program:  # (handle function)
 246   0/imm32
 247 
 248 Function-name:
 249   0/imm32
 250 Function-subx-name:
 251   4/imm32
 252 Function-inouts:  # (handle list var)
 253   8/imm32
 254 Function-outputs:  # (handle list var)
 255   0xc/imm32
 256 Function-body:  # (handle block)
 257   0x10/imm32
 258 Function-next:  # (handle function)
 259   0x14/imm32
 260 Function-size:  # (addr int)
 261   0x18/imm32/24
 262 
 263 Primitive-name:
 264   0/imm32
 265 Primitive-inouts:  # (handle list var)
 266   4/imm32
 267 Primitive-outputs:  # (handle list var)
 268   8/imm32
 269 Primitive-subx-name:  # (handle array byte)
 270   0xc/imm32
 271 Primitive-subx-rm32:  # enum arg-location
 272   0x10/imm32
 273 Primitive-subx-r32:  # enum arg-location
 274   0x14/imm32
 275 Primitive-subx-imm32:  # enum arg-location
 276   0x18/imm32
 277 Primitive-subx-disp32:  # enum arg-location  -- only for branches
 278   0x1c/imm32
 279 Primitive-output-is-write-only:  # boolean
 280   0x20/imm32
 281 Primitive-next:  # (handle function)
 282   0x24/imm32
 283 Primitive-size:  # (addr int)
 284   0x28/imm32/36
 285 
 286 Stmt-tag:
 287   0/imm32
 288 
 289 Block-statements:  # (handle list statement)
 290   4/imm32
 291 Block-var:  # (handle var)
 292   8/imm32
 293 
 294 Stmt1-operation:  # (handle array byte)
 295   4/imm32
 296 Stmt1-inouts:  # (handle list var)
 297   8/imm32
 298 Stmt1-outputs:  # (handle list var)
 299   0xc/imm32
 300 
 301 Vardef-var:  # (handle var)
 302   4/imm32
 303 
 304 Regvardef-operation:  # (handle array byte)
 305   4/imm32
 306 Regvardef-inouts:  # (handle list var)
 307   8/imm32
 308 Regvardef-outputs:  # (handle list var)  # will have exactly one element
 309   0xc/imm32
 310 
 311 Stmt-size:  # (addr int)
 312   0x10/imm32
 313 
 314 Var-name:
 315   0/imm32
 316 Var-type:
 317   4/imm32
 318 Var-block-depth:
 319   8/imm32
 320 Var-stack-offset:
 321   0xc/imm32
 322 Var-register:
 323   0x10/imm32
 324 Var-size:  # (addr int)
 325   0x14/imm32
 326 
 327 Any-register:  # "*"
 328   # size
 329   1/imm32
 330   # data
 331   2a/asterisk
 332 
 333 List-value:
 334   0/imm32
 335 List-next:
 336   4/imm32
 337 List-size:  # (addr int)
 338   8/imm32
 339 
 340 # Types are expressed as trees (s-expressions) of type-ids (ints).
 341 # However, there's no need for singletons, so we can assume (int) == int
 342 #   - if x->right == nil, x is an atom
 343 #   - x->left contains either a pointer to a pair, or an atomic type-id directly.
 344 #     type ids will be less than 0x10000 (MAX_TYPE_ID).
 345 
 346 Tree-left:  # either type-id or (addr tree type-id)
 347   0/imm32
 348 Tree-right:  # (addr tree type-id)
 349   4/imm32
 350 Tree-size:  # (addr int)
 351   8/imm32
 352 
 353 Max-type-id:
 354   0x10000/imm32
 355 
 356 == code
 357 
 358 Entry:
 359     # . prologue
 360     89/<- %ebp 4/r32/esp
 361     (new-segment *Heap-size Heap)
 362     # if (argv[1] == "test') run-tests()
 363     {
 364       # if (argc <= 1) break
 365       81 7/subop/compare *ebp 1/imm32
 366       7e/jump-if-<= break/disp8
 367       # if (argv[1] != "test") break
 368       (kernel-string-equal? *(ebp+8) "test")  # => eax
 369       3d/compare-eax-and 0/imm32
 370       74/jump-if-= break/disp8
 371       #
 372       (run-tests)
 373       # syscall(exit, *Num-test-failures)
 374       8b/-> *Num-test-failures 3/r32/ebx
 375       eb/jump $mu-main:end/disp8
 376     }
 377     # otherwise convert Stdin
 378     (convert-mu Stdin Stdout)
 379     (flush Stdout)
 380     # syscall(exit, 0)
 381     bb/copy-to-ebx 0/imm32
 382 $mu-main:end:
 383     b8/copy-to-eax 1/imm32/exit
 384     cd/syscall 0x80/imm8
 385 
 386 convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
 387     # . prologue
 388     55/push-ebp
 389     89/<- %ebp 4/r32/esp
 390     #
 391     (parse-mu *(ebp+8))
 392     (check-mu-types)
 393     (emit-subx *(ebp+0xc))
 394 $convert-mu:end:
 395     # . epilogue
 396     89/<- %esp 5/r32/ebp
 397     5d/pop-to-ebp
 398     c3/return
 399 
 400 test-convert-empty-input:
 401     # empty input => empty output
 402     # . prologue
 403     55/push-ebp
 404     89/<- %ebp 4/r32/esp
 405     # setup
 406     (clear-stream _test-input-stream)
 407     (clear-stream $_test-input-buffered-file->buffer)
 408     (clear-stream _test-output-stream)
 409     (clear-stream $_test-output-buffered-file->buffer)
 410     #
 411     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 412     (flush _test-output-buffered-file)
 413     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 414     # . epilogue
 415     89/<- %esp 5/r32/ebp
 416     5d/pop-to-ebp
 417     c3/return
 418 
 419 test-convert-function-skeleton:
 420     # . prologue
 421     55/push-ebp
 422     89/<- %ebp 4/r32/esp
 423     # setup
 424     (clear-stream _test-input-stream)
 425     (clear-stream $_test-input-buffered-file->buffer)
 426     (clear-stream _test-output-stream)
 427     (clear-stream $_test-output-buffered-file->buffer)
 428     #
 429     (write _test-input-stream "fn foo {\n")
 430     (write _test-input-stream "}\n")
 431     # convert
 432     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 433     (flush _test-output-buffered-file)
 434 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 440     # check output
 441     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
 442     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
 443     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
 444     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 445     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
 446     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 447     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 448     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
 449     # . epilogue
 450     89/<- %esp 5/r32/ebp
 451     5d/pop-to-ebp
 452     c3/return
 453 
 454 test-convert-multiple-function-skeletons:
 455     # . prologue
 456     55/push-ebp
 457     89/<- %ebp 4/r32/esp
 458     # setup
 459     (clear-stream _test-input-stream)
 460     (clear-stream $_test-input-buffered-file->buffer)
 461     (clear-stream _test-output-stream)
 462     (clear-stream $_test-output-buffered-file->buffer)
 463     #
 464     (write _test-input-stream "fn foo {\n")
 465     (write _test-input-stream "}\n")
 466     (write _test-input-stream "fn bar {\n")
 467     (write _test-input-stream "}\n")
 468     # convert
 469     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 470     (flush _test-output-buffered-file)
 471 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 477     # check first function
 478     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
 479     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
 480     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 481     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 482     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 483     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 484     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 485     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
 486     # check second function
 487     (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
 488     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
 489     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 490     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 491     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 492     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 493     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 494     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
 495     # . epilogue
 496     89/<- %esp 5/r32/ebp
 497     5d/pop-to-ebp
 498     c3/return
 499 
 500 test-convert-function-with-arg:
 501     # . prologue
 502     55/push-ebp
 503     89/<- %ebp 4/r32/esp
 504     # setup
 505     (clear-stream _test-input-stream)
 506     (clear-stream $_test-input-buffered-file->buffer)
 507     (clear-stream _test-output-stream)
 508     (clear-stream $_test-output-buffered-file->buffer)
 509     #
 510     (write _test-input-stream "fn foo n : int {\n")
 511     (write _test-input-stream "}\n")
 512     # convert
 513     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 514     (flush _test-output-buffered-file)
 515 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 521     # check output
 522     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
 523     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
 524     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
 525     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
 526     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
 527     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
 528     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
 529     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
 530     # . epilogue
 531     89/<- %esp 5/r32/ebp
 532     5d/pop-to-ebp
 533     c3/return
 534 
 535 test-convert-function-with-arg-and-body:
 536     # . prologue
 537     55/push-ebp
 538     89/<- %ebp 4/r32/esp
 539     # setup
 540     (clear-stream _test-input-stream)
 541     (clear-stream $_test-input-buffered-file->buffer)
 542     (clear-stream _test-output-stream)
 543     (clear-stream $_test-output-buffered-file->buffer)
 544     c7 0/subop/copy *Next-block-index 1/imm32
 545     #
 546     (write _test-input-stream "fn foo n : int {\n")
 547     (write _test-input-stream "  increment n\n")
 548     (write _test-input-stream "}\n")
 549     # convert
 550     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 551     (flush _test-output-buffered-file)
 552 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 558     # check output
 559     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
 560     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
 561     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
 562     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
 563     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
 564     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
 565     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
 566     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
 567     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
 568     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
 569     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
 570     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
 571     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
 572     # . epilogue
 573     89/<- %esp 5/r32/ebp
 574     5d/pop-to-ebp
 575     c3/return
 576 
 577 test-convert-function-distinguishes-args:
 578     # . prologue
 579     55/push-ebp
 580     89/<- %ebp 4/r32/esp
 581     # setup
 582     (clear-stream _test-input-stream)
 583     (clear-stream $_test-input-buffered-file->buffer)
 584     (clear-stream _test-output-stream)
 585     (clear-stream $_test-output-buffered-file->buffer)
 586     c7 0/subop/copy *Next-block-index 1/imm32
 587     #
 588     (write _test-input-stream "fn foo a: int, b: int {\n")
 589     (write _test-input-stream "  increment b\n")
 590     (write _test-input-stream "}\n")
 591     # convert
 592     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 593     (flush _test-output-buffered-file)
 594 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 600     # check output
 601     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
 602     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
 603     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
 604     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
 605     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
 606     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
 607     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
 608     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
 609     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
 610     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
 611     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
 612     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
 613     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
 614     # . epilogue
 615     89/<- %esp 5/r32/ebp
 616     5d/pop-to-ebp
 617     c3/return
 618 
 619 test-convert-function-returns-result:
 620     # . prologue
 621     55/push-ebp
 622     89/<- %ebp 4/r32/esp
 623     # setup
 624     (clear-stream _test-input-stream)
 625     (clear-stream $_test-input-buffered-file->buffer)
 626     (clear-stream _test-output-stream)
 627     (clear-stream $_test-output-buffered-file->buffer)
 628     c7 0/subop/copy *Next-block-index 1/imm32
 629     #
 630     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 631     (write _test-input-stream "  result <- copy a\n")
 632     (write _test-input-stream "  result <- increment\n")
 633     (write _test-input-stream "}\n")
 634     # convert
 635     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 636     (flush _test-output-buffered-file)
 637 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 643     # check output
 644     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-returns-result/0")
 645     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-returns-result/1")
 646     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-returns-result/2")
 647     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
 648     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-returns-result/4")
 649     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-returns-result/5")
 650     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/6")
 651     (check-next-stream-line-equal _test-output-stream "    40/increment-eax"    "F - test-convert-function-returns-result/7")
 652     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-returns-result/8")
 653     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-returns-result/9")
 654     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-returns-result/10")
 655     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/11")
 656     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-returns-result/12")
 657     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-returns-result/13")
 658     # . epilogue
 659     89/<- %esp 5/r32/ebp
 660     5d/pop-to-ebp
 661     c3/return
 662 
 663 test-convert-function-literal-arg:
 664     # . prologue
 665     55/push-ebp
 666     89/<- %ebp 4/r32/esp
 667     # setup
 668     (clear-stream _test-input-stream)
 669     (clear-stream $_test-input-buffered-file->buffer)
 670     (clear-stream _test-output-stream)
 671     (clear-stream $_test-output-buffered-file->buffer)
 672     c7 0/subop/copy *Next-block-index 1/imm32
 673     #
 674     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 675     (write _test-input-stream "  result <- copy a\n")
 676     (write _test-input-stream "  result <- add 1\n")
 677     (write _test-input-stream "}\n")
 678     # convert
 679     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 680     (flush _test-output-buffered-file)
 681 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 687     # check output
 688     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg/0")
 689     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg/1")
 690     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg/2")
 691     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg/3")
 692     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg/4")
 693     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg/5")
 694     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-literal-arg/6")
 695     (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-literal-arg/7")
 696     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg/8")
 697     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg/9")
 698     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg/10")
 699     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg/11")
 700     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg/12")
 701     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg/13")
 702     # . epilogue
 703     89/<- %esp 5/r32/ebp
 704     5d/pop-to-ebp
 705     c3/return
 706 
 707 test-convert-function-literal-arg-2:
 708     # . prologue
 709     55/push-ebp
 710     89/<- %ebp 4/r32/esp
 711     # setup
 712     (clear-stream _test-input-stream)
 713     (clear-stream $_test-input-buffered-file->buffer)
 714     (clear-stream _test-output-stream)
 715     (clear-stream $_test-output-buffered-file->buffer)
 716     c7 0/subop/copy *Next-block-index 1/imm32
 717     #
 718     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
 719     (write _test-input-stream "  result <- copy a\n")
 720     (write _test-input-stream "  result <- add 1\n")
 721     (write _test-input-stream "}\n")
 722     # convert
 723     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 724     (flush _test-output-buffered-file)
 725 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 731     # check output
 732     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg-2/0")
 733     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg-2/1")
 734     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg-2/2")
 735     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg-2/3")
 736     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg-2/4")
 737     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg-2/5")
 738     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-literal-arg-2/6")
 739     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-literal-arg-2/7")
 740     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg-2/8")
 741     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg-2/9")
 742     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg-2/10")
 743     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg-2/11")
 744     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg-2/12")
 745     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg-2/13")
 746     # . epilogue
 747     89/<- %esp 5/r32/ebp
 748     5d/pop-to-ebp
 749     c3/return
 750 
 751 test-convert-function-call-with-literal-arg:
 752     # . prologue
 753     55/push-ebp
 754     89/<- %ebp 4/r32/esp
 755     # setup
 756     (clear-stream _test-input-stream)
 757     (clear-stream $_test-input-buffered-file->buffer)
 758     (clear-stream _test-output-stream)
 759     (clear-stream $_test-output-buffered-file->buffer)
 760     c7 0/subop/copy *Next-block-index 1/imm32
 761     #
 762     (write _test-input-stream "fn main -> result/ebx: int {\n")
 763     (write _test-input-stream "  result <- do-add 3 4\n")
 764     (write _test-input-stream "}\n")
 765     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
 766     (write _test-input-stream "  result <- copy a\n")
 767     (write _test-input-stream "  result <- add b\n")
 768     (write _test-input-stream "}\n")
 769     # convert
 770     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 771     (flush _test-output-buffered-file)
 772 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 778     # check output
 779     (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
 780     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
 781     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
 782     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
 783     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
 784     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
 785     (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/6")
 786     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/7")
 787     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
 788     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/9")
 789     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/10")
 790     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/11")
 791     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/12")
 792     (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/13")
 793     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/14")
 794     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/15")
 795     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/16")
 796     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/17")
 797     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/18")
 798     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/19")
 799     (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/20")
 800     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/21")
 801     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/22")
 802     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/23")
 803     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/24")
 804     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/25")
 805     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/26")
 806     # . epilogue
 807     89/<- %esp 5/r32/ebp
 808     5d/pop-to-ebp
 809     c3/return
 810 
 811 test-convert-function-with-local-var-in-mem:
 812     # . prologue
 813     55/push-ebp
 814     89/<- %ebp 4/r32/esp
 815     # setup
 816     (clear-stream _test-input-stream)
 817     (clear-stream $_test-input-buffered-file->buffer)
 818     (clear-stream _test-output-stream)
 819     (clear-stream $_test-output-buffered-file->buffer)
 820     c7 0/subop/copy *Next-block-index 1/imm32
 821     #
 822     (write _test-input-stream "fn foo {\n")
 823     (write _test-input-stream "  var x: int\n")
 824     (write _test-input-stream "  increment x\n")
 825     (write _test-input-stream "}\n")
 826     # convert
 827     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 828     (flush _test-output-buffered-file)
 829 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 835     # check output
 836     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
 837     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
 838     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
 839     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
 840     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
 841     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
 842     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
 843     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
 844     (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")
 845     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
 846     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
 847     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
 848     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
 849     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
 850     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
 851     # . epilogue
 852     89/<- %esp 5/r32/ebp
 853     5d/pop-to-ebp
 854     c3/return
 855 
 856 test-convert-function-with-local-var-in-reg:
 857     # . prologue
 858     55/push-ebp
 859     89/<- %ebp 4/r32/esp
 860     # setup
 861     (clear-stream _test-input-stream)
 862     (clear-stream $_test-input-buffered-file->buffer)
 863     (clear-stream _test-output-stream)
 864     (clear-stream $_test-output-buffered-file->buffer)
 865     c7 0/subop/copy *Next-block-index 1/imm32
 866     #
 867     (write _test-input-stream "fn foo {\n")
 868     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 869     (write _test-input-stream "  x <- increment\n")
 870     (write _test-input-stream "}\n")
 871     # convert
 872     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 873     (flush _test-output-buffered-file)
 874 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 880     # check output
 881     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
 882     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
 883     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
 884     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
 885     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
 886     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
 887     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
 888     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
 889     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
 890     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
 891     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
 892     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
 893     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
 894     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
 895     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
 896     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
 897     # . epilogue
 898     89/<- %esp 5/r32/ebp
 899     5d/pop-to-ebp
 900     c3/return
 901 
 902 test-convert-function-with-local-var-in-block:
 903     # . prologue
 904     55/push-ebp
 905     89/<- %ebp 4/r32/esp
 906     # setup
 907     (clear-stream _test-input-stream)
 908     (clear-stream $_test-input-buffered-file->buffer)
 909     (clear-stream _test-output-stream)
 910     (clear-stream $_test-output-buffered-file->buffer)
 911     c7 0/subop/copy *Next-block-index 1/imm32
 912     #
 913     (write _test-input-stream "fn foo {\n")
 914     (write _test-input-stream "  {\n")
 915     (write _test-input-stream "    var x: int\n")
 916     (write _test-input-stream "    increment x\n")
 917     (write _test-input-stream "  }\n")
 918     (write _test-input-stream "}\n")
 919     # convert
 920     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 921     (flush _test-output-buffered-file)
 922 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 928     # check output
 929     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
 930     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
 931     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
 932     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
 933     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
 934     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
 935     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
 936     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
 937     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
 938     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
 939     (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")
 940     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
 941     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
 942     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
 943     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
 944     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
 945     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
 946     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
 947     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
 948     # . epilogue
 949     89/<- %esp 5/r32/ebp
 950     5d/pop-to-ebp
 951     c3/return
 952 
 953 test-convert-function-with-local-var-in-named-block:
 954     # . prologue
 955     55/push-ebp
 956     89/<- %ebp 4/r32/esp
 957     # setup
 958     (clear-stream _test-input-stream)
 959     (clear-stream $_test-input-buffered-file->buffer)
 960     (clear-stream _test-output-stream)
 961     (clear-stream $_test-output-buffered-file->buffer)
 962     c7 0/subop/copy *Next-block-index 1/imm32
 963     #
 964     (write _test-input-stream "fn foo {\n")
 965     (write _test-input-stream "  $bar: {\n")
 966     (write _test-input-stream "    var x: int\n")
 967     (write _test-input-stream "    increment x\n")
 968     (write _test-input-stream "  }\n")
 969     (write _test-input-stream "}\n")
 970     # convert
 971     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 972     (flush _test-output-buffered-file)
 973 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 979     # check output
 980     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
 981     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
 982     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
 983     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
 984     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
 985     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
 986     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
 987     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
 988     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
 989     (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")
 990     (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")
 991     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
 992     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
 993     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
 994     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
 995     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
 996     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
 997     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
 998     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
 999     # . epilogue
1000     89/<- %esp 5/r32/ebp
1001     5d/pop-to-ebp
1002     c3/return
1003 
1004 test-convert-function-with-branches-in-block:
1005     # . prologue
1006     55/push-ebp
1007     89/<- %ebp 4/r32/esp
1008     # setup
1009     (clear-stream _test-input-stream)
1010     (clear-stream $_test-input-buffered-file->buffer)
1011     (clear-stream _test-output-stream)
1012     (clear-stream $_test-output-buffered-file->buffer)
1013     c7 0/subop/copy *Next-block-index 1/imm32
1014     #
1015     (write _test-input-stream "fn foo x: int {\n")
1016     (write _test-input-stream "  {\n")
1017     (write _test-input-stream "    break-if->=\n")
1018     (write _test-input-stream "    loop-if-addr<\n")
1019     (write _test-input-stream "    increment x\n")
1020     (write _test-input-stream "    loop\n")
1021     (write _test-input-stream "  }\n")
1022     (write _test-input-stream "}\n")
1023     # convert
1024     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1025     (flush _test-output-buffered-file)
1026 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1032     # check output
1033     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
1034     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
1035     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
1036     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
1037     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
1038     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
1039     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
1040     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
1041     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
1042     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
1043     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
1044     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
1045     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
1046     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
1047     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
1048     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
1049     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
1050     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
1051     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
1052     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
1053     # . epilogue
1054     89/<- %esp 5/r32/ebp
1055     5d/pop-to-ebp
1056     c3/return
1057 
1058 test-convert-function-with-branches-in-named-block:
1059     # . prologue
1060     55/push-ebp
1061     89/<- %ebp 4/r32/esp
1062     # setup
1063     (clear-stream _test-input-stream)
1064     (clear-stream $_test-input-buffered-file->buffer)
1065     (clear-stream _test-output-stream)
1066     (clear-stream $_test-output-buffered-file->buffer)
1067     c7 0/subop/copy *Next-block-index 1/imm32
1068     #
1069     (write _test-input-stream "fn foo x: int {\n")
1070     (write _test-input-stream "  $bar: {\n")
1071     (write _test-input-stream "    break-if->= $bar\n")
1072     (write _test-input-stream "    loop-if-addr< $bar\n")
1073     (write _test-input-stream "    increment x\n")
1074     (write _test-input-stream "    loop\n")
1075     (write _test-input-stream "  }\n")
1076     (write _test-input-stream "}\n")
1077     # convert
1078     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1079     (flush _test-output-buffered-file)
1080 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1086     # check output
1087     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
1088     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
1089     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
1090     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
1091     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
1092     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
1093     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
1094     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
1095     (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")
1096     (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")
1097     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
1098     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
1099     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
1100     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
1101     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
1102     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
1103     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
1104     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
1105     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
1106     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
1107     # . epilogue
1108     89/<- %esp 5/r32/ebp
1109     5d/pop-to-ebp
1110     c3/return
1111 
1112 test-convert-function-with-var-in-nested-block:
1113     # . prologue
1114     55/push-ebp
1115     89/<- %ebp 4/r32/esp
1116     # setup
1117     (clear-stream _test-input-stream)
1118     (clear-stream $_test-input-buffered-file->buffer)
1119     (clear-stream _test-output-stream)
1120     (clear-stream $_test-output-buffered-file->buffer)
1121     c7 0/subop/copy *Next-block-index 1/imm32
1122     #
1123     (write _test-input-stream "fn foo x: int {\n")
1124     (write _test-input-stream "  {\n")
1125     (write _test-input-stream "    {\n")
1126     (write _test-input-stream "      var x: int\n")
1127     (write _test-input-stream "      increment x\n")
1128     (write _test-input-stream "    }\n")
1129     (write _test-input-stream "  }\n")
1130     (write _test-input-stream "}\n")
1131     # convert
1132     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1133     (flush _test-output-buffered-file)
1134 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1140     # check output
1141     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
1142     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
1143     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
1144     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
1145     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
1146     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
1147     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
1148     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
1149     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
1150     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
1151     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
1152     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
1153     (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")
1154     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
1155     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
1156     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
1157     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
1158     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
1159     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
1160     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
1161     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
1162     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
1163     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
1164     # . epilogue
1165     89/<- %esp 5/r32/ebp
1166     5d/pop-to-ebp
1167     c3/return
1168 
1169 test-convert-function-with-multiple-vars-in-nested-blocks:
1170     # . prologue
1171     55/push-ebp
1172     89/<- %ebp 4/r32/esp
1173     # setup
1174     (clear-stream _test-input-stream)
1175     (clear-stream $_test-input-buffered-file->buffer)
1176     (clear-stream _test-output-stream)
1177     (clear-stream $_test-output-buffered-file->buffer)
1178     c7 0/subop/copy *Next-block-index 1/imm32
1179     #
1180     (write _test-input-stream "fn foo x: int {\n")
1181     (write _test-input-stream "  {\n")
1182     (write _test-input-stream "    var x/eax: int <- copy 0\n")
1183     (write _test-input-stream "    {\n")
1184     (write _test-input-stream "      var y: int\n")
1185     (write _test-input-stream "      x <- add y\n")
1186     (write _test-input-stream "    }\n")
1187     (write _test-input-stream "  }\n")
1188     (write _test-input-stream "}\n")
1189     # convert
1190     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1191     (flush _test-output-buffered-file)
1192 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1198     # check output
1199     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
1200     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
1201     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
1202     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
1203     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
1204     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
1205     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
1206     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
1207     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
1208     (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")
1209     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
1210     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
1211     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
1212     (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")
1213     (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")
1214     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
1215     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
1216     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
1217     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
1218     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
1219     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
1220     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
1221     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
1222     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
1223     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
1224     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
1225     # . epilogue
1226     89/<- %esp 5/r32/ebp
1227     5d/pop-to-ebp
1228     c3/return
1229 
1230 test-convert-function-with-branches-and-local-vars:
1231     # A 'break' after a 'var' in a block creates a nested block for the remaining statements.
1232     # That way we always clean up all the 'var's.
1233     # . prologue
1234     55/push-ebp
1235     89/<- %ebp 4/r32/esp
1236     # setup
1237     (clear-stream _test-input-stream)
1238     (clear-stream $_test-input-buffered-file->buffer)
1239     (clear-stream _test-output-stream)
1240     (clear-stream $_test-output-buffered-file->buffer)
1241     c7 0/subop/copy *Next-block-index 1/imm32
1242     #
1243     (write _test-input-stream "fn foo {\n")
1244     (write _test-input-stream "  {\n")
1245     (write _test-input-stream "    var x: int\n")
1246     (write _test-input-stream "    break-if->=\n")
1247     (write _test-input-stream "    increment x\n")
1248     (write _test-input-stream "  }\n")
1249     (write _test-input-stream "}\n")
1250     # convert
1251     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1252     (flush _test-output-buffered-file)
1253 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1259     # check output
1260     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
1261     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
1262     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
1263     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
1264     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
1265     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
1266     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
1267     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
1268     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
1269     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
1270     (check-next-stream-line-equal _test-output-stream "        0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
1271     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/11")
1272     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/12")
1273     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/13")
1274     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/14")
1275     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/15")
1276     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/16")
1277     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
1278     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/18")
1279     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/19")
1280     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/20")
1281     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/21")
1282     # . epilogue
1283     89/<- %esp 5/r32/ebp
1284     5d/pop-to-ebp
1285     c3/return
1286 
1287 test-convert-function-with-loops-and-local-vars:
1288     # A 'loop' after a 'var' in a block is converted into a block that
1289     # performs all necessary cleanup before jumping. This results in some ugly
1290     # code duplication.
1291     # . prologue
1292     55/push-ebp
1293     89/<- %ebp 4/r32/esp
1294     # setup
1295     (clear-stream _test-input-stream)
1296     (clear-stream $_test-input-buffered-file->buffer)
1297     (clear-stream _test-output-stream)
1298     (clear-stream $_test-output-buffered-file->buffer)
1299     c7 0/subop/copy *Next-block-index 1/imm32
1300     #
1301     (write _test-input-stream "fn foo {\n")
1302     (write _test-input-stream "  {\n")
1303     (write _test-input-stream "    var x: int\n")
1304     (write _test-input-stream "    loop-if->=\n")
1305     (write _test-input-stream "    increment x\n")
1306     (write _test-input-stream "  }\n")
1307     (write _test-input-stream "}\n")
1308     # convert
1309     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1310     (flush _test-output-buffered-file)
1311 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1317     # check output
1318     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-loops-and-local-vars/0")
1319     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-loops-and-local-vars/1")
1320     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-loops-and-local-vars/2")
1321     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-loops-and-local-vars/3")
1322     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-loops-and-local-vars/4")
1323     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-loops-and-local-vars/5")
1324     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-loops-and-local-vars/6")
1325     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-loops-and-local-vars/7")
1326     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-loops-and-local-vars/8")
1327     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-loops-and-local-vars/9")
1328     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-loops-and-local-vars/10")
1329     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-loops-and-local-vars/11")
1330     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:loop/disp32"  "F - test-convert-function-with-loops-and-local-vars/12")
1331     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-loops-and-local-vars/13")
1332     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-loops-and-local-vars/14")
1333     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-loops-and-local-vars/15")
1334     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-loops-and-local-vars/16")
1335     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-loops-and-local-vars/17")
1336     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-loops-and-local-vars/18")
1337     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-loops-and-local-vars/19")
1338     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-loops-and-local-vars/20")
1339     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-loops-and-local-vars/21")
1340     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-loops-and-local-vars/22")
1341     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-loops-and-local-vars/23")
1342     # . epilogue
1343     89/<- %esp 5/r32/ebp
1344     5d/pop-to-ebp
1345     c3/return
1346 
1347 #######################################################
1348 # Parsing
1349 #######################################################
1350 
1351 parse-mu:  # in: (addr buffered-file)
1352     # pseudocode
1353     #   var curr-function: (addr (handle function)) = Program
1354     #   var line: (stream byte 512)
1355     #   var word-slice: slice
1356     #   while true                                  # line loop
1357     #     clear-stream(line)
1358     #     read-line-buffered(in, line)
1359     #     if (line->write == 0) break               # end of file
1360     #     word-slice = next-mu-token(line)
1361     #     if slice-empty?(word-slice)               # end of line
1362     #       continue
1363     #     else if slice-starts-with?(word-slice, "#")  # comment
1364     #       continue                                # end of line
1365     #     else if slice-equal(word-slice, "fn")
1366     #       var new-function: (handle function) = allocate(function)
1367     #       var vars: (stack (addr var) 256)
1368     #       populate-mu-function-header(in, new-function, vars)
1369     #       populate-mu-function-body(in, new-function, vars)
1370     #       assert(vars->top == 0)
1371     #       *curr-function = new-function
1372     #       curr-function = &new-function->next
1373     #     else
1374     #       abort()
1375     #
1376     # . prologue
1377     55/push-ebp
1378     89/<- %ebp 4/r32/esp
1379     # . save registers
1380     50/push-eax
1381     51/push-ecx
1382     52/push-edx
1383     53/push-ebx
1384     57/push-edi
1385     # var line/ecx: (stream byte 512)
1386     81 5/subop/subtract %esp 0x200/imm32
1387     68/push 0x200/imm32/length
1388     68/push 0/imm32/read
1389     68/push 0/imm32/write
1390     89/<- %ecx 4/r32/esp
1391     # var word-slice/edx: slice
1392     68/push 0/imm32/end
1393     68/push 0/imm32/start
1394     89/<- %edx 4/r32/esp
1395     # var curr-function/edi: (addr (handle function)) = Program
1396     bf/copy-to-edi Program/imm32
1397     # var vars/ebx: (stack (addr var) 256)
1398     81 5/subop/subtract %esp 0x400/imm32
1399     68/push 0x400/imm32/length
1400     68/push 0/imm32/top
1401     89/<- %ebx 4/r32/esp
1402     {
1403 $parse-mu:line-loop:
1404       (clear-stream %ecx)
1405       (read-line-buffered *(ebp+8) %ecx)
1406       # if (line->write == 0) break
1407       81 7/subop/compare *ecx 0/imm32
1408       0f 84/jump-if-= break/disp32
1409 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------
1415       (next-mu-token %ecx %edx)
1416       # if slice-empty?(word-slice) continue
1417       (slice-empty? %edx)
1418       3d/compare-eax-and 0/imm32
1419       0f 85/jump-if-!= loop/disp32
1420       # if (*word-slice->start == "#") continue
1421       # . eax = *word-slice->start
1422       8b/-> *edx 0/r32/eax
1423       8a/copy-byte *eax 0/r32/AL
1424       81 4/subop/and %eax 0xff/imm32
1425       # . if (eax == '#') continue
1426       3d/compare-eax-and 0x23/imm32/hash
1427       0f 84/jump-if-= loop/disp32
1428       # if (slice-equal?(word-slice, "fn")) parse a function
1429       {
1430 $parse-mu:fn:
1431         (slice-equal? %edx "fn")
1432         3d/compare-eax-and 0/imm32
1433         0f 84/jump-if-= break/disp32
1434         # var new-function/eax: (handle function) = populate-mu-function(in, new-function, vars)
1435         (allocate Heap *Function-size)  # => eax
1436         (zero-out %eax *Function-size)
1437         (clear-stack %ebx)
1438         (populate-mu-function-header %ecx %eax %ebx)
1439         (populate-mu-function-body *(ebp+8) %eax %ebx)
1440         # *curr-function = new-function
1441         89/<- *edi 0/r32/eax
1442         # curr-function = &new-function->next
1443         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
1444         e9/jump $parse-mu:line-loop/disp32
1445       }
1446       # otherwise abort
1447       e9/jump $parse-mu:error1/disp32
1448     } # end line loop
1449 $parse-mu:end:
1450     # . reclaim locals
1451     81 0/subop/add %esp 0x630/imm32
1452     # . restore registers
1453     5f/pop-to-edi
1454     5b/pop-to-ebx
1455     5a/pop-to-edx
1456     59/pop-to-ecx
1457     58/pop-to-eax
1458     # . epilogue
1459     89/<- %esp 5/r32/ebp
1460     5d/pop-to-ebp
1461     c3/return
1462 
1463 $parse-mu:error1:
1464     # error("unexpected top-level command: " word-slice "\n")
1465     (write-buffered Stderr "unexpected top-level command: ")
1466     (write-slice-buffered Stderr %edx)
1467     (write-buffered Stderr "\n")
1468     (flush Stderr)
1469     # . syscall(exit, 1)
1470     bb/copy-to-ebx  1/imm32
1471     b8/copy-to-eax  1/imm32/exit
1472     cd/syscall  0x80/imm8
1473     # never gets here
1474 
1475 $parse-mu:error2:
1476     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
1477     (print-int32-buffered Stderr *ebx)
1478     (write-buffered Stderr " vars not reclaimed after fn '")
1479     (write-slice-buffered Stderr *eax)  # Function-name
1480     (write-buffered Stderr "'\n")
1481     (flush Stderr)
1482     # . syscall(exit, 1)
1483     bb/copy-to-ebx  1/imm32
1484     b8/copy-to-eax  1/imm32/exit
1485     cd/syscall  0x80/imm8
1486     # never gets here
1487 
1488 # scenarios considered:
1489 # ✗ fn foo  # no block
1490 # ✓ fn foo {
1491 # ✗ fn foo { {
1492 # ✗ fn foo { }
1493 # ✗ fn foo { } {
1494 # ✗ fn foo x {
1495 # ✗ fn foo x: {
1496 # ✓ fn foo x: int {
1497 # ✓ fn foo x: int {
1498 # ✓ fn foo x: int -> y/eax: int {
1499 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
1500     # pseudocode:
1501     #   var name: slice
1502     #   next-word(first-line, name)
1503     #   assert(name not in '{' '}' '->')
1504     #   out->name = slice-to-string(name)
1505     #   var next-offset: int = 8
1506     #   ## inouts
1507     #   while true
1508     #     ## name
1509     #     name = next-word(first-line)
1510     #     if (name == '{') goto done
1511     #     if (name == '->') break
1512     #     assert(name != '}')
1513     #     var v: (handle var) = parse-var-with-type(name, first-line)
1514     #     assert(v->register == null)
1515     #     v->stack-offset = next-offset
1516     #     next-offset += size-of(v)
1517     #     # v->block-depth is implicitly 0
1518     #     out->inouts = append(out->inouts, v)
1519     #     push(vars, v)
1520     #   ## outputs
1521     #   while true
1522     #     ## name
1523     #     name = next-word(first-line)
1524     #     assert(name not in '{' '}' '->')
1525     #     var v: (handle var) = parse-var-with-type(name, first-line)
1526     #     assert(v->register != null)
1527     #     out->outputs = append(out->outputs, v)
1528     #   done:
1529     #
1530     # . prologue
1531     55/push-ebp
1532     89/<- %ebp 4/r32/esp
1533     # . save registers
1534     50/push-eax
1535     51/push-ecx
1536     52/push-edx
1537     53/push-ebx
1538     57/push-edi
1539     # edi = out
1540     8b/-> *(ebp+0xc) 7/r32/edi
1541     # var word-slice/ecx: slice
1542     68/push 0/imm32/end
1543     68/push 0/imm32/start
1544     89/<- %ecx 4/r32/esp
1545     # var next-offset/edx = 8
1546     ba/copy-to-edx 8/imm32
1547     # read function name
1548     (next-word *(ebp+8) %ecx)
1549     # error checking
1550     # TODO: error if name starts with 'break' or 'loop'
1551     # if (word-slice == '{') abort
1552     (slice-equal? %ecx "{")   # => eax
1553     3d/compare-eax-and 0/imm32
1554     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1555     # if (word-slice == '->') abort
1556     (slice-equal? %ecx "->")   # => eax
1557     3d/compare-eax-and 0/imm32
1558     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1559     # if (word-slice == '}') abort
1560     (slice-equal? %ecx "}")   # => eax
1561     3d/compare-eax-and 0/imm32
1562     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1563     # save function name
1564     (slice-to-string Heap %ecx)  # => eax
1565     89/<- *edi 0/r32/eax  # Function-name
1566     # initialize default subx-name as well
1567     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
1568     # save function inouts
1569     {
1570 $populate-mu-function-header:check-for-inout:
1571       (next-word *(ebp+8) %ecx)
1572       # if (word-slice == '{') goto done
1573       (slice-equal? %ecx "{")   # => eax
1574       3d/compare-eax-and 0/imm32
1575       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
1576       # if (word-slice == '->') break
1577       (slice-equal? %ecx "->")   # => eax
1578       3d/compare-eax-and 0/imm32
1579       0f 85/jump-if-!= break/disp32
1580       # if (word-slice == '}') abort
1581       (slice-equal? %ecx "}")   # => eax
1582       3d/compare-eax-and 0/imm32
1583       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1584       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
1585       (parse-var-with-type %ecx *(ebp+8))  # => eax
1586       89/<- %ebx 0/r32/eax
1587       # assert(v->register == null)
1588       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
1589       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
1590       # v->stack-offset = next-offset
1591       89/<- *(ebx+0xc) 2/r32/edx  # Var-stack-offset
1592       # next-offset += size-of(v)
1593       (size-of %ebx)  # => eax
1594       01/add %edx 0/r32/eax
1595       # v->block-depth is implicitly 0
1596       #
1597       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
1598       89/<- *(edi+8) 0/r32/eax  # Function-inouts
1599       (push *(ebp+0x10) %ebx)
1600       #
1601       e9/jump loop/disp32
1602     }
1603     # save function outputs
1604     {
1605 $parse-var-with-type:check-for-out:
1606       (next-word *(ebp+8) %ecx)
1607       # if (word-slice == '{') break
1608       (slice-equal? %ecx "{")   # => eax
1609       3d/compare-eax-and 0/imm32
1610       0f 85/jump-if-!= break/disp32
1611       # if (word-slice == '->') abort
1612       (slice-equal? %ecx "->")   # => eax
1613       3d/compare-eax-and 0/imm32
1614       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1615       # if (word-slice == '}') abort
1616       (slice-equal? %ecx "}")   # => eax
1617       3d/compare-eax-and 0/imm32
1618       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1619       #
1620       (parse-var-with-type %ecx *(ebp+8))  # => eax
1621       89/<- %ebx 0/r32/eax
1622       # assert(var->register != null)
1623       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
1624       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
1625       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
1626       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
1627       e9/jump loop/disp32
1628     }
1629 $populate-mu-function-header:done:
1630     (check-no-tokens-left *(ebp+8))
1631 $populate-mu-function-header:end:
1632     # . reclaim locals
1633     81 0/subop/add %esp 8/imm32
1634     # . restore registers
1635     5f/pop-to-edi
1636     5b/pop-to-ebx
1637     5a/pop-to-edx
1638     59/pop-to-ecx
1639     58/pop-to-eax
1640     # . epilogue
1641     89/<- %esp 5/r32/ebp
1642     5d/pop-to-ebp
1643     c3/return
1644 
1645 $populate-mu-function-header:error1:
1646     # error("function header not in form 'fn <name> {'")
1647     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
1648     (flush Stderr)
1649     (rewind-stream *(ebp+8))
1650     (write-stream 2 *(ebp+8))
1651     (write-buffered Stderr "'\n")
1652     (flush Stderr)
1653     # . syscall(exit, 1)
1654     bb/copy-to-ebx  1/imm32
1655     b8/copy-to-eax  1/imm32/exit
1656     cd/syscall  0x80/imm8
1657     # never gets here
1658 
1659 $populate-mu-function-header:error2:
1660     # error("function input '" var "' cannot be in a register")
1661     (write-buffered Stderr "function input '")
1662     (write-buffered Stderr *ebx)  # Var-name
1663     (write-buffered Stderr "' cannot be in a register")
1664     (flush Stderr)
1665     # . syscall(exit, 1)
1666     bb/copy-to-ebx  1/imm32
1667     b8/copy-to-eax  1/imm32/exit
1668     cd/syscall  0x80/imm8
1669     # never gets here
1670 
1671 $populate-mu-function-header:error3:
1672     # error("function input '" var "' must be in a register")
1673     (write-buffered Stderr "function input '")
1674     (write-buffered Stderr *eax)  # Var-name
1675     (write-buffered Stderr " must be in a register'")
1676     (flush Stderr)
1677     (rewind-stream *(ebp+8))
1678     (write-stream 2 *(ebp+8))
1679     (write-buffered Stderr "'\n")
1680     (flush Stderr)
1681     # . syscall(exit, 1)
1682     bb/copy-to-ebx  1/imm32
1683     b8/copy-to-eax  1/imm32/exit
1684     cd/syscall  0x80/imm8
1685     # never gets here
1686 
1687 test-function-header-with-arg:
1688     # . prologue
1689     55/push-ebp
1690     89/<- %ebp 4/r32/esp
1691     # setup
1692     (clear-stream _test-input-stream)
1693     (write _test-input-stream "foo n : int {\n")
1694     # var result/ecx: function
1695     2b/subtract-> *Function-size 4/r32/esp
1696     89/<- %ecx 4/r32/esp
1697     (zero-out %ecx *Function-size)
1698     # var vars/ebx: (stack (addr var) 16)
1699     81 5/subop/subtract %esp 0x10/imm32
1700     68/push 0x10/imm32/length
1701     68/push 0/imm32/top
1702     89/<- %ebx 4/r32/esp
1703     # convert
1704     (populate-mu-function-header _test-input-stream %ecx %ebx)
1705     # check result
1706     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
1707     # edx: (handle list var) = result->inouts
1708     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1709     # ebx: (handle var) = result->inouts->value
1710     8b/-> *edx 3/r32/ebx  # List-value
1711     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
1712     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1713     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
1714     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
1715     # . epilogue
1716     89/<- %esp 5/r32/ebp
1717     5d/pop-to-ebp
1718     c3/return
1719 
1720 test-function-header-with-multiple-args:
1721     # . prologue
1722     55/push-ebp
1723     89/<- %ebp 4/r32/esp
1724     # setup
1725     (clear-stream _test-input-stream)
1726     (write _test-input-stream "foo a: int, b: int c: int {\n")
1727     # result/ecx: (handle function)
1728     2b/subtract-> *Function-size 4/r32/esp
1729     89/<- %ecx 4/r32/esp
1730     (zero-out %ecx *Function-size)
1731     # var vars/ebx: (stack (addr var) 16)
1732     81 5/subop/subtract %esp 0x10/imm32
1733     68/push 0x10/imm32/length
1734     68/push 0/imm32/top
1735     89/<- %ebx 4/r32/esp
1736     # convert
1737     (populate-mu-function-header _test-input-stream %ecx %ebx)
1738     # check result
1739     (check-strings-equal *ecx "foo")  # Function-name
1740     # edx: (handle list var) = result->inouts
1741     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1742 $test-function-header-with-multiple-args:inout0:
1743     # ebx: (handle var) = result->inouts->value
1744     8b/-> *edx 3/r32/ebx  # List-value
1745     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
1746     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1747     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
1748     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
1749     # edx = result->inouts->next
1750     8b/-> *(edx+4) 2/r32/edx  # List-next
1751 $test-function-header-with-multiple-args:inout1:
1752     # ebx = result->inouts->next->value
1753     8b/-> *edx 3/r32/ebx  # List-value
1754     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
1755     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1756     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
1757     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
1758     # edx = result->inouts->next->next
1759     8b/-> *(edx+4) 2/r32/edx  # List-next
1760 $test-function-header-with-multiple-args:inout2:
1761     # ebx = result->inouts->next->next->value
1762     8b/-> *edx 3/r32/ebx  # List-value
1763     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
1764     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1765     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
1766     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
1767     # . epilogue
1768     89/<- %esp 5/r32/ebp
1769     5d/pop-to-ebp
1770     c3/return
1771 
1772 test-function-with-multiple-args-and-outputs:
1773     # . prologue
1774     55/push-ebp
1775     89/<- %ebp 4/r32/esp
1776     # setup
1777     (clear-stream _test-input-stream)
1778     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
1779     # result/ecx: (handle function)
1780     2b/subtract-> *Function-size 4/r32/esp
1781     89/<- %ecx 4/r32/esp
1782     (zero-out %ecx *Function-size)
1783     # var vars/ebx: (stack (addr var) 16)
1784     81 5/subop/subtract %esp 0x10/imm32
1785     68/push 0x10/imm32/length
1786     68/push 0/imm32/top
1787     89/<- %ebx 4/r32/esp
1788     # convert
1789     (populate-mu-function-header _test-input-stream %ecx %ebx)
1790     # check result
1791     (check-strings-equal *ecx "foo")  # Function-name
1792     # edx: (handle list var) = result->inouts
1793     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1794     # ebx: (handle var) = result->inouts->value
1795     8b/-> *edx 3/r32/ebx  # List-value
1796     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
1797     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1798     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
1799     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
1800     # edx = result->inouts->next
1801     8b/-> *(edx+4) 2/r32/edx  # List-next
1802     # ebx = result->inouts->next->value
1803     8b/-> *edx 3/r32/ebx  # List-value
1804     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
1805     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1806     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
1807     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
1808     # edx = result->inouts->next->next
1809     8b/-> *(edx+4) 2/r32/edx  # List-next
1810     # ebx = result->inouts->next->next->value
1811     8b/-> *edx 3/r32/ebx  # List-value
1812     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
1813     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1814     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
1815     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
1816     # edx: (handle list var) = result->outputs
1817     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
1818     # ebx: (handle var) = result->outputs->value
1819     8b/-> *edx 3/r32/ebx  # List-value
1820     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
1821     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
1822     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1823     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
1824     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
1825     # edx = result->outputs->next
1826     8b/-> *(edx+4) 2/r32/edx  # List-next
1827     # ebx = result->outputs->next->value
1828     8b/-> *edx 3/r32/ebx  # List-value
1829     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
1830     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
1831     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1832     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
1833     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
1834     # . epilogue
1835     89/<- %esp 5/r32/ebp
1836     5d/pop-to-ebp
1837     c3/return
1838 
1839 # format for variables with types
1840 #   x: int
1841 #   x: int
1842 #   x: int,
1843 # ignores at most one trailing colon or comma
1844 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
1845     # pseudocode:
1846     #   var v: (handle var) = allocate(Heap, Var-size)
1847     #   var s: slice
1848     #   next-token-from-slice(name->start, name->end, '/', s)
1849     #   var end: (addr byte) = s->end
1850     #   if (slice-ends-with(s, ":"))
1851     #     decrement s->end
1852     #   if (slice-ends-with(s, ","))
1853     #     decrement s->end
1854     #   v->name = slice-to-string(s)
1855     #   ## register
1856     #   next-token-from-slice(end, name->end, '/', s)
1857     #   if (slice-ends-with(s, ":"))
1858     #     decrement s->end
1859     #   if (slice-ends-with(s, ","))
1860     #     decrement s->end
1861     #   if (!slice-empty?(s))
1862     #     v->register = slice-to-string(s)
1863     #   ## type
1864     #   var type: (handle tree type-id) = parse-type(first-line)
1865     #   v->type = type
1866     #   return v
1867     #
1868     # . prologue
1869     55/push-ebp
1870     89/<- %ebp 4/r32/esp
1871     # . save registers
1872     51/push-ecx
1873     52/push-edx
1874     53/push-ebx
1875     56/push-esi
1876     57/push-edi
1877     # var result/edi: (handle var) = allocate(Heap, Var-size)
1878     (allocate Heap *Var-size)  # => eax
1879     (zero-out %eax *Var-size)
1880     89/<- %edi 0/r32/eax
1881     # esi = name
1882     8b/-> *(ebp+8) 6/r32/esi
1883     # var s/ecx: slice
1884     68/push 0/imm32/end
1885     68/push 0/imm32/start
1886     89/<- %ecx 4/r32/esp
1887 $parse-var-with-type:save-name:
1888     # save v->name
1889     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
1890     # . end/edx = s->end
1891     8b/-> *(ecx+4) 2/r32/edx
1892     # . if s ends with ':', decrement s->end
1893     {
1894       8b/-> *(ecx+4) 0/r32/eax
1895       48/decrement-eax
1896       8a/copy-byte *eax 3/r32/BL
1897       81 4/subop/and %ebx 0xff/imm32
1898       81 7/subop/compare %ebx 0x3a/imm32/colon
1899       75/jump-if-!= break/disp8
1900       89/<- *(ecx+4) 0/r32/eax
1901     }
1902     # . if s ends with ',', decrement s->end
1903     {
1904       8b/-> *(ecx+4) 0/r32/eax
1905       48/decrement-eax
1906       8a/copy-byte *eax 3/r32/BL
1907       81 4/subop/and %ebx 0xff/imm32
1908       81 7/subop/compare %ebx 0x2c/imm32/comma
1909       75/jump-if-!= break/disp8
1910       89/<- *(ecx+4) 0/r32/eax
1911     }
1912 $parse-var-with-type:write-name:
1913     (slice-to-string Heap %ecx)  # => eax
1914     89/<- *edi 0/r32/eax  # Var-name
1915     # save v->register
1916 $parse-var-with-type:save-register:
1917     (next-token-from-slice %edx *(esi+4) 0x2f %ecx)  # end, name->end, '/'
1918     # . if s ends with ':', decrement s->end
1919     {
1920       8b/-> *(ecx+4) 0/r32/eax
1921       48/decrement-eax
1922       8a/copy-byte *eax 3/r32/BL
1923       81 4/subop/and %ebx 0xff/imm32
1924       81 7/subop/compare %ebx 0x3a/imm32/colon
1925       75/jump-if-!= break/disp8
1926       89/<- *(ecx+4) 0/r32/eax
1927     }
1928     # . if s ends with ',', decrement s->end
1929     {
1930       8b/-> *(ecx+4) 0/r32/eax
1931       48/decrement-eax
1932       8a/copy-byte *eax 3/r32/BL
1933       81 4/subop/and %ebx 0xff/imm32
1934       81 7/subop/compare %ebx 0x2c/imm32/comma
1935       75/jump-if-!= break/disp8
1936       89/<- *(ecx+4) 0/r32/eax
1937     }
1938     # if (!slice-empty?(s)) v->register = slice-to-string(s)
1939     {
1940 $parse-var-with-type:write-register:
1941       # HACK: s->end can be less than s->start with all the decrements above
1942       # That's probably a sign we have the wrong algorithm for this function.
1943       8b/-> *ecx 0/r32/eax
1944       39/compare *(ecx+4) 0/r32/eax  # Slice-end
1945       76/jump-if-<= break/disp8
1946       (slice-to-string Heap %ecx)
1947       89/<- *(edi+0x10) 0/r32/eax  # Var-register
1948     }
1949 $parse-var-with-type:save-type:
1950     (parse-type Heap *(ebp+0xc))  # => eax
1951     89/<- *(edi+4) 0/r32/eax  # Var-type
1952 $parse-var-with-type:end:
1953     # return result
1954     89/<- %eax 7/r32/edi
1955     # . reclaim locals
1956     81 0/subop/add %esp 8/imm32
1957     # . restore registers
1958     5f/pop-to-edi
1959     5e/pop-to-esi
1960     5b/pop-to-ebx
1961     5a/pop-to-edx
1962     59/pop-to-ecx
1963     # . epilogue
1964     89/<- %esp 5/r32/ebp
1965     5d/pop-to-ebp
1966     c3/return
1967 
1968 $parse-var-with-type:abort:
1969     # error("function header not in form 'fn <name> {'")
1970     (write-buffered Stderr "var should have form 'name: type' in '")
1971     (flush Stderr)
1972     (rewind-stream *(ebp+0xc))
1973     (write-stream 2 *(ebp+0xc))
1974     (write-buffered Stderr "'\n")
1975     (flush Stderr)
1976     # . syscall(exit, 1)
1977     bb/copy-to-ebx  1/imm32
1978     b8/copy-to-eax  1/imm32/exit
1979     cd/syscall  0x80/imm8
1980     # never gets here
1981 
1982 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
1983     # pseudocode:
1984     #   var s: slice = next-mu-token(in)
1985     #   assert s != ""
1986     #   assert s != "->"
1987     #   assert s != "{"
1988     #   assert s != "}"
1989     #   if s == ")"
1990     #     return 0
1991     #   result = allocate(Tree)
1992     #   zero-out(result, *Tree-size)
1993     #   if s != "("
1994     #     result->left = pos-slice(Type-id, s)
1995     #     return
1996     #   result->left = parse-type(ad, in)
1997     #   result->right = parse-type-tree(ad, in)
1998     #
1999     # . prologue
2000     55/push-ebp
2001     89/<- %ebp 4/r32/esp
2002     # . save registers
2003     51/push-ecx
2004     52/push-edx
2005     # var s/ecx: slice
2006     68/push 0/imm32
2007     68/push 0/imm32
2008     89/<- %ecx 4/r32/esp
2009     # s = next-mu-token(in)
2010     (next-mu-token *(ebp+0xc) %ecx)
2011 #?     (write-buffered Stderr "tok: ")
2012 #?     (write-slice-buffered Stderr %ecx)
2013 #?     (write-buffered Stderr "$\n")
2014 #?     (flush Stderr)
2015     # assert s != ""
2016     (slice-equal? %ecx "")
2017     3d/compare-eax-and 0/imm32
2018     0f 85/jump-if-!= $parse-type:abort/disp32
2019     # assert s != "{"
2020     (slice-equal? %ecx "{")
2021     3d/compare-eax-and 0/imm32
2022     0f 85/jump-if-!= $parse-type:abort/disp32
2023     # assert s != "}"
2024     (slice-equal? %ecx "}")
2025     3d/compare-eax-and 0/imm32
2026     0f 85/jump-if-!= $parse-type:abort/disp32
2027     # assert s != "->"
2028     (slice-equal? %ecx "->")
2029     3d/compare-eax-and 0/imm32
2030     0f 85/jump-if-!= $parse-type:abort/disp32
2031     # if (s == ")") return 0
2032     (slice-equal? %ecx ")")
2033     3d/compare-eax-and 0/imm32
2034     b8/copy-to-eax 0/imm32
2035     0f 85/jump-if-!= $parse-type:end/disp32
2036     # var result/edx: (handle tree type-id)
2037     (allocate *(ebp+8) *Tree-size)  # => eax
2038     (zero-out %eax *Tree-size)
2039     89/<- %edx 0/r32/eax
2040     {
2041       # if (s != "(") break
2042       (slice-equal? %ecx "(")
2043       3d/compare-eax-and 0/imm32
2044       75/jump-if-!= break/disp8
2045       # result->left = pos-slice(Type-id, s)
2046       (pos-slice Type-id %ecx)
2047 #?       (write-buffered Stderr "=> {")
2048 #?       (print-int32-buffered Stderr %eax)
2049 #?       (write-buffered Stderr ", 0}\n")
2050 #?       (flush Stderr)
2051       89/<- *edx 0/r32/eax  # Tree-left
2052       e9/jump $parse-type:return-edx/disp32
2053     }
2054     # otherwise s == "("
2055     # result->left = parse-type(ad, in)
2056     (parse-type *(ebp+8) *(ebp+0xc))
2057 #?     (write-buffered Stderr "=> {")
2058 #?     (print-int32-buffered Stderr %eax)
2059     89/<- *edx 0/r32/eax  # Tree-left
2060     # result->right = parse-type-tree(ad, in)
2061     (parse-type-tree *(ebp+8) *(ebp+0xc))
2062 #?     (write-buffered Stderr Space)
2063 #?     (print-int32-buffered Stderr %eax)
2064 #?     (write-buffered Stderr "}\n")
2065 #?     (flush Stderr)
2066     89/<- *(edx+4) 0/r32/eax  # Tree-right
2067 $parse-type:return-edx:
2068     89/<- %eax 2/r32/edx
2069 $parse-type:end:
2070     # . reclaim locals
2071     81 0/subop/add %esp 8/imm32
2072     # . restore registers
2073     5a/pop-to-edx
2074     59/pop-to-ecx
2075     # . epilogue
2076     89/<- %esp 5/r32/ebp
2077     5d/pop-to-ebp
2078     c3/return
2079 
2080 $parse-type:abort:
2081     # error("unexpected token when parsing type: '" s "'\n")
2082     (write-buffered Stderr "unexpected token when parsing type: '")
2083     (write-slice-buffered Stderr %ecx)
2084     (write-buffered Stderr "'\n")
2085     (flush Stderr)
2086     # . syscall(exit, 1)
2087     bb/copy-to-ebx  1/imm32
2088     b8/copy-to-eax  1/imm32/exit
2089     cd/syscall  0x80/imm8
2090     # never gets here
2091 
2092 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2093     # pseudocode:
2094     #   var tmp: (handle tree type-id) = parse-type(ad, in)
2095     #   if tmp == 0
2096     #     return 0
2097     #   result = allocate(Tree)
2098     #   zero-out(result, *Tree-size)
2099     #   result->left = tmp
2100     #   result->right = parse-type-tree(ad, in)
2101     #
2102     # . prologue
2103     55/push-ebp
2104     89/<- %ebp 4/r32/esp
2105     # . save registers
2106     51/push-ecx
2107     52/push-edx
2108     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
2109     (parse-type *(ebp+8) *(ebp+0xc))
2110     # if (tmp == 0) return tmp
2111     3d/compare-eax-and 0/imm32
2112     74/jump-if-= $parse-type-tree:end/disp8
2113     # var tmp2/ecx = tmp
2114     89/<- %ecx 0/r32/eax
2115     # var result/edx: (handle tree type-id)
2116     (allocate *(ebp+8) *Tree-size)  # => eax
2117     (zero-out %eax *Tree-size)
2118     89/<- %edx 0/r32/eax
2119     # result->left = tmp2
2120     89/<- *edx 1/r32/ecx  # Tree-left
2121     # result->right = parse-type-tree(ad, in)
2122     (parse-type-tree *(ebp+8) *(ebp+0xc))
2123     89/<- *(edx+4) 0/r32/eax  # Tree-right
2124 $parse-type-tree:return-edx:
2125     89/<- %eax 2/r32/edx
2126 $parse-type-tree:end:
2127     # . restore registers
2128     5a/pop-to-edx
2129     59/pop-to-ecx
2130     # . epilogue
2131     89/<- %esp 5/r32/ebp
2132     5d/pop-to-ebp
2133     c3/return
2134 
2135 next-mu-token:  # in: (addr stream byte), out: (addr slice)
2136     # pseudocode:
2137     # start:
2138     #   skip-chars-matching-whitespace(in)
2139     #   if in->read >= in->write              # end of in
2140     #     out = {0, 0}
2141     #     return
2142     #   out->start = &in->data[in->read]
2143     #   var curr-byte/eax: byte = in->data[in->read]
2144     #   if curr->byte == ':'                  # comment token
2145     #     ++in->read
2146     #     goto start
2147     #   if curr->byte == ','                  # comment token
2148     #     ++in->read
2149     #     goto start
2150     #   if curr-byte == '#'                   # comment
2151     #     in->read = in->write                # skip to end of in
2152     #     goto done
2153     #   if curr-byte == '"'                   # string literal
2154     #     skip-string(in)
2155     #     goto done                           # no metadata
2156     #   if curr-byte == '('
2157     #     ++in->read
2158     #     goto done
2159     #   if curr-byte == ')'
2160     #     ++in->read
2161     #     goto done
2162     #   # read a word
2163     #   while true
2164     #     if in->read >= in->write
2165     #       break
2166     #     curr-byte = in->data[in->read]
2167     #     if curr-byte == ' '
2168     #       break
2169     #     if curr-byte == '\r'
2170     #       break
2171     #     if curr-byte == '\n'
2172     #       break
2173     #     if curr-byte == '('
2174     #       break
2175     #     if curr-byte == ')'
2176     #       break
2177     #     if curr-byte == ':'
2178     #       break
2179     #     if curr-byte == ','
2180     #       break
2181     #     ++in->read
2182     # done:
2183     #   out->end = &in->data[in->read]
2184     #   # hack: skip a few trailing delimiters, because we don't always use
2185     #   # this correct tokenizer in later tokens
2186     #   while true
2187     #     if in->read >= in->write
2188     #       break
2189     #     curr-byte = in->data[in->read]
2190     #     if curr-byte == ':'
2191     #       ++in->read
2192     #     else if curr-byte == ','
2193     #       ++in->read
2194     #     else
2195     #       break
2196     #
2197     # . prologue
2198     55/push-ebp
2199     89/<- %ebp 4/r32/esp
2200     # . save registers
2201     50/push-eax
2202     51/push-ecx
2203     56/push-esi
2204     57/push-edi
2205     # esi = in
2206     8b/-> *(ebp+8) 6/r32/esi
2207     # edi = out
2208     8b/-> *(ebp+0xc) 7/r32/edi
2209 $next-mu-token:start:
2210     (skip-chars-matching-whitespace %esi)
2211 $next-mu-token:check0:
2212     # if (in->read >= in->write) return out = {0, 0}
2213     # . ecx = in->read
2214     8b/-> *(esi+4) 1/r32/ecx
2215     # . if (ecx >= in->write) return out = {0, 0}
2216     3b/compare 1/r32/ecx *esi
2217     c7 0/subop/copy *edi 0/imm32
2218     c7 0/subop/copy *(edi+4) 0/imm32
2219     0f 8d/jump-if->= $next-mu-token:end/disp32
2220     # out->start = &in->data[in->read]
2221     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2222     89/<- *edi 0/r32/eax
2223     # var curr-byte/eax: byte = in->data[in->read]
2224     31/xor %eax 0/r32/eax
2225     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2226     {
2227 $next-mu-token:check-for-colon:
2228       # if (curr-byte != ':') break
2229       3d/compare-eax-and 0x3a/imm32/colon
2230       75/jump-if-!= break/disp8
2231       # ++in->read
2232       ff 0/subop/increment *(esi+4)
2233       # restart
2234       e9/jump $next-mu-token:start/disp32
2235     }
2236     {
2237 $next-mu-token:check-for-comma:
2238       # if (curr-byte != ',') break
2239       3d/compare-eax-and 0x2c/imm32/comma
2240       75/jump-if-!= break/disp8
2241       # ++in->read
2242       ff 0/subop/increment *(esi+4)
2243       # restart
2244       e9/jump $next-mu-token:start/disp32
2245     }
2246     {
2247 $next-mu-token:check-for-comment:
2248       # if (curr-byte != '#') break
2249       3d/compare-eax-and 0x23/imm32/pound
2250       75/jump-if-!= break/disp8
2251       # in->read = in->write  # skip rest of in
2252       8b/-> *esi 0/r32/eax
2253       89/<- *(esi+4) 0/r32/eax
2254       # return
2255       e9/jump $next-mu-token:done/disp32
2256     }
2257     {
2258 $next-mu-token:check-for-string-literal:
2259       # if (curr-byte != '"') break
2260       3d/compare-eax-and 0x22/imm32/dquote
2261       75/jump-if-!= break/disp8
2262       (skip-string %esi)
2263       # return
2264       e9/jump $next-mu-token:done/disp32
2265     }
2266     {
2267 $next-mu-token:check-for-open-paren:
2268       # if (curr-byte != '(') break
2269       3d/compare-eax-and 0x28/imm32/open-paren
2270       75/jump-if-!= break/disp8
2271       # ++in->read
2272       ff 0/subop/increment *(esi+4)
2273       # return
2274       e9/jump $next-mu-token:done/disp32
2275     }
2276     {
2277 $next-mu-token:check-for-close-paren:
2278       # if (curr-byte != ')') break
2279       3d/compare-eax-and 0x29/imm32/close-paren
2280       75/jump-if-!= break/disp8
2281       # ++in->read
2282       ff 0/subop/increment *(esi+4)
2283       # return
2284       e9/jump $next-mu-token:done/disp32
2285     }
2286     {
2287 $next-mu-token:regular-word-without-metadata:
2288       # if (in->read >= in->write) break
2289       # . ecx = in->read
2290       8b/-> *(esi+4) 1/r32/ecx
2291       # . if (ecx >= in->write) break
2292       3b/compare *esi 1/r32/ecx
2293       7d/jump-if->= break/disp8
2294       # var c/eax: byte = in->data[in->read]
2295       31/xor %eax 0/r32/eax
2296       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2297       # if (c == ' ') break
2298       3d/compare-eax-and 0x20/imm32/space
2299       74/jump-if-= break/disp8
2300       # if (c == '\r') break
2301       3d/compare-eax-and 0xd/imm32/carriage-return
2302       74/jump-if-= break/disp8
2303       # if (c == '\n') break
2304       3d/compare-eax-and 0xa/imm32/newline
2305       74/jump-if-= break/disp8
2306       # if (c == '(') break
2307       3d/compare-eax-and 0x28/imm32/open-paren
2308       0f 84/jump-if-= break/disp32
2309       # if (c == ')') break
2310       3d/compare-eax-and 0x29/imm32/close-paren
2311       0f 84/jump-if-= break/disp32
2312       # if (c == ':') break
2313       3d/compare-eax-and 0x3a/imm32/colon
2314       0f 84/jump-if-= break/disp32
2315       # if (c == ',') break
2316       3d/compare-eax-and 0x2c/imm32/comma
2317       0f 84/jump-if-= break/disp32
2318       # ++in->read
2319       ff 0/subop/increment *(esi+4)
2320       #
2321       e9/jump loop/disp32
2322     }
2323 $next-mu-token:done:
2324     # out->end = &in->data[in->read]
2325     8b/-> *(esi+4) 1/r32/ecx
2326     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2327     89/<- *(edi+4) 0/r32/eax
2328     {
2329 $next-mu-token:skip-trailing-delimiters:
2330       # if (in->read >= in->write) break
2331       # . ecx = in->read
2332       8b/-> *(esi+4) 1/r32/ecx
2333       # . if (ecx >= in->write) break
2334       3b/compare *esi 1/r32/ecx
2335       7d/jump-if->= break/disp8
2336       # var c/eax: byte = in->data[in->read]
2337       31/xor %eax 0/r32/eax
2338       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2339       # if (c == ':') ++in->read and loop
2340       {
2341         3d/compare-eax-and 0x3a/imm32/colon
2342         75/jump-if-!= break/disp8
2343         # ++in->read
2344         ff 0/subop/increment *(esi+4)
2345         #
2346         eb/jump $next-mu-token:skip-trailing-delimiters/disp8
2347       }
2348       # if (c == ',') ++in->read and loop
2349       {
2350         3d/compare-eax-and 0x2c/imm32/comma
2351         75/jump-if-!= break/disp8
2352         # ++in->read
2353         ff 0/subop/increment *(esi+4)
2354         #
2355         eb/jump $next-mu-token:skip-trailing-delimiters/disp8
2356       }
2357       # else break
2358     }
2359 $next-mu-token:end:
2360     # . restore registers
2361     5f/pop-to-edi
2362     5e/pop-to-esi
2363     59/pop-to-ecx
2364     58/pop-to-eax
2365     # . epilogue
2366     89/<- %esp 5/r32/ebp
2367     5d/pop-to-ebp
2368     c3/return
2369 
2370 # return the index in an array of strings matching 's'
2371 # index is denominated in elements, not bytes
2372 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
2373     # . prologue
2374     55/push-ebp
2375     89/<- %ebp 4/r32/esp
2376     # . save registers
2377     51/push-ecx
2378     52/push-edx
2379     53/push-ebx
2380     56/push-esi
2381 #?     (write-buffered Stderr "pos-slice: ")
2382 #?     (write-slice-buffered Stderr *(ebp+0xc))
2383 #?     (write-buffered Stderr "\n")
2384 #?     (flush Stderr)
2385     # esi = arr
2386     8b/-> *(ebp+8) 6/r32/esi
2387     # var index/ecx: int = 0
2388     b9/copy-to-ecx 0/imm32
2389     # var curr/edx: (addr (addr array byte)) = arr->data
2390     8d/copy-address *(esi+0xc) 2/r32/edx
2391     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
2392     8b/-> *esi 3/r32/ebx
2393     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
2394     {
2395 #?       (write-buffered Stderr "  ")
2396 #?       (print-int32-buffered Stderr %ecx)
2397 #?       (write-buffered Stderr "\n")
2398 #?       (flush Stderr)
2399       # if (curr >= max) return -1
2400       39/compare %edx 3/r32/ebx
2401       b8/copy-to-eax -1/imm32
2402       73/jump-if-addr>= $pos-slice:end/disp8
2403       # if (slice-equal?(s, *curr)) break
2404       (slice-equal? *(ebp+0xc) *edx)  # => eax
2405       3d/compare-eax-and 0/imm32
2406       75/jump-if-!= break/disp8
2407       # ++index
2408       41/increment-ecx
2409       # curr += 4
2410       81 0/subop/add %edx 4/imm32
2411       #
2412       eb/jump loop/disp8
2413     }
2414     # return index
2415     89/<- %eax 1/r32/ecx
2416 $pos-slice:end:
2417 #?     (write-buffered Stderr "=> ")
2418 #?     (print-int32-buffered Stderr %eax)
2419 #?     (write-buffered Stderr "\n")
2420     # . restore registers
2421     5e/pop-to-esi
2422     5b/pop-to-ebx
2423     5a/pop-to-edx
2424     59/pop-to-ecx
2425     # . epilogue
2426     89/<- %esp 5/r32/ebp
2427     5d/pop-to-ebp
2428     c3/return
2429 
2430 == data
2431 
2432 Type-id:  # (stream (address array byte))
2433   0x18/imm32/write
2434   0/imm32/read
2435   0x100/imm32/length
2436   # data
2437   "literal"/imm32  # 0
2438   "int"/imm32  # 1
2439   "addr"/imm32  # 2
2440   "array"/imm32  # 3
2441   "handle"/imm32  # 4
2442   "bool"/imm32  # 5
2443   0/imm32
2444   0/imm32
2445   # 0x20
2446   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2447   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2448   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2449   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2450   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2451   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2452   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2453 
2454 == code
2455 
2456 test-parse-var-with-type:
2457     # . prologue
2458     55/push-ebp
2459     89/<- %ebp 4/r32/esp
2460     # (eax..ecx) = "x:"
2461     b8/copy-to-eax "x:"/imm32
2462     8b/-> *eax 1/r32/ecx
2463     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2464     05/add-to-eax 4/imm32
2465     # var slice/ecx: slice = {eax, ecx}
2466     51/push-ecx
2467     50/push-eax
2468     89/<- %ecx 4/r32/esp
2469     # _test-input-stream contains "int"
2470     (clear-stream _test-input-stream)
2471     (write _test-input-stream "int")
2472     #
2473     (parse-var-with-type %ecx _test-input-stream)
2474     8b/-> *eax 2/r32/edx  # Var-name
2475     (check-strings-equal %edx "x" "F - test-var-with-type/name")
2476     8b/-> *(eax+4) 2/r32/edx  # Var-type
2477     (check-ints-equal *edx 1 "F - test-var-with-type/type")
2478     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
2479     # . epilogue
2480     89/<- %esp 5/r32/ebp
2481     5d/pop-to-ebp
2482     c3/return
2483 
2484 test-parse-var-with-type-and-register:
2485     # . prologue
2486     55/push-ebp
2487     89/<- %ebp 4/r32/esp
2488     # (eax..ecx) = "x/eax"
2489     b8/copy-to-eax "x/eax"/imm32
2490     8b/-> *eax 1/r32/ecx
2491     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2492     05/add-to-eax 4/imm32
2493     # var slice/ecx: slice = {eax, ecx}
2494     51/push-ecx
2495     50/push-eax
2496     89/<- %ecx 4/r32/esp
2497     # _test-input-stream contains ": int"
2498     (clear-stream _test-input-stream)
2499     (write _test-input-stream ": int")
2500     #
2501     (parse-var-with-type %ecx _test-input-stream)
2502     8b/-> *eax 2/r32/edx  # Var-name
2503     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
2504     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2505     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
2506     8b/-> *(eax+4) 2/r32/edx  # Var-type
2507     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
2508     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
2509     # . epilogue
2510     89/<- %esp 5/r32/ebp
2511     5d/pop-to-ebp
2512     c3/return
2513 
2514 test-parse-var-with-trailing-characters:
2515     # . prologue
2516     55/push-ebp
2517     89/<- %ebp 4/r32/esp
2518     # (eax..ecx) = "x:"
2519     b8/copy-to-eax "x:"/imm32
2520     8b/-> *eax 1/r32/ecx
2521     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2522     05/add-to-eax 4/imm32
2523     # var slice/ecx: slice = {eax, ecx}
2524     51/push-ecx
2525     50/push-eax
2526     89/<- %ecx 4/r32/esp
2527     # _test-input-stream contains "int,"
2528     (clear-stream _test-input-stream)
2529     (write _test-input-stream "int,")
2530     #
2531     (parse-var-with-type %ecx _test-input-stream)
2532     8b/-> *eax 2/r32/edx  # Var-name
2533     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
2534     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2535     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
2536     8b/-> *(eax+4) 2/r32/edx  # Var-type
2537     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
2538     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
2539     # . epilogue
2540     89/<- %esp 5/r32/ebp
2541     5d/pop-to-ebp
2542     c3/return
2543 
2544 test-parse-var-with-register-and-trailing-characters:
2545     # . prologue
2546     55/push-ebp
2547     89/<- %ebp 4/r32/esp
2548     # (eax..ecx) = "x/eax:"
2549     b8/copy-to-eax "x/eax:"/imm32
2550     8b/-> *eax 1/r32/ecx
2551     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2552     05/add-to-eax 4/imm32
2553     # var slice/ecx: slice = {eax, ecx}
2554     51/push-ecx
2555     50/push-eax
2556     89/<- %ecx 4/r32/esp
2557     # _test-input-stream contains "int,"
2558     (clear-stream _test-input-stream)
2559     (write _test-input-stream "int,")
2560     #
2561     (parse-var-with-type %ecx _test-input-stream)
2562     8b/-> *eax 2/r32/edx  # Var-name
2563     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
2564     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2565     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
2566     8b/-> *(eax+4) 2/r32/edx  # Var-type
2567     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
2568     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
2569     # . epilogue
2570     89/<- %esp 5/r32/ebp
2571     5d/pop-to-ebp
2572     c3/return
2573 
2574 test-parse-var-with-compound-type:
2575     # . prologue
2576     55/push-ebp
2577     89/<- %ebp 4/r32/esp
2578     # (eax..ecx) = "x:"
2579     b8/copy-to-eax "x:"/imm32
2580     8b/-> *eax 1/r32/ecx
2581     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2582     05/add-to-eax 4/imm32
2583     # var slice/ecx: slice = {eax, ecx}
2584     51/push-ecx
2585     50/push-eax
2586     89/<- %ecx 4/r32/esp
2587     # _test-input-stream contains "(addr int)"
2588     (clear-stream _test-input-stream)
2589     (write _test-input-stream "(addr int)")
2590     #
2591     (parse-var-with-type %ecx _test-input-stream)
2592     8b/-> *eax 2/r32/edx  # Var-name
2593     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
2594     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2595     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
2596     # var type/edx: (handle tree type-id) = var->type
2597     8b/-> *(eax+4) 2/r32/edx  # Var-type
2598     # type->left == atom(addr)
2599     8b/-> *edx 0/r32/eax  # Atom-value
2600     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
2601     # type->right->left == atom(int)
2602     8b/-> *(edx+4) 2/r32/edx  # Tree-right
2603     8b/-> *edx 0/r32/eax  # Tree-left
2604     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
2605     # type->right->right == null
2606     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
2607     # . epilogue
2608     89/<- %esp 5/r32/ebp
2609     5d/pop-to-ebp
2610     c3/return
2611 
2612 # identifier starts with a letter or '$' or '_'
2613 # no constraints at the moment on later letters
2614 # all we really want to do so far is exclude '{', '}' and '->'
2615 is-identifier?:  # in: (addr slice) -> result/eax: boolean
2616     # . prologue
2617     55/push-ebp
2618     89/<- %ebp 4/r32/esp
2619     # if (slice-empty?(in)) return false
2620     (slice-empty? *(ebp+8))  # => eax
2621     3d/compare-eax-and 0/imm32
2622     75/jump-if-!= $is-identifier?:false/disp8
2623     # var c/eax: byte = *in->start
2624     8b/-> *(ebp+8) 0/r32/eax
2625     8b/-> *eax 0/r32/eax
2626     8a/copy-byte *eax 0/r32/AL
2627     81 4/subop/and %eax 0xff/imm32
2628     # if (c == '$') return true
2629     3d/compare-eax-and 0x24/imm32/$
2630     74/jump-if-= $is-identifier?:true/disp8
2631     # if (c == '_') return true
2632     3d/compare-eax-and 0x5f/imm32/_
2633     74/jump-if-= $is-identifier?:true/disp8
2634     # drop case
2635     25/and-eax-with 0x5f/imm32
2636     # if (c < 'A') return false
2637     3d/compare-eax-and 0x41/imm32/A
2638     7c/jump-if-< $is-identifier?:false/disp8
2639     # if (c > 'Z') return false
2640     3d/compare-eax-and 0x5a/imm32/Z
2641     7f/jump-if-> $is-identifier?:false/disp8
2642     # otherwise return true
2643 $is-identifier?:true:
2644     b8/copy-to-eax 1/imm32/true
2645     eb/jump $is-identifier?:end/disp8
2646 $is-identifier?:false:
2647     b8/copy-to-eax 0/imm32/false
2648 $is-identifier?:end:
2649     # . epilogue
2650     89/<- %esp 5/r32/ebp
2651     5d/pop-to-ebp
2652     c3/return
2653 
2654 test-is-identifier-dollar:
2655     # . prologue
2656     55/push-ebp
2657     89/<- %ebp 4/r32/esp
2658     # (eax..ecx) = "$a"
2659     b8/copy-to-eax "$a"/imm32
2660     8b/-> *eax 1/r32/ecx
2661     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2662     05/add-to-eax 4/imm32
2663     # var slice/ecx: slice = {eax, ecx}
2664     51/push-ecx
2665     50/push-eax
2666     89/<- %ecx 4/r32/esp
2667     #
2668     (is-identifier? %ecx)
2669     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
2670     # . epilogue
2671     89/<- %esp 5/r32/ebp
2672     5d/pop-to-ebp
2673     c3/return
2674 
2675 test-is-identifier-underscore:
2676     # . prologue
2677     55/push-ebp
2678     89/<- %ebp 4/r32/esp
2679     # (eax..ecx) = "_a"
2680     b8/copy-to-eax "_a"/imm32
2681     8b/-> *eax 1/r32/ecx
2682     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2683     05/add-to-eax 4/imm32
2684     # var slice/ecx: slice = {eax, ecx}
2685     51/push-ecx
2686     50/push-eax
2687     89/<- %ecx 4/r32/esp
2688     #
2689     (is-identifier? %ecx)
2690     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
2691     # . epilogue
2692     89/<- %esp 5/r32/ebp
2693     5d/pop-to-ebp
2694     c3/return
2695 
2696 test-is-identifier-a:
2697     # . prologue
2698     55/push-ebp
2699     89/<- %ebp 4/r32/esp
2700     # (eax..ecx) = "a$"
2701     b8/copy-to-eax "a$"/imm32
2702     8b/-> *eax 1/r32/ecx
2703     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2704     05/add-to-eax 4/imm32
2705     # var slice/ecx: slice = {eax, ecx}
2706     51/push-ecx
2707     50/push-eax
2708     89/<- %ecx 4/r32/esp
2709     #
2710     (is-identifier? %ecx)
2711     (check-ints-equal %eax 1 "F - test-is-identifier-a")
2712     # . epilogue
2713     89/<- %esp 5/r32/ebp
2714     5d/pop-to-ebp
2715     c3/return
2716 
2717 test-is-identifier-z:
2718     # . prologue
2719     55/push-ebp
2720     89/<- %ebp 4/r32/esp
2721     # (eax..ecx) = "z$"
2722     b8/copy-to-eax "z$"/imm32
2723     8b/-> *eax 1/r32/ecx
2724     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2725     05/add-to-eax 4/imm32
2726     # var slice/ecx: slice = {eax, ecx}
2727     51/push-ecx
2728     50/push-eax
2729     89/<- %ecx 4/r32/esp
2730     #
2731     (is-identifier? %ecx)
2732     (check-ints-equal %eax 1 "F - test-is-identifier-z")
2733     # . epilogue
2734     89/<- %esp 5/r32/ebp
2735     5d/pop-to-ebp
2736     c3/return
2737 
2738 test-is-identifier-A:
2739     # . prologue
2740     55/push-ebp
2741     89/<- %ebp 4/r32/esp
2742     # (eax..ecx) = "A$"
2743     b8/copy-to-eax "A$"/imm32
2744     8b/-> *eax 1/r32/ecx
2745     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2746     05/add-to-eax 4/imm32
2747     # var slice/ecx: slice = {eax, ecx}
2748     51/push-ecx
2749     50/push-eax
2750     89/<- %ecx 4/r32/esp
2751     #
2752     (is-identifier? %ecx)
2753     (check-ints-equal %eax 1 "F - test-is-identifier-A")
2754     # . epilogue
2755     89/<- %esp 5/r32/ebp
2756     5d/pop-to-ebp
2757     c3/return
2758 
2759 test-is-identifier-Z:
2760     # . prologue
2761     55/push-ebp
2762     89/<- %ebp 4/r32/esp
2763     # (eax..ecx) = "Z$"
2764     b8/copy-to-eax "Z$"/imm32
2765     8b/-> *eax 1/r32/ecx
2766     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2767     05/add-to-eax 4/imm32
2768     # var slice/ecx: slice = {eax, ecx}
2769     51/push-ecx
2770     50/push-eax
2771     89/<- %ecx 4/r32/esp
2772     #
2773     (is-identifier? %ecx)
2774     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
2775     # . epilogue
2776     89/<- %esp 5/r32/ebp
2777     5d/pop-to-ebp
2778     c3/return
2779 
2780 test-is-identifier-@:
2781     # character before 'A' is invalid
2782     # . prologue
2783     55/push-ebp
2784     89/<- %ebp 4/r32/esp
2785     # (eax..ecx) = "@a"
2786     b8/copy-to-eax "@a"/imm32
2787     8b/-> *eax 1/r32/ecx
2788     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2789     05/add-to-eax 4/imm32
2790     # var slice/ecx: slice = {eax, ecx}
2791     51/push-ecx
2792     50/push-eax
2793     89/<- %ecx 4/r32/esp
2794     #
2795     (is-identifier? %ecx)
2796     (check-ints-equal %eax 0 "F - test-is-identifier-@")
2797     # . epilogue
2798     89/<- %esp 5/r32/ebp
2799     5d/pop-to-ebp
2800     c3/return
2801 
2802 test-is-identifier-square-bracket:
2803     # character after 'Z' is invalid
2804     # . prologue
2805     55/push-ebp
2806     89/<- %ebp 4/r32/esp
2807     # (eax..ecx) = "[a"
2808     b8/copy-to-eax "[a"/imm32
2809     8b/-> *eax 1/r32/ecx
2810     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2811     05/add-to-eax 4/imm32
2812     # var slice/ecx: slice = {eax, ecx}
2813     51/push-ecx
2814     50/push-eax
2815     89/<- %ecx 4/r32/esp
2816     #
2817     (is-identifier? %ecx)
2818     (check-ints-equal %eax 0 "F - test-is-identifier-@")
2819     # . epilogue
2820     89/<- %esp 5/r32/ebp
2821     5d/pop-to-ebp
2822     c3/return
2823 
2824 test-is-identifier-backtick:
2825     # character before 'a' is invalid
2826     # . prologue
2827     55/push-ebp
2828     89/<- %ebp 4/r32/esp
2829     # (eax..ecx) = "`a"
2830     b8/copy-to-eax "`a"/imm32
2831     8b/-> *eax 1/r32/ecx
2832     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2833     05/add-to-eax 4/imm32
2834     # var slice/ecx: slice = {eax, ecx}
2835     51/push-ecx
2836     50/push-eax
2837     89/<- %ecx 4/r32/esp
2838     #
2839     (is-identifier? %ecx)
2840     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
2841     # . epilogue
2842     89/<- %esp 5/r32/ebp
2843     5d/pop-to-ebp
2844     c3/return
2845 
2846 test-is-identifier-curly-brace-open:
2847     # character after 'z' is invalid; also used for blocks
2848     # . prologue
2849     55/push-ebp
2850     89/<- %ebp 4/r32/esp
2851     # (eax..ecx) = "{a"
2852     b8/copy-to-eax "{a"/imm32
2853     8b/-> *eax 1/r32/ecx
2854     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2855     05/add-to-eax 4/imm32
2856     # var slice/ecx: slice = {eax, ecx}
2857     51/push-ecx
2858     50/push-eax
2859     89/<- %ecx 4/r32/esp
2860     #
2861     (is-identifier? %ecx)
2862     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
2863     # . epilogue
2864     89/<- %esp 5/r32/ebp
2865     5d/pop-to-ebp
2866     c3/return
2867 
2868 test-is-identifier-curly-brace-close:
2869     # . prologue
2870     55/push-ebp
2871     89/<- %ebp 4/r32/esp
2872     # (eax..ecx) = "}a"
2873     b8/copy-to-eax "}a"/imm32
2874     8b/-> *eax 1/r32/ecx
2875     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2876     05/add-to-eax 4/imm32
2877     # var slice/ecx: slice = {eax, ecx}
2878     51/push-ecx
2879     50/push-eax
2880     89/<- %ecx 4/r32/esp
2881     #
2882     (is-identifier? %ecx)
2883     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
2884     # . epilogue
2885     89/<- %esp 5/r32/ebp
2886     5d/pop-to-ebp
2887     c3/return
2888 
2889 test-is-identifier-hyphen:
2890     # disallow leading '-' since '->' has special meaning
2891     # . prologue
2892     55/push-ebp
2893     89/<- %ebp 4/r32/esp
2894     # (eax..ecx) = "-a"
2895     b8/copy-to-eax "-a"/imm32
2896     8b/-> *eax 1/r32/ecx
2897     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2898     05/add-to-eax 4/imm32
2899     # var slice/ecx: slice = {eax, ecx}
2900     51/push-ecx
2901     50/push-eax
2902     89/<- %ecx 4/r32/esp
2903     #
2904     (is-identifier? %ecx)
2905     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
2906     # . epilogue
2907     89/<- %esp 5/r32/ebp
2908     5d/pop-to-ebp
2909     c3/return
2910 
2911 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
2912     # . prologue
2913     55/push-ebp
2914     89/<- %ebp 4/r32/esp
2915     # . save registers
2916     50/push-eax
2917     56/push-esi
2918     57/push-edi
2919     # esi = in
2920     8b/-> *(ebp+8) 6/r32/esi
2921     # edi = out
2922     8b/-> *(ebp+0xc) 7/r32/edi
2923     # initialize some global state
2924     c7 0/subop/copy *Curr-block-depth 1/imm32
2925     c7 0/subop/copy *Next-local-stack-offset -4/imm32
2926     # var eax: (handle block) = parse-mu-block(in, vars, fn)
2927     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
2928     # out->body = eax
2929     89/<- *(edi+0x10) 0/r32/eax  # Function-body
2930 $populate-mu-function-body:end:
2931     # . restore registers
2932     5f/pop-to-edi
2933     5e/pop-to-esi
2934     58/pop-to-eax
2935     # . epilogue
2936     89/<- %esp 5/r32/ebp
2937     5d/pop-to-ebp
2938     c3/return
2939 
2940 == data
2941 
2942 # Global state added to each var record when parsing a function
2943 
2944 Curr-block-depth:  # (addr int)
2945     0/imm32
2946 Next-local-stack-offset:  # (addr int)
2947     -4/imm32
2948 
2949 Next-block-index:  # (addr int)
2950     1/imm32
2951 
2952 == code
2953 
2954 # parses a block, assuming that the leading '{' has already been read by the caller
2955 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
2956     # pseudocode:
2957     #   var line: (stream byte 512)
2958     #   var word-slice: slice
2959     #   increment *Curr-block-depth
2960     #   result/eax = allocate(Heap, Stmt-size)
2961     #   result->tag = 0/block
2962     #   result->name = some unique name
2963     #   while true                                  # line loop
2964     #     clear-stream(line)
2965     #     read-line-buffered(in, line)
2966     #     if (line->write == 0) break               # end of file
2967     #     word-slice = next-word(line)
2968     #     if slice-empty?(word-slice)               # end of line
2969     #       continue
2970     #     else if slice-starts-with?(word-slice, "#")
2971     #       continue
2972     #     else if slice-equal?(word-slice, "{")
2973     #       assert(no-tokens-in(line))
2974     #       block = parse-mu-block(in, vars, fn)
2975     #       append-to-block(result, block)
2976     #     else if slice-equal?(word-slice, "}")
2977     #       break
2978     #     else if slice-ends-with?(word-slice, ":")
2979     #       # TODO: error-check the rest of 'line'
2980     #       --word-slice->end to skip ':'
2981     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
2982     #       append-to-block(result, named-block)
2983     #     else if slice-equal?(word-slice, "var")
2984     #       var-def = parse-mu-var-def(line, vars)
2985     #       append-to-block(result, var-def)
2986     #     else
2987     #       stmt = parse-mu-stmt(line, vars, fn)
2988     #       append-to-block(result, stmt)
2989     #   decrement *Curr-block-depth
2990     #   return result
2991     #
2992     # . prologue
2993     55/push-ebp
2994     89/<- %ebp 4/r32/esp
2995     # . save registers
2996     51/push-ecx
2997     52/push-edx
2998     53/push-ebx
2999     57/push-edi
3000     # var line/ecx: (stream byte 512)
3001     81 5/subop/subtract %esp 0x200/imm32
3002     68/push 0x200/imm32/length
3003     68/push 0/imm32/read
3004     68/push 0/imm32/write
3005     89/<- %ecx 4/r32/esp
3006     # var word-slice/edx: slice
3007     68/push 0/imm32/end
3008     68/push 0/imm32/start
3009     89/<- %edx 4/r32/esp
3010     # edi = result
3011     (allocate Heap *Stmt-size)  # => eax
3012     (zero-out %eax *Stmt-size)
3013     89/<- %edi 0/r32/eax
3014     # set result->tag
3015     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
3016     # set result->var
3017     (new-block-name *(ebp+0x10))  # => eax
3018     89/<- *(edi+8) 0/r32/eax  # Block-var
3019     # push result->var to vars
3020     (push *(ebp+0xc) %eax)
3021     # increment *Curr-block-depth
3022     ff 0/subop/increment *Curr-block-depth
3023     {
3024 $parse-mu-block:line-loop:
3025       # line = read-line-buffered(in)
3026       (clear-stream %ecx)
3027       (read-line-buffered *(ebp+8) %ecx)
3028 #?       (write-buffered Stderr "line: ")
3029 #?       (write-stream-data Stderr %ecx)
3030 #?       (write-buffered Stderr Newline)
3031 #?       (flush Stderr)
3032       # if (line->write == 0) break
3033       81 7/subop/compare *ecx 0/imm32
3034       0f 84/jump-if-= break/disp32
3035       # word-slice = next-word(line)
3036       (next-word %ecx %edx)
3037 #?       (write-buffered Stderr "word: ")
3038 #?       (write-slice-buffered Stderr %edx)
3039 #?       (write-buffered Stderr Newline)
3040 #?       (flush Stderr)
3041       # if slice-empty?(word-slice) continue
3042       (slice-empty? %edx)
3043       3d/compare-eax-and 0/imm32
3044       0f 85/jump-if-!= loop/disp32
3045       # if (slice-starts-with?(word-slice, '#') continue
3046       # . eax = *word-slice->start
3047       8b/-> *edx 0/r32/eax
3048       8a/copy-byte *eax 0/r32/AL
3049       81 4/subop/and %eax 0xff/imm32
3050       # . if (eax == '#') continue
3051       3d/compare-eax-and 0x23/imm32/hash
3052       0f 84/jump-if-= loop/disp32
3053       # if slice-equal?(word-slice, "{")
3054       {
3055 $parse-mu-block:check-for-block:
3056         (slice-equal? %edx "{")
3057         3d/compare-eax-and 0/imm32
3058         74/jump-if-= break/disp8
3059         (check-no-tokens-left %ecx)
3060         # parse new block and append
3061         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3062         (append-to-block Heap %edi %eax)
3063         e9/jump $parse-mu-block:line-loop/disp32
3064       }
3065       # if slice-equal?(word-slice, "}") break
3066 $parse-mu-block:check-for-end:
3067       (slice-equal? %edx "}")
3068       3d/compare-eax-and 0/imm32
3069       0f 85/jump-if-!= break/disp32
3070       # if slice-ends-with?(word-slice, ":") parse named block and append
3071       {
3072 $parse-mu-block:check-for-named-block:
3073         # . eax = *(word-slice->end-1)
3074         8b/-> *(edx+4) 0/r32/eax
3075         48/decrement-eax
3076         8a/copy-byte *eax 0/r32/AL
3077         81 4/subop/and %eax 0xff/imm32
3078         # . if (eax != ':') break
3079         3d/compare-eax-and 0x3a/imm32/colon
3080         0f 85/jump-if-!= break/disp32
3081         # TODO: error-check the rest of 'line'
3082         #
3083         # skip ':'
3084         ff 1/subop/decrement *(edx+4)  # Slice-end
3085         #
3086         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3087         (append-to-block Heap %edi %eax)
3088         e9/jump $parse-mu-block:line-loop/disp32
3089       }
3090       # if slice-equal?(word-slice, "var")
3091       {
3092 $parse-mu-block:check-for-var:
3093         (slice-equal? %edx "var")
3094         3d/compare-eax-and 0/imm32
3095         74/jump-if-= break/disp8
3096         #
3097         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
3098         (append-to-block Heap %edi %eax)
3099         e9/jump $parse-mu-block:line-loop/disp32
3100       }
3101 $parse-mu-block:regular-stmt:
3102       # otherwise
3103       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3104       (append-to-block Heap %edi %eax)
3105       e9/jump loop/disp32
3106     } # end line loop
3107     # decrement *Curr-block-depth
3108     ff 1/subop/decrement *Curr-block-depth
3109     #
3110     (pop *(ebp+0xc))  # => eax
3111     # return result
3112     89/<- %eax 7/r32/edi
3113 $parse-mu-block:end:
3114     # . reclaim locals
3115     81 0/subop/add %esp 0x214/imm32
3116     # . restore registers
3117     5f/pop-to-edi
3118     5b/pop-to-ebx
3119     5a/pop-to-edx
3120     59/pop-to-ecx
3121     # . epilogue
3122     89/<- %esp 5/r32/ebp
3123     5d/pop-to-ebp
3124     c3/return
3125 
3126 $parse-mu-block:abort:
3127     # error("'{' or '}' should be on its own line, but got '")
3128     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3129     (rewind-stream %ecx)
3130     (write-stream 2 %ecx)
3131     (write-buffered Stderr "'\n")
3132     (flush Stderr)
3133     # . syscall(exit, 1)
3134     bb/copy-to-ebx  1/imm32
3135     b8/copy-to-eax  1/imm32/exit
3136     cd/syscall  0x80/imm8
3137     # never gets here
3138 
3139 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
3140     # . prologue
3141     55/push-ebp
3142     89/<- %ebp 4/r32/esp
3143     # . save registers
3144     51/push-ecx
3145     52/push-edx
3146     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
3147     8b/-> *(ebp+8) 0/r32/eax
3148     8b/-> *eax 0/r32/eax  # Function-name
3149     8b/-> *eax 0/r32/eax  # String-length
3150     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
3151     89/<- %ecx 0/r32/eax
3152     # var name/edx: (stream byte n)
3153     29/subtract %esp 1/r32/ecx
3154     ff 6/subop/push %ecx
3155     68/push 0/imm32/read
3156     68/push 0/imm32/write
3157     89/<- %edx 4/r32/esp
3158     (clear-stream %edx)
3159     # eax = fn->name
3160     8b/-> *(ebp+8) 0/r32/eax
3161     8b/-> *eax 0/r32/eax  # Function-name
3162     # construct result using Next-block-index (and increment it)
3163     (write %edx "$")
3164     (write %edx %eax)
3165     (write %edx ":")
3166     (print-int32 %edx *Next-block-index)
3167     ff 0/subop/increment *Next-block-index
3168     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
3169     # . eax = name->write
3170     8b/-> *edx 0/r32/eax
3171     # . edx = name->data
3172     8d/copy-address *(edx+0xc) 2/r32/edx
3173     # . eax = name->write + name->data
3174     01/add %eax 2/r32/edx
3175     # . push {edx, eax}
3176     ff 6/subop/push %eax
3177     ff 6/subop/push %edx
3178     89/<- %eax 4/r32/esp
3179     # var final-name/edx : (addr array byte) = slice-to-string(s)
3180     (slice-to-string Heap %eax)  # => eax
3181     89/<- %edx 0/r32/eax
3182     # set result->var
3183     # . var type/eax: (handle tree type-id) = literal
3184     (allocate Heap *Tree-size)  # => eax
3185     (zero-out %eax *Tree-size)  # default type is 'literal'
3186     # . var result/eax: (handle var) = new-var(final-name, type)
3187     (new-var Heap %edx %eax *Curr-block-depth 0 0)  # => eax
3188 $new-block-name:end:
3189     # . reclaim locals
3190     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
3191     81 0/subop/add %ecx 8/imm32  # slice
3192     01/add %esp 1/r32/ecx
3193     # . restore registers
3194     5a/pop-to-edx
3195     59/pop-to-ecx
3196     # . epilogue
3197     89/<- %esp 5/r32/ebp
3198     5d/pop-to-ebp
3199     c3/return
3200 
3201 check-no-tokens-left:  # line: (addr stream byte)
3202     # . prologue
3203     55/push-ebp
3204     89/<- %ebp 4/r32/esp
3205     # . save registers
3206     50/push-eax
3207     51/push-ecx
3208     # var s/ecx: slice
3209     68/push 0/imm32/end
3210     68/push 0/imm32/start
3211     89/<- %ecx 4/r32/esp
3212     #
3213     (next-word *(ebp+8) %ecx)
3214     # if slice-empty?(s) return
3215     (slice-empty? %ecx)
3216     3d/compare-eax-and 0/imm32
3217     75/jump-if-!= $check-no-tokens-left:end/disp8
3218     # if (slice-starts-with?(s, '#') return
3219     # . eax = *s->start
3220     8b/-> *edx 0/r32/eax
3221     8a/copy-byte *eax 0/r32/AL
3222     81 4/subop/and %eax 0xff/imm32
3223     # . if (eax == '#') continue
3224     3d/compare-eax-and 0x23/imm32/hash
3225     74/jump-if-= $check-no-tokens-left:end/disp8
3226     # abort
3227     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3228     (rewind-stream %ecx)
3229     (write-stream 2 %ecx)
3230     (write-buffered Stderr "'\n")
3231     (flush Stderr)
3232     # . syscall(exit, 1)
3233     bb/copy-to-ebx  1/imm32
3234     b8/copy-to-eax  1/imm32/exit
3235     cd/syscall  0x80/imm8
3236     # never gets here
3237 $check-no-tokens-left:end:
3238     # . reclaim locals
3239     81 0/subop/add %esp 8/imm32
3240     # . restore registers
3241     59/pop-to-ecx
3242     58/pop-to-eax
3243     # . epilogue
3244     89/<- %esp 5/r32/ebp
3245     5d/pop-to-ebp
3246     c3/return
3247 
3248 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3249     # pseudocode:
3250     #   var s: (addr array byte) = slice-to-string(name)
3251     #   var v: (handle var) = new-var(s, 0)
3252     #   v->block-depth = *Curr-block-depth  # containing block depth
3253     #   push(vars, v)
3254     #   result = parse-mu-block(in, vars, fn)
3255     #   pop(vars)
3256     #   result->name = s
3257     #   return result
3258     #
3259     # . prologue
3260     55/push-ebp
3261     89/<- %ebp 4/r32/esp
3262     # . save registers
3263     51/push-ecx
3264     # var s/ecx: (addr array byte) = slice-to-string(name)
3265     (slice-to-string Heap *(ebp+8))  # => eax
3266     89/<- %ecx 0/r32/eax
3267     # var type/eax: (handle tree type-id) = literal
3268     (allocate Heap *Tree-size)  # => eax
3269     (zero-out %eax *Tree-size)  # default type is 'literal'
3270     # var v/ecx: (handle var) = new-var(s, type)
3271     (new-var Heap %ecx %eax *Curr-block-depth 0 0)  # => eax
3272     89/<- %ecx 0/r32/eax
3273     # push(vars, v)
3274     (push *(ebp+0x10) %ecx)
3275     # eax = result
3276     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
3277     # pop the var
3278     50/push-eax
3279     (pop *(ebp+0x10))  # => eax
3280     58/pop-to-eax
3281     # result->tag = named-block
3282     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
3283     # result->var = v
3284     89/<- *(eax+8) 1/r32/ecx  # Block-var
3285 $parse-mu-named-block:end:
3286     # . restore registers
3287     59/pop-to-ecx
3288     # . epilogue
3289     89/<- %esp 5/r32/ebp
3290     5d/pop-to-ebp
3291     c3/return
3292 
3293 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
3294     # . prologue
3295     55/push-ebp
3296     89/<- %ebp 4/r32/esp
3297     # . save registers
3298     51/push-ecx
3299     52/push-edx
3300     # var word-slice/ecx: slice
3301     68/push 0/imm32/end
3302     68/push 0/imm32/start
3303     89/<- %ecx 4/r32/esp
3304     # var v/edx: (handle var) = parse-var-with-type(line)
3305     (next-word *(ebp+8) %ecx)
3306     (parse-var-with-type %ecx *(ebp+8))  # => eax
3307     89/<- %edx 0/r32/eax
3308     # v->stack-offset = *Next-local-stack-offset
3309     8b/-> *Next-local-stack-offset 0/r32/eax
3310     89/<- *(edx+0xc) 0/r32/eax  # Var-stack-offset
3311     # *Next-local-stack-offset -= size-of(v)
3312     (size-of %edx)  # => eax
3313     29/subtract-from *Next-local-stack-offset 0/r32/eax
3314     # v->block-depth = *Curr-block-depth
3315     8b/-> *Curr-block-depth 0/r32/eax
3316     89/<- *(edx+8) 0/r32/eax
3317     #
3318     (push *(ebp+0xc) %edx)
3319     # either v has no register and there's no more to this line
3320     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
3321     3d/compare-eax-and 0/imm32
3322     {
3323       75/jump-if-!= break/disp8
3324       # TODO: ensure that there's nothing else on this line
3325       (new-vardef Heap %edx)  # => eax
3326       eb/jump $parse-mu-var-def:end/disp8
3327     }
3328     # or v has a register and there's more to this line
3329     {
3330       74/jump-if-= break/disp8
3331       # ensure that the next word is '<-'
3332       (next-word *(ebp+8) %ecx)
3333       (slice-equal? %ecx "<-")  # => eax
3334       3d/compare-eax-and 0/imm32
3335       74/jump-if-= $parse-mu-var-def:abort/disp8
3336       #
3337       (new-regvardef Heap %edx)  # => eax
3338       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
3339     }
3340 $parse-mu-var-def:end:
3341     # . reclaim locals
3342     81 0/subop/add %esp 8/imm32
3343     # . restore registers
3344     5a/pop-to-edx
3345     59/pop-to-ecx
3346     # . epilogue
3347     89/<- %esp 5/r32/ebp
3348     5d/pop-to-ebp
3349     c3/return
3350 
3351 $parse-mu-var-def:abort:
3352     (rewind-stream *(ebp+8))
3353     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
3354     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
3355     (flush Stderr)
3356     (write-stream 2 *(ebp+8))
3357     (write-buffered Stderr "'\n")
3358     (flush Stderr)
3359     # . syscall(exit, 1)
3360     bb/copy-to-ebx  1/imm32
3361     b8/copy-to-eax  1/imm32/exit
3362     cd/syscall  0x80/imm8
3363     # never gets here
3364 
3365 test-parse-mu-var-def:
3366     # 'var n: int'
3367     # . prologue
3368     55/push-ebp
3369     89/<- %ebp 4/r32/esp
3370     # setup
3371     (clear-stream _test-input-stream)
3372     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
3373     # var vars/ecx: (stack (addr var) 4)
3374     81 5/subop/subtract %esp 0x10/imm32
3375     68/push 0x10/imm32/length
3376     68/push 0/imm32/top
3377     89/<- %ecx 4/r32/esp
3378     (clear-stack %ecx)
3379     # convert
3380     (parse-mu-var-def _test-input-stream %ecx)  # => eax
3381     # check result
3382     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is vardef
3383     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
3384     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
3385     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
3386     # TODO: ensure stack-offset is -4
3387     # TODO: ensure block-depth is 1
3388     # ensure type is int
3389     8b/-> *(eax+4) 0/r32/eax  # Var-type
3390     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
3391     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
3392     # . epilogue
3393     89/<- %esp 5/r32/ebp
3394     5d/pop-to-ebp
3395     c3/return
3396 
3397 test-parse-mu-reg-var-def:
3398     # 'var n/eax: int <- copy 0'
3399     # . prologue
3400     55/push-ebp
3401     89/<- %ebp 4/r32/esp
3402     # setup
3403     (clear-stream _test-input-stream)
3404     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
3405     # var vars/ecx: (stack (addr var) 4)
3406     81 5/subop/subtract %esp 0x10/imm32
3407     68/push 0x10/imm32/length
3408     68/push 0/imm32/top
3409     89/<- %ecx 4/r32/esp
3410     (clear-stack %ecx)
3411     # convert
3412     (parse-mu-var-def _test-input-stream %ecx)  # => eax
3413     # check result
3414     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is regvardef
3415     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
3416     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
3417     8b/-> *eax 0/r32/eax  # List-value
3418     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
3419     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
3420     # TODO: ensure stack-offset is -4
3421     # TODO: ensure block-depth is 1
3422     # ensure type is int
3423     8b/-> *(eax+4) 0/r32/eax  # Var-type
3424     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
3425     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
3426     # . epilogue
3427     89/<- %esp 5/r32/ebp
3428     5d/pop-to-ebp
3429     c3/return
3430 
3431 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3432     # pseudocode:
3433     #   var name: slice
3434     #   result = allocate(Heap, Stmt-size)
3435     #   if stmt-has-outputs?(line)
3436     #     while true
3437     #       name = next-mu-token(line)
3438     #       if (name == '<-') break
3439     #       assert(is-identifier?(name))
3440     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
3441     #       result->outputs = append(result->outputs, v)
3442     #   add-operation-and-inputs-to-stmt(result, line, vars)
3443     #
3444     # . prologue
3445     55/push-ebp
3446     89/<- %ebp 4/r32/esp
3447     # . save registers
3448     51/push-ecx
3449     57/push-edi
3450     # var name/ecx: slice
3451     68/push 0/imm32/end
3452     68/push 0/imm32/start
3453     89/<- %ecx 4/r32/esp
3454     # result/edi: (handle stmt)
3455     (allocate Heap *Stmt-size)  # => eax
3456     (zero-out %eax *Stmt-size)
3457     89/<- %edi 0/r32/eax
3458     # result->tag = 1/stmt
3459     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
3460     {
3461       (stmt-has-outputs? *(ebp+8))
3462       3d/compare-eax-and 0/imm32
3463       0f 84/jump-if-= break/disp32
3464       {
3465 $parse-mu-stmt:read-outputs:
3466         # name = next-mu-token(line)
3467         (next-mu-token *(ebp+8) %ecx)
3468         # if slice-empty?(word-slice) break
3469         (slice-empty? %ecx)
3470         3d/compare-eax-and 0/imm32
3471         0f 85/jump-if-!= break/disp32
3472         # if (name == "<-") break
3473         (slice-equal? %ecx "<-")
3474         3d/compare-eax-and 0/imm32
3475         75/jump-if-!= break/disp8
3476         # assert(is-identifier?(name))
3477         (is-identifier? %ecx)
3478         3d/compare-eax-and 0/imm32
3479         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
3480         #
3481         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3482         (append-list Heap %eax *(edi+0xc))  # Stmt1-outputs => eax
3483         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
3484         e9/jump loop/disp32
3485       }
3486     }
3487     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
3488 $parse-mu-stmt:end:
3489     # return result
3490     89/<- %eax 7/r32/edi
3491     # . reclaim locals
3492     81 0/subop/add %esp 8/imm32
3493     # . restore registers
3494     5f/pop-to-edi
3495     59/pop-to-ecx
3496     # . epilogue
3497     89/<- %esp 5/r32/ebp
3498     5d/pop-to-ebp
3499     c3/return
3500 
3501 $parse-mu-stmt:abort:
3502     # error("invalid identifier '" name "'\n")
3503     (write-buffered Stderr "invalid identifier '")
3504     (write-slice-buffered Stderr %ecx)
3505     (write-buffered Stderr "'\n")
3506     (flush Stderr)
3507     # . syscall(exit, 1)
3508     bb/copy-to-ebx  1/imm32
3509     b8/copy-to-eax  1/imm32/exit
3510     cd/syscall  0x80/imm8
3511     # never gets here
3512 
3513 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
3514     # pseudocode:
3515     #   stmt->name = slice-to-string(next-mu-token(line))
3516     #   while true
3517     #     name = next-mu-token(line)
3518     #     v = lookup-var-or-literal(name)
3519     #     stmt->inouts = append(stmt->inouts, v)
3520     #
3521     # . prologue
3522     55/push-ebp
3523     89/<- %ebp 4/r32/esp
3524     # . save registers
3525     50/push-eax
3526     51/push-ecx
3527     57/push-edi
3528     # edi = stmt
3529     8b/-> *(ebp+8) 7/r32/edi
3530     # var name/ecx: slice
3531     68/push 0/imm32/end
3532     68/push 0/imm32/start
3533     89/<- %ecx 4/r32/esp
3534 $add-operation-and-inputs-to-stmt:read-operation:
3535     (next-mu-token *(ebp+0xc) %ecx)
3536     (slice-to-string Heap %ecx)  # => eax
3537     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
3538     {
3539 $add-operation-and-inputs-to-stmt:read-inouts:
3540       # name = next-mu-token(line)
3541       (next-mu-token *(ebp+0xc) %ecx)
3542       # if slice-empty?(word-slice) break
3543       (slice-empty? %ecx)  # => eax
3544       3d/compare-eax-and 0/imm32
3545       0f 85/jump-if-!= break/disp32
3546       # if (name == "<-") abort
3547       (slice-equal? %ecx "<-")
3548       3d/compare-eax-and 0/imm32
3549       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
3550       #
3551       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
3552       (append-list Heap %eax *(edi+8))  # Stmt1-inouts or Regvardef-inouts => eax
3553       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
3554       e9/jump loop/disp32
3555     }
3556 $add-operation-and-inputs-to-stmt:end:
3557     # . reclaim locals
3558     81 0/subop/add %esp 8/imm32
3559     # . restore registers
3560     5f/pop-to-edi
3561     59/pop-to-ecx
3562     58/pop-to-eax
3563     # . epilogue
3564     89/<- %esp 5/r32/ebp
3565     5d/pop-to-ebp
3566     c3/return
3567 
3568 $add-operation-and-inputs-to-stmt:abort:
3569     # error("invalid statement '" line "'\n")
3570     (rewind-stream *(ebp+8))
3571     (write-buffered Stderr "invalid identifier '")
3572     (flush Stderr)
3573     (write-stream 2 *(ebp+8))
3574     (write-buffered Stderr "'\n")
3575     (flush Stderr)
3576     # . syscall(exit, 1)
3577     bb/copy-to-ebx  1/imm32
3578     b8/copy-to-eax  1/imm32/exit
3579     cd/syscall  0x80/imm8
3580     # never gets here
3581 
3582 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
3583     # . prologue
3584     55/push-ebp
3585     89/<- %ebp 4/r32/esp
3586     # . save registers
3587     51/push-ecx
3588     # var word-slice/ecx: slice
3589     68/push 0/imm32/end
3590     68/push 0/imm32/start
3591     89/<- %ecx 4/r32/esp
3592     # result = false
3593     b8/copy-to-eax 0/imm32/false
3594     (rewind-stream *(ebp+8))
3595     {
3596       (next-mu-token *(ebp+8) %ecx)
3597       # if slice-empty?(word-slice) break
3598       (slice-empty? %ecx)
3599       3d/compare-eax-and 0/imm32
3600       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
3601       0f 85/jump-if-!= break/disp32
3602       # if slice-starts-with?(word-slice, '#') break
3603       # . eax = *word-slice->start
3604       8b/-> *ecx 0/r32/eax
3605       8a/copy-byte *eax 0/r32/AL
3606       81 4/subop/and %eax 0xff/imm32
3607       # . if (eax == '#') break
3608       3d/compare-eax-and 0x23/imm32/hash
3609       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
3610       0f 84/jump-if-= break/disp32
3611       # if slice-equal?(word-slice, '<-') return true
3612       (slice-equal? %ecx "<-")
3613       3d/compare-eax-and 0/imm32
3614       74/jump-if-= loop/disp8
3615       b8/copy-to-eax 1/imm32/true
3616     }
3617 $stmt-has-outputs:end:
3618     (rewind-stream *(ebp+8))
3619     # . reclaim locals
3620     81 0/subop/add %esp 8/imm32
3621     # . restore registers
3622     59/pop-to-ecx
3623     # . epilogue
3624     89/<- %esp 5/r32/ebp
3625     5d/pop-to-ebp
3626     c3/return
3627 
3628 # if 'name' starts with a digit, create a new literal var for it
3629 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
3630 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
3631     # . prologue
3632     55/push-ebp
3633     89/<- %ebp 4/r32/esp
3634     # . save registers
3635     51/push-ecx
3636     56/push-esi
3637     # esi = name
3638     8b/-> *(ebp+8) 6/r32/esi
3639     # if slice-empty?(name) abort
3640     (slice-empty? %esi)  # => eax
3641     3d/compare-eax-and 0/imm32
3642     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
3643     # var c/ecx: byte = *name->start
3644     8b/-> *esi 1/r32/ecx
3645     8a/copy-byte *ecx 1/r32/CL
3646     81 4/subop/and %ecx 0xff/imm32
3647     # if is-decimal-digit?(c) return new var(name)
3648     {
3649       (is-decimal-digit? %ecx)  # => eax
3650       81 7/subop/compare %eax 0/imm32
3651       74/jump-if-= break/disp8
3652       (new-literal-integer Heap %esi)  # => eax
3653       eb/jump $lookup-var-or-literal:end/disp8
3654     }
3655     # else if (c == '"') return new var(name)
3656     {
3657       81 7/subop/compare %ecx 0x22/imm32/dquote
3658       75/jump-if-!= break/disp8
3659       (new-literal-string Heap %esi)  # => eax
3660       eb/jump $lookup-var-or-literal:end/disp8
3661     }
3662     # otherwise return lookup-var(name, vars)
3663     {
3664       (lookup-var %esi *(ebp+0xc))  # => eax
3665     }
3666 $lookup-var-or-literal:end:
3667     # . restore registers
3668     5e/pop-to-esi
3669     59/pop-to-ecx
3670     # . epilogue
3671     89/<- %esp 5/r32/ebp
3672     5d/pop-to-ebp
3673     c3/return
3674 
3675 $lookup-var-or-literal:abort:
3676     (write-buffered Stderr "empty variable!")
3677     (flush Stderr)
3678     # . syscall(exit, 1)
3679     bb/copy-to-ebx  1/imm32
3680     b8/copy-to-eax  1/imm32/exit
3681     cd/syscall  0x80/imm8
3682     # never gets here
3683 
3684 # return first 'name' from the top (back) of 'vars' and abort if not found
3685 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
3686     # . prologue
3687     55/push-ebp
3688     89/<- %ebp 4/r32/esp
3689     # var target/eax: (handle array byte) = slice-to-string(name)
3690     (slice-to-string Heap *(ebp+8))  # => eax
3691     #
3692     (lookup-var-helper %eax *(ebp+0xc))  # => eax
3693     # if (result == 0) abort
3694     3d/compare-eax-and 0/imm32
3695     74/jump-if-= $lookup-var:abort/disp8
3696 $lookup-var:end:
3697     # . epilogue
3698     89/<- %esp 5/r32/ebp
3699     5d/pop-to-ebp
3700     c3/return
3701 
3702 $lookup-var:abort:
3703     (write-buffered Stderr "unknown variable '")
3704     (write-slice-buffered Stderr *(ebp+8))
3705     (write-buffered Stderr "'\n")
3706     (flush Stderr)
3707     # . syscall(exit, 1)
3708     bb/copy-to-ebx  1/imm32
3709     b8/copy-to-eax  1/imm32/exit
3710     cd/syscall  0x80/imm8
3711     # never gets here
3712 
3713 # return first 'name' from the top (back) of 'vars', and 0/null if not found
3714 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
3715     # pseudocode:
3716     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
3717     #   var min = vars->data
3718     #   while curr >= min
3719     #     var v: (handle var) = *curr
3720     #     if v->name == name
3721     #       return v
3722     #   return 0
3723     #
3724     # . prologue
3725     55/push-ebp
3726     89/<- %ebp 4/r32/esp
3727     # . save registers
3728     52/push-edx
3729     53/push-ebx
3730     56/push-esi
3731     # esi = vars
3732     8b/-> *(ebp+0xc) 6/r32/esi
3733     # ebx = vars->top
3734     8b/-> *esi 3/r32/ebx
3735     # if (vars->top > vars->length) abort
3736     3b/compare 0/r32/eax *(esi+4)
3737     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
3738     # var min/edx: (addr handle var) = vars->data
3739     8d/copy-address *(esi+8) 2/r32/edx
3740     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
3741     81 5/subop/subtract %ebx 4/imm32
3742     8d/copy-address *(esi+ebx+8) 3/r32/ebx
3743     {
3744       # if (curr < min) return 0
3745       39/compare %ebx 2/r32/edx
3746       b8/copy-to-eax 0/imm32
3747       0f 82/jump-if-addr< break/disp32
3748       # var v/eax: (handle var) = *curr
3749       8b/-> *ebx 0/r32/eax
3750       # if (v->name == name) return v
3751       (string-equal? *eax *(ebp+8))  # Var-name
3752       3d/compare-eax-and 0/imm32
3753       8b/-> *ebx 0/r32/eax
3754       75/jump-if-!= break/disp8
3755       # curr -= 4
3756       81 5/subop/subtract %ebx 4/imm32
3757       e9/jump loop/disp32
3758     }
3759 $lookup-var-helper:end:
3760     # . restore registers
3761     5e/pop-to-esi
3762     5b/pop-to-ebx
3763     5a/pop-to-edx
3764     # . epilogue
3765     89/<- %esp 5/r32/ebp
3766     5d/pop-to-ebp
3767     c3/return
3768 
3769 $lookup-var-helper:error1:
3770     (write-buffered Stderr "malformed stack when looking up '")
3771     (write-slice-buffered Stderr *(ebp+8))
3772     (write-buffered Stderr "'\n")
3773     (flush Stderr)
3774     # . syscall(exit, 1)
3775     bb/copy-to-ebx  1/imm32
3776     b8/copy-to-eax  1/imm32/exit
3777     cd/syscall  0x80/imm8
3778     # never gets here
3779 
3780 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
3781 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
3782     # . prologue
3783     55/push-ebp
3784     89/<- %ebp 4/r32/esp
3785     # . save registers
3786     51/push-ecx
3787     # var target/ecx: (handle array byte) = slice-to-string(name)
3788     (slice-to-string Heap *(ebp+8))  # => eax
3789     89/<- %ecx 0/r32/eax
3790     #
3791     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
3792     {
3793       # if (result != 0) return
3794       3d/compare-eax-and 0/imm32
3795       75/jump-if-!= break/disp8
3796       # if name is one of fn's outputs, return it
3797       {
3798         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
3799         3d/compare-eax-and 0/imm32
3800         # otherwise abort
3801         0f 84/jump-if-!= $lookup-var:abort/disp32
3802       }
3803     }
3804 $lookup-or-define-var:end:
3805     # . restore registers
3806     59/pop-to-ecx
3807     # . epilogue
3808     89/<- %esp 5/r32/ebp
3809     5d/pop-to-ebp
3810     c3/return
3811 
3812 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) => result/eax: (handle var)
3813     # . prologue
3814     55/push-ebp
3815     89/<- %ebp 4/r32/esp
3816     # . save registers
3817     51/push-ecx
3818     # var curr/ecx: (handle list var) = fn->outputs
3819     8b/-> *(ebp+8) 1/r32/ecx
3820     8b/-> *(ecx+0xc) 1/r32/ecx
3821     # while curr != null
3822     {
3823       81 7/subop/compare %ecx 0/imm32
3824       74/jump-if-= break/disp8
3825       # var v: (handle var) = *curr
3826       8b/-> *ecx 0/r32/eax  # List-value
3827       # if (curr->name == name) return curr
3828       50/push-eax
3829       (string-equal? *eax *(ebp+0xc))
3830       3d/compare-eax-and 0/imm32
3831       58/pop-to-eax
3832       75/jump-if-!= $find-in-function-outputs:end/disp8
3833       # curr = curr->next
3834       8b/-> *(ecx+4) 1/r32/ecx  # List-next
3835       eb/jump loop/disp8
3836     }
3837     b8/copy-to-eax 0/imm32
3838 $find-in-function-outputs:end:
3839     # . restore registers
3840     59/pop-to-ecx
3841     # . epilogue
3842     89/<- %esp 5/r32/ebp
3843     5d/pop-to-ebp
3844     c3/return
3845 
3846 test-parse-mu-stmt:
3847     # . prologue
3848     55/push-ebp
3849     89/<- %ebp 4/r32/esp
3850     # setup
3851     (clear-stream _test-input-stream)
3852     (write _test-input-stream "increment n\n")
3853     # var vars/ecx: (stack (addr var) 4)
3854     81 5/subop/subtract %esp 0x10/imm32
3855     68/push 0x10/imm32/length
3856     68/push 0/imm32/top
3857     89/<- %ecx 4/r32/esp
3858     (clear-stack %ecx)
3859     # var v/edx: var
3860     81 5/subop/subtract %esp 0x14/imm32  # Var-size
3861     89/<- %edx 4/r32/esp
3862     (zero-out %edx 0x14)  # Var-size
3863     # v->name = "n"
3864     c7 0/subop/copy *edx "n"/imm32  # Var-name
3865     #
3866     (push %ecx %edx)
3867     # convert
3868     (parse-mu-stmt _test-input-stream %ecx)  # => eax
3869     # check result
3870     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
3871     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
3872     # edx: (handle list var) = result->inouts
3873     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
3874     # ebx: (handle var) = result->inouts->value
3875     8b/-> *edx 3/r32/ebx  # List-value
3876     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
3877     # . epilogue
3878     89/<- %esp 5/r32/ebp
3879     5d/pop-to-ebp
3880     c3/return
3881 
3882 test-parse-mu-stmt-with-comma:
3883     # . prologue
3884     55/push-ebp
3885     89/<- %ebp 4/r32/esp
3886     # setup
3887     (clear-stream _test-input-stream)
3888     (write _test-input-stream "copy-to n, 3\n")
3889     # var vars/ecx: (stack (addr var) 4)
3890     81 5/subop/subtract %esp 0x10/imm32
3891     68/push 0x10/imm32/length
3892     68/push 0/imm32/top
3893     89/<- %ecx 4/r32/esp
3894     (clear-stack %ecx)
3895     # var v/edx: var
3896     81 5/subop/subtract %esp 0x14/imm32  # Var-size
3897     89/<- %edx 4/r32/esp
3898     (zero-out %edx 0x14)  # Var-size
3899     # v->name = "n"
3900     c7 0/subop/copy *edx "n"/imm32  # Var-name
3901     #
3902     (push %ecx %edx)
3903     # convert
3904     (parse-mu-stmt _test-input-stream %ecx)  # => eax
3905     # check result
3906     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
3907     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
3908     # edx: (handle list var) = result->inouts
3909     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
3910     # ebx: (handle var) = result->inouts->value
3911     8b/-> *edx 3/r32/ebx  # List-value
3912     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
3913     # . epilogue
3914     89/<- %esp 5/r32/ebp
3915     5d/pop-to-ebp
3916     c3/return
3917 
3918 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)
3919     # . prologue
3920     55/push-ebp
3921     89/<- %ebp 4/r32/esp
3922     # . save registers
3923     51/push-ecx
3924     #
3925     (allocate *(ebp+8) *Function-size)  # => eax
3926     8b/-> *(ebp+0xc) 1/r32/ecx
3927     89/<- *eax 1/r32/ecx  # Function-name
3928     8b/-> *(ebp+0x10) 1/r32/ecx
3929     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
3930     8b/-> *(ebp+0x14) 1/r32/ecx
3931     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
3932     8b/-> *(ebp+0x18) 1/r32/ecx
3933     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
3934     8b/-> *(ebp+0x1c) 1/r32/ecx
3935     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
3936     8b/-> *(ebp+0x20) 1/r32/ecx
3937     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
3938 $new-function:end:
3939     # . restore registers
3940     59/pop-to-ecx
3941     # . epilogue
3942     89/<- %esp 5/r32/ebp
3943     5d/pop-to-ebp
3944     c3/return
3945 
3946 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)
3947     # . prologue
3948     55/push-ebp
3949     89/<- %ebp 4/r32/esp
3950     # . save registers
3951     51/push-ecx
3952     #
3953     (allocate *(ebp+8) *Var-size)  # => eax
3954     8b/-> *(ebp+0xc) 1/r32/ecx
3955     89/<- *eax 1/r32/ecx  # Var-name
3956     8b/-> *(ebp+0x10) 1/r32/ecx
3957     89/<- *(eax+4) 1/r32/ecx  # Var-type
3958     8b/-> *(ebp+0x14) 1/r32/ecx
3959     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
3960     8b/-> *(ebp+0x18) 1/r32/ecx
3961     89/<- *(eax+0xc) 1/r32/ecx  # Var-stack-offset
3962     8b/-> *(ebp+0x1c) 1/r32/ecx
3963     89/<- *(eax+0x10) 1/r32/ecx  # Var-register
3964 $new-var:end:
3965     # . restore registers
3966     59/pop-to-ecx
3967     # . epilogue
3968     89/<- %esp 5/r32/ebp
3969     5d/pop-to-ebp
3970     c3/return
3971 
3972 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
3973     # . prologue
3974     55/push-ebp
3975     89/<- %ebp 4/r32/esp
3976     # . save registers
3977     51/push-ecx
3978     # if (!is-hex-int?(name)) abort
3979     (is-hex-int? *(ebp+0xc))  # => eax
3980     3d/compare-eax-and 0/imm32
3981     0f 84/jump-if-= $new-literal-integer:abort/disp32
3982     # var s/ecx: (addr array byte)
3983     (slice-to-string Heap *(ebp+0xc))  # => eax
3984     89/<- %ecx 0/r32/eax
3985     # result/ecx = new var(s)
3986     (allocate *(ebp+8) *Var-size)  # => eax
3987     (zero-out %eax *Var-size)
3988     89/<- *eax 1/r32/ecx  # Var-name
3989     89/<- %ecx 0/r32/eax
3990     # result->type = new type()
3991     (allocate *(ebp+8) *Tree-size)  # => eax
3992     (zero-out %eax *Tree-size)  # default type is 'literal'
3993     89/<- *(ecx+4) 0/r32/eax  # Var-type
3994     # move result to eax
3995     89/<- %eax 1/r32/ecx
3996 $new-literal-integer:end:
3997     # . restore registers
3998     59/pop-to-ecx
3999     # . epilogue
4000     89/<- %esp 5/r32/ebp
4001     5d/pop-to-ebp
4002     c3/return
4003 
4004 $new-literal-integer:abort:
4005     (write-buffered Stderr "variable cannot begin with a digit '")
4006     (write-slice-buffered Stderr *(ebp+0xc))
4007     (write-buffered Stderr "'\n")
4008     (flush Stderr)
4009     # . syscall(exit, 1)
4010     bb/copy-to-ebx  1/imm32
4011     b8/copy-to-eax  1/imm32/exit
4012     cd/syscall  0x80/imm8
4013     # never gets here
4014 
4015 new-literal-string:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4016     # . prologue
4017     55/push-ebp
4018     89/<- %ebp 4/r32/esp
4019     # . save registers
4020     51/push-ecx
4021     # var s/ecx: (addr array byte)
4022     (slice-to-string Heap *(ebp+0xc))  # => eax
4023     89/<- %ecx 0/r32/eax
4024     # result/ecx = new var(s)
4025     (allocate *(ebp+8) *Var-size)  # => eax
4026     (zero-out %eax *Var-size)
4027     89/<- *eax 1/r32/ecx  # Var-name
4028     89/<- %ecx 0/r32/eax
4029     # result->type = new type()
4030     (allocate *(ebp+8) *Tree-size)  # => eax
4031     (zero-out %eax *Tree-size)  # default type is 'literal'
4032     89/<- *(ecx+4) 0/r32/eax  # Var-type
4033     # move result to eax
4034     89/<- %eax 1/r32/ecx
4035 $new-literal-string:end:
4036     # . restore registers
4037     59/pop-to-ecx
4038     # . epilogue
4039     89/<- %esp 5/r32/ebp
4040     5d/pop-to-ebp
4041     c3/return
4042 
4043 new-label:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4044     # . prologue
4045     55/push-ebp
4046     89/<- %ebp 4/r32/esp
4047     # . save registers
4048     51/push-ecx
4049     # var s/ecx: (addr array byte)
4050     (slice-to-string Heap *(ebp+0xc))  # => eax
4051     89/<- %ecx 0/r32/eax
4052     #
4053     (allocate *(ebp+8) *Var-size)  # => eax
4054     89/<- *eax 1/r32/ecx  # Var-name
4055     89/<- %ecx 0/r32/eax
4056     (allocate *(ebp+8) *Tree-size)  # => eax
4057     (zero-out %eax *Tree-size)  # labels are literals
4058     89/<- *(ecx+4) 0/r32/eax  # Var-type
4059     89/<- %eax 1/r32/ecx
4060     c7 0/subop/copy *(eax+8) 0/imm32  # Var-block-depth
4061     c7 0/subop/copy *(eax+0xc) 0/imm32  # Var-stack-offset
4062     c7 0/subop/copy *(eax+0x10) 0/imm32  # Var-register
4063 $new-label:end:
4064     # . restore registers
4065     59/pop-to-ecx
4066     # . epilogue
4067     89/<- %esp 5/r32/ebp
4068     5d/pop-to-ebp
4069     c3/return
4070 
4071 new-block:  # ad: (addr allocation-descriptor), data: (handle list statement) -> result/eax: (handle statement)
4072     # . prologue
4073     55/push-ebp
4074     89/<- %ebp 4/r32/esp
4075     # . save registers
4076     51/push-ecx
4077     #
4078     (allocate *(ebp+8) *Stmt-size)  # => eax
4079     (zero-out %eax *Stmt-size)
4080     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
4081     8b/-> *(ebp+0xc) 1/r32/ecx
4082     89/<- *(eax+4) 1/r32/ecx  # Block-statements
4083 $new-block:end:
4084     # . restore registers
4085     59/pop-to-ecx
4086     # . epilogue
4087     89/<- %esp 5/r32/ebp
4088     5d/pop-to-ebp
4089     c3/return
4090 
4091 new-vardef:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement)
4092     # . prologue
4093     55/push-ebp
4094     89/<- %ebp 4/r32/esp
4095     # . save registers
4096     51/push-ecx
4097     #
4098     (allocate *(ebp+8) *Stmt-size)  # => eax
4099     (zero-out %eax *Stmt-size)
4100     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
4101     # result->var = var
4102     8b/-> *(ebp+0xc) 1/r32/ecx
4103     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
4104 $new-vardef:end:
4105     # . restore registers
4106     59/pop-to-ecx
4107     # . epilogue
4108     89/<- %esp 5/r32/ebp
4109     5d/pop-to-ebp
4110     c3/return
4111 
4112 new-regvardef:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement)
4113     # . prologue
4114     55/push-ebp
4115     89/<- %ebp 4/r32/esp
4116     # . save registers
4117     51/push-ecx
4118     57/push-edi
4119     # ecx = var
4120     8b/-> *(ebp+0xc) 1/r32/ecx
4121     # edi = result
4122     (allocate *(ebp+8) *Stmt-size)  # => eax
4123     89/<- %edi 0/r32/eax
4124     (zero-out %edi *Stmt-size)
4125     # set tag
4126     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
4127     # set output
4128     (append-list Heap %ecx *(edi+0xc))  # Regvardef-outputs => eax
4129     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
4130 $new-regvardef:end:
4131     89/<- %eax 7/r32/edi
4132     # . restore registers
4133     5f/pop-to-edi
4134     59/pop-to-ecx
4135     # . epilogue
4136     89/<- %esp 5/r32/ebp
4137     5d/pop-to-ebp
4138     c3/return
4139 
4140 new-list:  # ad: (addr allocation-descriptor), value: _type, next: (handle list _type) -> result/eax: (handle list _type)
4141     # . prologue
4142     55/push-ebp
4143     89/<- %ebp 4/r32/esp
4144     # . save registers
4145     51/push-ecx
4146     #
4147     (allocate *(ebp+8) *List-size)  # => eax
4148     8b/-> *(ebp+0xc) 1/r32/ecx
4149     89/<- *eax 1/r32/ecx  # List-value
4150     8b/-> *(ebp+0x10) 1/r32/ecx
4151     89/<- *(eax+4) 1/r32/ecx  # List-next
4152 $new-list:end:
4153     # . restore registers
4154     59/pop-to-ecx
4155     # . epilogue
4156     89/<- %esp 5/r32/ebp
4157     5d/pop-to-ebp
4158     c3/return
4159 
4160 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
4161     # . prologue
4162     55/push-ebp
4163     89/<- %ebp 4/r32/esp
4164     # . save registers
4165     51/push-ecx
4166     #
4167     (allocate *(ebp+8) *List-size)  # => eax
4168     8b/-> *(ebp+0xc) 1/r32/ecx
4169     89/<- *eax 1/r32/ecx  # List-value
4170     # if (list == null) return result
4171     81 7/subop/compare *(ebp+0x10) 0/imm32
4172     74/jump-if-= $new-list:end/disp8
4173     # otherwise append
4174     # var curr/ecx = list
4175     8b/-> *(ebp+0x10) 1/r32/ecx
4176     # while (curr->next != null) curr = curr->next
4177     {
4178       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
4179       74/jump-if-= break/disp8
4180       # curr = curr->next
4181       8b/-> *(ecx+4) 1/r32/ecx
4182       eb/jump loop/disp8
4183     }
4184     # curr->next = result
4185     89/<- *(ecx+4) 0/r32/eax
4186     # return list
4187     8b/-> *(ebp+0x10) 0/r32/eax
4188 $append-list:end:
4189     # . restore registers
4190     59/pop-to-ecx
4191     # . epilogue
4192     89/<- %esp 5/r32/ebp
4193     5d/pop-to-ebp
4194     c3/return
4195 
4196 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
4197     # . prologue
4198     55/push-ebp
4199     89/<- %ebp 4/r32/esp
4200     # . save registers
4201     56/push-esi
4202     # esi = block
4203     8b/-> *(ebp+0xc) 6/r32/esi
4204     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-statements
4205     89/<- *(esi+4) 0/r32/eax  # Block-statements
4206 $append-to-block:end:
4207     # . restore registers
4208     5e/pop-to-esi
4209     # . epilogue
4210     89/<- %esp 5/r32/ebp
4211     5d/pop-to-ebp
4212     c3/return
4213 
4214 #######################################################
4215 # Type-checking
4216 #######################################################
4217 
4218 check-mu-types:
4219     # . prologue
4220     55/push-ebp
4221     89/<- %ebp 4/r32/esp
4222     #
4223 $check-mu-types:end:
4224     # . epilogue
4225     89/<- %esp 5/r32/ebp
4226     5d/pop-to-ebp
4227     c3/return
4228 
4229 size-of:  # v: (addr var) -> result/eax: int
4230     # . prologue
4231     55/push-ebp
4232     89/<- %ebp 4/r32/esp
4233     # if v is a literal, return 0
4234     8b/-> *(ebp+8) 0/r32/eax
4235     8b/-> *(eax+4) 0/r32/eax  # Var-type
4236     81 7/subop/compare *eax 0/imm32  # Tree-left
4237     b8/copy-to-eax 0/imm32
4238     74/jump-if-= $size-of:end/disp8
4239     # hard-coded since we only support 'int' types for now
4240     b8/copy-to-eax 4/imm32
4241 $size-of:end:
4242     # . epilogue
4243     89/<- %esp 5/r32/ebp
4244     5d/pop-to-ebp
4245     c3/return
4246 
4247 == data
4248 
4249 # not yet used, but it will be
4250 Type-size:  # (stream int)
4251   0x18/imm32/write
4252   0/imm32/read
4253   0x100/imm32/length
4254   # data
4255   4/imm32  # literal
4256   4/imm32  # int
4257   4/imm32  # addr
4258   0/imm32  # array (logic elsewhere)
4259   8/imm32  # handle (fat pointer)
4260   4/imm32  # bool
4261   0/imm32
4262   0/imm32
4263   # 0x20
4264   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4265   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4266   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4267   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4268   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4269   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4270   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4271 
4272 == code
4273 
4274 #######################################################
4275 # Code-generation
4276 #######################################################
4277 
4278 emit-subx:  # out: (addr buffered-file)
4279     # . prologue
4280     55/push-ebp
4281     89/<- %ebp 4/r32/esp
4282     # . save registers
4283     50/push-eax
4284     51/push-ecx
4285     57/push-edi
4286     # edi = out
4287     8b/-> *(ebp+8) 7/r32/edi
4288     # var curr/ecx: (handle function) = *Program
4289     8b/-> *Program 1/r32/ecx
4290     {
4291       # if (curr == null) break
4292       81 7/subop/compare %ecx 0/imm32
4293       0f 84/jump-if-= break/disp32
4294       (emit-subx-function %edi %ecx)
4295       # curr = curr->next
4296       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
4297       e9/jump loop/disp32
4298     }
4299 $emit-subx:end:
4300     # . restore registers
4301     5f/pop-to-edi
4302     59/pop-to-ecx
4303     58/pop-to-eax
4304     # . epilogue
4305     89/<- %esp 5/r32/ebp
4306     5d/pop-to-ebp
4307     c3/return
4308 
4309 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
4310     # . prologue
4311     55/push-ebp
4312     89/<- %ebp 4/r32/esp
4313     # . save registers
4314     50/push-eax
4315     51/push-ecx
4316     52/push-edx
4317     57/push-edi
4318     # edi = out
4319     8b/-> *(ebp+8) 7/r32/edi
4320     # ecx = f
4321     8b/-> *(ebp+0xc) 1/r32/ecx
4322     # var vars/edx: (stack (addr var) 256)
4323     81 5/subop/subtract %esp 0x400/imm32
4324     68/push 0x400/imm32/length
4325     68/push 0/imm32/top
4326     89/<- %edx 4/r32/esp
4327     #
4328     (write-buffered %edi *ecx)
4329     (write-buffered %edi ":\n")
4330     # Important: each block's depth during code-generation should be identical
4331     # to what it was during parsing.
4332     c7 0/subop/copy *Curr-block-depth 1/imm32
4333     (emit-subx-prologue %edi)
4334     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
4335     (emit-subx-epilogue %edi)
4336 $emit-subx-function:end:
4337     # . reclaim locals
4338     81 0/subop/add %esp 408/imm32
4339     # . restore registers
4340     5f/pop-to-edi
4341     5a/pop-to-edx
4342     59/pop-to-ecx
4343     58/pop-to-eax
4344     # . epilogue
4345     89/<- %esp 5/r32/ebp
4346     5d/pop-to-ebp
4347     c3/return
4348 
4349 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
4350     # . prologue
4351     55/push-ebp
4352     89/<- %ebp 4/r32/esp
4353     # . save registers
4354     50/push-eax
4355     51/push-ecx
4356     52/push-edx
4357     56/push-esi
4358     # esi = stmts
4359     8b/-> *(ebp+0xc) 6/r32/esi
4360     # var var-seen?/edx: boolean <- copy false
4361     ba/copy-to-edx 0/imm32/false
4362     #
4363     {
4364 $emit-subx-stmt-list:loop:
4365       81 7/subop/compare %esi 0/imm32
4366       0f 84/jump-if-= break/disp32
4367       # var curr-stmt/ecx = stmts->value
4368       8b/-> *esi 1/r32/ecx  # List-value
4369       {
4370 $emit-subx-stmt-list:check-for-block:
4371         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
4372         75/jump-if-!= break/disp8
4373 $emit-subx-stmt-list:block:
4374         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
4375       }
4376       {
4377 $emit-subx-stmt-list:check-for-stmt:
4378         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
4379         0f 85/jump-if-!= break/disp32
4380         {
4381           # if !var-seen? break
4382           81 7/subop/compare %edx 0/imm32/false
4383           0f 84/jump-if-= break/disp32
4384 $emit-subx-stmt-list:check-for-break:
4385           # if (!string-starts-with?(var->operation, "break")) break
4386           (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
4387           3d/compare-eax-and 0/imm32
4388           0f 84/jump-if-= break/disp32
4389           81 7/subop/compare *(ecx+8) 0/imm32  # Stmt1-inouts
4390           {
4391             0f 85/jump-if-!= break/disp32
4392 $emit-subx-stmt-list:zero-arg-break:
4393             # create a new block for the remaining statements
4394             (emit-indent *(ebp+8) *Curr-block-depth)
4395             (write-buffered *(ebp+8) "{\n")
4396             ff 0/subop/increment *Curr-block-depth
4397             (emit-subx-stmt-list *(ebp+8) %esi *(ebp+0x10))
4398             ff 1/subop/decrement *Curr-block-depth
4399             (emit-indent *(ebp+8) *Curr-block-depth)
4400             (write-buffered *(ebp+8) "}\n")
4401             # return $emit-subx-stmt-list
4402             e9/jump $emit-subx-stmt-list:cleanup/disp32
4403           }
4404           {
4405             0f 84/jump-if-= break/disp32
4406 $emit-subx-stmt-list:one-arg-break:
4407             # TODO
4408             # continue
4409             e9/jump $emit-subx-stmt-list:continue/disp32
4410           }
4411         }
4412         {
4413           # if !var-seen? break
4414           81 7/subop/compare %edx 0/imm32/false
4415           0f 84/jump-if-= break/disp32
4416 $emit-subx-stmt-list:check-for-loop:
4417           # if (!string-starts-with?(var->operation, "loop")) break
4418           (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
4419           3d/compare-eax-and 0/imm32
4420           0f 84/jump-if-= break/disp32
4421           81 7/subop/compare *(ecx+8) 0/imm32  # Stmt1-inouts
4422           {
4423             0f 85/jump-if-!= break/disp32
4424 $emit-subx-stmt-list:zero-arg-loop:
4425             # var old-block-depth/eax: int = Curr-block-depth - 1
4426             8b/-> *Curr-block-depth 0/r32/eax
4427             # cleanup prologue
4428             (emit-indent *(ebp+8) *Curr-block-depth)
4429             (write-buffered *(ebp+8) "{\n")
4430             ff 0/subop/increment *Curr-block-depth
4431             #
4432             (emit-reverse-break *(ebp+8) %ecx)
4433             # clean up until old block depth
4434             (emit-cleanup-code *(ebp+8) *(ebp+0x10) %eax)
4435             # var target-block-depth/eax: int = Curr-block-depth - 1
4436             48/decrement-eax
4437             # emit jump to target block
4438             (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %eax)
4439             # cleanup epilogue
4440             ff 1/subop/decrement *Curr-block-depth
4441             (emit-indent *(ebp+8) *Curr-block-depth)
4442             (write-buffered *(ebp+8) "}\n")
4443             # continue
4444             e9/jump $emit-subx-stmt-list:continue/disp32
4445           }
4446           {
4447             0f 84/jump-if-= break/disp32
4448 $emit-subx-stmt-list:one-arg-loop:
4449             # TODO
4450             # continue
4451             e9/jump $emit-subx-stmt-list:continue/disp32
4452           }
4453         }
4454 $emit-subx-stmt-list:stmt:
4455         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
4456       }
4457       {
4458 $emit-subx-stmt-list:check-for-vardef:
4459         81 7/subop/compare *ecx 2/imm32/vardef  # Stmt-tag
4460         75/jump-if-!= break/disp8
4461 $emit-subx-stmt-list:vardef:
4462         (emit-subx-var-def *(ebp+8) %ecx)
4463         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
4464         # var-seen? = true
4465         ba/copy-to-edx 1/imm32/true
4466       }
4467       {
4468 $emit-subx-stmt-list:check-for-regvardef:
4469         81 7/subop/compare *ecx 3/imm32/regvardef  # Stmt-tag
4470         0f 85/jump-if-!= break/disp32
4471 $emit-subx-stmt-list:regvardef:
4472         # TODO: ensure that there's exactly one output
4473         # var output/eax: (handle var) = curr-stmt->outputs->value
4474         8b/-> *(ecx+0xc) 0/r32/eax
4475         8b/-> *eax 0/r32/eax
4476         # ensure that output is in a register
4477         81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
4478         0f 84/jump-if-= $emit-subx-stmt-list:abort-regvardef-without-register/disp32
4479         # emit spill
4480         (emit-indent *(ebp+8) *Curr-block-depth)
4481         (write-buffered *(ebp+8) "ff 6/subop/push %")
4482         (write-buffered *(ebp+8) *(eax+0x10))
4483         (write-buffered *(ebp+8) Newline)
4484         # register variable definition
4485         (push *(ebp+0x10) %eax)
4486         # emit the instruction as usual
4487         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
4488         # var-seen? = true
4489         ba/copy-to-edx 1/imm32/true
4490       }
4491 $emit-subx-stmt-list:continue:
4492       # TODO: raise an error on unrecognized Stmt-tag
4493       8b/-> *(esi+4) 6/r32/esi  # List-next
4494       e9/jump loop/disp32
4495     }
4496 $emit-subx-stmt-list:cleanup:
4497     (emit-cleanup-code *(ebp+8) *(ebp+0x10) *Curr-block-depth)
4498     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
4499 $emit-subx-stmt-list:end:
4500     # . restore registers
4501     5e/pop-to-esi
4502     5a/pop-to-edx
4503     59/pop-to-ecx
4504     58/pop-to-eax
4505     # . epilogue
4506     89/<- %esp 5/r32/ebp
4507     5d/pop-to-ebp
4508     c3/return
4509 
4510 $emit-subx-stmt-list:abort-regvardef-without-register:
4511     # error("var '" var->name "' initialized from an instruction must live in a register\n")
4512     (write-buffered Stderr "var '")
4513     (write-buffered Stderr *eax)  # Var-name
4514     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
4515     (flush Stderr)
4516     # . syscall(exit, 1)
4517     bb/copy-to-ebx  1/imm32
4518     b8/copy-to-eax  1/imm32/exit
4519     cd/syscall  0x80/imm8
4520     # never gets here
4521 
4522 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
4523     # . prologue
4524     55/push-ebp
4525     89/<- %ebp 4/r32/esp
4526     # . save registers
4527     50/push-eax
4528     # eax = stmt
4529     8b/-> *(ebp+0xc) 0/r32/eax
4530     #
4531     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
4532     (emit-indent *(ebp+8) *Curr-block-depth)
4533     (write-buffered *(ebp+8) *eax)
4534     (write-buffered *(ebp+8) " break/disp32\n")
4535 $emit-reverse-break:end:
4536     # . restore registers
4537     58/pop-to-eax
4538     # . epilogue
4539     89/<- %esp 5/r32/ebp
4540     5d/pop-to-ebp
4541     c3/return
4542 
4543 == data
4544 
4545 Reverse-branch:  # (table string string)
4546   # a table is a stream
4547   0xa0/imm32/write
4548   0/imm32/read
4549   0xa0/imm32/length
4550   # data
4551   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
4552   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
4553   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
4554   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
4555   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
4556   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
4557   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
4558   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
4559   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
4560   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
4561   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
4562   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
4563   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
4564   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
4565   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
4566   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
4567   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
4568   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
4569   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
4570   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
4571 
4572 == code
4573 
4574 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int
4575     # . prologue
4576     55/push-ebp
4577     89/<- %ebp 4/r32/esp
4578     # . save registers
4579     50/push-eax
4580     51/push-ecx
4581     52/push-edx
4582     53/push-ebx
4583     # ecx = vars
4584     8b/-> *(ebp+0xc) 1/r32/ecx
4585     # var eax: int = vars->top
4586     8b/-> *ecx 0/r32/eax
4587     # var min/ecx: (address (handle var)) = vars->data
4588     81 0/subop/add %ecx 8/imm32
4589     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
4590     81 5/subop/subtract %eax 4/imm32
4591     8d/copy-address *(ecx+eax) 0/r32/eax
4592     # edx = depth
4593     8b/-> *(ebp+0x10) 2/r32/edx
4594     {
4595 $emit-unconditional-jump-to-depth:loop:
4596       # if (curr < min) break
4597       39/compare %eax 1/r32/ecx
4598       0f 82/jump-if-addr< break/disp32
4599       # var v/ebx: (handle var) = *curr
4600       8b/-> *eax 3/r32/ebx
4601       # if (v->block-depth < until-block-depth) break
4602       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
4603       0f 8c/jump-if-< break/disp32
4604       {
4605 $emit-unconditional-jump-to-depth:check:
4606         # if v->block-depth != until-block-depth, continue
4607         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
4608         75/jump-if-!= break/disp8
4609 $emit-unconditional-jump-to-depth:depth-found:
4610         # if v is not a literal, continue
4611         # . var eax: int = size-of(v)
4612         50/push-eax
4613         (size-of %ebx)  # => eax
4614         # . if (eax != 0) continue
4615         3d/compare-eax-and 0/imm32
4616         58/pop-to-eax
4617         #
4618         75/jump-if-!= break/disp8
4619 $emit-unconditional-jump-to-depth:label-found:
4620         # emit unconditional jump, then return
4621         (emit-indent *(ebp+8) *Curr-block-depth)
4622         (write-buffered *(ebp+8) "e9/jump ")
4623         (write-buffered *(ebp+8) *ebx)  # Var-name
4624         (write-buffered *(ebp+8) ":loop/disp32\n")  # Var-name
4625         eb/jump $emit-unconditional-jump-to-depth:end/disp8
4626       }
4627       # curr -= 4
4628       2d/subtract-from-eax 4/imm32
4629       e9/jump loop/disp32
4630     }
4631     # TODO: error if no label at 'depth' was found
4632 $emit-unconditional-jump-to-depth:end:
4633     # . restore registers
4634     5b/pop-to-ebx
4635     5a/pop-to-edx
4636     59/pop-to-ecx
4637     58/pop-to-eax
4638     # . epilogue
4639     89/<- %esp 5/r32/ebp
4640     5d/pop-to-ebp
4641     c3/return
4642 
4643 # emit clean-up code for 'vars' until some block depth
4644 # doesn't actually modify 'vars' so we need traverse manually inside the stack
4645 emit-cleanup-code:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
4646     # . prologue
4647     55/push-ebp
4648     89/<- %ebp 4/r32/esp
4649     # . save registers
4650     50/push-eax
4651     51/push-ecx
4652     52/push-edx
4653     53/push-ebx
4654     # ecx = vars
4655     8b/-> *(ebp+0xc) 1/r32/ecx
4656     # var eax: int = vars->top
4657     8b/-> *ecx 0/r32/eax
4658     # var min/ecx: (address (handle var)) = vars->data
4659     81 0/subop/add %ecx 8/imm32
4660     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
4661     81 5/subop/subtract %eax 4/imm32
4662     8d/copy-address *(ecx+eax) 0/r32/eax
4663     # edx = until-block-depth
4664     8b/-> *(ebp+0x10) 2/r32/edx
4665     {
4666 $emit-cleanup-code:loop:
4667       # if (curr < min) break
4668       39/compare %eax 1/r32/ecx
4669       0f 82/jump-if-addr< break/disp32
4670       # var v/ebx: (handle var) = *curr
4671       8b/-> *eax 3/r32/ebx
4672       # if (v->block-depth < until-block-depth) break
4673       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
4674       0f 8c/jump-if-< break/disp32
4675       # if v is in a register
4676       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
4677       {
4678         74/jump-if-= break/disp8
4679 $emit-cleanup-code:reclaim-var-in-register:
4680         (emit-indent *(ebp+8) *Curr-block-depth)
4681         (write-buffered *(ebp+8) "8f 0/subop/pop %")
4682         (write-buffered *(ebp+8) *(ebx+0x10))
4683         (write-buffered *(ebp+8) Newline)
4684       }
4685       # otherwise v is on the stack
4686       {
4687         75/jump-if-!= break/disp8
4688 $emit-cleanup-code:reclaim-var-on-stack:
4689         50/push-eax
4690         (size-of %ebx)  # => eax
4691         # don't emit code for labels
4692         3d/compare-eax-and 0/imm32
4693         74/jump-if-= break/disp8
4694         #
4695         (emit-indent *(ebp+8) *Curr-block-depth)
4696         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
4697         (print-int32-buffered *(ebp+8) %eax)
4698         (write-buffered *(ebp+8) "/imm32\n")
4699         58/pop-to-eax
4700       }
4701       # curr -= 4
4702       2d/subtract-from-eax 4/imm32
4703       e9/jump loop/disp32
4704     }
4705 $emit-cleanup-code:end:
4706     # . restore registers
4707     5b/pop-to-ebx
4708     5a/pop-to-edx
4709     59/pop-to-ecx
4710     58/pop-to-eax
4711     # . epilogue
4712     89/<- %esp 5/r32/ebp
4713     5d/pop-to-ebp
4714     c3/return
4715 
4716 # clean up global state for 'vars' until some block depth
4717 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
4718     # . prologue
4719     55/push-ebp
4720     89/<- %ebp 4/r32/esp
4721     # . save registers
4722     50/push-eax
4723     51/push-ecx
4724     56/push-esi
4725     # esi = vars
4726     8b/-> *(ebp+8) 6/r32/esi
4727     # ecx = until-block-depth
4728     8b/-> *(ebp+0xc) 1/r32/ecx
4729     {
4730 $clean-up-blocks:reclaim-loop:
4731       # if (vars->top <= 0) break
4732       81 7/subop/compare *esi 0/imm32  # Stack-top
4733       7e/jump-if-<= break/disp8
4734       # var v/eax : (handle var) = top(vars)
4735       (top %esi)  # => eax
4736       # if (v->block-depth < until-block-depth) break
4737       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
4738       7c/jump-if-< break/disp8
4739       # if v is on the stack, update Next-local-stack-offset
4740       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
4741       {
4742         75/jump-if-!= break/disp8
4743 $clean-up-blocks:reclaim-var-on-stack:
4744         (size-of %eax)  # => eax
4745         01/add *Next-local-stack-offset 0/r32/eax
4746       }
4747       (pop %esi)
4748       e9/jump loop/disp32
4749     }
4750 $clean-up-blocks:end:
4751     # . restore registers
4752     5e/pop-to-esi
4753     59/pop-to-ecx
4754     58/pop-to-eax
4755     # . epilogue
4756     89/<- %esp 5/r32/ebp
4757     5d/pop-to-ebp
4758     c3/return
4759 
4760 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle statement)
4761     # . prologue
4762     55/push-ebp
4763     89/<- %ebp 4/r32/esp
4764     # . save registers
4765     50/push-eax
4766     51/push-ecx
4767     # eax = stmt
4768     8b/-> *(ebp+0xc) 0/r32/eax
4769     # var n/eax: int = size-of(stmt->var)
4770     (size-of *(eax+4))  # Vardef-var => eax
4771     # while n > 0
4772     {
4773       3d/compare-eax-with 0/imm32
4774       7e/jump-if-<= break/disp8
4775       (emit-indent *(ebp+8) *Curr-block-depth)
4776       (write-buffered *(ebp+8) "68/push 0/imm32\n")
4777       # n -= 4
4778       2d/subtract-from-eax 4/imm32
4779       #
4780       eb/jump loop/disp8
4781     }
4782 $emit-subx-var-def:end:
4783     # . restore registers
4784     59/pop-to-ecx
4785     58/pop-to-eax
4786     # . epilogue
4787     89/<- %esp 5/r32/ebp
4788     5d/pop-to-ebp
4789     c3/return
4790 
4791 emit-subx-statement:  # out: (addr buffered-file), stmt: (handle statement), primitives: (handle primitive), functions: (handle function)
4792     # . prologue
4793     55/push-ebp
4794     89/<- %ebp 4/r32/esp
4795     # . save registers
4796     50/push-eax
4797     51/push-ecx
4798     # if stmt matches a primitive, emit it
4799     {
4800 $emit-subx-statement:check-for-primitive:
4801       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
4802       3d/compare-eax-and 0/imm32
4803       74/jump-if-= break/disp8
4804 $emit-subx-statement:primitive:
4805       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
4806       e9/jump $emit-subx-statement:end/disp32
4807     }
4808     # else if stmt matches a function, emit a call to it
4809     {
4810 $emit-subx-statement:check-for-call:
4811       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
4812       3d/compare-eax-and 0/imm32
4813       74/jump-if-= break/disp8
4814 $emit-subx-statement:call:
4815       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
4816       e9/jump $emit-subx-statement:end/disp32
4817     }
4818     # else abort
4819     e9/jump $emit-subx-statement:abort/disp32
4820 $emit-subx-statement:end:
4821     # . restore registers
4822     59/pop-to-ecx
4823     58/pop-to-eax
4824     # . epilogue
4825     89/<- %esp 5/r32/ebp
4826     5d/pop-to-ebp
4827     c3/return
4828 
4829 $emit-subx-statement:abort:
4830     # error("couldn't translate '" stmt "'\n")
4831     (write-buffered Stderr "couldn't translate an instruction with operation '")
4832     8b/-> *(ebp+0xc) 0/r32/eax
4833     (write-buffered Stderr *(eax+4))  # Stmt1-operation
4834     (write-buffered Stderr "'\n")
4835     (flush Stderr)
4836     # . syscall(exit, 1)
4837     bb/copy-to-ebx  1/imm32
4838     b8/copy-to-eax  1/imm32/exit
4839     cd/syscall  0x80/imm8
4840     # never gets here
4841 
4842 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
4843     # . prologue
4844     55/push-ebp
4845     89/<- %ebp 4/r32/esp
4846     # . save registers
4847     50/push-eax
4848     51/push-ecx
4849     56/push-esi
4850     # esi = block
4851     8b/-> *(ebp+0xc) 6/r32/esi
4852     # var stmts/eax: (handle list statement) = block->statements
4853     8b/-> *(esi+4) 0/r32/eax  # Block-statements
4854     #
4855     {
4856 $emit-subx-block:check-empty:
4857       81 7/subop/compare %eax 0/imm32
4858       0f 84/jump-if-= break/disp32
4859       (emit-indent *(ebp+8) *Curr-block-depth)
4860       (write-buffered *(ebp+8) "{\n")
4861       # var v/ecx: (addr array byte) = block->var->name
4862       8b/-> *(esi+8) 1/r32/ecx  # Block-var
4863       (write-buffered *(ebp+8) *ecx)  # Var-name
4864       (write-buffered *(ebp+8) ":loop:\n")
4865       ff 0/subop/increment *Curr-block-depth
4866       (push *(ebp+0x10) %ecx)
4867       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
4868       (pop *(ebp+0x10))  # => eax
4869       ff 1/subop/decrement *Curr-block-depth
4870       (emit-indent *(ebp+8) *Curr-block-depth)
4871       (write-buffered *(ebp+8) "}\n")
4872       (write-buffered *(ebp+8) *ecx)  # Var-name
4873       (write-buffered *(ebp+8) ":break:\n")
4874     }
4875 $emit-subx-block:end:
4876     # . restore registers
4877     5e/pop-to-esi
4878     59/pop-to-ecx
4879     58/pop-to-eax
4880     # . epilogue
4881     89/<- %esp 5/r32/ebp
4882     5d/pop-to-ebp
4883     c3/return
4884 
4885 # Primitives supported
4886 # For each operation, put variants with hard-coded registers before flexible ones.
4887 == data
4888 Primitives:
4889 # - increment/decrement
4890 _Primitive-inc-eax:
4891     # var/eax <- increment => 40/increment-eax
4892     "increment"/imm32/name
4893     0/imm32/no-inouts
4894     Single-int-var-in-eax/imm32/outputs
4895     "40/increment-eax"/imm32/subx-name
4896     0/imm32/no-rm32
4897     0/imm32/no-r32
4898     0/imm32/no-imm32
4899     0/imm32/no-disp32
4900     0/imm32/output-is-write-only
4901     _Primitive-inc-ecx/imm32/next
4902 _Primitive-inc-ecx:
4903     # var/ecx <- increment => 41/increment-ecx
4904     "increment"/imm32/name
4905     0/imm32/no-inouts
4906     Single-int-var-in-ecx/imm32/outputs
4907     "41/increment-ecx"/imm32/subx-name
4908     0/imm32/no-rm32
4909     0/imm32/no-r32
4910     0/imm32/no-imm32
4911     0/imm32/no-disp32
4912     0/imm32/output-is-write-only
4913     _Primitive-inc-edx/imm32/next
4914 _Primitive-inc-edx:
4915     # var/edx <- increment => 42/increment-edx
4916     "increment"/imm32/name
4917     0/imm32/no-inouts
4918     Single-int-var-in-edx/imm32/outputs
4919     "42/increment-edx"/imm32/subx-name
4920     0/imm32/no-rm32
4921     0/imm32/no-r32
4922     0/imm32/no-imm32
4923     0/imm32/no-disp32
4924     0/imm32/output-is-write-only
4925     _Primitive-inc-ebx/imm32/next
4926 _Primitive-inc-ebx:
4927     # var/ebx <- increment => 43/increment-ebx
4928     "increment"/imm32/name
4929     0/imm32/no-inouts
4930     Single-int-var-in-ebx/imm32/outputs
4931     "43/increment-ebx"/imm32/subx-name
4932     0/imm32/no-rm32
4933     0/imm32/no-r32
4934     0/imm32/no-imm32
4935     0/imm32/no-disp32
4936     0/imm32/output-is-write-only
4937     _Primitive-inc-esi/imm32/next
4938 _Primitive-inc-esi:
4939     # var/esi <- increment => 46/increment-esi
4940     "increment"/imm32/name
4941     0/imm32/no-inouts
4942     Single-int-var-in-esi/imm32/outputs
4943     "46/increment-esi"/imm32/subx-name
4944     0/imm32/no-rm32
4945     0/imm32/no-r32
4946     0/imm32/no-imm32
4947     0/imm32/no-disp32
4948     0/imm32/output-is-write-only
4949     _Primitive-inc-edi/imm32/next
4950 _Primitive-inc-edi:
4951     # var/edi <- increment => 47/increment-edi
4952     "increment"/imm32/name
4953     0/imm32/no-inouts
4954     Single-int-var-in-edi/imm32/outputs
4955     "47/increment-edi"/imm32/subx-name
4956     0/imm32/no-rm32
4957     0/imm32/no-r32
4958     0/imm32/no-imm32
4959     0/imm32/no-disp32
4960     0/imm32/output-is-write-only
4961     _Primitive-dec-eax/imm32/next
4962 _Primitive-dec-eax:
4963     # var/eax <- decrement => 48/decrement-eax
4964     "decrement"/imm32/name
4965     0/imm32/no-inouts
4966     Single-int-var-in-eax/imm32/outputs
4967     "48/decrement-eax"/imm32/subx-name
4968     0/imm32/no-rm32
4969     0/imm32/no-r32
4970     0/imm32/no-imm32
4971     0/imm32/no-disp32
4972     0/imm32/output-is-write-only
4973     _Primitive-dec-ecx/imm32/next
4974 _Primitive-dec-ecx:
4975     # var/ecx <- decrement => 49/decrement-ecx
4976     "decrement"/imm32/name
4977     0/imm32/no-inouts
4978     Single-int-var-in-ecx/imm32/outputs
4979     "49/decrement-ecx"/imm32/subx-name
4980     0/imm32/no-rm32
4981     0/imm32/no-r32
4982     0/imm32/no-imm32
4983     0/imm32/no-disp32
4984     0/imm32/output-is-write-only
4985     _Primitive-dec-edx/imm32/next
4986 _Primitive-dec-edx:
4987     # var/edx <- decrement => 4a/decrement-edx
4988     "decrement"/imm32/name
4989     0/imm32/no-inouts
4990     Single-int-var-in-edx/imm32/outputs
4991     "4a/decrement-edx"/imm32/subx-name
4992     0/imm32/no-rm32
4993     0/imm32/no-r32
4994     0/imm32/no-imm32
4995     0/imm32/no-disp32
4996     0/imm32/output-is-write-only
4997     _Primitive-dec-ebx/imm32/next
4998 _Primitive-dec-ebx:
4999     # var/ebx <- decrement => 4b/decrement-ebx
5000     "decrement"/imm32/name
5001     0/imm32/no-inouts
5002     Single-int-var-in-ebx/imm32/outputs
5003     "4b/decrement-ebx"/imm32/subx-name
5004     0/imm32/no-rm32
5005     0/imm32/no-r32
5006     0/imm32/no-imm32
5007     0/imm32/no-disp32
5008     0/imm32/output-is-write-only
5009     _Primitive-dec-esi/imm32/next
5010 _Primitive-dec-esi:
5011     # var/esi <- decrement => 4e/decrement-esi
5012     "decrement"/imm32/name
5013     0/imm32/no-inouts
5014     Single-int-var-in-esi/imm32/outputs
5015     "4e/decrement-esi"/imm32/subx-name
5016     0/imm32/no-rm32
5017     0/imm32/no-r32
5018     0/imm32/no-imm32
5019     0/imm32/no-disp32
5020     0/imm32/output-is-write-only
5021     _Primitive-dec-edi/imm32/next
5022 _Primitive-dec-edi:
5023     # var/edi <- decrement => 4f/decrement-edi
5024     "decrement"/imm32/name
5025     0/imm32/no-inouts
5026     Single-int-var-in-edi/imm32/outputs
5027     "4f/decrement-edi"/imm32/subx-name
5028     0/imm32/no-rm32
5029     0/imm32/no-r32
5030     0/imm32/no-imm32
5031     0/imm32/no-disp32
5032     0/imm32/output-is-write-only
5033     _Primitive-inc-mem/imm32/next
5034 _Primitive-inc-mem:
5035     # increment var => ff 0/subop/increment *(ebp+__)
5036     "increment"/imm32/name
5037     Single-int-var-on-stack/imm32/inouts
5038     0/imm32/no-outputs
5039     "ff 0/subop/increment"/imm32/subx-name
5040     1/imm32/rm32-is-first-inout
5041     0/imm32/no-r32
5042     0/imm32/no-imm32
5043     0/imm32/no-disp32
5044     0/imm32/output-is-write-only
5045     _Primitive-inc-reg/imm32/next
5046 _Primitive-inc-reg:
5047     # var/reg <- increment => ff 0/subop/increment %__
5048     "increment"/imm32/name
5049     0/imm32/no-inouts
5050     Single-int-var-in-some-register/imm32/outputs
5051     "ff 0/subop/increment"/imm32/subx-name
5052     3/imm32/rm32-is-first-output
5053     0/imm32/no-r32
5054     0/imm32/no-imm32
5055     0/imm32/no-disp32
5056     0/imm32/output-is-write-only
5057     _Primitive-dec-mem/imm32/next
5058 _Primitive-dec-mem:
5059     # decrement var => ff 1/subop/decrement *(ebp+__)
5060     "decrement"/imm32/name
5061     Single-int-var-on-stack/imm32/inouts
5062     0/imm32/no-outputs
5063     "ff 1/subop/decrement"/imm32/subx-name
5064     1/imm32/rm32-is-first-inout
5065     0/imm32/no-r32
5066     0/imm32/no-imm32
5067     0/imm32/no-disp32
5068     0/imm32/output-is-write-only
5069     _Primitive-dec-reg/imm32/next
5070 _Primitive-dec-reg:
5071     # var/reg <- decrement => ff 1/subop/decrement %__
5072     "decrement"/imm32/name
5073     0/imm32/no-inouts
5074     Single-int-var-in-some-register/imm32/outputs
5075     "ff 1/subop/decrement"/imm32/subx-name
5076     3/imm32/rm32-is-first-output
5077     0/imm32/no-r32
5078     0/imm32/no-imm32
5079     0/imm32/no-disp32
5080     0/imm32/output-is-write-only
5081     _Primitive-add-to-eax/imm32/next
5082 # - add
5083 _Primitive-add-to-eax:
5084     # var/eax <- add lit => 05/add-to-eax lit/imm32
5085     "add"/imm32/name
5086     Single-lit-var/imm32/inouts
5087     Single-int-var-in-eax/imm32/outputs
5088     "05/add-to-eax"/imm32/subx-name
5089     0/imm32/no-rm32
5090     0/imm32/no-r32
5091     1/imm32/imm32-is-first-inout
5092     0/imm32/no-disp32
5093     0/imm32/output-is-write-only
5094     _Primitive-add-reg-to-reg/imm32/next
5095 _Primitive-add-reg-to-reg:
5096     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
5097     "add"/imm32/name
5098     Single-int-var-in-some-register/imm32/inouts
5099     Single-int-var-in-some-register/imm32/outputs
5100     "01/add-to"/imm32/subx-name
5101     3/imm32/rm32-is-first-output
5102     1/imm32/r32-is-first-inout
5103     0/imm32/no-imm32
5104     0/imm32/no-disp32
5105     0/imm32/output-is-write-only
5106     _Primitive-add-reg-to-mem/imm32/next
5107 _Primitive-add-reg-to-mem:
5108     # add-to var1 var2/reg => 01/add-to var1 var2/r32
5109     "add-to"/imm32/name
5110     Two-args-int-stack-int-reg/imm32/inouts
5111     0/imm32/outputs
5112     "01/add-to"/imm32/subx-name
5113     1/imm32/rm32-is-first-inout
5114     2/imm32/r32-is-second-inout
5115     0/imm32/no-imm32
5116     0/imm32/no-disp32
5117     0/imm32/output-is-write-only
5118     _Primitive-add-mem-to-reg/imm32/next
5119 _Primitive-add-mem-to-reg:
5120     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
5121     "add"/imm32/name
5122     Single-int-var-on-stack/imm32/inouts
5123     Single-int-var-in-some-register/imm32/outputs
5124     "03/add"/imm32/subx-name
5125     1/imm32/rm32-is-first-inout
5126     3/imm32/r32-is-first-output
5127     0/imm32/no-imm32
5128     0/imm32/no-disp32
5129     0/imm32/output-is-write-only
5130     _Primitive-add-lit-to-reg/imm32/next
5131 _Primitive-add-lit-to-reg:
5132     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
5133     "add"/imm32/name
5134     Single-lit-var/imm32/inouts
5135     Single-int-var-in-some-register/imm32/outputs
5136     "81 0/subop/add"/imm32/subx-name
5137     3/imm32/rm32-is-first-output
5138     0/imm32/no-r32
5139     1/imm32/imm32-is-first-inout
5140     0/imm32/no-disp32
5141     0/imm32/output-is-write-only
5142     _Primitive-add-lit-to-mem/imm32/next
5143 _Primitive-add-lit-to-mem:
5144     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
5145     "add-to"/imm32/name
5146     Int-var-and-literal/imm32/inouts
5147     0/imm32/outputs
5148     "81 0/subop/add"/imm32/subx-name
5149     1/imm32/rm32-is-first-inout
5150     0/imm32/no-r32
5151     2/imm32/imm32-is-second-inout
5152     0/imm32/no-disp32
5153     0/imm32/output-is-write-only
5154     _Primitive-subtract-from-eax/imm32/next
5155 # - subtract
5156 _Primitive-subtract-from-eax:
5157     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
5158     "subtract"/imm32/name
5159     Single-lit-var/imm32/inouts
5160     Single-int-var-in-eax/imm32/outputs
5161     "2d/subtract-from-eax"/imm32/subx-name
5162     0/imm32/no-rm32
5163     0/imm32/no-r32
5164     1/imm32/imm32-is-first-inout
5165     0/imm32/no-disp32
5166     0/imm32/output-is-write-only
5167     _Primitive-subtract-reg-from-reg/imm32/next
5168 _Primitive-subtract-reg-from-reg:
5169     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
5170     "subtract"/imm32/name
5171     Single-int-var-in-some-register/imm32/inouts
5172     Single-int-var-in-some-register/imm32/outputs
5173     "29/subtract-from"/imm32/subx-name
5174     3/imm32/rm32-is-first-output
5175     1/imm32/r32-is-first-inout
5176     0/imm32/no-imm32
5177     0/imm32/no-disp32
5178     0/imm32/output-is-write-only
5179     _Primitive-subtract-reg-from-mem/imm32/next
5180 _Primitive-subtract-reg-from-mem:
5181     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
5182     "subtract-from"/imm32/name
5183     Two-args-int-stack-int-reg/imm32/inouts
5184     0/imm32/outputs
5185     "29/subtract-from"/imm32/subx-name
5186     1/imm32/rm32-is-first-inout
5187     2/imm32/r32-is-second-inout
5188     0/imm32/no-imm32
5189     0/imm32/no-disp32
5190     0/imm32/output-is-write-only
5191     _Primitive-subtract-mem-from-reg/imm32/next
5192 _Primitive-subtract-mem-from-reg:
5193     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
5194     "subtract"/imm32/name
5195     Single-int-var-on-stack/imm32/inouts
5196     Single-int-var-in-some-register/imm32/outputs
5197     "2b/subtract"/imm32/subx-name
5198     1/imm32/rm32-is-first-inout
5199     3/imm32/r32-is-first-output
5200     0/imm32/no-imm32
5201     0/imm32/no-disp32
5202     0/imm32/output-is-write-only
5203     _Primitive-subtract-lit-from-reg/imm32/next
5204 _Primitive-subtract-lit-from-reg:
5205     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
5206     "subtract"/imm32/name
5207     Single-lit-var/imm32/inouts
5208     Single-int-var-in-some-register/imm32/outputs
5209     "81 5/subop/subtract"/imm32/subx-name
5210     3/imm32/rm32-is-first-output
5211     0/imm32/no-r32
5212     1/imm32/imm32-is-first-inout
5213     0/imm32/no-disp32
5214     0/imm32/output-is-write-only
5215     _Primitive-subtract-lit-from-mem/imm32/next
5216 _Primitive-subtract-lit-from-mem:
5217     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
5218     "subtract-from"/imm32/name
5219     Int-var-and-literal/imm32/inouts
5220     0/imm32/outputs
5221     "81 5/subop/subtract"/imm32/subx-name
5222     1/imm32/rm32-is-first-inout
5223     0/imm32/no-r32
5224     2/imm32/imm32-is-first-inout
5225     0/imm32/no-disp32
5226     0/imm32/output-is-write-only
5227     _Primitive-and-with-eax/imm32/next
5228 # - and
5229 _Primitive-and-with-eax:
5230     # var/eax <- and lit => 25/and-with-eax lit/imm32
5231     "and"/imm32/name
5232     Single-lit-var/imm32/inouts
5233     Single-int-var-in-eax/imm32/outputs
5234     "25/and-with-eax"/imm32/subx-name
5235     0/imm32/no-rm32
5236     0/imm32/no-r32
5237     1/imm32/imm32-is-first-inout
5238     0/imm32/no-disp32
5239     0/imm32/output-is-write-only
5240     _Primitive-and-reg-with-reg/imm32/next
5241 _Primitive-and-reg-with-reg:
5242     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
5243     "and"/imm32/name
5244     Single-int-var-in-some-register/imm32/inouts
5245     Single-int-var-in-some-register/imm32/outputs
5246     "21/and-with"/imm32/subx-name
5247     3/imm32/rm32-is-first-output
5248     1/imm32/r32-is-first-inout
5249     0/imm32/no-imm32
5250     0/imm32/no-disp32
5251     0/imm32/output-is-write-only
5252     _Primitive-and-reg-with-mem/imm32/next
5253 _Primitive-and-reg-with-mem:
5254     # and-with var1 var2/reg => 21/and-with var1 var2/r32
5255     "and-with"/imm32/name
5256     Two-args-int-stack-int-reg/imm32/inouts
5257     0/imm32/outputs
5258     "21/and-with"/imm32/subx-name
5259     1/imm32/rm32-is-first-inout
5260     2/imm32/r32-is-second-inout
5261     0/imm32/no-imm32
5262     0/imm32/no-disp32
5263     0/imm32/output-is-write-only
5264     _Primitive-and-mem-with-reg/imm32/next
5265 _Primitive-and-mem-with-reg:
5266     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
5267     "and"/imm32/name
5268     Single-int-var-on-stack/imm32/inouts
5269     Single-int-var-in-some-register/imm32/outputs
5270     "23/and"/imm32/subx-name
5271     1/imm32/rm32-is-first-inout
5272     3/imm32/r32-is-first-output
5273     0/imm32/no-imm32
5274     0/imm32/no-disp32
5275     0/imm32/output-is-write-only
5276     _Primitive-and-lit-with-reg/imm32/next
5277 _Primitive-and-lit-with-reg:
5278     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
5279     "and"/imm32/name
5280     Single-lit-var/imm32/inouts
5281     Single-int-var-in-some-register/imm32/outputs
5282     "81 4/subop/and"/imm32/subx-name
5283     3/imm32/rm32-is-first-output
5284     0/imm32/no-r32
5285     1/imm32/imm32-is-first-inout
5286     0/imm32/no-disp32
5287     0/imm32/output-is-write-only
5288     _Primitive-and-lit-with-mem/imm32/next
5289 _Primitive-and-lit-with-mem:
5290     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
5291     "and-with"/imm32/name
5292     Int-var-and-literal/imm32/inouts
5293     0/imm32/outputs
5294     "81 4/subop/and"/imm32/subx-name
5295     1/imm32/rm32-is-first-inout
5296     0/imm32/no-r32
5297     2/imm32/imm32-is-first-inout
5298     0/imm32/no-disp32
5299     0/imm32/output-is-write-only
5300     _Primitive-or-with-eax/imm32/next
5301 # - or
5302 _Primitive-or-with-eax:
5303     # var/eax <- or lit => 0d/or-with-eax lit/imm32
5304     "or"/imm32/name
5305     Single-lit-var/imm32/inouts
5306     Single-int-var-in-eax/imm32/outputs
5307     "0d/or-with-eax"/imm32/subx-name
5308     0/imm32/no-rm32
5309     0/imm32/no-r32
5310     1/imm32/imm32-is-first-inout
5311     0/imm32/no-disp32
5312     0/imm32/output-is-write-only
5313     _Primitive-or-reg-with-reg/imm32/next
5314 _Primitive-or-reg-with-reg:
5315     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
5316     "or"/imm32/name
5317     Single-int-var-in-some-register/imm32/inouts
5318     Single-int-var-in-some-register/imm32/outputs
5319     "09/or-with"/imm32/subx-name
5320     3/imm32/rm32-is-first-output
5321     1/imm32/r32-is-first-inout
5322     0/imm32/no-imm32
5323     0/imm32/no-disp32
5324     0/imm32/output-is-write-only
5325     _Primitive-or-reg-with-mem/imm32/next
5326 _Primitive-or-reg-with-mem:
5327     # or-with var1 var2/reg => 09/or-with var1 var2/r32
5328     "or-with"/imm32/name
5329     Two-args-int-stack-int-reg/imm32/inouts
5330     0/imm32/outputs
5331     "09/or-with"/imm32/subx-name
5332     1/imm32/rm32-is-first-inout
5333     2/imm32/r32-is-second-inout
5334     0/imm32/no-imm32
5335     0/imm32/no-disp32
5336     0/imm32/output-is-write-only
5337     _Primitive-or-mem-with-reg/imm32/next
5338 _Primitive-or-mem-with-reg:
5339     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
5340     "or"/imm32/name
5341     Single-int-var-on-stack/imm32/inouts
5342     Single-int-var-in-some-register/imm32/outputs
5343     "0b/or"/imm32/subx-name
5344     1/imm32/rm32-is-first-inout
5345     3/imm32/r32-is-first-output
5346     0/imm32/no-imm32
5347     0/imm32/no-disp32
5348     0/imm32/output-is-write-only
5349     _Primitive-or-lit-with-reg/imm32/next
5350 _Primitive-or-lit-with-reg:
5351     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
5352     "or"/imm32/name
5353     Single-lit-var/imm32/inouts
5354     Single-int-var-in-some-register/imm32/outputs
5355     "81 1/subop/or"/imm32/subx-name
5356     3/imm32/rm32-is-first-output
5357     0/imm32/no-r32
5358     1/imm32/imm32-is-first-inout
5359     0/imm32/no-disp32
5360     0/imm32/output-is-write-only
5361     _Primitive-or-lit-with-mem/imm32/next
5362 _Primitive-or-lit-with-mem:
5363     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
5364     "or-with"/imm32/name
5365     Int-var-and-literal/imm32/inouts
5366     0/imm32/outputs
5367     "81 1/subop/or"/imm32/subx-name
5368     1/imm32/rm32-is-first-inout
5369     0/imm32/no-r32
5370     2/imm32/imm32-is-second-inout
5371     0/imm32/no-disp32
5372     0/imm32/output-is-write-only
5373     _Primitive-xor-with-eax/imm32/next
5374 # - xor
5375 _Primitive-xor-with-eax:
5376     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
5377     "xor"/imm32/name
5378     Single-lit-var/imm32/inouts
5379     Single-int-var-in-eax/imm32/outputs
5380     "35/xor-with-eax"/imm32/subx-name
5381     0/imm32/no-rm32
5382     0/imm32/no-r32
5383     1/imm32/imm32-is-first-inout
5384     0/imm32/no-disp32
5385     0/imm32/output-is-write-only
5386     _Primitive-xor-reg-with-reg/imm32/next
5387 _Primitive-xor-reg-with-reg:
5388     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
5389     "xor"/imm32/name
5390     Single-int-var-in-some-register/imm32/inouts
5391     Single-int-var-in-some-register/imm32/outputs
5392     "31/xor-with"/imm32/subx-name
5393     3/imm32/rm32-is-first-output
5394     1/imm32/r32-is-first-inout
5395     0/imm32/no-imm32
5396     0/imm32/no-disp32
5397     0/imm32/output-is-write-only
5398     _Primitive-xor-reg-with-mem/imm32/next
5399 _Primitive-xor-reg-with-mem:
5400     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
5401     "xor-with"/imm32/name
5402     Two-args-int-stack-int-reg/imm32/inouts
5403     0/imm32/outputs
5404     "31/xor-with"/imm32/subx-name
5405     1/imm32/rm32-is-first-inout
5406     2/imm32/r32-is-second-inout
5407     0/imm32/no-imm32
5408     0/imm32/no-disp32
5409     0/imm32/output-is-write-only
5410     _Primitive-xor-mem-with-reg/imm32/next
5411 _Primitive-xor-mem-with-reg:
5412     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
5413     "xor"/imm32/name
5414     Single-int-var-on-stack/imm32/inouts
5415     Single-int-var-in-some-register/imm32/outputs
5416     "33/xor"/imm32/subx-name
5417     1/imm32/rm32-is-first-inout
5418     3/imm32/r32-is-first-output
5419     0/imm32/no-imm32
5420     0/imm32/no-disp32
5421     0/imm32/output-is-write-only
5422     _Primitive-xor-lit-with-reg/imm32/next
5423 _Primitive-xor-lit-with-reg:
5424     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
5425     "xor"/imm32/name
5426     Single-lit-var/imm32/inouts
5427     Single-int-var-in-some-register/imm32/outputs
5428     "81 6/subop/xor"/imm32/subx-name
5429     3/imm32/rm32-is-first-output
5430     0/imm32/no-r32
5431     1/imm32/imm32-is-first-inout
5432     0/imm32/no-disp32
5433     0/imm32/output-is-write-only
5434     _Primitive-xor-lit-with-mem/imm32/next
5435 _Primitive-xor-lit-with-mem:
5436     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
5437     "xor-with"/imm32/name
5438     Int-var-and-literal/imm32/inouts
5439     0/imm32/outputs
5440     "81 6/subop/xor"/imm32/subx-name
5441     1/imm32/rm32-is-first-inout
5442     0/imm32/no-r32
5443     2/imm32/imm32-is-first-inout
5444     0/imm32/no-disp32
5445     0/imm32/output-is-write-only
5446     _Primitive-copy-to-eax/imm32/next
5447 # - copy
5448 _Primitive-copy-to-eax:
5449     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
5450     "copy"/imm32/name
5451     Single-lit-var/imm32/inouts
5452     Single-int-var-in-eax/imm32/outputs
5453     "b8/copy-to-eax"/imm32/subx-name
5454     0/imm32/no-rm32
5455     0/imm32/no-r32
5456     1/imm32/imm32-is-first-inout
5457     0/imm32/no-disp32
5458     1/imm32/output-is-write-only
5459     _Primitive-copy-to-ecx/imm32/next
5460 _Primitive-copy-to-ecx:
5461     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
5462     "copy"/imm32/name
5463     Single-lit-var/imm32/inouts
5464     Single-int-var-in-ecx/imm32/outputs
5465     "b9/copy-to-ecx"/imm32/subx-name
5466     0/imm32/no-rm32
5467     0/imm32/no-r32
5468     1/imm32/imm32-is-first-inout
5469     0/imm32/no-disp32
5470     1/imm32/output-is-write-only
5471     _Primitive-copy-to-edx/imm32/next
5472 _Primitive-copy-to-edx:
5473     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
5474     "copy"/imm32/name
5475     Single-lit-var/imm32/inouts
5476     Single-int-var-in-edx/imm32/outputs
5477     "ba/copy-to-edx"/imm32/subx-name
5478     0/imm32/no-rm32
5479     0/imm32/no-r32
5480     1/imm32/imm32-is-first-inout
5481     0/imm32/no-disp32
5482     1/imm32/output-is-write-only
5483     _Primitive-copy-to-ebx/imm32/next
5484 _Primitive-copy-to-ebx:
5485     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
5486     "copy"/imm32/name
5487     Single-lit-var/imm32/inouts
5488     Single-int-var-in-ebx/imm32/outputs
5489     "bb/copy-to-ebx"/imm32/subx-name
5490     0/imm32/no-rm32
5491     0/imm32/no-r32
5492     1/imm32/imm32-is-first-inout
5493     0/imm32/no-disp32
5494     1/imm32/output-is-write-only
5495     _Primitive-copy-to-esi/imm32/next
5496 _Primitive-copy-to-esi:
5497     # var/esi <- copy lit => be/copy-to-esi lit/imm32
5498     "copy"/imm32/name
5499     Single-lit-var/imm32/inouts
5500     Single-int-var-in-esi/imm32/outputs
5501     "be/copy-to-esi"/imm32/subx-name
5502     0/imm32/no-rm32
5503     0/imm32/no-r32
5504     1/imm32/imm32-is-first-inout
5505     0/imm32/no-disp32
5506     1/imm32/output-is-write-only
5507     _Primitive-copy-to-edi/imm32/next
5508 _Primitive-copy-to-edi:
5509     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
5510     "copy"/imm32/name
5511     Single-lit-var/imm32/inouts
5512     Single-int-var-in-edi/imm32/outputs
5513     "bf/copy-to-edi"/imm32/subx-name
5514     0/imm32/no-rm32
5515     0/imm32/no-r32
5516     1/imm32/imm32-is-first-inout
5517     0/imm32/no-disp32
5518     1/imm32/output-is-write-only
5519     _Primitive-copy-reg-to-reg/imm32/next
5520 _Primitive-copy-reg-to-reg:
5521     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
5522     "copy"/imm32/name
5523     Single-int-var-in-some-register/imm32/inouts
5524     Single-int-var-in-some-register/imm32/outputs
5525     "89/copy-to"/imm32/subx-name
5526     3/imm32/rm32-is-first-output
5527     1/imm32/r32-is-first-inout
5528     0/imm32/no-imm32
5529     0/imm32/no-disp32
5530     1/imm32/output-is-write-only
5531     _Primitive-copy-reg-to-mem/imm32/next
5532 _Primitive-copy-reg-to-mem:
5533     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
5534     "copy-to"/imm32/name
5535     Two-args-int-stack-int-reg/imm32/inouts
5536     0/imm32/outputs
5537     "89/copy-to"/imm32/subx-name
5538     1/imm32/rm32-is-first-inout
5539     2/imm32/r32-is-second-inout
5540     0/imm32/no-imm32
5541     0/imm32/no-disp32
5542     1/imm32/output-is-write-only
5543     _Primitive-copy-mem-to-reg/imm32/next
5544 _Primitive-copy-mem-to-reg:
5545     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
5546     "copy"/imm32/name
5547     Single-int-var-on-stack/imm32/inouts
5548     Single-int-var-in-some-register/imm32/outputs
5549     "8b/copy-from"/imm32/subx-name
5550     1/imm32/rm32-is-first-inout
5551     3/imm32/r32-is-first-output
5552     0/imm32/no-imm32
5553     0/imm32/no-disp32
5554     1/imm32/output-is-write-only
5555     _Primitive-copy-lit-to-reg/imm32/next
5556 _Primitive-copy-lit-to-reg:
5557     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
5558     "copy"/imm32/name
5559     Single-lit-var/imm32/inouts
5560     Single-int-var-in-some-register/imm32/outputs
5561     "c7 0/subop/copy"/imm32/subx-name
5562     3/imm32/rm32-is-first-output
5563     0/imm32/no-r32
5564     1/imm32/imm32-is-first-inout
5565     0/imm32/no-disp32
5566     1/imm32/output-is-write-only
5567     _Primitive-copy-lit-to-mem/imm32/next
5568 _Primitive-copy-lit-to-mem:
5569     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
5570     "copy-to"/imm32/name
5571     Int-var-and-literal/imm32/inouts
5572     0/imm32/outputs
5573     "c7 0/subop/copy"/imm32/subx-name
5574     1/imm32/rm32-is-first-inout
5575     0/imm32/no-r32
5576     2/imm32/imm32-is-first-inout
5577     0/imm32/no-disp32
5578     1/imm32/output-is-write-only
5579     _Primitive-compare-mem-with-reg/imm32/next
5580 # - compare
5581 _Primitive-compare-mem-with-reg:
5582     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
5583     "compare"/imm32/name
5584     Two-args-int-stack-int-reg/imm32/inouts
5585     0/imm32/outputs
5586     "39/compare->"/imm32/subx-name
5587     1/imm32/rm32-is-first-inout
5588     2/imm32/r32-is-second-inout
5589     0/imm32/no-imm32
5590     0/imm32/no-disp32
5591     0/imm32/output-is-write-only
5592     _Primitive-compare-reg-with-mem/imm32/next
5593 _Primitive-compare-reg-with-mem:
5594     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
5595     "compare"/imm32/name
5596     Two-args-int-reg-int-stack/imm32/inouts
5597     0/imm32/outputs
5598     "3b/compare<-"/imm32/subx-name
5599     2/imm32/rm32-is-second-inout
5600     1/imm32/r32-is-first-inout
5601     0/imm32/no-imm32
5602     0/imm32/no-disp32
5603     0/imm32/output-is-write-only
5604     _Primitive-compare-eax-with-literal/imm32/next
5605 _Primitive-compare-eax-with-literal:
5606     # compare var1/eax n => 3d/compare-eax-with n/imm32
5607     "compare"/imm32/name
5608     Two-args-int-eax-int-literal/imm32/inouts
5609     0/imm32/outputs
5610     "3d/compare-eax-with"/imm32/subx-name
5611     0/imm32/no-rm32
5612     0/imm32/no-r32
5613     2/imm32/imm32-is-second-inout
5614     0/imm32/no-disp32
5615     0/imm32/output-is-write-only
5616     _Primitive-compare-regmem-with-literal/imm32/next
5617 _Primitive-compare-regmem-with-literal:
5618     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
5619     "compare"/imm32/name
5620     Int-var-and-literal/imm32/inouts
5621     0/imm32/outputs
5622     "81 7/subop/compare"/imm32/subx-name
5623     1/imm32/rm32-is-first-inout
5624     0/imm32/no-r32
5625     2/imm32/imm32-is-second-inout
5626     0/imm32/no-disp32
5627     0/imm32/output-is-write-only
5628     _Primitive-multiply-reg-by-mem/imm32/next
5629 # - multiply
5630 _Primitive-multiply-reg-by-mem:
5631     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
5632     "multiply"/imm32/name
5633     Single-int-var-on-stack/imm32/inouts
5634     Single-int-var-in-some-register/imm32/outputs
5635     "0f af/multiply"/imm32/subx-name
5636     1/imm32/rm32-is-first-inout
5637     3/imm32/r32-is-first-output
5638     0/imm32/no-imm32
5639     0/imm32/no-disp32
5640     0/imm32/output-is-write-only
5641     _Primitive-break-if-addr</imm32/next
5642 # - branches
5643 _Primitive-break-if-addr<:
5644     "break-if-addr<"/imm32/name
5645     0/imm32/inouts
5646     0/imm32/outputs
5647     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
5648     0/imm32/no-rm32
5649     0/imm32/no-r32
5650     0/imm32/no-imm32
5651     0/imm32/no-disp32
5652     0/imm32/no-output
5653     _Primitive-break-if-addr>=/imm32/next
5654 _Primitive-break-if-addr>=:
5655     "break-if-addr>="/imm32/name
5656     0/imm32/inouts
5657     0/imm32/outputs
5658     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
5659     0/imm32/no-rm32
5660     0/imm32/no-r32
5661     0/imm32/no-imm32
5662     0/imm32/no-disp32
5663     0/imm32/no-output
5664     _Primitive-break-if-=/imm32/next
5665 _Primitive-break-if-=:
5666     "break-if-="/imm32/name
5667     0/imm32/inouts
5668     0/imm32/outputs
5669     "0f 84/jump-if-= break/disp32"/imm32/subx-name
5670     0/imm32/no-rm32
5671     0/imm32/no-r32
5672     0/imm32/no-imm32
5673     0/imm32/no-disp32
5674     0/imm32/no-output
5675     _Primitive-break-if-!=/imm32/next
5676 _Primitive-break-if-!=:
5677     "break-if-!="/imm32/name
5678     0/imm32/inouts
5679     0/imm32/outputs
5680     "0f 85/jump-if-!= break/disp32"/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/no-output
5686     _Primitive-break-if-addr<=/imm32/next
5687 _Primitive-break-if-addr<=:
5688     "break-if-addr<="/imm32/name
5689     0/imm32/inouts
5690     0/imm32/outputs
5691     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
5692     0/imm32/no-rm32
5693     0/imm32/no-r32
5694     0/imm32/no-imm32
5695     0/imm32/no-disp32
5696     0/imm32/no-output
5697     _Primitive-break-if-addr>/imm32/next
5698 _Primitive-break-if-addr>:
5699     "break-if-addr>"/imm32/name
5700     0/imm32/inouts
5701     0/imm32/outputs
5702     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
5703     0/imm32/no-rm32
5704     0/imm32/no-r32
5705     0/imm32/no-imm32
5706     0/imm32/no-disp32
5707     0/imm32/no-output
5708     _Primitive-break-if-</imm32/next
5709 _Primitive-break-if-<:
5710     "break-if-<"/imm32/name
5711     0/imm32/inouts
5712     0/imm32/outputs
5713     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
5714     0/imm32/no-rm32
5715     0/imm32/no-r32
5716     0/imm32/no-imm32
5717     0/imm32/no-disp32
5718     0/imm32/no-output
5719     _Primitive-break-if->=/imm32/next
5720 _Primitive-break-if->=:
5721     "break-if->="/imm32/name
5722     0/imm32/inouts
5723     0/imm32/outputs
5724     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
5725     0/imm32/no-rm32
5726     0/imm32/no-r32
5727     0/imm32/no-imm32
5728     0/imm32/no-disp32
5729     0/imm32/no-output
5730     _Primitive-break-if-<=/imm32/next
5731 _Primitive-break-if-<=:
5732     "break-if-<="/imm32/name
5733     0/imm32/inouts
5734     0/imm32/outputs
5735     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
5736     0/imm32/no-rm32
5737     0/imm32/no-r32
5738     0/imm32/no-imm32
5739     0/imm32/no-disp32
5740     0/imm32/no-output
5741     _Primitive-break-if->/imm32/next
5742 _Primitive-break-if->:
5743     "break-if->"/imm32/name
5744     0/imm32/inouts
5745     0/imm32/outputs
5746     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
5747     0/imm32/no-rm32
5748     0/imm32/no-r32
5749     0/imm32/no-imm32
5750     0/imm32/no-disp32
5751     0/imm32/no-output
5752     _Primitive-loop-if-addr</imm32/next
5753 _Primitive-loop-if-addr<:
5754     "loop-if-addr<"/imm32/name
5755     0/imm32/inouts
5756     0/imm32/outputs
5757     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
5758     0/imm32/no-rm32
5759     0/imm32/no-r32
5760     0/imm32/no-imm32
5761     0/imm32/no-disp32
5762     0/imm32/no-output
5763     _Primitive-loop-if-addr>=/imm32/next
5764 _Primitive-loop-if-addr>=:
5765     "loop-if-addr>="/imm32/name
5766     0/imm32/inouts
5767     0/imm32/outputs
5768     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
5769     0/imm32/no-rm32
5770     0/imm32/no-r32
5771     0/imm32/no-imm32
5772     0/imm32/no-disp32
5773     0/imm32/no-output
5774     _Primitive-loop-if-=/imm32/next
5775 _Primitive-loop-if-=:
5776     "loop-if-="/imm32/name
5777     0/imm32/inouts
5778     0/imm32/outputs
5779     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
5780     0/imm32/no-rm32
5781     0/imm32/no-r32
5782     0/imm32/no-imm32
5783     0/imm32/no-disp32
5784     0/imm32/no-output
5785     _Primitive-loop-if-!=/imm32/next
5786 _Primitive-loop-if-!=:
5787     "loop-if-!="/imm32/name
5788     0/imm32/inouts
5789     0/imm32/outputs
5790     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
5791     0/imm32/no-rm32
5792     0/imm32/no-r32
5793     0/imm32/no-imm32
5794     0/imm32/no-disp32
5795     0/imm32/no-output
5796     _Primitive-loop-if-addr<=/imm32/next
5797 _Primitive-loop-if-addr<=:
5798     "loop-if-addr<="/imm32/name
5799     0/imm32/inouts
5800     0/imm32/outputs
5801     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
5802     0/imm32/no-rm32
5803     0/imm32/no-r32
5804     0/imm32/no-imm32
5805     0/imm32/no-disp32
5806     0/imm32/no-output
5807     _Primitive-loop-if-addr>/imm32/next
5808 _Primitive-loop-if-addr>:
5809     "loop-if-addr>"/imm32/name
5810     0/imm32/inouts
5811     0/imm32/outputs
5812     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
5813     0/imm32/no-rm32
5814     0/imm32/no-r32
5815     0/imm32/no-imm32
5816     0/imm32/no-disp32
5817     0/imm32/no-output
5818     _Primitive-loop-if-</imm32/next
5819 _Primitive-loop-if-<:
5820     "loop-if-<"/imm32/name
5821     0/imm32/inouts
5822     0/imm32/outputs
5823     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
5824     0/imm32/no-rm32
5825     0/imm32/no-r32
5826     0/imm32/no-imm32
5827     0/imm32/no-disp32
5828     0/imm32/no-output
5829     _Primitive-loop-if->=/imm32/next
5830 _Primitive-loop-if->=:
5831     "loop-if->="/imm32/name
5832     0/imm32/inouts
5833     0/imm32/outputs
5834     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
5835     0/imm32/no-rm32
5836     0/imm32/no-r32
5837     0/imm32/no-imm32
5838     0/imm32/no-disp32
5839     0/imm32/no-output
5840     _Primitive-loop-if-<=/imm32/next
5841 _Primitive-loop-if-<=:
5842     "loop-if-<="/imm32/name
5843     0/imm32/inouts
5844     0/imm32/outputs
5845     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
5846     0/imm32/no-rm32
5847     0/imm32/no-r32
5848     0/imm32/no-imm32
5849     0/imm32/no-disp32
5850     0/imm32/no-output
5851     _Primitive-loop-if->/imm32/next
5852 _Primitive-loop-if->:
5853     "loop-if->"/imm32/name
5854     0/imm32/inouts
5855     0/imm32/outputs
5856     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
5857     0/imm32/no-rm32
5858     0/imm32/no-r32
5859     0/imm32/no-imm32
5860     0/imm32/no-disp32
5861     0/imm32/no-output
5862     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
5863 _Primitive-loop:
5864     "loop"/imm32/name
5865     0/imm32/inouts
5866     0/imm32/outputs
5867     "e9/jump loop/disp32"/imm32/subx-name
5868     0/imm32/no-rm32
5869     0/imm32/no-r32
5870     0/imm32/no-imm32
5871     0/imm32/no-disp32
5872     0/imm32/no-output
5873     _Primitive-break-if-addr<-named/imm32/next
5874 # - branches to named blocks
5875 _Primitive-break-if-addr<-named:
5876     "break-if-addr<"/imm32/name
5877     Single-lit-var/imm32/inouts
5878     0/imm32/outputs
5879     "0f 82/jump-if-addr<"/imm32/subx-name
5880     0/imm32/no-rm32
5881     0/imm32/no-r32
5882     0/imm32/no-imm32
5883     1/imm32/disp32-is-first-inout
5884     0/imm32/no-output
5885     _Primitive-break-if-addr>=-named/imm32/next
5886 _Primitive-break-if-addr>=-named:
5887     "break-if-addr>="/imm32/name
5888     Single-lit-var/imm32/inouts
5889     0/imm32/outputs
5890     "0f 83/jump-if-addr>="/imm32/subx-name
5891     0/imm32/no-rm32
5892     0/imm32/no-r32
5893     0/imm32/no-imm32
5894     1/imm32/disp32-is-first-inout
5895     0/imm32/no-output
5896     _Primitive-break-if-=-named/imm32/next
5897 _Primitive-break-if-=-named:
5898     "break-if-="/imm32/name
5899     Single-lit-var/imm32/inouts
5900     0/imm32/outputs
5901     "0f 84/jump-if-="/imm32/subx-name
5902     0/imm32/no-rm32
5903     0/imm32/no-r32
5904     0/imm32/no-imm32
5905     1/imm32/disp32-is-first-inout
5906     0/imm32/no-output
5907     _Primitive-break-if-!=-named/imm32/next
5908 _Primitive-break-if-!=-named:
5909     "break-if-!="/imm32/name
5910     Single-lit-var/imm32/inouts
5911     0/imm32/outputs
5912     "0f 85/jump-if-!="/imm32/subx-name
5913     0/imm32/no-rm32
5914     0/imm32/no-r32
5915     0/imm32/no-imm32
5916     1/imm32/disp32-is-first-inout
5917     0/imm32/no-output
5918     _Primitive-break-if-addr<=-named/imm32/next
5919 _Primitive-break-if-addr<=-named:
5920     "break-if-addr<="/imm32/name
5921     Single-lit-var/imm32/inouts
5922     0/imm32/outputs
5923     "0f 86/jump-if-addr<="/imm32/subx-name
5924     0/imm32/no-rm32
5925     0/imm32/no-r32
5926     0/imm32/no-imm32
5927     1/imm32/disp32-is-first-inout
5928     0/imm32/no-output
5929     _Primitive-break-if-addr>-named/imm32/next
5930 _Primitive-break-if-addr>-named:
5931     "break-if-addr>"/imm32/name
5932     Single-lit-var/imm32/inouts
5933     0/imm32/outputs
5934     "0f 87/jump-if-addr>"/imm32/subx-name
5935     0/imm32/no-rm32
5936     0/imm32/no-r32
5937     0/imm32/no-imm32
5938     1/imm32/disp32-is-first-inout
5939     0/imm32/no-output
5940     _Primitive-break-if-<-named/imm32/next
5941 _Primitive-break-if-<-named:
5942     "break-if-<"/imm32/name
5943     Single-lit-var/imm32/inouts
5944     0/imm32/outputs
5945     "0f 8c/jump-if-<"/imm32/subx-name
5946     0/imm32/no-rm32
5947     0/imm32/no-r32
5948     0/imm32/no-imm32
5949     1/imm32/disp32-is-first-inout
5950     0/imm32/no-output
5951     _Primitive-break-if->=-named/imm32/next
5952 _Primitive-break-if->=-named:
5953     "break-if->="/imm32/name
5954     Single-lit-var/imm32/inouts
5955     0/imm32/outputs
5956     "0f 8d/jump-if->="/imm32/subx-name
5957     0/imm32/no-rm32
5958     0/imm32/no-r32
5959     0/imm32/no-imm32
5960     1/imm32/disp32-is-first-inout
5961     0/imm32/no-output
5962     _Primitive-break-if-<=-named/imm32/next
5963 _Primitive-break-if-<=-named:
5964     "break-if-<="/imm32/name
5965     Single-lit-var/imm32/inouts
5966     0/imm32/outputs
5967     "0f 8e/jump-if-<="/imm32/subx-name
5968     0/imm32/no-rm32
5969     0/imm32/no-r32
5970     0/imm32/no-imm32
5971     1/imm32/disp32-is-first-inout
5972     0/imm32/no-output
5973     _Primitive-break-if->-named/imm32/next
5974 _Primitive-break-if->-named:
5975     "break-if->"/imm32/name
5976     Single-lit-var/imm32/inouts
5977     0/imm32/outputs
5978     "0f 8f/jump-if->"/imm32/subx-name
5979     0/imm32/no-rm32
5980     0/imm32/no-r32
5981     0/imm32/no-imm32
5982     1/imm32/disp32-is-first-inout
5983     0/imm32/no-output
5984     _Primitive-loop-if-addr<-named/imm32/next
5985 _Primitive-loop-if-addr<-named:
5986     "loop-if-addr<"/imm32/name
5987     Single-lit-var/imm32/inouts
5988     0/imm32/outputs
5989     "0f 82/jump-if-addr<"/imm32/subx-name
5990     0/imm32/no-rm32
5991     0/imm32/no-r32
5992     0/imm32/no-imm32
5993     1/imm32/disp32-is-first-inout
5994     0/imm32/no-output
5995     _Primitive-loop-if-addr>=-named/imm32/next
5996 _Primitive-loop-if-addr>=-named:
5997     "loop-if-addr>="/imm32/name
5998     Single-lit-var/imm32/inouts
5999     0/imm32/outputs
6000     "0f 83/jump-if-addr>="/imm32/subx-name
6001     0/imm32/no-rm32
6002     0/imm32/no-r32
6003     0/imm32/no-imm32
6004     1/imm32/disp32-is-first-inout
6005     0/imm32/no-output
6006     _Primitive-loop-if-=-named/imm32/next
6007 _Primitive-loop-if-=-named:
6008     "loop-if-="/imm32/name
6009     Single-lit-var/imm32/inouts
6010     0/imm32/outputs
6011     "0f 84/jump-if-="/imm32/subx-name
6012     0/imm32/no-rm32
6013     0/imm32/no-r32
6014     0/imm32/no-imm32
6015     1/imm32/disp32-is-first-inout
6016     0/imm32/no-output
6017     _Primitive-loop-if-!=-named/imm32/next
6018 _Primitive-loop-if-!=-named:
6019     "loop-if-!="/imm32/name
6020     Single-lit-var/imm32/inouts
6021     0/imm32/outputs
6022     "0f 85/jump-if-!="/imm32/subx-name
6023     0/imm32/no-rm32
6024     0/imm32/no-r32
6025     0/imm32/no-imm32
6026     1/imm32/disp32-is-first-inout
6027     0/imm32/no-output
6028     _Primitive-loop-if-addr<=-named/imm32/next
6029 _Primitive-loop-if-addr<=-named:
6030     "loop-if-addr<="/imm32/name
6031     Single-lit-var/imm32/inouts
6032     0/imm32/outputs
6033     "0f 86/jump-if-addr<="/imm32/subx-name
6034     0/imm32/no-rm32
6035     0/imm32/no-r32
6036     0/imm32/no-imm32
6037     1/imm32/disp32-is-first-inout
6038     0/imm32/no-output
6039     _Primitive-loop-if-addr>-named/imm32/next
6040 _Primitive-loop-if-addr>-named:
6041     "loop-if-addr>"/imm32/name
6042     Single-lit-var/imm32/inouts
6043     0/imm32/outputs
6044     "0f 87/jump-if-addr>"/imm32/subx-name
6045     0/imm32/no-rm32
6046     0/imm32/no-r32
6047     0/imm32/no-imm32
6048     1/imm32/disp32-is-first-inout
6049     0/imm32/no-output
6050     _Primitive-loop-if-<-named/imm32/next
6051 _Primitive-loop-if-<-named:
6052     "loop-if-<"/imm32/name
6053     Single-lit-var/imm32/inouts
6054     0/imm32/outputs
6055     "0f 8c/jump-if-<"/imm32/subx-name
6056     0/imm32/no-rm32
6057     0/imm32/no-r32
6058     0/imm32/no-imm32
6059     1/imm32/disp32-is-first-inout
6060     0/imm32/no-output
6061     _Primitive-loop-if->=-named/imm32/next
6062 _Primitive-loop-if->=-named:
6063     "loop-if->="/imm32/name
6064     Single-lit-var/imm32/inouts
6065     0/imm32/outputs
6066     "0f 8d/jump-if->="/imm32/subx-name
6067     0/imm32/no-rm32
6068     0/imm32/no-r32
6069     0/imm32/no-imm32
6070     1/imm32/disp32-is-first-inout
6071     0/imm32/no-output
6072     _Primitive-loop-if-<=-named/imm32/next
6073 _Primitive-loop-if-<=-named:
6074     "loop-if-<="/imm32/name
6075     Single-lit-var/imm32/inouts
6076     0/imm32/outputs
6077     "0f 8e/jump-if-<="/imm32/subx-name
6078     0/imm32/no-rm32
6079     0/imm32/no-r32
6080     0/imm32/no-imm32
6081     1/imm32/disp32-is-first-inout
6082     0/imm32/no-output
6083     _Primitive-loop-if->-named/imm32/next
6084 _Primitive-loop-if->-named:
6085     "loop-if->"/imm32/name
6086     Single-lit-var/imm32/inouts
6087     0/imm32/outputs
6088     "0f 8f/jump-if->"/imm32/subx-name
6089     0/imm32/no-rm32
6090     0/imm32/no-r32
6091     0/imm32/no-imm32
6092     1/imm32/disp32-is-first-inout
6093     0/imm32/no-output
6094     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
6095 _Primitive-loop-named:
6096     "loop"/imm32/name
6097     Single-lit-var/imm32/inouts
6098     0/imm32/outputs
6099     "e9/jump"/imm32/subx-name
6100     0/imm32/no-rm32
6101     0/imm32/no-r32
6102     0/imm32/no-imm32
6103     1/imm32/disp32-is-first-inout
6104     0/imm32/no-output
6105     0/imm32/next
6106 
6107 Single-int-var-on-stack:
6108     Int-var-on-stack/imm32
6109     0/imm32/next
6110 
6111 Int-var-on-stack:
6112     "arg1"/imm32/name
6113     Type-int/imm32
6114     1/imm32/some-block-depth
6115     1/imm32/some-stack-offset
6116     0/imm32/no-register
6117 
6118 Two-args-int-stack-int-reg:
6119     Int-var-on-stack/imm32
6120     Single-int-var-in-some-register/imm32/next
6121 
6122 Two-args-int-reg-int-stack:
6123     Int-var-in-some-register/imm32
6124     Single-int-var-on-stack/imm32/next
6125 
6126 Two-args-int-eax-int-literal:
6127     Int-var-in-eax/imm32
6128     Single-lit-var/imm32/next
6129 
6130 Int-var-and-literal:
6131     Int-var-on-stack/imm32
6132     Single-lit-var/imm32/next
6133 
6134 Single-int-var-in-some-register:
6135     Int-var-in-some-register/imm32
6136     0/imm32/next
6137 
6138 Int-var-in-some-register:
6139     "arg1"/imm32/name
6140     Type-int/imm32
6141     1/imm32/some-block-depth
6142     0/imm32/no-stack-offset
6143     "*"/imm32/register
6144 
6145 Single-int-var-in-eax:
6146     Int-var-in-eax/imm32
6147     0/imm32/next
6148 
6149 Int-var-in-eax:
6150     "arg1"/imm32/name
6151     Type-int/imm32
6152     1/imm32/some-block-depth
6153     0/imm32/no-stack-offset
6154     "eax"/imm32/register
6155 
6156 Single-int-var-in-ecx:
6157     Int-var-in-ecx/imm32
6158     0/imm32/next
6159 
6160 Int-var-in-ecx:
6161     "arg1"/imm32/name
6162     Type-int/imm32
6163     1/imm32/some-block-depth
6164     0/imm32/no-stack-offset
6165     "ecx"/imm32/register
6166 
6167 Single-int-var-in-edx:
6168     Int-var-in-edx/imm32
6169     0/imm32/next
6170 
6171 Int-var-in-edx:
6172     "arg1"/imm32/name
6173     Type-int/imm32
6174     1/imm32/some-block-depth
6175     0/imm32/no-stack-offset
6176     "edx"/imm32/register
6177 
6178 Single-int-var-in-ebx:
6179     Int-var-in-ebx/imm32
6180     0/imm32/next
6181 
6182 Int-var-in-ebx:
6183     "arg1"/imm32/name
6184     Type-int/imm32
6185     1/imm32/some-block-depth
6186     0/imm32/no-stack-offset
6187     "ebx"/imm32/register
6188 
6189 Single-int-var-in-esi:
6190     Int-var-in-esi/imm32
6191     0/imm32/next
6192 
6193 Int-var-in-esi:
6194     "arg1"/imm32/name
6195     Type-int/imm32
6196     1/imm32/some-block-depth
6197     0/imm32/no-stack-offset
6198     "esi"/imm32/register
6199 
6200 Single-int-var-in-edi:
6201     Int-var-in-edi/imm32
6202     0/imm32/next
6203 
6204 Int-var-in-edi:
6205     "arg1"/imm32/name
6206     Type-int/imm32
6207     1/imm32/some-block-depth
6208     0/imm32/no-stack-offset
6209     "edi"/imm32/register
6210 
6211 Single-lit-var:
6212     Lit-var/imm32
6213     0/imm32/next
6214 
6215 Lit-var:
6216     "literal"/imm32/name
6217     Type-literal/imm32
6218     1/imm32/some-block-depth
6219     0/imm32/no-stack-offset
6220     0/imm32/no-register
6221 
6222 Type-int:
6223     1/imm32/left/int
6224     0/imm32/right/null
6225 
6226 Type-literal:
6227     0/imm32/left/literal
6228     0/imm32/right/null
6229 
6230 == code
6231 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle statement), primitive: (handle function)
6232     # . prologue
6233     55/push-ebp
6234     89/<- %ebp 4/r32/esp
6235     # . save registers
6236     50/push-eax
6237     51/push-ecx
6238     # ecx = primitive
6239     8b/-> *(ebp+0x10) 1/r32/ecx
6240     # emit primitive name
6241     (emit-indent *(ebp+8) *Curr-block-depth)
6242     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
6243     # emit rm32 if necessary
6244     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
6245     # emit r32 if necessary
6246     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
6247     # emit imm32 if necessary
6248     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
6249     # emit disp32 if necessary
6250     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
6251     (write-buffered *(ebp+8) Newline)
6252 $emit-subx-primitive:end:
6253     # . restore registers
6254     59/pop-to-ecx
6255     58/pop-to-eax
6256     # . epilogue
6257     89/<- %esp 5/r32/ebp
6258     5d/pop-to-ebp
6259     c3/return
6260 
6261 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6262     # . prologue
6263     55/push-ebp
6264     89/<- %ebp 4/r32/esp
6265     # . save registers
6266     50/push-eax
6267     # if (l == 0) return
6268     81 7/subop/compare *(ebp+0xc) 0/imm32
6269     74/jump-if-= $emit-subx-rm32:end/disp8
6270     #
6271     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6272     (emit-subx-var-as-rm32 *(ebp+8) %eax)  # out, var
6273 $emit-subx-rm32:end:
6274     # . restore registers
6275     58/pop-to-eax
6276     # . epilogue
6277     89/<- %esp 5/r32/ebp
6278     5d/pop-to-ebp
6279     c3/return
6280 
6281 get-stmt-operand-from-arg-location:  # stmt: (handle statement), l: arg-location -> var/eax: (handle variable)
6282     # . prologue
6283     55/push-ebp
6284     89/<- %ebp 4/r32/esp
6285     # . save registers
6286     51/push-ecx
6287     # eax = l
6288     8b/-> *(ebp+0xc) 0/r32/eax
6289     # ecx = stmt
6290     8b/-> *(ebp+8) 1/r32/ecx
6291     # if (l == 1) return stmt->inouts->var
6292     {
6293       3d/compare-eax-and 1/imm32
6294       75/jump-if-!= break/disp8
6295 $get-stmt-operand-from-arg-location:1:
6296       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
6297       8b/-> *eax 0/r32/eax  # Operand-var
6298       eb/jump $get-stmt-operand-from-arg-location:end/disp8
6299     }
6300     # if (l == 2) return stmt->inouts->next->var
6301     {
6302       3d/compare-eax-and 2/imm32
6303       75/jump-if-!= break/disp8
6304 $get-stmt-operand-from-arg-location:2:
6305       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
6306       8b/-> *(eax+4) 0/r32/eax  # Operand-next
6307       8b/-> *eax 0/r32/eax  # Operand-var
6308       eb/jump $get-stmt-operand-from-arg-location:end/disp8
6309     }
6310     # if (l == 3) return stmt->outputs
6311     {
6312       3d/compare-eax-and 3/imm32
6313       75/jump-if-!= break/disp8
6314 $get-stmt-operand-from-arg-location:3:
6315       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
6316       8b/-> *eax 0/r32/eax  # Operand-var
6317       eb/jump $get-stmt-operand-from-arg-location:end/disp8
6318     }
6319     # abort
6320     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
6321 $get-stmt-operand-from-arg-location:end:
6322     # . restore registers
6323     59/pop-to-ecx
6324     # . epilogue
6325     89/<- %esp 5/r32/ebp
6326     5d/pop-to-ebp
6327     c3/return
6328 
6329 $get-stmt-operand-from-arg-location:abort:
6330     # error("invalid arg-location " eax)
6331     (write-buffered Stderr "invalid arg-location ")
6332     (print-int32-buffered Stderr %eax)
6333     (write-buffered Stderr Newline)
6334     (flush Stderr)
6335     # . syscall(exit, 1)
6336     bb/copy-to-ebx  1/imm32
6337     b8/copy-to-eax  1/imm32/exit
6338     cd/syscall  0x80/imm8
6339     # never gets here
6340 
6341 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6342     # . prologue
6343     55/push-ebp
6344     89/<- %ebp 4/r32/esp
6345     # . save registers
6346     50/push-eax
6347     51/push-ecx
6348     # if (location == 0) return
6349     81 7/subop/compare *(ebp+0xc) 0/imm32
6350     0f 84/jump-if-= $emit-subx-r32:end/disp32
6351     #
6352     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6353     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
6354     (write-buffered *(ebp+8) Space)
6355     (print-int32-buffered *(ebp+8) *eax)
6356     (write-buffered *(ebp+8) "/r32")
6357 $emit-subx-r32:end:
6358     # . restore registers
6359     59/pop-to-ecx
6360     58/pop-to-eax
6361     # . epilogue
6362     89/<- %esp 5/r32/ebp
6363     5d/pop-to-ebp
6364     c3/return
6365 
6366 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6367     # . prologue
6368     55/push-ebp
6369     89/<- %ebp 4/r32/esp
6370     # . save registers
6371     50/push-eax
6372     51/push-ecx
6373     # if (location == 0) return
6374     81 7/subop/compare *(ebp+0xc) 0/imm32
6375     74/jump-if-= $emit-subx-imm32:end/disp8
6376     #
6377     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6378     (write-buffered *(ebp+8) Space)
6379     (write-buffered *(ebp+8) *eax)  # Var-name
6380     (write-buffered *(ebp+8) "/imm32")
6381 $emit-subx-imm32:end:
6382     # . restore registers
6383     59/pop-to-ecx
6384     58/pop-to-eax
6385     # . epilogue
6386     89/<- %esp 5/r32/ebp
6387     5d/pop-to-ebp
6388     c3/return
6389 
6390 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6391     # . prologue
6392     55/push-ebp
6393     89/<- %ebp 4/r32/esp
6394     # . save registers
6395     50/push-eax
6396     51/push-ecx
6397     # if (location == 0) return
6398     81 7/subop/compare *(ebp+0xc) 0/imm32
6399     0f 84/jump-if-= $emit-subx-disp32:end/disp32
6400     #
6401     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6402     (write-buffered *(ebp+8) Space)
6403     (write-buffered *(ebp+8) *eax)  # Var-name
6404     # hack: if instruction operation starts with "break", emit ":break"
6405     # var name/ecx: (addr array byte) = stmt->operation
6406     8b/-> *(ebp+0x10) 0/r32/eax
6407     8b/-> *(eax+4) 1/r32/ecx
6408     {
6409       (string-starts-with? %ecx "break")  # => eax
6410       3d/compare-eax-and 0/imm32
6411       74/jump-if-= break/disp8
6412       (write-buffered *(ebp+8) ":break")
6413     }
6414     # hack: if instruction operation starts with "loop", emit ":loop"
6415     {
6416       (string-starts-with? %ecx "loop")  # => eax
6417       3d/compare-eax-and 0/imm32
6418       74/jump-if-= break/disp8
6419       (write-buffered *(ebp+8) ":loop")
6420     }
6421     (write-buffered *(ebp+8) "/disp32")
6422 $emit-subx-disp32:end:
6423     # . restore registers
6424     59/pop-to-ecx
6425     58/pop-to-eax
6426     # . epilogue
6427     89/<- %esp 5/r32/ebp
6428     5d/pop-to-ebp
6429     c3/return
6430 
6431 emit-subx-call:  # out: (addr buffered-file), stmt: (handle statement), callee: (handle function)
6432     # . prologue
6433     55/push-ebp
6434     89/<- %ebp 4/r32/esp
6435     # . save registers
6436     50/push-eax
6437     51/push-ecx
6438     #
6439     (emit-indent *(ebp+8) *Curr-block-depth)
6440     (write-buffered *(ebp+8) "(")
6441     # - emit function name
6442     8b/-> *(ebp+0x10) 1/r32/ecx
6443     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
6444     # - emit arguments
6445     # var curr/ecx: (handle list var) = stmt->inouts
6446     8b/-> *(ebp+0xc) 1/r32/ecx
6447     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
6448     {
6449       # if (curr == null) break
6450       81 7/subop/compare %ecx 0/imm32
6451       74/jump-if-= break/disp8
6452       #
6453       (emit-subx-call-operand *(ebp+8) *ecx)
6454       # curr = curr->next
6455       8b/-> *(ecx+4) 1/r32/ecx
6456       eb/jump loop/disp8
6457     }
6458     #
6459     (write-buffered *(ebp+8) ")\n")
6460 $emit-subx-call:end:
6461     # . restore registers
6462     59/pop-to-ecx
6463     58/pop-to-eax
6464     # . epilogue
6465     89/<- %esp 5/r32/ebp
6466     5d/pop-to-ebp
6467     c3/return
6468 
6469 emit-subx-call-operand:  # out: (addr buffered-file), operand: (handle variable)
6470     # shares code with emit-subx-var-as-rm32
6471     # . prologue
6472     55/push-ebp
6473     89/<- %ebp 4/r32/esp
6474     # . save registers
6475     50/push-eax
6476     # eax = operand
6477     8b/-> *(ebp+0xc) 0/r32/eax
6478     # if (operand->register) emit "%__"
6479     {
6480       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
6481       74/jump-if-= break/disp8
6482 $emit-subx-call-operand:register:
6483       (write-buffered *(ebp+8) " %")
6484       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
6485       e9/jump $emit-subx-call-operand:end/disp32
6486     }
6487     # else if (operand->stack-offset) emit "*(ebp+__)"
6488     {
6489       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
6490       74/jump-if-= break/disp8
6491 $emit-subx-call-operand:stack:
6492       (write-buffered *(ebp+8) Space)
6493       (write-buffered *(ebp+8) "*(ebp+")
6494       8b/-> *(ebp+0xc) 0/r32/eax
6495       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
6496       (write-buffered *(ebp+8) ")")
6497       e9/jump $emit-subx-call-operand:end/disp32
6498     }
6499     # else if (operand->type == literal) emit "__"
6500     {
6501       50/push-eax
6502       8b/-> *(eax+4) 0/r32/eax  # Var-type
6503       81 7/subop/compare *eax 0/imm32  # Tree-left
6504       58/pop-to-eax
6505       75/jump-if-!= break/disp8
6506 $emit-subx-call-operand:literal:
6507       (write-buffered *(ebp+8) Space)
6508       (write-buffered *(ebp+8) *eax)
6509     }
6510 $emit-subx-call-operand:end:
6511     # . restore registers
6512     58/pop-to-eax
6513     # . epilogue
6514     89/<- %esp 5/r32/ebp
6515     5d/pop-to-ebp
6516     c3/return
6517 
6518 emit-subx-var-as-rm32:  # out: (addr buffered-file), operand: (handle variable)
6519     # . prologue
6520     55/push-ebp
6521     89/<- %ebp 4/r32/esp
6522     # . save registers
6523     50/push-eax
6524     # eax = operand
6525     8b/-> *(ebp+0xc) 0/r32/eax
6526     # if (operand->register) emit "%__"
6527     {
6528       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
6529       74/jump-if-= break/disp8
6530 $emit-subx-var-as-rm32:register:
6531       (write-buffered *(ebp+8) " %")
6532       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
6533     }
6534     # else if (operand->stack-offset) emit "*(ebp+__)"
6535     {
6536       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
6537       74/jump-if-= break/disp8
6538 $emit-subx-var-as-rm32:stack:
6539       (write-buffered *(ebp+8) Space)
6540       (write-buffered *(ebp+8) "*(ebp+")
6541       8b/-> *(ebp+0xc) 0/r32/eax
6542       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
6543       (write-buffered *(ebp+8) ")")
6544     }
6545 $emit-subx-var-as-rm32:end:
6546     # . restore registers
6547     58/pop-to-eax
6548     # . epilogue
6549     89/<- %esp 5/r32/ebp
6550     5d/pop-to-ebp
6551     c3/return
6552 
6553 find-matching-function:  # functions: (addr function), stmt: (handle statement) -> result/eax: (handle function)
6554     # . prologue
6555     55/push-ebp
6556     89/<- %ebp 4/r32/esp
6557     # . save registers
6558     51/push-ecx
6559     # var curr/ecx: (handle function) = functions
6560     8b/-> *(ebp+8) 1/r32/ecx
6561     {
6562       # if (curr == null) break
6563       81 7/subop/compare %ecx 0/imm32
6564       74/jump-if-= break/disp8
6565       # if match(stmt, curr) return curr
6566       {
6567         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
6568         3d/compare-eax-and 0/imm32
6569         74/jump-if-= break/disp8
6570         89/<- %eax 1/r32/ecx
6571         eb/jump $find-matching-function:end/disp8
6572       }
6573       # curr = curr->next
6574       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
6575       eb/jump loop/disp8
6576     }
6577     # return null
6578     b8/copy-to-eax 0/imm32
6579 $find-matching-function:end:
6580     # . restore registers
6581     59/pop-to-ecx
6582     # . epilogue
6583     89/<- %esp 5/r32/ebp
6584     5d/pop-to-ebp
6585     c3/return
6586 
6587 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle statement) -> result/eax: (handle primitive)
6588     # . prologue
6589     55/push-ebp
6590     89/<- %ebp 4/r32/esp
6591     # . save registers
6592     51/push-ecx
6593     # var curr/ecx: (handle primitive) = primitives
6594     8b/-> *(ebp+8) 1/r32/ecx
6595     {
6596 $find-matching-primitive:loop:
6597       # if (curr == null) break
6598       81 7/subop/compare %ecx 0/imm32
6599       0f 84/jump-if-= break/disp32
6600 #?       (write-buffered Stderr "prim: ")
6601 #?       (write-buffered Stderr *ecx)  # Primitive-name
6602 #?       (write-buffered Stderr " => ")
6603 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
6604 #?       (write-buffered Stderr Newline)
6605 #?       (flush Stderr)
6606       # if match(curr, stmt) return curr
6607       {
6608         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
6609         3d/compare-eax-and 0/imm32
6610         74/jump-if-= break/disp8
6611         89/<- %eax 1/r32/ecx
6612         eb/jump $find-matching-primitive:end/disp8
6613       }
6614 $find-matching-primitive:next-primitive:
6615       # curr = curr->next
6616       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
6617       e9/jump loop/disp32
6618     }
6619     # return null
6620     b8/copy-to-eax 0/imm32
6621 $find-matching-primitive:end:
6622     # . restore registers
6623     59/pop-to-ecx
6624     # . epilogue
6625     89/<- %esp 5/r32/ebp
6626     5d/pop-to-ebp
6627     c3/return
6628 
6629 mu-stmt-matches-function?:  # stmt: (handle statement), function: (handle function) => result/eax: boolean
6630     # . prologue
6631     55/push-ebp
6632     89/<- %ebp 4/r32/esp
6633     # . save registers
6634     51/push-ecx
6635     # return function->name == stmt->operation
6636     8b/-> *(ebp+8) 1/r32/ecx
6637     8b/-> *(ebp+0xc) 0/r32/eax
6638     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
6639 $mu-stmt-matches-function?:end:
6640     # . restore registers
6641     59/pop-to-ecx
6642     # . epilogue
6643     89/<- %esp 5/r32/ebp
6644     5d/pop-to-ebp
6645     c3/return
6646 
6647 mu-stmt-matches-primitive?:  # stmt: (handle statement), primitive: (handle primitive) => result/eax: boolean
6648     # A mu stmt matches a primitive if the name matches, all the inout vars
6649     # match, and all the output vars match.
6650     # Vars match if types match and registers match.
6651     # In addition, a stmt output matches a primitive's output if types match
6652     # and the primitive has a wildcard register.
6653     # . prologue
6654     55/push-ebp
6655     89/<- %ebp 4/r32/esp
6656     # . save registers
6657     51/push-ecx
6658     52/push-edx
6659     53/push-ebx
6660     56/push-esi
6661     57/push-edi
6662     # ecx = stmt
6663     8b/-> *(ebp+8) 1/r32/ecx
6664     # edx = primitive
6665     8b/-> *(ebp+0xc) 2/r32/edx
6666     {
6667 $mu-stmt-matches-primitive?:check-name:
6668       # if (primitive->name != stmt->operation) return false
6669       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
6670       3d/compare-eax-and 0/imm32
6671       75/jump-if-!= break/disp8
6672       b8/copy-to-eax 0/imm32
6673       e9/jump $mu-stmt-matches-primitive?:end/disp32
6674     }
6675 $mu-stmt-matches-primitive?:check-inouts:
6676     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
6677     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts
6678     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
6679     {
6680       # if (curr == 0 && curr2 == 0) move on to check outputs
6681       {
6682         81 7/subop/compare %esi 0/imm32
6683         75/jump-if-!= break/disp8
6684 $mu-stmt-matches-primitive?:stmt-inout-is-null:
6685         {
6686           81 7/subop/compare %edi 0/imm32
6687           75/jump-if-!= break/disp8
6688           #
6689           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
6690         }
6691         # return false
6692         b8/copy-to-eax 0/imm32/false
6693         e9/jump $mu-stmt-matches-primitive?:end/disp32
6694       }
6695       # if (curr2 == 0) return false
6696       {
6697         81 7/subop/compare %edi 0/imm32
6698         75/jump-if-!= break/disp8
6699 $mu-stmt-matches-primitive?:prim-inout-is-null:
6700         b8/copy-to-eax 0/imm32/false
6701         e9/jump $mu-stmt-matches-primitive?:end/disp32
6702       }
6703       # if (curr != curr2) return false
6704       {
6705         (operand-matches-primitive? *esi *edi)  # => eax
6706         3d/compare-eax-and 0/imm32
6707         75/jump-if-!= break/disp8
6708         b8/copy-to-eax 0/imm32/false
6709         e9/jump $mu-stmt-matches-primitive?:end/disp32
6710       }
6711       # curr=curr->next
6712       8b/-> *(esi+4) 6/r32/esi  # Operand-next
6713       # curr2=curr2->next
6714       8b/-> *(edi+4) 7/r32/edi  # Operand-next
6715       eb/jump loop/disp8
6716     }
6717 $mu-stmt-matches-primitive?:check-outputs:
6718     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
6719     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
6720     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
6721     {
6722       # if (curr == 0) return (curr2 == 0)
6723       {
6724 $mu-stmt-matches-primitive?:check-output:
6725         81 7/subop/compare %esi 0/imm32
6726         75/jump-if-!= break/disp8
6727         {
6728           81 7/subop/compare %edi 0/imm32
6729           75/jump-if-!= break/disp8
6730           # return true
6731           b8/copy-to-eax 1/imm32
6732           e9/jump $mu-stmt-matches-primitive?:end/disp32
6733         }
6734         # return false
6735         b8/copy-to-eax 0/imm32
6736         e9/jump $mu-stmt-matches-primitive?:end/disp32
6737       }
6738       # if (curr2 == 0) return false
6739       {
6740         81 7/subop/compare %edi 0/imm32
6741         75/jump-if-!= break/disp8
6742         b8/copy-to-eax 0/imm32
6743         e9/jump $mu-stmt-matches-primitive?:end/disp32
6744       }
6745       # if (curr != curr2) return false
6746       {
6747         (operand-matches-primitive? *esi *edi)  # List-value List-value => eax
6748         3d/compare-eax-and 0/imm32
6749         75/jump-if-!= break/disp8
6750         b8/copy-to-eax 0/imm32
6751         e9/jump $mu-stmt-matches-primitive?:end/disp32
6752       }
6753       # curr=curr->next
6754       8b/-> *(esi+4) 6/r32/esi  # Operand-next
6755       # curr2=curr2->next
6756       8b/-> *(edi+4) 7/r32/edi  # Operand-next
6757       eb/jump loop/disp8
6758     }
6759 $mu-stmt-matches-primitive?:return-true:
6760     b8/copy-to-eax 1/imm32
6761 $mu-stmt-matches-primitive?:end:
6762     # . restore registers
6763     5f/pop-to-edi
6764     5e/pop-to-esi
6765     5b/pop-to-ebx
6766     5a/pop-to-edx
6767     59/pop-to-ecx
6768     # . epilogue
6769     89/<- %esp 5/r32/ebp
6770     5d/pop-to-ebp
6771     c3/return
6772 
6773 operand-matches-primitive?:  # var: (handle var), prim-var: (handle var) => result/eax: boolean
6774     # . prologue
6775     55/push-ebp
6776     89/<- %ebp 4/r32/esp
6777     # . save registers
6778     56/push-esi
6779     57/push-edi
6780     # esi = var
6781     8b/-> *(ebp+8) 6/r32/esi
6782     # edi = prim-var
6783     8b/-> *(ebp+0xc) 7/r32/edi
6784     # if (var->type != prim-var->type) return false
6785     (type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
6786     3d/compare-eax-and 0/imm32
6787     b8/copy-to-eax 0/imm32/false
6788     74/jump-if-= $operand-matches-primitive?:end/disp8
6789     # return false if var->register doesn't match prim-var->register
6790     {
6791       # if addresses are equal, don't return here
6792       8b/-> *(esi+0x10) 0/r32/eax
6793       39/compare *(edi+0x10) 0/r32/eax
6794       74/jump-if-= break/disp8
6795       # if either address is 0, return false
6796       3d/compare-eax-and 0/imm32
6797       74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
6798       81 7/subop/compare *(edi+0x10) 0/imm32
6799       74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
6800       # if prim-var->register is "*", return true
6801       (string-equal? *(edi+0x10) "*")  # Var-register
6802       3d/compare-eax-and 0/imm32
6803       b8/copy-to-eax 1/imm32/true
6804       75/jump-if-!= $operand-matches-primitive?:end/disp8
6805       # if string contents don't match, return false
6806       (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register
6807       3d/compare-eax-and 0/imm32
6808       b8/copy-to-eax 0/imm32/false
6809       74/jump-if-= $operand-matches-primitive?:end/disp8
6810     }
6811     # return true
6812     b8/copy-to-eax 1/imm32/true
6813 $operand-matches-primitive?:end:
6814     # . restore registers
6815     5f/pop-to-edi
6816     5e/pop-to-esi
6817     # . epilogue
6818     89/<- %esp 5/r32/ebp
6819     5d/pop-to-ebp
6820     c3/return
6821 
6822 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) => result/eax: boolean
6823     # . prologue
6824     55/push-ebp
6825     89/<- %ebp 4/r32/esp
6826     # . save registers
6827     51/push-ecx
6828     52/push-edx
6829     # ecx = a
6830     8b/-> *(ebp+8) 1/r32/ecx
6831     # edx = b
6832     8b/-> *(ebp+0xc) 2/r32/edx
6833     # if (a == b) return true
6834     8b/-> %ecx 0/r32/eax  # Var-type
6835     39/compare %edx 0/r32/eax  # Var-type
6836     b8/copy-to-eax 1/imm32/true
6837     74/jump-if-= $type-equal?:end/disp8
6838     # if (a < MAX_TYPE_ID) return false
6839     81 7/subop/compare %ecx 0x10000/imm32
6840     b8/copy-to-eax 0/imm32/false
6841     72/jump-if-addr< $type-equal?:end/disp8
6842     # if (b < MAX_TYPE_ID) return false
6843     81 7/subop/compare %edx 0x10000/imm32
6844     b8/copy-to-eax 0/imm32/false
6845     72/jump-if-addr< $type-equal?:end/disp8
6846     # if (!type-equal?(a->left, b->left)) return false
6847     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
6848     3d/compare-eax-and 0/imm32
6849     74/jump-if-= $type-equal?:end/disp8
6850     # return type-equal?(a->right, b->right)
6851     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
6852 $type-equal?:end:
6853     # . restore registers
6854     5a/pop-to-edx
6855     59/pop-to-ecx
6856     # . epilogue
6857     89/<- %esp 5/r32/ebp
6858     5d/pop-to-ebp
6859     c3/return
6860 
6861 test-emit-subx-statement-primitive:
6862     # Primitive operation on a variable on the stack.
6863     #   increment foo
6864     # =>
6865     #   ff 0/subop/increment *(ebp-8)
6866     #
6867     # There's a variable on the var stack as follows:
6868     #   name: 'foo'
6869     #   type: int
6870     #   stack-offset: -8
6871     #
6872     # There's a primitive with this info:
6873     #   name: 'increment'
6874     #   inouts: int/mem
6875     #   value: 'ff 0/subop/increment'
6876     #
6877     # There's nothing in functions.
6878     #
6879     # . prologue
6880     55/push-ebp
6881     89/<- %ebp 4/r32/esp
6882     # setup
6883     (clear-stream _test-output-stream)
6884     (clear-stream $_test-output-buffered-file->buffer)
6885     # var type/ecx: (handle tree type-id) = int
6886     68/push 0/imm32/right/null
6887     68/push 1/imm32/left/int
6888     89/<- %ecx 4/r32/esp
6889     # var var-foo/ecx: var
6890     68/push 0/imm32/no-register
6891     68/push -8/imm32/stack-offset
6892     68/push 1/imm32/block-depth
6893     51/push-ecx
6894     68/push "foo"/imm32
6895     89/<- %ecx 4/r32/esp
6896     # var operand/ebx: (list var)
6897     68/push 0/imm32/next
6898     51/push-ecx/var-foo
6899     89/<- %ebx 4/r32/esp
6900     # var stmt/esi: statement
6901     68/push 0/imm32/next
6902     68/push 0/imm32/outputs
6903     53/push-ebx/operands
6904     68/push "increment"/imm32/operation
6905     68/push 1/imm32
6906     89/<- %esi 4/r32/esp
6907     # var primitives/ebx: primitive
6908     68/push 0/imm32/next
6909     68/push 0/imm32/output-is-write-only
6910     68/push 0/imm32/no-disp32
6911     68/push 0/imm32/no-imm32
6912     68/push 0/imm32/no-r32
6913     68/push 1/imm32/rm32-is-first-inout
6914     68/push "ff 0/subop/increment"/imm32/subx-name
6915     68/push 0/imm32/outputs
6916     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
6917     68/push "increment"/imm32/name
6918     89/<- %ebx 4/r32/esp
6919     # convert
6920     c7 0/subop/copy *Curr-block-depth 0/imm32
6921     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
6922     (flush _test-output-buffered-file)
6923 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
6929     # check output
6930     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive")
6931     # . epilogue
6932     89/<- %esp 5/r32/ebp
6933     5d/pop-to-ebp
6934     c3/return
6935 
6936 test-emit-subx-statement-primitive-register:
6937     # Primitive operation on a variable in a register.
6938     #   foo <- increment
6939     # =>
6940     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
6941     #
6942     # There's a variable on the var stack as follows:
6943     #   name: 'foo'
6944     #   type: int
6945     #   register: 'eax'
6946     #
6947     # There's a primitive with this info:
6948     #   name: 'increment'
6949     #   out: int/reg
6950     #   value: 'ff 0/subop/increment'
6951     #
6952     # There's nothing in functions.
6953     #
6954     # . prologue
6955     55/push-ebp
6956     89/<- %ebp 4/r32/esp
6957     # setup
6958     (clear-stream _test-output-stream)
6959     (clear-stream $_test-output-buffered-file->buffer)
6960     # var type/ecx: (handle tree type-id) = int
6961     68/push 0/imm32/right/null
6962     68/push 1/imm32/left/int
6963     89/<- %ecx 4/r32/esp
6964     # var var-foo/ecx: var in eax
6965     68/push "eax"/imm32/register
6966     68/push 0/imm32/no-stack-offset
6967     68/push 1/imm32/block-depth
6968     51/push-ecx
6969     68/push "foo"/imm32
6970     89/<- %ecx 4/r32/esp
6971     # var operand/ebx: (list var)
6972     68/push 0/imm32/next
6973     51/push-ecx/var-foo
6974     89/<- %ebx 4/r32/esp
6975     # var stmt/esi: statement
6976     68/push 0/imm32/next
6977     53/push-ebx/outputs
6978     68/push 0/imm32/inouts
6979     68/push "increment"/imm32/operation
6980     68/push 1/imm32
6981     89/<- %esi 4/r32/esp
6982     # var formal-var/ebx: var in any register
6983     68/push Any-register/imm32
6984     68/push 0/imm32/no-stack-offset
6985     68/push 1/imm32/block-depth
6986     ff 6/subop/push *(ecx+4)  # Var-type
6987     68/push "dummy"/imm32
6988     89/<- %ebx 4/r32/esp
6989     # var operand/ebx: (list var)
6990     68/push 0/imm32/next
6991     53/push-ebx/formal-var
6992     89/<- %ebx 4/r32/esp
6993     # var primitives/ebx: primitive
6994     68/push 0/imm32/next
6995     68/push 0/imm32/output-is-write-only
6996     68/push 0/imm32/no-disp32
6997     68/push 0/imm32/no-imm32
6998     68/push 0/imm32/no-r32
6999     68/push 3/imm32/rm32-in-first-output
7000     68/push "ff 0/subop/increment"/imm32/subx-name
7001     53/push-ebx/outputs
7002     68/push 0/imm32/inouts
7003     68/push "increment"/imm32/name
7004     89/<- %ebx 4/r32/esp
7005     # convert
7006     c7 0/subop/copy *Curr-block-depth 0/imm32
7007     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
7008     (flush _test-output-buffered-file)
7009 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7015     # check output
7016     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register")
7017     # . epilogue
7018     89/<- %esp 5/r32/ebp
7019     5d/pop-to-ebp
7020     c3/return
7021 
7022 test-emit-subx-statement-select-primitive:
7023     # Select the right primitive between overloads.
7024     #   foo <- increment
7025     # =>
7026     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
7027     #
7028     # There's a variable on the var stack as follows:
7029     #   name: 'foo'
7030     #   type: int
7031     #   register: 'eax'
7032     #
7033     # There's two primitives, as follows:
7034     #   - name: 'increment'
7035     #     out: int/reg
7036     #     value: 'ff 0/subop/increment'
7037     #   - name: 'increment'
7038     #     inout: int/mem
7039     #     value: 'ff 0/subop/increment'
7040     #
7041     # There's nothing in functions.
7042     #
7043     # . prologue
7044     55/push-ebp
7045     89/<- %ebp 4/r32/esp
7046     # setup
7047     (clear-stream _test-output-stream)
7048     (clear-stream $_test-output-buffered-file->buffer)
7049     # var type/ecx: (handle tree type-id) = int
7050     68/push 0/imm32/right/null
7051     68/push 1/imm32/left/int
7052     89/<- %ecx 4/r32/esp
7053     # var var-foo/ecx: var in eax
7054     68/push "eax"/imm32/register
7055     68/push 0/imm32/no-stack-offset
7056     68/push 1/imm32/block-depth
7057     51/push-ecx
7058     68/push "foo"/imm32
7059     89/<- %ecx 4/r32/esp
7060     # var real-outputs/edi: (list var)
7061     68/push 0/imm32/next
7062     51/push-ecx/var-foo
7063     89/<- %edi 4/r32/esp
7064     # var stmt/esi: statement
7065     68/push 0/imm32/next
7066     57/push-edi/outputs
7067     68/push 0/imm32/inouts
7068     68/push "increment"/imm32/operation
7069     68/push 1/imm32
7070     89/<- %esi 4/r32/esp
7071     # var formal-var/ebx: var in any register
7072     68/push Any-register/imm32
7073     68/push 0/imm32/no-stack-offset
7074     68/push 1/imm32/block-depth
7075     ff 6/subop/push *(ecx+4)  # Var-type
7076     68/push "dummy"/imm32
7077     89/<- %ebx 4/r32/esp
7078     # var formal-outputs/ebx: (list var) = {formal-var, 0}
7079     68/push 0/imm32/next
7080     53/push-ebx/formal-var
7081     89/<- %ebx 4/r32/esp
7082     # var primitive1/ebx: primitive
7083     68/push 0/imm32/next
7084     68/push 0/imm32/output-is-write-only
7085     68/push 0/imm32/no-disp32
7086     68/push 0/imm32/no-imm32
7087     68/push 0/imm32/no-r32
7088     68/push 3/imm32/rm32-in-first-output
7089     68/push "ff 0/subop/increment"/imm32/subx-name
7090     53/push-ebx/outputs/formal-outputs
7091     68/push 0/imm32/inouts
7092     68/push "increment"/imm32/name
7093     89/<- %ebx 4/r32/esp
7094     # var primitives/ebx: primitive
7095     53/push-ebx/next
7096     68/push 0/imm32/output-is-write-only
7097     68/push 0/imm32/no-disp32
7098     68/push 0/imm32/no-imm32
7099     68/push 0/imm32/no-r32
7100     68/push 1/imm32/rm32-is-first-inout
7101     68/push "ff 0/subop/increment"/imm32/subx-name
7102     68/push 0/imm32/outputs
7103     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
7104     68/push "increment"/imm32/name
7105     89/<- %ebx 4/r32/esp
7106     # convert
7107     c7 0/subop/copy *Curr-block-depth 0/imm32
7108     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
7109     (flush _test-output-buffered-file)
7110 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7116     # check output
7117     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive")
7118     # . epilogue
7119     89/<- %esp 5/r32/ebp
7120     5d/pop-to-ebp
7121     c3/return
7122 
7123 test-emit-subx-statement-select-primitive-2:
7124     # Select the right primitive between overloads.
7125     #   foo <- increment
7126     # =>
7127     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
7128     #
7129     # There's a variable on the var stack as follows:
7130     #   name: 'foo'
7131     #   type: int
7132     #   register: 'eax'
7133     #
7134     # There's two primitives, as follows:
7135     #   - name: 'increment'
7136     #     out: int/reg
7137     #     value: 'ff 0/subop/increment'
7138     #   - name: 'increment'
7139     #     inout: int/mem
7140     #     value: 'ff 0/subop/increment'
7141     #
7142     # There's nothing in functions.
7143     #
7144     # . prologue
7145     55/push-ebp
7146     89/<- %ebp 4/r32/esp
7147     # setup
7148     (clear-stream _test-output-stream)
7149     (clear-stream $_test-output-buffered-file->buffer)
7150     # var type/ecx: (handle tree type-id) = int
7151     68/push 0/imm32/right/null
7152     68/push 1/imm32/left/int
7153     89/<- %ecx 4/r32/esp
7154     # var var-foo/ecx: var in eax
7155     68/push "eax"/imm32/register
7156     68/push 0/imm32/no-stack-offset
7157     68/push 1/imm32/block-depth
7158     51/push-ecx
7159     68/push "foo"/imm32
7160     89/<- %ecx 4/r32/esp
7161     # var inouts/edi: (list var)
7162     68/push 0/imm32/next
7163     51/push-ecx/var-foo
7164     89/<- %edi 4/r32/esp
7165     # var stmt/esi: statement
7166     68/push 0/imm32/next
7167     68/push 0/imm32/outputs
7168     57/push-edi/inouts
7169     68/push "increment"/imm32/operation
7170     68/push 1/imm32
7171     89/<- %esi 4/r32/esp
7172     # var formal-var/ebx: var in any register
7173     68/push Any-register/imm32
7174     68/push 0/imm32/no-stack-offset
7175     68/push 1/imm32/block-depth
7176     ff 6/subop/push *(ecx+4)  # Var-type
7177     68/push "dummy"/imm32
7178     89/<- %ebx 4/r32/esp
7179     # var operand/ebx: (list var)
7180     68/push 0/imm32/next
7181     53/push-ebx/formal-var
7182     89/<- %ebx 4/r32/esp
7183     # var primitive1/ebx: primitive
7184     68/push 0/imm32/next
7185     68/push 0/imm32/output-is-write-only
7186     68/push 0/imm32/no-disp32
7187     68/push 0/imm32/no-imm32
7188     68/push 0/imm32/no-r32
7189     68/push 3/imm32/rm32-in-first-output
7190     68/push "ff 0/subop/increment"/imm32/subx-name
7191     53/push-ebx/outputs/formal-outputs
7192     68/push 0/imm32/inouts
7193     68/push "increment"/imm32/name
7194     89/<- %ebx 4/r32/esp
7195     # var primitives/ebx: primitive
7196     53/push-ebx/next
7197     68/push 0/imm32/output-is-write-only
7198     68/push 0/imm32/no-disp32
7199     68/push 0/imm32/no-imm32
7200     68/push 0/imm32/no-r32
7201     68/push 1/imm32/rm32-is-first-inout
7202     68/push "ff 0/subop/increment"/imm32/subx-name
7203     68/push 0/imm32/outputs
7204     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
7205     68/push "increment"/imm32/name
7206     89/<- %ebx 4/r32/esp
7207     # convert
7208     c7 0/subop/copy *Curr-block-depth 0/imm32
7209     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
7210     (flush _test-output-buffered-file)
7211 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7217     # check output
7218     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2")
7219     # . epilogue
7220     89/<- %esp 5/r32/ebp
7221     5d/pop-to-ebp
7222     c3/return
7223 
7224 test-increment-register:
7225     # Select the right register between overloads.
7226     #   foo <- increment
7227     # =>
7228     #   50/increment-eax
7229     #
7230     # There's a variable on the var stack as follows:
7231     #   name: 'foo'
7232     #   type: int
7233     #   register: 'eax'
7234     #
7235     # Primitives are the global definitions.
7236     #
7237     # There are no functions defined.
7238     #
7239     # . prologue
7240     55/push-ebp
7241     89/<- %ebp 4/r32/esp
7242     # setup
7243     (clear-stream _test-output-stream)
7244     (clear-stream $_test-output-buffered-file->buffer)
7245     # var type/ecx: (handle tree type-id) = int
7246     68/push 0/imm32/right/null
7247     68/push 1/imm32/left/int
7248     89/<- %ecx 4/r32/esp
7249     # var var-foo/ecx: var in eax
7250     68/push "eax"/imm32/register
7251     68/push 0/imm32/no-stack-offset
7252     68/push 1/imm32/block-depth
7253     51/push-ecx
7254     68/push "foo"/imm32
7255     89/<- %ecx 4/r32/esp
7256     # var real-outputs/edi: (list var)
7257     68/push 0/imm32/next
7258     51/push-ecx/var-foo
7259     89/<- %edi 4/r32/esp
7260     # var stmt/esi: statement
7261     68/push 0/imm32/next
7262     57/push-edi/outputs
7263     68/push 0/imm32/inouts
7264     68/push "increment"/imm32/operation
7265     68/push 1/imm32/regular-statement
7266     89/<- %esi 4/r32/esp
7267     # convert
7268     c7 0/subop/copy *Curr-block-depth 0/imm32
7269     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7270     (flush _test-output-buffered-file)
7271 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7277     # check output
7278     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
7279     # . epilogue
7280     89/<- %esp 5/r32/ebp
7281     5d/pop-to-ebp
7282     c3/return
7283 
7284 test-increment-var:
7285     # Select the right primitive between overloads.
7286     #   foo <- increment
7287     # =>
7288     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
7289     #
7290     # There's a variable on the var stack as follows:
7291     #   name: 'foo'
7292     #   type: int
7293     #   register: 'eax'
7294     #
7295     # Primitives are the global definitions.
7296     #
7297     # There are no functions defined.
7298     #
7299     # . prologue
7300     55/push-ebp
7301     89/<- %ebp 4/r32/esp
7302     # setup
7303     (clear-stream _test-output-stream)
7304     (clear-stream $_test-output-buffered-file->buffer)
7305     # var type/ecx: (handle tree type-id) = int
7306     68/push 0/imm32/right/null
7307     68/push 1/imm32/left/int
7308     89/<- %ecx 4/r32/esp
7309     # var var-foo/ecx: var in eax
7310     68/push "eax"/imm32/register
7311     68/push 0/imm32/no-stack-offset
7312     68/push 1/imm32/block-depth
7313     51/push-ecx
7314     68/push "foo"/imm32
7315     89/<- %ecx 4/r32/esp
7316     # var inouts/edi: (list var)
7317     68/push 0/imm32/next
7318     51/push-ecx/var-foo
7319     89/<- %edi 4/r32/esp
7320     # var stmt/esi: statement
7321     68/push 0/imm32/next
7322     68/push 0/imm32/outputs
7323     57/push-edi/inouts
7324     68/push "increment"/imm32/operation
7325     68/push 1/imm32
7326     89/<- %esi 4/r32/esp
7327     # convert
7328     c7 0/subop/copy *Curr-block-depth 0/imm32
7329     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7330     (flush _test-output-buffered-file)
7331 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7337     # check output
7338     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-var")
7339     # . epilogue
7340     89/<- %esp 5/r32/ebp
7341     5d/pop-to-ebp
7342     c3/return
7343 
7344 test-add-reg-to-reg:
7345     #   var1/reg <- add var2/reg
7346     # =>
7347     #   01/add %var1 var2
7348     #
7349     # . prologue
7350     55/push-ebp
7351     89/<- %ebp 4/r32/esp
7352     # setup
7353     (clear-stream _test-output-stream)
7354     (clear-stream $_test-output-buffered-file->buffer)
7355     # var type/ecx: (handle tree type-id) = int
7356     68/push 0/imm32/right/null
7357     68/push 1/imm32/left/int
7358     89/<- %ecx 4/r32/esp
7359     # var var-var1/ecx: var in eax
7360     68/push "eax"/imm32/register
7361     68/push 0/imm32/no-stack-offset
7362     68/push 1/imm32/block-depth
7363     51/push-ecx
7364     68/push "var1"/imm32
7365     89/<- %ecx 4/r32/esp
7366     # var var-var2/edx: var in ecx
7367     68/push "ecx"/imm32/register
7368     68/push 0/imm32/no-stack-offset
7369     68/push 1/imm32/block-depth
7370     ff 6/subop/push *(ecx+4)  # Var-type
7371     68/push "var2"/imm32
7372     89/<- %edx 4/r32/esp
7373     # var inouts/esi: (list var2)
7374     68/push 0/imm32/next
7375     52/push-edx/var-var2
7376     89/<- %esi 4/r32/esp
7377     # var outputs/edi: (list var1)
7378     68/push 0/imm32/next
7379     51/push-ecx/var-var1
7380     89/<- %edi 4/r32/esp
7381     # var stmt/esi: statement
7382     68/push 0/imm32/next
7383     57/push-edi/outputs
7384     56/push-esi/inouts
7385     68/push "add"/imm32/operation
7386     68/push 1/imm32
7387     89/<- %esi 4/r32/esp
7388     # convert
7389     c7 0/subop/copy *Curr-block-depth 0/imm32
7390     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7391     (flush _test-output-buffered-file)
7392 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7398     # check output
7399     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
7400     # . epilogue
7401     89/<- %esp 5/r32/ebp
7402     5d/pop-to-ebp
7403     c3/return
7404 
7405 test-add-reg-to-mem:
7406     #   add-to var1 var2/reg
7407     # =>
7408     #   01/add *(ebp+__) var2
7409     #
7410     # . prologue
7411     55/push-ebp
7412     89/<- %ebp 4/r32/esp
7413     # setup
7414     (clear-stream _test-output-stream)
7415     (clear-stream $_test-output-buffered-file->buffer)
7416     # var type/ecx: (handle tree type-id) = int
7417     68/push 0/imm32/right/null
7418     68/push 1/imm32/left/int
7419     89/<- %ecx 4/r32/esp
7420     # var var-var1/ecx: var
7421     68/push 0/imm32/no-register
7422     68/push 8/imm32/stack-offset
7423     68/push 1/imm32/block-depth
7424     51/push-ecx
7425     68/push "var1"/imm32
7426     89/<- %ecx 4/r32/esp
7427     # var var-var2/edx: var in ecx
7428     68/push "ecx"/imm32/register
7429     68/push 0/imm32/no-stack-offset
7430     68/push 1/imm32/block-depth
7431     ff 6/subop/push *(ecx+4)  # Var-type
7432     68/push "var2"/imm32
7433     89/<- %edx 4/r32/esp
7434     # var inouts/esi: (list var2)
7435     68/push 0/imm32/next
7436     52/push-edx/var-var2
7437     89/<- %esi 4/r32/esp
7438     # var inouts = (list var1 var2)
7439     56/push-esi/next
7440     51/push-ecx/var-var1
7441     89/<- %esi 4/r32/esp
7442     # var stmt/esi: statement
7443     68/push 0/imm32/next
7444     68/push 0/imm32/outputs
7445     56/push-esi/inouts
7446     68/push "add-to"/imm32/operation
7447     68/push 1/imm32
7448     89/<- %esi 4/r32/esp
7449     # convert
7450     c7 0/subop/copy *Curr-block-depth 0/imm32
7451     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7452     (flush _test-output-buffered-file)
7453 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7459     # check output
7460     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
7461     # . epilogue
7462     89/<- %esp 5/r32/ebp
7463     5d/pop-to-ebp
7464     c3/return
7465 
7466 test-add-mem-to-reg:
7467     #   var1/reg <- add var2
7468     # =>
7469     #   03/add *(ebp+__) var1
7470     #
7471     # . prologue
7472     55/push-ebp
7473     89/<- %ebp 4/r32/esp
7474     # setup
7475     (clear-stream _test-output-stream)
7476     (clear-stream $_test-output-buffered-file->buffer)
7477     # var type/ecx: (handle tree type-id) = int
7478     68/push 0/imm32/right/null
7479     68/push 1/imm32/left/int
7480     89/<- %ecx 4/r32/esp
7481     # var var-var1/ecx: var in eax
7482     68/push "eax"/imm32/register
7483     68/push 0/imm32/no-stack-offset
7484     68/push 1/imm32/block-depth
7485     51/push-ecx
7486     68/push "var1"/imm32
7487     89/<- %ecx 4/r32/esp
7488     # var var-var2/edx: var
7489     68/push 0/imm32/no-register
7490     68/push 8/imm32/stack-offset
7491     68/push 1/imm32/block-depth
7492     ff 6/subop/push *(ecx+4)  # Var-type
7493     68/push "var2"/imm32
7494     89/<- %edx 4/r32/esp
7495     # var inouts/esi: (list var2)
7496     68/push 0/imm32/next
7497     52/push-edx/var-var2
7498     89/<- %esi 4/r32/esp
7499     # var outputs/edi: (list var1)
7500     68/push 0/imm32/next
7501     51/push-ecx/var-var1
7502     89/<- %edi 4/r32/esp
7503     # var stmt/esi: statement
7504     68/push 0/imm32/next
7505     57/push-edi/outputs
7506     56/push-esi/inouts
7507     68/push "add"/imm32/operation
7508     68/push 1/imm32
7509     89/<- %esi 4/r32/esp
7510     # convert
7511     c7 0/subop/copy *Curr-block-depth 0/imm32
7512     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7513     (flush _test-output-buffered-file)
7514 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7520     # check output
7521     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
7522     # . epilogue
7523     89/<- %esp 5/r32/ebp
7524     5d/pop-to-ebp
7525     c3/return
7526 
7527 test-add-literal-to-eax:
7528     #   var1/eax <- add 0x34
7529     # =>
7530     #   05/add-to-eax 0x34/imm32
7531     #
7532     # . prologue
7533     55/push-ebp
7534     89/<- %ebp 4/r32/esp
7535     # setup
7536     (clear-stream _test-output-stream)
7537     (clear-stream $_test-output-buffered-file->buffer)
7538     # var type/ecx: (handle tree type-id) = int
7539     68/push 0/imm32/right/null
7540     68/push 1/imm32/left/int
7541     89/<- %ecx 4/r32/esp
7542     # var var-var1/ecx: var in eax
7543     68/push "eax"/imm32/register
7544     68/push 0/imm32/no-stack-offset
7545     68/push 1/imm32/block-depth
7546     51/push-ecx
7547     68/push "var1"/imm32
7548     89/<- %ecx 4/r32/esp
7549     # var type/edx: (handle tree type-id) = literal
7550     68/push 0/imm32/right/null
7551     68/push 0/imm32/left/literal
7552     89/<- %edx 4/r32/esp
7553     # var var-var2/edx: var literal
7554     68/push 0/imm32/no-register
7555     68/push 0/imm32/no-stack-offset
7556     68/push 1/imm32/block-depth
7557     52/push-edx
7558     68/push "0x34"/imm32
7559     89/<- %edx 4/r32/esp
7560     # var inouts/esi: (list var2)
7561     68/push 0/imm32/next
7562     52/push-edx/var-var2
7563     89/<- %esi 4/r32/esp
7564     # var outputs/edi: (list var1)
7565     68/push 0/imm32/next
7566     51/push-ecx/var-var1
7567     89/<- %edi 4/r32/esp
7568     # var stmt/esi: statement
7569     68/push 0/imm32/next
7570     57/push-edi/outputs
7571     56/push-esi/inouts
7572     68/push "add"/imm32/operation
7573     68/push 1/imm32
7574     89/<- %esi 4/r32/esp
7575     # convert
7576     c7 0/subop/copy *Curr-block-depth 0/imm32
7577     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7578     (flush _test-output-buffered-file)
7579 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7585     # check output
7586     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
7587     # . epilogue
7588     89/<- %esp 5/r32/ebp
7589     5d/pop-to-ebp
7590     c3/return
7591 
7592 test-add-literal-to-reg:
7593     #   var1/ecx <- add 0x34
7594     # =>
7595     #   81 0/subop/add %ecx 0x34/imm32
7596     #
7597     # . prologue
7598     55/push-ebp
7599     89/<- %ebp 4/r32/esp
7600     # setup
7601     (clear-stream _test-output-stream)
7602     (clear-stream $_test-output-buffered-file->buffer)
7603     # var type/ecx: (handle tree type-id) = int
7604     68/push 0/imm32/right/null
7605     68/push 1/imm32/left/int
7606     89/<- %ecx 4/r32/esp
7607     # var var-var1/ecx: var in ecx
7608     68/push "ecx"/imm32/register
7609     68/push 0/imm32/no-stack-offset
7610     68/push 1/imm32/block-depth
7611     51/push-ecx
7612     68/push "var1"/imm32
7613     89/<- %ecx 4/r32/esp
7614     # var type/edx: (handle tree type-id) = literal
7615     68/push 0/imm32/right/null
7616     68/push 0/imm32/left/literal
7617     89/<- %edx 4/r32/esp
7618     # var var-var2/edx: var literal
7619     68/push 0/imm32/no-register
7620     68/push 0/imm32/no-stack-offset
7621     68/push 1/imm32/block-depth
7622     52/push-edx
7623     68/push "0x34"/imm32
7624     89/<- %edx 4/r32/esp
7625     # var inouts/esi: (list var2)
7626     68/push 0/imm32/next
7627     52/push-edx/var-var2
7628     89/<- %esi 4/r32/esp
7629     # var outputs/edi: (list var1)
7630     68/push 0/imm32/next
7631     51/push-ecx/var-var1
7632     89/<- %edi 4/r32/esp
7633     # var stmt/esi: statement
7634     68/push 0/imm32/next
7635     57/push-edi/outputs
7636     56/push-esi/inouts
7637     68/push "add"/imm32/operation
7638     68/push 1/imm32
7639     89/<- %esi 4/r32/esp
7640     # convert
7641     c7 0/subop/copy *Curr-block-depth 0/imm32
7642     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7643     (flush _test-output-buffered-file)
7644 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7650     # check output
7651     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
7652     # . epilogue
7653     89/<- %esp 5/r32/ebp
7654     5d/pop-to-ebp
7655     c3/return
7656 
7657 test-add-literal-to-mem:
7658     #   add-to var1, 0x34
7659     # =>
7660     #   81 0/subop/add %eax 0x34/imm32
7661     #
7662     # . prologue
7663     55/push-ebp
7664     89/<- %ebp 4/r32/esp
7665     # setup
7666     (clear-stream _test-output-stream)
7667     (clear-stream $_test-output-buffered-file->buffer)
7668     # var type/ecx: (handle tree type-id) = int
7669     68/push 0/imm32/right/null
7670     68/push 1/imm32/left/int
7671     89/<- %ecx 4/r32/esp
7672     # var var-var1/ecx: var
7673     68/push 0/imm32/no-register
7674     68/push 8/imm32/stack-offset
7675     68/push 1/imm32/block-depth
7676     51/push-ecx
7677     68/push "var1"/imm32
7678     89/<- %ecx 4/r32/esp
7679     # var type/edx: (handle tree type-id) = literal
7680     68/push 0/imm32/right/null
7681     68/push 0/imm32/left/literal
7682     89/<- %edx 4/r32/esp
7683     # var var-var2/edx: var literal
7684     68/push 0/imm32/no-register
7685     68/push 0/imm32/no-stack-offset
7686     68/push 1/imm32/block-depth
7687     52/push-edx
7688     68/push "0x34"/imm32
7689     89/<- %edx 4/r32/esp
7690     # var inouts/esi: (list var2)
7691     68/push 0/imm32/next
7692     52/push-edx/var-var2
7693     89/<- %esi 4/r32/esp
7694     # var inouts = (list var1 inouts)
7695     56/push-esi/next
7696     51/push-ecx/var-var1
7697     89/<- %esi 4/r32/esp
7698     # var stmt/esi: statement
7699     68/push 0/imm32/next
7700     68/push 0/imm32/outputs
7701     56/push-esi/inouts
7702     68/push "add-to"/imm32/operation
7703     68/push 1/imm32
7704     89/<- %esi 4/r32/esp
7705     # convert
7706     c7 0/subop/copy *Curr-block-depth 0/imm32
7707     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7708     (flush _test-output-buffered-file)
7709 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7715     # check output
7716     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
7717     # . epilogue
7718     89/<- %esp 5/r32/ebp
7719     5d/pop-to-ebp
7720     c3/return
7721 
7722 test-compare-mem-with-reg:
7723     #   compare var1, var2/eax
7724     # =>
7725     #   39/compare *(ebp+___) 0/r32/eax
7726     #
7727     # . prologue
7728     55/push-ebp
7729     89/<- %ebp 4/r32/esp
7730     # setup
7731     (clear-stream _test-output-stream)
7732     (clear-stream $_test-output-buffered-file->buffer)
7733     # var type/ecx: (handle tree type-id) = int
7734     68/push 0/imm32/right/null
7735     68/push 1/imm32/left/int
7736     89/<- %ecx 4/r32/esp
7737     # var var-var2/ecx: var in eax
7738     68/push "eax"/imm32/register
7739     68/push 0/imm32/no-stack-offset
7740     68/push 1/imm32/block-depth
7741     51/push-ecx
7742     68/push "var2"/imm32
7743     89/<- %ecx 4/r32/esp
7744     # var var-var1/edx: var
7745     68/push 0/imm32/no-register
7746     68/push 8/imm32/stack-offset
7747     68/push 1/imm32/block-depth
7748     ff 6/subop/push *(ecx+4)  # Var-type
7749     68/push "var1"/imm32
7750     89/<- %edx 4/r32/esp
7751     # var inouts/esi: (list var1 var2)
7752     68/push 0/imm32/next
7753     51/push-ecx/var-var2
7754     89/<- %esi 4/r32/esp
7755     56/push-esi
7756     52/push-edx/var-var1
7757     89/<- %esi 4/r32/esp
7758     # var stmt/esi: statement
7759     68/push 0/imm32/next
7760     68/push 0/imm32/outputs
7761     56/push-esi/inouts
7762     68/push "compare"/imm32/operation
7763     68/push 1/imm32
7764     89/<- %esi 4/r32/esp
7765     # convert
7766     c7 0/subop/copy *Curr-block-depth 0/imm32
7767     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7768     (flush _test-output-buffered-file)
7769 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7775     # check output
7776     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
7777     # . epilogue
7778     89/<- %esp 5/r32/ebp
7779     5d/pop-to-ebp
7780     c3/return
7781 
7782 test-compare-reg-with-mem:
7783     #   compare var1/eax, var2
7784     # =>
7785     #   3b/compare *(ebp+___) 0/r32/eax
7786     #
7787     # . prologue
7788     55/push-ebp
7789     89/<- %ebp 4/r32/esp
7790     # setup
7791     (clear-stream _test-output-stream)
7792     (clear-stream $_test-output-buffered-file->buffer)
7793     # var type/ecx: (handle tree type-id) = int
7794     68/push 0/imm32/right/null
7795     68/push 1/imm32/left/int
7796     89/<- %ecx 4/r32/esp
7797     # var var-var1/ecx: var in eax
7798     68/push "eax"/imm32/register
7799     68/push 0/imm32/no-stack-offset
7800     68/push 1/imm32/block-depth
7801     51/push-ecx
7802     68/push "var1"/imm32
7803     89/<- %ecx 4/r32/esp
7804     # var var-var2/edx: var
7805     68/push 0/imm32/no-register
7806     68/push 8/imm32/stack-offset
7807     68/push 1/imm32/block-depth
7808     ff 6/subop/push *(ecx+4)  # Var-type
7809     68/push "var2"/imm32
7810     89/<- %edx 4/r32/esp
7811     # var inouts/esi: (list var1 var2)
7812     68/push 0/imm32/next
7813     52/push-edx/var-var2
7814     89/<- %esi 4/r32/esp
7815     56/push-esi
7816     51/push-ecx/var-var1
7817     89/<- %esi 4/r32/esp
7818     # var stmt/esi: statement
7819     68/push 0/imm32/next
7820     68/push 0/imm32/outputs
7821     56/push-esi/inouts
7822     68/push "compare"/imm32/operation
7823     68/push 1/imm32
7824     89/<- %esi 4/r32/esp
7825     # convert
7826     c7 0/subop/copy *Curr-block-depth 0/imm32
7827     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7828     (flush _test-output-buffered-file)
7829 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7835     # check output
7836     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
7837     # . epilogue
7838     89/<- %esp 5/r32/ebp
7839     5d/pop-to-ebp
7840     c3/return
7841 
7842 test-compare-mem-with-literal:
7843     #   compare var1, 0x34
7844     # =>
7845     #   81 7/subop/compare *(ebp+___) 0x34/imm32
7846     #
7847     # . prologue
7848     55/push-ebp
7849     89/<- %ebp 4/r32/esp
7850     # setup
7851     (clear-stream _test-output-stream)
7852     (clear-stream $_test-output-buffered-file->buffer)
7853     # var type/ecx: (handle tree type-id) = int
7854     68/push 0/imm32/right/null
7855     68/push 1/imm32/left/int
7856     89/<- %ecx 4/r32/esp
7857     # var var-var1/ecx: var
7858     68/push 0/imm32/no-register
7859     68/push 8/imm32/stack-offset
7860     68/push 1/imm32/block-depth
7861     51/push-ecx
7862     68/push "var1"/imm32
7863     89/<- %ecx 4/r32/esp
7864     # var type/edx: (handle tree type-id) = literal
7865     68/push 0/imm32/right/null
7866     68/push 0/imm32/left/literal
7867     89/<- %edx 4/r32/esp
7868     # var var-var2/edx: var literal
7869     68/push 0/imm32/no-register
7870     68/push 0/imm32/no-stack-offset
7871     68/push 1/imm32/block-depth
7872     52/push-edx
7873     68/push "0x34"/imm32
7874     89/<- %edx 4/r32/esp
7875     # var inouts/esi: (list var2)
7876     68/push 0/imm32/next
7877     52/push-edx/var-var2
7878     89/<- %esi 4/r32/esp
7879     # var inouts = (list var1 inouts)
7880     56/push-esi/next
7881     51/push-ecx/var-var1
7882     89/<- %esi 4/r32/esp
7883     # var stmt/esi: statement
7884     68/push 0/imm32/next
7885     68/push 0/imm32/outputs
7886     56/push-esi/inouts
7887     68/push "compare"/imm32/operation
7888     68/push 1/imm32
7889     89/<- %esi 4/r32/esp
7890     # convert
7891     c7 0/subop/copy *Curr-block-depth 0/imm32
7892     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7893     (flush _test-output-buffered-file)
7894 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7900     # check output
7901     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
7902     # . epilogue
7903     89/<- %esp 5/r32/ebp
7904     5d/pop-to-ebp
7905     c3/return
7906 
7907 test-compare-eax-with-literal:
7908     #   compare var1/eax 0x34
7909     # =>
7910     #   3d/compare-eax-with 0x34/imm32
7911     #
7912     # . prologue
7913     55/push-ebp
7914     89/<- %ebp 4/r32/esp
7915     # setup
7916     (clear-stream _test-output-stream)
7917     (clear-stream $_test-output-buffered-file->buffer)
7918     # var type/ecx: (handle tree type-id) = int
7919     68/push 0/imm32/right/null
7920     68/push 1/imm32/left/int
7921     89/<- %ecx 4/r32/esp
7922     # var var-var1/ecx: var in eax
7923     68/push "eax"/imm32/register
7924     68/push 0/imm32/no-stack-offset
7925     68/push 1/imm32/block-depth
7926     51/push-ecx
7927     68/push "var1"/imm32
7928     89/<- %ecx 4/r32/esp
7929     # var type/edx: (handle tree type-id) = literal
7930     68/push 0/imm32/right/null
7931     68/push 0/imm32/left/literal
7932     89/<- %edx 4/r32/esp
7933     # var var-var2/edx: var literal
7934     68/push 0/imm32/no-register
7935     68/push 0/imm32/no-stack-offset
7936     68/push 1/imm32/block-depth
7937     52/push-edx
7938     68/push "0x34"/imm32
7939     89/<- %edx 4/r32/esp
7940     # var inouts/esi: (list var2)
7941     68/push 0/imm32/next
7942     52/push-edx/var-var2
7943     89/<- %esi 4/r32/esp
7944     # var inouts = (list var1 inouts)
7945     56/push-esi/next
7946     51/push-ecx/var-var1
7947     89/<- %esi 4/r32/esp
7948     # var stmt/esi: statement
7949     68/push 0/imm32/next
7950     68/push 0/imm32/outputs
7951     56/push-esi/inouts
7952     68/push "compare"/imm32/operation
7953     68/push 1/imm32/regular-stmt
7954     89/<- %esi 4/r32/esp
7955     # convert
7956     c7 0/subop/copy *Curr-block-depth 0/imm32
7957     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7958     (flush _test-output-buffered-file)
7959 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7965     # check output
7966     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
7967     # . epilogue
7968     89/<- %esp 5/r32/ebp
7969     5d/pop-to-ebp
7970     c3/return
7971 
7972 test-compare-reg-with-literal:
7973     #   compare var1/ecx 0x34
7974     # =>
7975     #   81 7/subop/compare %ecx 0x34/imm32
7976     #
7977     # . prologue
7978     55/push-ebp
7979     89/<- %ebp 4/r32/esp
7980     # setup
7981     (clear-stream _test-output-stream)
7982     (clear-stream $_test-output-buffered-file->buffer)
7983     # var type/ecx: (handle tree type-id) = int
7984     68/push 0/imm32/right/null
7985     68/push 1/imm32/left/int
7986     89/<- %ecx 4/r32/esp
7987     # var var-var1/ecx: var in ecx
7988     68/push "ecx"/imm32/register
7989     68/push 0/imm32/no-stack-offset
7990     68/push 1/imm32/block-depth
7991     51/push-ecx
7992     68/push "var1"/imm32
7993     89/<- %ecx 4/r32/esp
7994     # var type/edx: (handle tree type-id) = literal
7995     68/push 0/imm32/right/null
7996     68/push 0/imm32/left/literal
7997     89/<- %edx 4/r32/esp
7998     # var var-var2/edx: var literal
7999     68/push 0/imm32/no-register
8000     68/push 0/imm32/no-stack-offset
8001     68/push 1/imm32/block-depth
8002     52/push-edx
8003     68/push "0x34"/imm32
8004     89/<- %edx 4/r32/esp
8005     # var inouts/esi: (list var2)
8006     68/push 0/imm32/next
8007     52/push-edx/var-var2
8008     89/<- %esi 4/r32/esp
8009     # var inouts = (list var1 inouts)
8010     56/push-esi/next
8011     51/push-ecx/var-var1
8012     89/<- %esi 4/r32/esp
8013     # var stmt/esi: statement
8014     68/push 0/imm32/next
8015     68/push 0/imm32/outputs
8016     56/push-esi/inouts
8017     68/push "compare"/imm32/operation
8018     68/push 1/imm32/regular-stmt
8019     89/<- %esi 4/r32/esp
8020     # convert
8021     c7 0/subop/copy *Curr-block-depth 0/imm32
8022     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
8023     (flush _test-output-buffered-file)
8024 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
8030     # check output
8031     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
8032     # . epilogue
8033     89/<- %esp 5/r32/ebp
8034     5d/pop-to-ebp
8035     c3/return
8036 
8037 test-emit-subx-statement-function-call:
8038     # Call a function on a variable on the stack.
8039     #   f foo
8040     # =>
8041     #   (f2 *(ebp-8))
8042     # (Changing the function name supports overloading in general, but here it
8043     # just serves to help disambiguate things.)
8044     #
8045     # There's a variable on the var stack as follows:
8046     #   name: 'foo'
8047     #   type: int
8048     #   stack-offset: -8
8049     #
8050     # There's nothing in primitives.
8051     #
8052     # There's a function with this info:
8053     #   name: 'f'
8054     #   inout: int/mem
8055     #   value: 'f2'
8056     #
8057     # . prologue
8058     55/push-ebp
8059     89/<- %ebp 4/r32/esp
8060     # setup
8061     (clear-stream _test-output-stream)
8062     (clear-stream $_test-output-buffered-file->buffer)
8063     # var type/ecx: (handle tree type-id) = int
8064     68/push 0/imm32/right/null
8065     68/push 1/imm32/left/int
8066     89/<- %ecx 4/r32/esp
8067     # var var-foo/ecx: var
8068     68/push 0/imm32/no-register
8069     68/push -8/imm32/stack-offset
8070     68/push 0/imm32/block-depth
8071     51/push-ecx
8072     68/push "foo"/imm32
8073     89/<- %ecx 4/r32/esp
8074     # var operands/esi: (list var)
8075     68/push 0/imm32/next
8076     51/push-ecx/var-foo
8077     89/<- %esi 4/r32/esp
8078     # var stmt/esi: statement
8079     68/push 0/imm32/next
8080     68/push 0/imm32/outputs
8081     56/push-esi/inouts
8082     68/push "f"/imm32/operation
8083     68/push 1/imm32
8084     89/<- %esi 4/r32/esp
8085     # var functions/ebx: function
8086     68/push 0/imm32/next
8087     68/push 0/imm32/body
8088     68/push 0/imm32/outputs
8089     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
8090     68/push "f2"/imm32/subx-name
8091     68/push "f"/imm32/name
8092     89/<- %ebx 4/r32/esp
8093     # convert
8094     c7 0/subop/copy *Curr-block-depth 0/imm32
8095     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
8096     (flush _test-output-buffered-file)
8097 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
8103     # check output
8104     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call")
8105     # . epilogue
8106     89/<- %esp 5/r32/ebp
8107     5d/pop-to-ebp
8108     c3/return
8109 
8110 test-emit-subx-statement-function-call-with-literal-arg:
8111     # Call a function on a literal.
8112     #   f 34
8113     # =>
8114     #   (f2 34)
8115     #
8116     # . prologue
8117     55/push-ebp
8118     89/<- %ebp 4/r32/esp
8119     # setup
8120     (clear-stream _test-output-stream)
8121     (clear-stream $_test-output-buffered-file->buffer)
8122     # var type/ecx: (handle tree type-id) = literal
8123     68/push 0/imm32/right/null
8124     68/push 0/imm32/left/literal
8125     89/<- %ecx 4/r32/esp
8126     # var var-foo/ecx: var literal
8127     68/push 0/imm32/no-register
8128     68/push 0/imm32/no-stack-offset
8129     68/push 0/imm32/block-depth
8130     51/push-ecx
8131     68/push "34"/imm32
8132     89/<- %ecx 4/r32/esp
8133     # var operands/esi: (list var)
8134     68/push 0/imm32/next
8135     51/push-ecx/var-foo
8136     89/<- %esi 4/r32/esp
8137     # var stmt/esi: statement
8138     68/push 0/imm32/next
8139     68/push 0/imm32/outputs
8140     56/push-esi/inouts
8141     68/push "f"/imm32/operation
8142     68/push 1/imm32
8143     89/<- %esi 4/r32/esp
8144     # var functions/ebx: function
8145     68/push 0/imm32/next
8146     68/push 0/imm32/body
8147     68/push 0/imm32/outputs
8148     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
8149     68/push "f2"/imm32/subx-name
8150     68/push "f"/imm32/name
8151     89/<- %ebx 4/r32/esp
8152     # convert
8153     c7 0/subop/copy *Curr-block-depth 0/imm32
8154     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
8155     (flush _test-output-buffered-file)
8156 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
8162     # check output
8163     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg")
8164     # . epilogue
8165     89/<- %esp 5/r32/ebp
8166     5d/pop-to-ebp
8167     c3/return
8168 
8169 emit-indent:  # out: (addr buffered-file), n: int
8170     # . prologue
8171     55/push-ebp
8172     89/<- %ebp 4/r32/esp
8173     # . save registers
8174     50/push-eax
8175     # var i/eax: int = n
8176     8b/-> *(ebp+0xc) 0/r32/eax
8177     {
8178       # if (i <= 0) break
8179       3d/compare-eax-with 0/imm32
8180       7e/jump-if-<= break/disp8
8181       (write-buffered *(ebp+8) "  ")
8182       48/decrement-eax
8183       eb/jump loop/disp8
8184     }
8185 $emit-indent:end:
8186     # . restore registers
8187     58/pop-to-eax
8188     # . epilogue
8189     89/<- %esp 5/r32/ebp
8190     5d/pop-to-ebp
8191     c3/return
8192 
8193 emit-subx-prologue:  # out: (addr buffered-file)
8194     # . prologue
8195     55/push-ebp
8196     89/<- %ebp 4/r32/esp
8197     #
8198     (write-buffered *(ebp+8) "  # . prologue\n")
8199     (write-buffered *(ebp+8) "  55/push-ebp\n")
8200     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
8201 $emit-subx-prologue:end:
8202     # . epilogue
8203     89/<- %esp 5/r32/ebp
8204     5d/pop-to-ebp
8205     c3/return
8206 
8207 emit-subx-epilogue:  # out: (addr buffered-file)
8208     # . prologue
8209     55/push-ebp
8210     89/<- %ebp 4/r32/esp
8211     #
8212     (write-buffered *(ebp+8) "  # . epilogue\n")
8213     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
8214     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
8215     (write-buffered *(ebp+8) "  c3/return\n")
8216 $emit-subx-epilogue:end:
8217     # . epilogue
8218     89/<- %esp 5/r32/ebp
8219     5d/pop-to-ebp
8220     c3/return