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
 108 #     tag 2: a variable defined on the stack
 109 #     tag 3: a variable defined in a register
 110 #     tag 4: a named block
 111 #
 112 #   A block contains:
 113 #     tag: 0
 114 #     statements: (handle list statement)
 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 #   A named block contains:
 134 #     tag: 4
 135 #     name: (handle array byte) -- starting with '$'
 136 #     statements: (handle list statement)
 137 
 138 # == Translation: managing the stack
 139 # Now that we know what the language looks like in the large, let's think
 140 # about how translation happens from the bottom up. One crucial piece of the
 141 # puzzle is how Mu will clean up variables defined on the stack for you.
 142 #
 143 # Assume that we maintain a 'functions' list while parsing source code. And a
 144 # 'primitives' list is a global constant. Both these contain enough information
 145 # to perform type-checking on function calls or primitive statements, respectively.
 146 #
 147 # Defining variables pushes them on a stack with the current block depth and
 148 # enough information about their location (stack offset or register).
 149 # Starting a block increments the current block id.
 150 # Each statement now has enough information to emit code for it.
 151 # Ending a block is where the magic happens:
 152 #   pop all variables at the current block depth
 153 #   emit code to restore all register variables introduced at the current depth
 154 #   emit code to clean up all stack variables at the current depth (just increment esp)
 155 #   decrement the current block depth
 156 #
 157 # Formal types:
 158 #   live-vars: stack of vars
 159 #   var:
 160 #     name: (handle array byte)
 161 #     type: (handle tree type-id)
 162 #     block: int
 163 #     stack-offset: int  (added to ebp)
 164 #     register: (handle array byte)
 165 #       either usual register names
 166 #       or '*' to indicate any register
 167 #   At most one of stack-offset or register-index must be non-zero.
 168 #   A register of '*' designates a variable _template_. Only legal in formal
 169 #   parameters for primitives.
 170 
 171 # == Translating a single function call
 172 # This one's easy. Assuming we've already checked things, we just drop the
 173 # outputs (which use hard-coded registers) and emit inputs in a standard format.
 174 #
 175 # out1, out2, out3, ... <- name inout1, inout2, inout3, ...
 176 # =>
 177 # (subx-name inout1 inout2 inout3)
 178 #
 179 # Formal types:
 180 #   functions: linked list of info
 181 #     name: (handle array byte)
 182 #     inouts: linked list of vars
 183 #     outputs: linked list of vars
 184 #     body: block (singleton linked list)
 185 #     subx-name: (handle array byte)
 186 
 187 # == Translating a single primitive instruction
 188 # A second crucial piece of the puzzle is how Mu converts fairly regular
 189 # primitives with their uniform syntax to SubX instructions with their gnarly
 190 # x86 details.
 191 #
 192 # Mu instructions have inputs and outputs. Primitives can have up to 2 of
 193 # them.
 194 # SubX instructions have rm32 and r32 operands.
 195 # The translation between them covers almost all the possibilities.
 196 #   Instructions with 1 inout may turn into ones with 1 rm32
 197 #     (e.g. incrementing a var on the stack)
 198 #   Instructions with 1 output may turn into ones with 1 rm32
 199 #     (e.g. incrementing a var in a register)
 200 #   1 inout and 1 output may turn into 1 rm32 and 1 r32
 201 #     (e.g. adding a var to a reg)
 202 #   2 inouts may turn into 1 rm32 and 1 r32
 203 #     (e.g. adding a reg to a var)
 204 #   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
 205 #     (e.g. adding a constant to a var)
 206 #   1 output and 1 literal may turn into 1 rm32 and 1 imm32
 207 #     (e.g. adding a constant to a reg)
 208 #   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
 209 #     (special-case: divide edx:eax by a var or reg)
 210 # Observations:
 211 #   We always emit rm32. It may be the first inout or the first output.
 212 #   We may emit r32 or imm32 or neither.
 213 #   When we emit r32 it may come from first inout or second inout or first output.
 214 #
 215 # Accordingly, the formal data structure for a primitive looks like this:
 216 #   primitives: linked list of info
 217 #     name: (handle array byte)
 218 #     mu-inouts: linked list of vars to check
 219 #     mu-outputs: linked list of vars to check; at most a singleton
 220 #     subx-name: (handle array byte)
 221 #     subx-rm32: enum arg-location
 222 #     subx-r32: enum arg-location
 223 #     subx-imm32: enum arg-location
 224 #     subx-disp32: enum arg-location
 225 #     output-is-write-only: boolean
 226 #   arg-location: enum
 227 #     0 means none
 228 #     1 means first inout
 229 #     2 means second inout
 230 #     3 means first output
 231 
 232 # == Translating a block
 233 # Emit block name if necessary
 234 # Emit '{'
 235 # When you encounter a statement, emit it as above
 236 # When you encounter a variable declaration
 237 #   emit any code needed for it (bzeros)
 238 #   push it on the var stack
 239 #   update register dict if necessary
 240 # When you encounter '}'
 241 #   While popping variables off the var stack until block id changes
 242 #     Emit code needed to clean up the stack
 243 #       either increment esp
 244 #       or pop into appropriate register
 245 
 246 # The rest is straightforward.
 247 
 248 == data
 249 
 250 Program:  # (handle function)
 251   0/imm32
 252 
 253 Function-name:
 254   0/imm32
 255 Function-subx-name:
 256   4/imm32
 257 Function-inouts:  # (handle list var)
 258   8/imm32
 259 Function-outputs:  # (handle list var)
 260   0xc/imm32
 261 Function-body:  # (handle block)
 262   0x10/imm32
 263 Function-next:  # (handle function)
 264   0x14/imm32
 265 Function-size:  # (addr int)
 266   0x18/imm32/24
 267 
 268 Primitive-name:
 269   0/imm32
 270 Primitive-inouts:  # (handle list var)
 271   4/imm32
 272 Primitive-outputs:  # (handle list var)
 273   8/imm32
 274 Primitive-subx-name:  # (handle array byte)
 275   0xc/imm32
 276 Primitive-subx-rm32:  # enum arg-location
 277   0x10/imm32
 278 Primitive-subx-r32:  # enum arg-location
 279   0x14/imm32
 280 Primitive-subx-imm32:  # enum arg-location
 281   0x18/imm32
 282 Primitive-subx-disp32:  # enum arg-location  -- only for branches
 283   0x1c/imm32
 284 Primitive-output-is-write-only:  # boolean
 285   0x20/imm32
 286 Primitive-next:  # (handle function)
 287   0x24/imm32
 288 Primitive-size:  # (addr int)
 289   0x28/imm32/36
 290 
 291 Stmt-tag:
 292   0/imm32
 293 
 294 Block-statements:  # (handle list statement)
 295   4/imm32
 296 
 297 Stmt1-operation:  # (handle array byte)
 298   4/imm32
 299 Stmt1-inouts:  # (handle list var)
 300   8/imm32
 301 Stmt1-outputs:  # (handle list var)
 302   0xc/imm32
 303 
 304 Vardef-var:  # (handle var)
 305   4/imm32
 306 
 307 Regvardef-operation:  # (handle array byte)
 308   4/imm32
 309 Regvardef-inouts:  # (handle list var)
 310   8/imm32
 311 Regvardef-outputs:  # (handle list var)  # will have exactly one element
 312   0xc/imm32
 313 
 314 Named-block-statements:  # (handle list statement)
 315   4/imm32
 316 Named-block-name:  # (handle array byte)
 317   8/imm32
 318 
 319 Stmt-size:  # (addr int)
 320   0x10/imm32
 321 
 322 Var-name:
 323   0/imm32
 324 Var-type:
 325   4/imm32
 326 Var-block:
 327   8/imm32
 328 Var-stack-offset:
 329   0xc/imm32
 330 Var-register:
 331   0x10/imm32
 332 Var-size:  # (addr int)
 333   0x14/imm32
 334 
 335 Any-register:  # "*"
 336   # size
 337   1/imm32
 338   # data
 339   2a/asterisk
 340 
 341 List-value:
 342   0/imm32
 343 List-next:
 344   4/imm32
 345 List-size:  # (addr int)
 346   8/imm32
 347 
 348 # Types are expressed as trees (s-expressions) of type-ids (ints).
 349 # However, there's no need for singletons, so we can assume (int) == int
 350 #   - if x->right == nil, x is an atom
 351 #   - x->left contains either a pointer to a pair, or an atomic type-id directly.
 352 #     type ids will be less than 0x10000 (MAX_TYPE_ID).
 353 
 354 Tree-left:  # either type-id or (addr tree type-id)
 355   0/imm32
 356 Tree-right:  # (addr tree type-id)
 357   4/imm32
 358 Tree-size:  # (addr int)
 359   8/imm32
 360 
 361 Max-type-id:
 362   0x10000/imm32
 363 
 364 == code
 365 
 366 Entry:
 367     # . prologue
 368     89/<- %ebp 4/r32/esp
 369     (new-segment *Heap-size Heap)
 370     # if (argv[1] == "test') run-tests()
 371     {
 372       # if (argc <= 1) break
 373       81 7/subop/compare *ebp 1/imm32
 374       7e/jump-if-<= break/disp8
 375       # if (argv[1] != "test") break
 376       (kernel-string-equal? *(ebp+8) "test")  # => eax
 377       3d/compare-eax-and 0/imm32
 378       74/jump-if-= break/disp8
 379       #
 380       (run-tests)
 381       # syscall(exit, *Num-test-failures)
 382       8b/-> *Num-test-failures 3/r32/ebx
 383       eb/jump $mu-main:end/disp8
 384     }
 385     # otherwise convert Stdin
 386     (convert-mu Stdin Stdout)
 387     (flush Stdout)
 388     # syscall(exit, 0)
 389     bb/copy-to-ebx 0/imm32
 390 $mu-main:end:
 391     b8/copy-to-eax 1/imm32/exit
 392     cd/syscall 0x80/imm8
 393 
 394 convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
 395     # . prologue
 396     55/push-ebp
 397     89/<- %ebp 4/r32/esp
 398     #
 399     (parse-mu *(ebp+8))
 400     (check-mu-types)
 401     (emit-subx *(ebp+0xc))
 402 $convert-mu:end:
 403     # . epilogue
 404     89/<- %esp 5/r32/ebp
 405     5d/pop-to-ebp
 406     c3/return
 407 
 408 test-convert-empty-input:
 409     # empty input => empty output
 410     # . prologue
 411     55/push-ebp
 412     89/<- %ebp 4/r32/esp
 413     # setup
 414     (clear-stream _test-input-stream)
 415     (clear-stream $_test-input-buffered-file->buffer)
 416     (clear-stream _test-output-stream)
 417     (clear-stream $_test-output-buffered-file->buffer)
 418     #
 419     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 420     (flush _test-output-buffered-file)
 421     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 422     # . epilogue
 423     89/<- %esp 5/r32/ebp
 424     5d/pop-to-ebp
 425     c3/return
 426 
 427 test-convert-function-skeleton:
 428     # empty function decl => function prologue and epilogue
 429     #   fn foo {
 430     #   }
 431     # =>
 432     #   foo:
 433     #     # . prologue
 434     #     55/push-ebp
 435     #     89/<- %ebp 4/r32/esp
 436     #     # . epilogue
 437     #     89/<- %esp 5/r32/ebp
 438     #     5d/pop-to-ebp
 439     #     c3/return
 440     # . prologue
 441     55/push-ebp
 442     89/<- %ebp 4/r32/esp
 443     # setup
 444     (clear-stream _test-input-stream)
 445     (clear-stream $_test-input-buffered-file->buffer)
 446     (clear-stream _test-output-stream)
 447     (clear-stream $_test-output-buffered-file->buffer)
 448     #
 449     (write _test-input-stream "fn foo {\n")
 450     (write _test-input-stream "}\n")
 451     # convert
 452     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 453     (flush _test-output-buffered-file)
 454 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 460     # check output
 461     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-skeleton/0")
 462     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-skeleton/1")
 463     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-skeleton/2")
 464     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 465     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-skeleton/4")
 466     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 467     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 468     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-skeleton/7")
 469     # . epilogue
 470     89/<- %esp 5/r32/ebp
 471     5d/pop-to-ebp
 472     c3/return
 473 
 474 test-convert-multiple-function-skeletons:
 475     # multiple functions correctly organized into a linked list
 476     #   fn foo {
 477     #   }
 478     #   fn bar {
 479     #   }
 480     # =>
 481     #   foo:
 482     #     # . prologue
 483     #     55/push-ebp
 484     #     89/<- %ebp 4/r32/esp
 485     #     # . epilogue
 486     #     89/<- %esp 5/r32/ebp
 487     #     5d/pop-to-ebp
 488     #     c3/return
 489     #   bar:
 490     #     # . prologue
 491     #     55/push-ebp
 492     #     89/<- %ebp 4/r32/esp
 493     #     # . epilogue
 494     #     89/<- %esp 5/r32/ebp
 495     #     5d/pop-to-ebp
 496     #     c3/return
 497     # . prologue
 498     55/push-ebp
 499     89/<- %ebp 4/r32/esp
 500     # setup
 501     (clear-stream _test-input-stream)
 502     (clear-stream $_test-input-buffered-file->buffer)
 503     (clear-stream _test-output-stream)
 504     (clear-stream $_test-output-buffered-file->buffer)
 505     #
 506     (write _test-input-stream "fn foo {\n")
 507     (write _test-input-stream "}\n")
 508     (write _test-input-stream "fn bar {\n")
 509     (write _test-input-stream "}\n")
 510     # convert
 511     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 512     (flush _test-output-buffered-file)
 513 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 519     # check first function
 520     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-multiple-function-skeletons/0")
 521     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/1")
 522     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 523     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 524     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 525     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 526     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 527     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/7")
 528     # check second function
 529     (check-next-stream-line-equal _test-output-stream "bar:"                  "F - test-convert-multiple-function-skeletons/10")
 530     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/11")
 531     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 532     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 533     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 534     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 535     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 536     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/17")
 537     # . epilogue
 538     89/<- %esp 5/r32/ebp
 539     5d/pop-to-ebp
 540     c3/return
 541 
 542 test-convert-function-with-arg:
 543     # function with one arg
 544     #   fn foo n : int {
 545     #   }
 546     # =>
 547     #   foo:
 548     #     # . prologue
 549     #     55/push-ebp
 550     #     89/<- %ebp 4/r32/esp
 551     #     # . epilogue
 552     #     89/<- %esp 5/r32/ebp
 553     #     5d/pop-to-ebp
 554     #     c3/return
 555     # . prologue
 556     55/push-ebp
 557     89/<- %ebp 4/r32/esp
 558     # setup
 559     (clear-stream _test-input-stream)
 560     (clear-stream $_test-input-buffered-file->buffer)
 561     (clear-stream _test-output-stream)
 562     (clear-stream $_test-output-buffered-file->buffer)
 563     #
 564     (write _test-input-stream "fn foo n : int {\n")
 565     (write _test-input-stream "}\n")
 566     # convert
 567     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 568     (flush _test-output-buffered-file)
 569 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 575     # check output
 576     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-arg/0")
 577     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-arg/1")
 578     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-arg/2")
 579     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
 580     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-arg/4")
 581     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
 582     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
 583     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-arg/7")
 584     # . epilogue
 585     89/<- %esp 5/r32/ebp
 586     5d/pop-to-ebp
 587     c3/return
 588 
 589 test-convert-function-with-arg-and-body:
 590     # function with one arg and one instruction in the body
 591     #   fn foo n : int {
 592     #     increment n
 593     #   }
 594     # =>
 595     #   foo:
 596     #     # . prologue
 597     #     55/push-ebp
 598     #     89/<- %ebp 4/r32/esp
 599     #     {
 600     #       ff 0/subop/increment *(ebp+8)
 601     #     }
 602     #     # . epilogue
 603     #     89/<- %esp 5/r32/ebp
 604     #     5d/pop-to-ebp
 605     #     c3/return
 606     # . prologue
 607     55/push-ebp
 608     89/<- %ebp 4/r32/esp
 609     # setup
 610     (clear-stream _test-input-stream)
 611     (clear-stream $_test-input-buffered-file->buffer)
 612     (clear-stream _test-output-stream)
 613     (clear-stream $_test-output-buffered-file->buffer)
 614     #
 615     (write _test-input-stream "fn foo n : int {\n")
 616     (write _test-input-stream "  increment n\n")
 617     (write _test-input-stream "}\n")
 618     # convert
 619     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 620     (flush _test-output-buffered-file)
 621 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 627     # check output
 628     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-arg-and-body/0")
 629     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-arg-and-body/1")
 630     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
 631     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
 632     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-arg-and-body/4")
 633     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/5")
 634     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-arg-and-body/6")
 635     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-arg-and-body/7")
 636     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/8")
 637     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/9")
 638     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-arg-and-body/10")
 639     # . epilogue
 640     89/<- %esp 5/r32/ebp
 641     5d/pop-to-ebp
 642     c3/return
 643 
 644 test-convert-function-distinguishes-args:
 645     # function with two args refers to second one in body
 646     #   fn foo a: int, b: int {
 647     #     increment b
 648     #   }
 649     # =>
 650     #   foo:
 651     #     # . prologue
 652     #     55/push-ebp
 653     #     89/<- %ebp 4/r32/esp
 654     #     {
 655     #       ff 0/subop/increment *(ebp+0xc)
 656     #     }
 657     #     # . epilogue
 658     #     89/<- %esp 5/r32/ebp
 659     #     5d/pop-to-ebp
 660     #     c3/return
 661     # . prologue
 662     55/push-ebp
 663     89/<- %ebp 4/r32/esp
 664     # setup
 665     (clear-stream _test-input-stream)
 666     (clear-stream $_test-input-buffered-file->buffer)
 667     (clear-stream _test-output-stream)
 668     (clear-stream $_test-output-buffered-file->buffer)
 669     #
 670     (write _test-input-stream "fn foo a: int, b: int {\n")
 671     (write _test-input-stream "  increment b\n")
 672     (write _test-input-stream "}\n")
 673     # convert
 674     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 675     (flush _test-output-buffered-file)
 676 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 682     # check output
 683     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-distinguishes-args/0")
 684     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-distinguishes-args/1")
 685     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
 686     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
 687     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-distinguishes-args/4")
 688     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/5")
 689     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-distinguishes-args/6")
 690     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-distinguishes-args/7")
 691     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/8")
 692     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/9")
 693     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-distinguishes-args/10")
 694     # . epilogue
 695     89/<- %esp 5/r32/ebp
 696     5d/pop-to-ebp
 697     c3/return
 698 
 699 test-convert-function-returns-result:
 700     # function writes to output
 701     #   fn foo a: int, b: int -> result/eax: int {
 702     #     result <- copy a
 703     #     result <- increment
 704     #   }
 705     # =>
 706     #   foo:
 707     #     # . prologue
 708     #     55/push-ebp
 709     #     89/<- %ebp 4/r32/esp
 710     #     {
 711     #       89/-> *(ebp+8) 0/r32/eax
 712     #       40/increment-eax
 713     #     }
 714     #     # . epilogue
 715     #     89/<- %esp 5/r32/ebp
 716     #     5d/pop-to-ebp
 717     #     c3/return
 718     # . prologue
 719     55/push-ebp
 720     89/<- %ebp 4/r32/esp
 721     # setup
 722     (clear-stream _test-input-stream)
 723     (clear-stream $_test-input-buffered-file->buffer)
 724     (clear-stream _test-output-stream)
 725     (clear-stream $_test-output-buffered-file->buffer)
 726     #
 727     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 728     (write _test-input-stream "  result <- copy a\n")
 729     (write _test-input-stream "  result <- increment\n")
 730     (write _test-input-stream "}\n")
 731     # convert
 732     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 733     (flush _test-output-buffered-file)
 734 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 740     # check output
 741     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-returns-result/0")
 742     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-returns-result/1")
 743     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-returns-result/2")
 744     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
 745     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-returns-result/4")
 746     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/5")
 747     (check-next-stream-line-equal _test-output-stream "40/increment-eax"      "F - test-convert-function-returns-result/6")
 748     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-returns-result/7")
 749     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-returns-result/8")
 750     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/9")
 751     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-returns-result/10")
 752     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-returns-result/11")
 753     # . epilogue
 754     89/<- %esp 5/r32/ebp
 755     5d/pop-to-ebp
 756     c3/return
 757 
 758 test-convert-function-literal-arg:
 759     # function writes to output
 760     #   fn foo a: int, b: int -> result/eax: int {
 761     #     result <- copy a
 762     #     result <- add 1
 763     #   }
 764     # =>
 765     #   foo:
 766     #     # . prologue
 767     #     55/push-ebp
 768     #     89/<- %ebp 4/r32/esp
 769     #     {
 770     #       89/-> *(ebp+8) 0/r32/eax
 771     #       05/add-to-eax 1/imm32
 772     #     }
 773     #     # . epilogue
 774     #     89/<- %esp 5/r32/ebp
 775     #     5d/pop-to-ebp
 776     #     c3/return
 777     # . prologue
 778     55/push-ebp
 779     89/<- %ebp 4/r32/esp
 780     # setup
 781     (clear-stream _test-input-stream)
 782     (clear-stream $_test-input-buffered-file->buffer)
 783     (clear-stream _test-output-stream)
 784     (clear-stream $_test-output-buffered-file->buffer)
 785     #
 786     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 787     (write _test-input-stream "  result <- copy a\n")
 788     (write _test-input-stream "  result <- add 1\n")
 789     (write _test-input-stream "}\n")
 790     # convert
 791     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 792     (flush _test-output-buffered-file)
 793 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 799     # check output
 800     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-literal-arg/0")
 801     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-literal-arg/1")
 802     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-literal-arg/2")
 803     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg/3")
 804     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-literal-arg/4")
 805     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-literal-arg/5")
 806     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 1/imm32"  "F - test-convert-function-literal-arg/6")
 807     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-literal-arg/7")
 808     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-literal-arg/8")
 809     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg/9")
 810     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-literal-arg/10")
 811     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-literal-arg/11")
 812     # . epilogue
 813     89/<- %esp 5/r32/ebp
 814     5d/pop-to-ebp
 815     c3/return
 816 
 817 test-convert-function-literal-arg-2:
 818     # function writes to output
 819     #   fn foo a: int, b: int -> result/ebx: int {
 820     #     result <- copy a
 821     #     result <- add 1
 822     #   }
 823     # =>
 824     #   foo:
 825     #     # . prologue
 826     #     55/push-ebp
 827     #     89/<- %ebp 4/r32/esp
 828     #     {
 829     #       89/-> *(ebp+8) 3/r32/ebx
 830     #       81 0/subop/add %ebx 1/imm32
 831     #     }
 832     #     # . epilogue
 833     #     89/<- %esp 5/r32/ebp
 834     #     5d/pop-to-ebp
 835     #     c3/return
 836     # . prologue
 837     55/push-ebp
 838     89/<- %ebp 4/r32/esp
 839     # setup
 840     (clear-stream _test-input-stream)
 841     (clear-stream $_test-input-buffered-file->buffer)
 842     (clear-stream _test-output-stream)
 843     (clear-stream $_test-output-buffered-file->buffer)
 844     #
 845     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
 846     (write _test-input-stream "  result <- copy a\n")
 847     (write _test-input-stream "  result <- add 1\n")
 848     (write _test-input-stream "}\n")
 849     # convert
 850     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 851     (flush _test-output-buffered-file)
 852 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 858     # check output
 859     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-literal-arg-2/0")
 860     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-literal-arg-2/1")
 861     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-literal-arg-2/2")
 862     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg-2/3")
 863     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-literal-arg-2/4")
 864     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-literal-arg-2/5")
 865     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-literal-arg-2/6")
 866     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-literal-arg-2/7")
 867     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-literal-arg-2/8")
 868     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg-2/9")
 869     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-literal-arg-2/10")
 870     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-literal-arg-2/11")
 871     # . epilogue
 872     89/<- %esp 5/r32/ebp
 873     5d/pop-to-ebp
 874     c3/return
 875 
 876 test-convert-function-call-with-literal-arg:
 877     # function writes to output
 878     #   fn main -> result/ebx: int {
 879     #     result <- do-add 3 4
 880     #   }
 881     #
 882     #   fn do-add a: int, b: int -> result/ebx: int {
 883     #     result <- copy a
 884     #     result <- add b
 885     #   }
 886     # =>
 887     #   main:
 888     #     # . prologue
 889     #     55/push-ebp
 890     #     89/<- %ebp 4/r32/esp
 891     #     {
 892     #       (do-add 3 4)
 893     #     }
 894     #     # . epilogue
 895     #     89/<- %esp 5/r32/ebp
 896     #     5d/pop-to-ebp
 897     #     c3/return
 898     #   do-add:
 899     #     # . prologue
 900     #     55/push-ebp
 901     #     89/<- %ebp 4/r32/esp
 902     #     {
 903     #       8b/-> *(ebp+8) 3/r32/ebx
 904     #       03/add-to 3/r32/ebx *(ebp+0xc)
 905     #     }
 906     #     # . epilogue
 907     #     89/<- %esp 5/r32/ebp
 908     #     5d/pop-to-ebp
 909     #     c3/return
 910     # . prologue
 911     55/push-ebp
 912     89/<- %ebp 4/r32/esp
 913     # setup
 914     (clear-stream _test-input-stream)
 915     (clear-stream $_test-input-buffered-file->buffer)
 916     (clear-stream _test-output-stream)
 917     (clear-stream $_test-output-buffered-file->buffer)
 918     #
 919     (write _test-input-stream "fn main -> result/ebx: int {\n")
 920     (write _test-input-stream "  result <- do-add 3 4\n")
 921     (write _test-input-stream "}\n")
 922     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
 923     (write _test-input-stream "  result <- copy a\n")
 924     (write _test-input-stream "  result <- add b\n")
 925     (write _test-input-stream "}\n")
 926     # convert
 927     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 928     (flush _test-output-buffered-file)
 929 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 935     # check output
 936     (check-next-stream-line-equal _test-output-stream "main:"                 "F - test-convert-function-call-with-literal-arg/0")
 937     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-call-with-literal-arg/1")
 938     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
 939     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
 940     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-call-with-literal-arg/4")
 941     (check-next-stream-line-equal _test-output-stream "(do-add 3 4)"          "F - test-convert-function-call-with-literal-arg/5")
 942     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-call-with-literal-arg/6")
 943     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-call-with-literal-arg/7")
 944     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/8")
 945     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/9")
 946     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-call-with-literal-arg/10")
 947     (check-next-stream-line-equal _test-output-stream "do-add:"               "F - test-convert-function-call-with-literal-arg/11")
 948     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-call-with-literal-arg/12")
 949     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-call-with-literal-arg/13")
 950     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/14")
 951     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-call-with-literal-arg/15")
 952     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/16")
 953     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/17")
 954     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-call-with-literal-arg/18")
 955     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-call-with-literal-arg/19")
 956     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/20")
 957     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/21")
 958     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-call-with-literal-arg/22")
 959     # . epilogue
 960     89/<- %esp 5/r32/ebp
 961     5d/pop-to-ebp
 962     c3/return
 963 
 964 test-convert-function-with-local-var-in-mem:
 965     # empty function decl => function prologue and epilogue
 966     #   fn foo {
 967     #     var x: int
 968     #     increment x
 969     #   }
 970     # =>
 971     #   foo:
 972     #     # . prologue
 973     #     55/push-ebp
 974     #     89/<- %ebp 4/r32/esp
 975     #     {
 976     #       68/push 0/imm32
 977     #       ff 0/subop/increment *(ebp-4)
 978     #       81 0/subop/add %esp 4/imm32
 979     #     }
 980     #     # . epilogue
 981     #     89/<- %esp 5/r32/ebp
 982     #     5d/pop-to-ebp
 983     #     c3/return
 984     # . prologue
 985     55/push-ebp
 986     89/<- %ebp 4/r32/esp
 987     # setup
 988     (clear-stream _test-input-stream)
 989     (clear-stream $_test-input-buffered-file->buffer)
 990     (clear-stream _test-output-stream)
 991     (clear-stream $_test-output-buffered-file->buffer)
 992     #
 993     (write _test-input-stream "fn foo {\n")
 994     (write _test-input-stream "  var x: int\n")
 995     (write _test-input-stream "  increment x\n")
 996     (write _test-input-stream "}\n")
 997     # convert
 998     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 999     (flush _test-output-buffered-file)
1000 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1006     # check output
1007     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-local-var-in-mem/0")
1008     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
1009     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
1010     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
1011     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-local-var-in-mem/4")
1012     (check-next-stream-line-equal _test-output-stream "68/push 0/imm32"       "F - test-convert-function-with-local-var-in-mem/5")
1013     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/6")
1014     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-mem/7")
1015     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-local-var-in-mem/8")
1016     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-local-var-in-mem/9")
1017     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/10")
1018     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/11")
1019     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-local-var-in-mem/12")
1020     # . epilogue
1021     89/<- %esp 5/r32/ebp
1022     5d/pop-to-ebp
1023     c3/return
1024 
1025 test-convert-function-with-local-var-in-reg:
1026     # empty function decl => function prologue and epilogue
1027     #   fn foo {
1028     #     var x/ecx: int <- copy 3
1029     #     x <- increment
1030     #   }
1031     # =>
1032     #   foo:
1033     #     # . prologue
1034     #     55/push-ebp
1035     #     89/<- %ebp 4/r32/esp
1036     #     {
1037     #       ff 6/subop/push %ecx
1038     #       b9/copy-to-ecx 3/imm32
1039     #       41/increment-ecx
1040     #       8f 0/subop/pop %ecx
1041     #     }
1042     #     # . epilogue
1043     #     89/<- %esp 5/r32/ebp
1044     #     5d/pop-to-ebp
1045     #     c3/return
1046     # . prologue
1047     55/push-ebp
1048     89/<- %ebp 4/r32/esp
1049     # setup
1050     (clear-stream _test-input-stream)
1051     (clear-stream $_test-input-buffered-file->buffer)
1052     (clear-stream _test-output-stream)
1053     (clear-stream $_test-output-buffered-file->buffer)
1054     #
1055     (write _test-input-stream "fn foo {\n")
1056     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
1057     (write _test-input-stream "  x <- increment\n")
1058     (write _test-input-stream "}\n")
1059     # convert
1060     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1061     (flush _test-output-buffered-file)
1062 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1068     # check output
1069     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-local-var-in-reg/0")
1070     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
1071     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
1072     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
1073     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-local-var-in-reg/4")
1074     (check-next-stream-line-equal _test-output-stream "ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/5")
1075     (check-next-stream-line-equal _test-output-stream "b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/6")
1076     (check-next-stream-line-equal _test-output-stream "41/increment-ecx"      "F - test-convert-function-with-local-var-in-reg/7")
1077     (check-next-stream-line-equal _test-output-stream "8f 0/subop/pop %ecx"   "F - test-convert-function-with-local-var-in-reg/8")
1078     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-local-var-in-reg/9")
1079     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-local-var-in-reg/10")
1080     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/11")
1081     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/12")
1082     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-local-var-in-reg/13")
1083     # . epilogue
1084     89/<- %esp 5/r32/ebp
1085     5d/pop-to-ebp
1086     c3/return
1087 
1088 test-convert-function-with-local-var-in-block:
1089     # empty function decl => function prologue and epilogue
1090     #   fn foo {
1091     #     {
1092     #       var x: int
1093     #       increment x
1094     #     }
1095     #   }
1096     # =>
1097     #   foo:
1098     #     # . prologue
1099     #     55/push-ebp
1100     #     89/<- %ebp 4/r32/esp
1101     #     {
1102     #       {
1103     #         68/push 0/imm32
1104     #         ff 0/subop/increment *(ebp-4)
1105     #         81 0/subop/add %esp 4/imm32
1106     #       }
1107     #     }
1108     #     # . epilogue
1109     #     89/<- %esp 5/r32/ebp
1110     #     5d/pop-to-ebp
1111     #     c3/return
1112     # . prologue
1113     55/push-ebp
1114     89/<- %ebp 4/r32/esp
1115     # setup
1116     (clear-stream _test-input-stream)
1117     (clear-stream $_test-input-buffered-file->buffer)
1118     (clear-stream _test-output-stream)
1119     (clear-stream $_test-output-buffered-file->buffer)
1120     #
1121     (write _test-input-stream "fn foo {\n")
1122     (write _test-input-stream "  {\n")
1123     (write _test-input-stream "    var x: int\n")
1124     (write _test-input-stream "    increment x\n")
1125     (write _test-input-stream "  }\n")
1126     (write _test-input-stream "}\n")
1127     # convert
1128     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1129     (flush _test-output-buffered-file)
1130 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1136     # check output
1137     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-local-var-in-block/0")
1138     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-local-var-in-block/1")
1139     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
1140     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
1141     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-local-var-in-block/4")
1142     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-local-var-in-block/5")
1143     (check-next-stream-line-equal _test-output-stream "68/push 0/imm32"       "F - test-convert-function-with-local-var-in-block/6")
1144     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/7")
1145     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-block/8")
1146     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-local-var-in-block/9")
1147     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-local-var-in-block/10")
1148     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-local-var-in-block/11")
1149     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/12")
1150     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/13")
1151     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-local-var-in-block/14")
1152     # . epilogue
1153     89/<- %esp 5/r32/ebp
1154     5d/pop-to-ebp
1155     c3/return
1156 
1157 test-convert-function-with-local-var-in-named-block:
1158     # empty function decl => function prologue and epilogue
1159     #   fn foo {
1160     #     $bar: {
1161     #       var x: int
1162     #       increment x
1163     #     }
1164     #   }
1165     # =>
1166     #   foo:
1167     #     # . prologue
1168     #     55/push-ebp
1169     #     89/<- %ebp 4/r32/esp
1170     #     {
1171     #       {
1172     #     $bar:loop:
1173     #         68/push 0/imm32
1174     #         ff 0/subop/increment *(ebp-4)
1175     #         81 0/subop/add %esp 4/imm32
1176     #       }
1177     #     $bar:break:
1178     #     }
1179     #     # . epilogue
1180     #     89/<- %esp 5/r32/ebp
1181     #     5d/pop-to-ebp
1182     #     c3/return
1183     # . prologue
1184     55/push-ebp
1185     89/<- %ebp 4/r32/esp
1186     # setup
1187     (clear-stream _test-input-stream)
1188     (clear-stream $_test-input-buffered-file->buffer)
1189     (clear-stream _test-output-stream)
1190     (clear-stream $_test-output-buffered-file->buffer)
1191     #
1192     (write _test-input-stream "fn foo {\n")
1193     (write _test-input-stream "  $bar: {\n")
1194     (write _test-input-stream "    var x: int\n")
1195     (write _test-input-stream "    increment x\n")
1196     (write _test-input-stream "  }\n")
1197     (write _test-input-stream "}\n")
1198     # convert
1199     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1200     (flush _test-output-buffered-file)
1201 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1207     # check output
1208     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-local-var-in-named-block/0")
1209     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
1210     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
1211     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
1212     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-local-var-in-named-block/4")
1213     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-local-var-in-named-block/5")
1214     (check-next-stream-line-equal _test-output-stream "$bar:loop:"            "F - test-convert-function-with-local-var-in-named-block/6")
1215     (check-next-stream-line-equal _test-output-stream "68/push 0/imm32"       "F - test-convert-function-with-local-var-in-named-block/7")
1216     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-named-block/8")
1217     (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/9")
1218     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-local-var-in-named-block/10")
1219     (check-next-stream-line-equal _test-output-stream "$bar:break:"           "F - test-convert-function-with-local-var-in-named-block/11")
1220     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-local-var-in-named-block/12")
1221     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-local-var-in-named-block/13")
1222     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/14")
1223     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/15")
1224     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-local-var-in-named-block/16")
1225     # . epilogue
1226     89/<- %esp 5/r32/ebp
1227     5d/pop-to-ebp
1228     c3/return
1229 
1230 test-convert-function-with-branches-in-block:
1231     # empty function decl => function prologue and epilogue
1232     #   fn foo x: int {
1233     #     {
1234     #       break-if->=
1235     #       loop-if-addr<
1236     #       increment x
1237     #       loop
1238     #     }
1239     #   }
1240     # =>
1241     #   foo:
1242     #     # . prologue
1243     #     55/push-ebp
1244     #     89/<- %ebp 4/r32/esp
1245     #     {
1246     #       {
1247     #         0f 8d/jump-if->= break/disp32
1248     #         0f 82/jump-if-addr< loop/disp32
1249     #         ff 0/subop/increment *(ebp+8)
1250     #         e9/jump loop/disp32
1251     #       }
1252     #     }
1253     #     # . epilogue
1254     #     89/<- %esp 5/r32/ebp
1255     #     5d/pop-to-ebp
1256     #     c3/return
1257     # . prologue
1258     55/push-ebp
1259     89/<- %ebp 4/r32/esp
1260     # setup
1261     (clear-stream _test-input-stream)
1262     (clear-stream $_test-input-buffered-file->buffer)
1263     (clear-stream _test-output-stream)
1264     (clear-stream $_test-output-buffered-file->buffer)
1265     #
1266     (write _test-input-stream "fn foo x: int {\n")
1267     (write _test-input-stream "  {\n")
1268     (write _test-input-stream "    break-if->=\n")
1269     (write _test-input-stream "    loop-if-addr<\n")
1270     (write _test-input-stream "    increment x\n")
1271     (write _test-input-stream "    loop\n")
1272     (write _test-input-stream "  }\n")
1273     (write _test-input-stream "}\n")
1274     # convert
1275     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1276     (flush _test-output-buffered-file)
1277 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1283     # check output
1284     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-branches-in-block/0")
1285     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-branches-in-block/1")
1286     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
1287     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
1288     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-branches-in-block/4")
1289     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-branches-in-block/5")
1290     (check-next-stream-line-equal _test-output-stream "0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/6")
1291     (check-next-stream-line-equal _test-output-stream "0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/7")
1292     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/8")
1293     (check-next-stream-line-equal _test-output-stream "e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-block/9")
1294     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-branches-in-block/10")
1295     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-branches-in-block/12")
1296     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-branches-in-block/13")
1297     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/14")
1298     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/15")
1299     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-branches-in-block/16")
1300     # . epilogue
1301     89/<- %esp 5/r32/ebp
1302     5d/pop-to-ebp
1303     c3/return
1304 
1305 test-convert-function-with-branches-in-named-block:
1306     # empty function decl => function prologue and epilogue
1307     #   fn foo x: int {
1308     #     $bar: {
1309     #       break-if->= $bar
1310     #       loop-if-addr< $bar
1311     #       increment x
1312     #       loop
1313     #     }
1314     #   }
1315     # =>
1316     #   foo:
1317     #     # . prologue
1318     #     55/push-ebp
1319     #     89/<- %ebp 4/r32/esp
1320     #     {
1321     #       {
1322     #   $bar:loop:
1323     #         0f 8d/jump-if->= $bar:break/disp32
1324     #         0f 82/jump-if-addr< $bar:loop/disp32
1325     #         ff 0/subop/increment *(ebp+8)
1326     #         e9/jump loop/disp32
1327     #       }
1328     #   $bar:break:
1329     #     }
1330     #     # . epilogue
1331     #     89/<- %esp 5/r32/ebp
1332     #     5d/pop-to-ebp
1333     #     c3/return
1334     # . prologue
1335     55/push-ebp
1336     89/<- %ebp 4/r32/esp
1337     # setup
1338     (clear-stream _test-input-stream)
1339     (clear-stream $_test-input-buffered-file->buffer)
1340     (clear-stream _test-output-stream)
1341     (clear-stream $_test-output-buffered-file->buffer)
1342     #
1343     (write _test-input-stream "fn foo x: int {\n")
1344     (write _test-input-stream "  $bar: {\n")
1345     (write _test-input-stream "    break-if->= $bar\n")
1346     (write _test-input-stream "    loop-if-addr< $bar\n")
1347     (write _test-input-stream "    increment x\n")
1348     (write _test-input-stream "    loop\n")
1349     (write _test-input-stream "  }\n")
1350     (write _test-input-stream "}\n")
1351     # convert
1352     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1353     (flush _test-output-buffered-file)
1354 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1360     # check output
1361     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-branches-in-named-block/0")
1362     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
1363     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
1364     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
1365     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-branches-in-named-block/4")
1366     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-branches-in-named-block/5")
1367     (check-next-stream-line-equal _test-output-stream "$bar:loop:"            "F - test-convert-function-with-branches-in-named-block/6")
1368     (check-next-stream-line-equal _test-output-stream "0f 8d/jump-if->= $bar:break/disp32"  "F - test-convert-function-with-branches-in-named-block/7")
1369     (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/8")
1370     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/9")
1371     (check-next-stream-line-equal _test-output-stream "e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/10")
1372     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-branches-in-named-block/11")
1373     (check-next-stream-line-equal _test-output-stream "$bar:break:"           "F - test-convert-function-with-branches-in-named-block/12")
1374     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-branches-in-named-block/13")
1375     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-branches-in-named-block/14")
1376     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/15")
1377     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/16")
1378     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-branches-in-named-block/17")
1379     # . epilogue
1380     89/<- %esp 5/r32/ebp
1381     5d/pop-to-ebp
1382     c3/return
1383 
1384 #######################################################
1385 # Parsing
1386 #######################################################
1387 
1388 parse-mu:  # in: (addr buffered-file)
1389     # pseudocode
1390     #   var curr-function: (addr (handle function)) = Program
1391     #   var line: (stream byte 512)
1392     #   var word-slice: slice
1393     #   while true                                  # line loop
1394     #     clear-stream(line)
1395     #     read-line-buffered(in, line)
1396     #     if (line->write == 0) break               # end of file
1397     #     word-slice = next-mu-token(line)
1398     #     if slice-empty?(word-slice)               # end of line
1399     #       continue
1400     #     else if slice-starts-with?(word-slice, "#")  # comment
1401     #       continue                                # end of line
1402     #     else if slice-equal(word-slice, "fn")
1403     #       var new-function: (handle function) = allocate(function)
1404     #       var vars: (stack (addr var) 256)
1405     #       populate-mu-function-header(in, new-function, vars)
1406     #       populate-mu-function-body(in, new-function, vars)
1407     #       assert(vars->top == 0)
1408     #       *curr-function = new-function
1409     #       curr-function = &new-function->next
1410     #     else
1411     #       abort()
1412     #
1413     # . prologue
1414     55/push-ebp
1415     89/<- %ebp 4/r32/esp
1416     # . save registers
1417     50/push-eax
1418     51/push-ecx
1419     52/push-edx
1420     53/push-ebx
1421     57/push-edi
1422     # var line/ecx: (stream byte 512)
1423     81 5/subop/subtract %esp 0x200/imm32
1424     68/push 0x200/imm32/length
1425     68/push 0/imm32/read
1426     68/push 0/imm32/write
1427     89/<- %ecx 4/r32/esp
1428     # var word-slice/edx: slice
1429     68/push 0/imm32/end
1430     68/push 0/imm32/start
1431     89/<- %edx 4/r32/esp
1432     # var curr-function/edi: (addr (handle function)) = Program
1433     bf/copy-to-edi Program/imm32
1434     # var vars/ebx: (stack (addr var) 256)
1435     81 5/subop/subtract %esp 0x400/imm32
1436     68/push 0x400/imm32/length
1437     68/push 0/imm32/top
1438     89/<- %ebx 4/r32/esp
1439     {
1440 $parse-mu:line-loop:
1441       (clear-stream %ecx)
1442       (read-line-buffered *(ebp+8) %ecx)
1443       # if (line->write == 0) break
1444       81 7/subop/compare *ecx 0/imm32
1445       0f 84/jump-if-= break/disp32
1446 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------
1452       (next-mu-token %ecx %edx)
1453       # if slice-empty?(word-slice) continue
1454       (slice-empty? %edx)
1455       3d/compare-eax-and 0/imm32
1456       0f 85/jump-if-!= loop/disp32
1457       # if (*word-slice->start == "#") continue
1458       # . eax = *word-slice->start
1459       8b/-> *edx 0/r32/eax
1460       8a/copy-byte *eax 0/r32/AL
1461       81 4/subop/and %eax 0xff/imm32
1462       # . if (eax == '#') continue
1463       3d/compare-eax-and 0x23/imm32/hash
1464       0f 84/jump-if-= loop/disp32
1465       # if (slice-equal?(word-slice, "fn")) parse a function
1466       {
1467 $parse-mu:fn:
1468         (slice-equal? %edx "fn")
1469         3d/compare-eax-and 0/imm32
1470         0f 84/jump-if-= break/disp32
1471         # var new-function/eax: (handle function) = populate-mu-function(in, new-function, vars)
1472         (allocate Heap *Function-size)  # => eax
1473         (zero-out %eax *Function-size)
1474         (clear-stack %ebx)
1475         (populate-mu-function-header %ecx %eax %ebx)
1476         (populate-mu-function-body *(ebp+8) %eax %ebx)
1477         # *curr-function = new-function
1478         89/<- *edi 0/r32/eax
1479         # curr-function = &new-function->next
1480         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
1481         e9/jump $parse-mu:line-loop/disp32
1482       }
1483       # otherwise abort
1484       e9/jump $parse-mu:error1/disp32
1485     } # end line loop
1486 $parse-mu:end:
1487     # . reclaim locals
1488     81 0/subop/add %esp 0x630/imm32
1489     # . restore registers
1490     5f/pop-to-edi
1491     5b/pop-to-ebx
1492     5a/pop-to-edx
1493     59/pop-to-ecx
1494     58/pop-to-eax
1495     # . epilogue
1496     89/<- %esp 5/r32/ebp
1497     5d/pop-to-ebp
1498     c3/return
1499 
1500 $parse-mu:error1:
1501     # error("unexpected top-level command: " word-slice "\n")
1502     (write-buffered Stderr "unexpected top-level command: ")
1503     (write-slice-buffered Stderr %edx)
1504     (write-buffered Stderr "\n")
1505     (flush Stderr)
1506     # . syscall(exit, 1)
1507     bb/copy-to-ebx  1/imm32
1508     b8/copy-to-eax  1/imm32/exit
1509     cd/syscall  0x80/imm8
1510     # never gets here
1511 
1512 $parse-mu:error2:
1513     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
1514     (print-int32-buffered Stderr *ebx)
1515     (write-buffered Stderr " vars not reclaimed after fn '")
1516     (write-slice-buffered Stderr *eax)  # Function-name
1517     (write-buffered Stderr "'\n")
1518     (flush Stderr)
1519     # . syscall(exit, 1)
1520     bb/copy-to-ebx  1/imm32
1521     b8/copy-to-eax  1/imm32/exit
1522     cd/syscall  0x80/imm8
1523     # never gets here
1524 
1525 # scenarios considered:
1526 # ✗ fn foo  # no block
1527 # ✓ fn foo {
1528 # ✗ fn foo { {
1529 # ✗ fn foo { }
1530 # ✗ fn foo { } {
1531 # ✗ fn foo x {
1532 # ✗ fn foo x: {
1533 # ✓ fn foo x: int {
1534 # ✓ fn foo x: int {
1535 # ✓ fn foo x: int -> y/eax: int {
1536 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
1537     # pseudocode:
1538     #   var name: slice
1539     #   next-word(first-line, name)
1540     #   assert(name not in '{' '}' '->')
1541     #   out->name = slice-to-string(name)
1542     #   var next-offset: int = 8
1543     #   ## inouts
1544     #   while true
1545     #     ## name
1546     #     name = next-word(first-line)
1547     #     if (name == '{') goto done
1548     #     if (name == '->') break
1549     #     assert(name != '}')
1550     #     var v: (handle var) = parse-var-with-type(name, first-line)
1551     #     assert(v->register == null)
1552     #     v->stack-offset = next-offset
1553     #     next-offset += size-of(v)
1554     #     out->inouts = append(out->inouts, v)
1555     #     push(vars, v)
1556     #   ## outputs
1557     #   while true
1558     #     ## name
1559     #     name = next-word(first-line)
1560     #     assert(name not in '{' '}' '->')
1561     #     var v: (handle var) = parse-var-with-type(name, first-line)
1562     #     assert(v->register != null)
1563     #     out->outputs = append(out->outputs, v)
1564     #   done:
1565     #
1566     # . prologue
1567     55/push-ebp
1568     89/<- %ebp 4/r32/esp
1569     # . save registers
1570     50/push-eax
1571     51/push-ecx
1572     52/push-edx
1573     53/push-ebx
1574     57/push-edi
1575     # edi = out
1576     8b/-> *(ebp+0xc) 7/r32/edi
1577     # var word-slice/ecx: slice
1578     68/push 0/imm32/end
1579     68/push 0/imm32/start
1580     89/<- %ecx 4/r32/esp
1581     # var next-offset/edx = 8
1582     ba/copy-to-edx 8/imm32
1583     # read function name
1584     (next-word *(ebp+8) %ecx)
1585     # error checking
1586     # if (word-slice == '{') abort
1587     (slice-equal? %ecx "{")   # => eax
1588     3d/compare-eax-and 0/imm32
1589     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1590     # if (word-slice == '->') abort
1591     (slice-equal? %ecx "->")   # => eax
1592     3d/compare-eax-and 0/imm32
1593     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1594     # if (word-slice == '}') abort
1595     (slice-equal? %ecx "}")   # => eax
1596     3d/compare-eax-and 0/imm32
1597     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1598     # save function name
1599     (slice-to-string Heap %ecx)  # => eax
1600     89/<- *edi 0/r32/eax  # Function-name
1601     # initialize default subx-name as well
1602     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
1603     # save function inouts
1604     {
1605 $populate-mu-function-header:check-for-inout:
1606       (next-word *(ebp+8) %ecx)
1607       # if (word-slice == '{') goto done
1608       (slice-equal? %ecx "{")   # => eax
1609       3d/compare-eax-and 0/imm32
1610       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
1611       # if (word-slice == '->') break
1612       (slice-equal? %ecx "->")   # => eax
1613       3d/compare-eax-and 0/imm32
1614       0f 85/jump-if-!= break/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       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
1620       (parse-var-with-type %ecx *(ebp+8))  # => eax
1621       89/<- %ebx 0/r32/eax
1622       # assert(v->register == null)
1623       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
1624       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
1625       # v->stack-offset = next-offset
1626       89/<- *(ebx+0xc) 2/r32/edx  # Var-stack-offset
1627       # next-offset += size-of(v)
1628       (size-of %ebx)  # => eax
1629       01/add %edx 0/r32/eax
1630       #
1631       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
1632       89/<- *(edi+8) 0/r32/eax  # Function-inouts
1633       (push *(ebp+0x10) %ebx)
1634       #
1635       e9/jump loop/disp32
1636     }
1637     # save function outputs
1638     {
1639 $parse-var-with-type:check-for-out:
1640       (next-word *(ebp+8) %ecx)
1641       # if (word-slice == '{') break
1642       (slice-equal? %ecx "{")   # => eax
1643       3d/compare-eax-and 0/imm32
1644       0f 85/jump-if-!= break/disp32
1645       # if (word-slice == '->') abort
1646       (slice-equal? %ecx "->")   # => eax
1647       3d/compare-eax-and 0/imm32
1648       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1649       # if (word-slice == '}') abort
1650       (slice-equal? %ecx "}")   # => eax
1651       3d/compare-eax-and 0/imm32
1652       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
1653       #
1654       (parse-var-with-type %ecx *(ebp+8))  # => eax
1655       89/<- %ebx 0/r32/eax
1656       # assert(var->register != null)
1657       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
1658       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
1659       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
1660       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
1661       e9/jump loop/disp32
1662     }
1663 $populate-mu-function-header:done:
1664     (check-no-tokens-left *(ebp+8))
1665 $populate-mu-function-header:end:
1666     # . reclaim locals
1667     81 0/subop/add %esp 8/imm32
1668     # . restore registers
1669     5f/pop-to-edi
1670     5b/pop-to-ebx
1671     5a/pop-to-edx
1672     59/pop-to-ecx
1673     58/pop-to-eax
1674     # . epilogue
1675     89/<- %esp 5/r32/ebp
1676     5d/pop-to-ebp
1677     c3/return
1678 
1679 $populate-mu-function-header:error1:
1680     # error("function header not in form 'fn <name> {'")
1681     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
1682     (flush Stderr)
1683     (rewind-stream *(ebp+8))
1684     (write-stream 2 *(ebp+8))
1685     (write-buffered Stderr "'\n")
1686     (flush Stderr)
1687     # . syscall(exit, 1)
1688     bb/copy-to-ebx  1/imm32
1689     b8/copy-to-eax  1/imm32/exit
1690     cd/syscall  0x80/imm8
1691     # never gets here
1692 
1693 $populate-mu-function-header:error2:
1694     # error("function input '" var "' cannot be in a register")
1695     (write-buffered Stderr "function input '")
1696     (write-buffered Stderr *ebx)  # Var-name
1697     (write-buffered Stderr "' cannot be in a register")
1698     (flush Stderr)
1699     # . syscall(exit, 1)
1700     bb/copy-to-ebx  1/imm32
1701     b8/copy-to-eax  1/imm32/exit
1702     cd/syscall  0x80/imm8
1703     # never gets here
1704 
1705 $populate-mu-function-header:error3:
1706     # error("function input '" var "' must be in a register")
1707     (write-buffered Stderr "function input '")
1708     (write-buffered Stderr *eax)  # Var-name
1709     (write-buffered Stderr " must be in a register'")
1710     (flush Stderr)
1711     (rewind-stream *(ebp+8))
1712     (write-stream 2 *(ebp+8))
1713     (write-buffered Stderr "'\n")
1714     (flush Stderr)
1715     # . syscall(exit, 1)
1716     bb/copy-to-ebx  1/imm32
1717     b8/copy-to-eax  1/imm32/exit
1718     cd/syscall  0x80/imm8
1719     # never gets here
1720 
1721 test-function-header-with-arg:
1722     # 'foo n : int {'
1723     # . prologue
1724     55/push-ebp
1725     89/<- %ebp 4/r32/esp
1726     # setup
1727     (clear-stream _test-input-stream)
1728     (write _test-input-stream "foo n : int {\n")
1729     # var result/ecx: function
1730     2b/subtract-> *Function-size 4/r32/esp
1731     89/<- %ecx 4/r32/esp
1732     (zero-out %ecx *Function-size)
1733     # var vars/ebx: (stack (addr var) 16)
1734     81 5/subop/subtract %esp 0x10/imm32
1735     68/push 0x10/imm32/length
1736     68/push 0/imm32/top
1737     89/<- %ebx 4/r32/esp
1738     # convert
1739     (populate-mu-function-header _test-input-stream %ecx %ebx)
1740     # check result
1741     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
1742     # edx: (handle list var) = result->inouts
1743     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1744     # ebx: (handle var) = result->inouts->value
1745     8b/-> *edx 3/r32/ebx  # List-value
1746     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
1747     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1748     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
1749     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
1750     # . epilogue
1751     89/<- %esp 5/r32/ebp
1752     5d/pop-to-ebp
1753     c3/return
1754 
1755 test-function-header-with-multiple-args:
1756     # 'fn foo a: int, b: int, c: int {'
1757     # . prologue
1758     55/push-ebp
1759     89/<- %ebp 4/r32/esp
1760     # setup
1761     (clear-stream _test-input-stream)
1762     (write _test-input-stream "foo a: int, b: int c: int {\n")
1763     # result/ecx: (handle function)
1764     2b/subtract-> *Function-size 4/r32/esp
1765     89/<- %ecx 4/r32/esp
1766     (zero-out %ecx *Function-size)
1767     # var vars/ebx: (stack (addr var) 16)
1768     81 5/subop/subtract %esp 0x10/imm32
1769     68/push 0x10/imm32/length
1770     68/push 0/imm32/top
1771     89/<- %ebx 4/r32/esp
1772     # convert
1773     (populate-mu-function-header _test-input-stream %ecx %ebx)
1774     # check result
1775     (check-strings-equal *ecx "foo")  # Function-name
1776     # edx: (handle list var) = result->inouts
1777     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1778 $test-function-header-with-multiple-args:inout0:
1779     # ebx: (handle var) = result->inouts->value
1780     8b/-> *edx 3/r32/ebx  # List-value
1781     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
1782     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1783     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
1784     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
1785     # edx = result->inouts->next
1786     8b/-> *(edx+4) 2/r32/edx  # List-next
1787 $test-function-header-with-multiple-args:inout1:
1788     # ebx = result->inouts->next->value
1789     8b/-> *edx 3/r32/ebx  # List-value
1790     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
1791     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1792     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
1793     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
1794     # edx = result->inouts->next->next
1795     8b/-> *(edx+4) 2/r32/edx  # List-next
1796 $test-function-header-with-multiple-args:inout2:
1797     # ebx = result->inouts->next->next->value
1798     8b/-> *edx 3/r32/ebx  # List-value
1799     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
1800     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1801     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
1802     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
1803     # . epilogue
1804     89/<- %esp 5/r32/ebp
1805     5d/pop-to-ebp
1806     c3/return
1807 
1808 test-function-with-multiple-args-and-outputs:
1809     # fn foo a: int, b: int, c: int -> x: int, y: int {
1810     # . prologue
1811     55/push-ebp
1812     89/<- %ebp 4/r32/esp
1813     # setup
1814     (clear-stream _test-input-stream)
1815     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
1816     # result/ecx: (handle function)
1817     2b/subtract-> *Function-size 4/r32/esp
1818     89/<- %ecx 4/r32/esp
1819     (zero-out %ecx *Function-size)
1820     # var vars/ebx: (stack (addr var) 16)
1821     81 5/subop/subtract %esp 0x10/imm32
1822     68/push 0x10/imm32/length
1823     68/push 0/imm32/top
1824     89/<- %ebx 4/r32/esp
1825     # convert
1826     (populate-mu-function-header _test-input-stream %ecx %ebx)
1827     # check result
1828     (check-strings-equal *ecx "foo")  # Function-name
1829     # edx: (handle list var) = result->inouts
1830     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1831     # ebx: (handle var) = result->inouts->value
1832     8b/-> *edx 3/r32/ebx  # List-value
1833     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
1834     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1835     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
1836     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
1837     # edx = result->inouts->next
1838     8b/-> *(edx+4) 2/r32/edx  # List-next
1839     # ebx = result->inouts->next->value
1840     8b/-> *edx 3/r32/ebx  # List-value
1841     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
1842     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1843     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
1844     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
1845     # edx = result->inouts->next->next
1846     8b/-> *(edx+4) 2/r32/edx  # List-next
1847     # ebx = result->inouts->next->next->value
1848     8b/-> *edx 3/r32/ebx  # List-value
1849     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
1850     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1851     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
1852     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
1853     # edx: (handle list var) = result->outputs
1854     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
1855     # ebx: (handle var) = result->outputs->value
1856     8b/-> *edx 3/r32/ebx  # List-value
1857     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
1858     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
1859     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1860     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
1861     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
1862     # edx = result->outputs->next
1863     8b/-> *(edx+4) 2/r32/edx  # List-next
1864     # ebx = result->outputs->next->value
1865     8b/-> *edx 3/r32/ebx  # List-value
1866     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
1867     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
1868     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1869     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
1870     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
1871     # . epilogue
1872     89/<- %esp 5/r32/ebp
1873     5d/pop-to-ebp
1874     c3/return
1875 
1876 # format for variables with types
1877 #   x: int
1878 #   x: int
1879 #   x: int,
1880 # ignores at most one trailing colon or comma
1881 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
1882     # pseudocode:
1883     #   var v: (handle var) = allocate(Heap, Var-size)
1884     #   var s: slice
1885     #   next-token-from-slice(name->start, name->end, '/', s)
1886     #   var end: (addr byte) = s->end
1887     #   if (slice-ends-with(s, ":"))
1888     #     decrement s->end
1889     #   if (slice-ends-with(s, ","))
1890     #     decrement s->end
1891     #   v->name = slice-to-string(s)
1892     #   ## register
1893     #   next-token-from-slice(end, name->end, '/', s)
1894     #   if (slice-ends-with(s, ":"))
1895     #     decrement s->end
1896     #   if (slice-ends-with(s, ","))
1897     #     decrement s->end
1898     #   if (!slice-empty?(s))
1899     #     v->register = slice-to-string(s)
1900     #   ## type
1901     #   var type: (handle tree type-id) = parse-type(first-line)
1902     #   v->type = type
1903     #   return v
1904     #
1905     # . prologue
1906     55/push-ebp
1907     89/<- %ebp 4/r32/esp
1908     # . save registers
1909     51/push-ecx
1910     52/push-edx
1911     53/push-ebx
1912     56/push-esi
1913     57/push-edi
1914     # var result/edi: (handle var) = allocate(Heap, Var-size)
1915     (allocate Heap *Var-size)  # => eax
1916     (zero-out %eax *Var-size)
1917     89/<- %edi 0/r32/eax
1918     # esi = name
1919     8b/-> *(ebp+8) 6/r32/esi
1920     # var s/ecx: slice
1921     68/push 0/imm32/end
1922     68/push 0/imm32/start
1923     89/<- %ecx 4/r32/esp
1924 $parse-var-with-type:save-name:
1925     # save v->name
1926     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
1927     # . end/edx = s->end
1928     8b/-> *(ecx+4) 2/r32/edx
1929     # . if s ends with ':', decrement s->end
1930     {
1931       8b/-> *(ecx+4) 0/r32/eax
1932       48/decrement-eax
1933       8a/copy-byte *eax 3/r32/BL
1934       81 4/subop/and %ebx 0xff/imm32
1935       81 7/subop/compare %ebx 0x3a/imm32/colon
1936       75/jump-if-!= break/disp8
1937       89/<- *(ecx+4) 0/r32/eax
1938     }
1939     # . if s ends with ',', decrement s->end
1940     {
1941       8b/-> *(ecx+4) 0/r32/eax
1942       48/decrement-eax
1943       8a/copy-byte *eax 3/r32/BL
1944       81 4/subop/and %ebx 0xff/imm32
1945       81 7/subop/compare %ebx 0x2c/imm32/comma
1946       75/jump-if-!= break/disp8
1947       89/<- *(ecx+4) 0/r32/eax
1948     }
1949 $parse-var-with-type:write-name:
1950     (slice-to-string Heap %ecx)  # => eax
1951     89/<- *edi 0/r32/eax  # Var-name
1952     # save v->register
1953 $parse-var-with-type:save-register:
1954     (next-token-from-slice %edx *(esi+4) 0x2f %ecx)  # end, name->end, '/'
1955     # . if s ends with ':', decrement s->end
1956     {
1957       8b/-> *(ecx+4) 0/r32/eax
1958       48/decrement-eax
1959       8a/copy-byte *eax 3/r32/BL
1960       81 4/subop/and %ebx 0xff/imm32
1961       81 7/subop/compare %ebx 0x3a/imm32/colon
1962       75/jump-if-!= break/disp8
1963       89/<- *(ecx+4) 0/r32/eax
1964     }
1965     # . if s ends with ',', decrement s->end
1966     {
1967       8b/-> *(ecx+4) 0/r32/eax
1968       48/decrement-eax
1969       8a/copy-byte *eax 3/r32/BL
1970       81 4/subop/and %ebx 0xff/imm32
1971       81 7/subop/compare %ebx 0x2c/imm32/comma
1972       75/jump-if-!= break/disp8
1973       89/<- *(ecx+4) 0/r32/eax
1974     }
1975     # if (!slice-empty?(s)) v->register = slice-to-string(s)
1976     {
1977 $parse-var-with-type:write-register:
1978       # HACK: s->end can be less than s->start with all the decrements above
1979       # That's probably a sign we have the wrong algorithm for this function.
1980       8b/-> *ecx 0/r32/eax
1981       39/compare 0/r32/eax *(ecx+4)
1982       76/jump-if-<= break/disp8
1983       (slice-to-string Heap %ecx)
1984       89/<- *(edi+0x10) 0/r32/eax  # Var-register
1985     }
1986 $parse-var-with-type:save-type:
1987     (parse-type Heap *(ebp+0xc))  # => eax
1988     89/<- *(edi+4) 0/r32/eax  # Var-type
1989 $parse-var-with-type:end:
1990     # return result
1991     89/<- %eax 7/r32/edi
1992     # . reclaim locals
1993     81 0/subop/add %esp 8/imm32
1994     # . restore registers
1995     5f/pop-to-edi
1996     5e/pop-to-esi
1997     5b/pop-to-ebx
1998     5a/pop-to-edx
1999     59/pop-to-ecx
2000     # . epilogue
2001     89/<- %esp 5/r32/ebp
2002     5d/pop-to-ebp
2003     c3/return
2004 
2005 $parse-var-with-type:abort:
2006     # error("function header not in form 'fn <name> {'")
2007     (write-buffered Stderr "var should have form 'name: type' in '")
2008     (flush Stderr)
2009     (rewind-stream *(ebp+0xc))
2010     (write-stream 2 *(ebp+0xc))
2011     (write-buffered Stderr "'\n")
2012     (flush Stderr)
2013     # . syscall(exit, 1)
2014     bb/copy-to-ebx  1/imm32
2015     b8/copy-to-eax  1/imm32/exit
2016     cd/syscall  0x80/imm8
2017     # never gets here
2018 
2019 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2020     # pseudocode:
2021     #   var s: slice = next-mu-token(in)
2022     #   assert s != ""
2023     #   assert s != "->"
2024     #   assert s != "{"
2025     #   assert s != "}"
2026     #   if s == ")"
2027     #     return 0
2028     #   result = allocate(Tree)
2029     #   zero-out(result, *Tree-size)
2030     #   if s != "("
2031     #     result->left = pos-slice(Type-id, s)
2032     #     return
2033     #   result->left = parse-type(ad, in)
2034     #   result->right = parse-type-tree(ad, in)
2035     #
2036     # . prologue
2037     55/push-ebp
2038     89/<- %ebp 4/r32/esp
2039     # . save registers
2040     51/push-ecx
2041     52/push-edx
2042     # var s/ecx: slice
2043     68/push 0/imm32
2044     68/push 0/imm32
2045     89/<- %ecx 4/r32/esp
2046     # s = next-mu-token(in)
2047     (next-mu-token *(ebp+0xc) %ecx)
2048 #?     (write-buffered Stderr "tok: ")
2049 #?     (write-slice-buffered Stderr %ecx)
2050 #?     (write-buffered Stderr "$\n")
2051 #?     (flush Stderr)
2052     # assert s != ""
2053     (slice-equal? %ecx "")
2054     3d/compare-eax-and 0/imm32
2055     0f 85/jump-if-!= $parse-type:abort/disp32
2056     # assert s != "{"
2057     (slice-equal? %ecx "{")
2058     3d/compare-eax-and 0/imm32
2059     0f 85/jump-if-!= $parse-type:abort/disp32
2060     # assert s != "}"
2061     (slice-equal? %ecx "}")
2062     3d/compare-eax-and 0/imm32
2063     0f 85/jump-if-!= $parse-type:abort/disp32
2064     # assert s != "->"
2065     (slice-equal? %ecx "->")
2066     3d/compare-eax-and 0/imm32
2067     0f 85/jump-if-!= $parse-type:abort/disp32
2068     # if (s == ")") return 0
2069     (slice-equal? %ecx ")")
2070     3d/compare-eax-and 0/imm32
2071     b8/copy-to-eax 0/imm32
2072     0f 85/jump-if-!= $parse-type:end/disp32
2073     # var result/edx: (handle tree type-id)
2074     (allocate *(ebp+8) *Tree-size)  # => eax
2075     (zero-out %eax *Tree-size)
2076     89/<- %edx 0/r32/eax
2077     {
2078       # if (s != "(") break
2079       (slice-equal? %ecx "(")
2080       3d/compare-eax-and 0/imm32
2081       75/jump-if-!= break/disp8
2082       # result->left = pos-slice(Type-id, s)
2083       (pos-slice Type-id %ecx)
2084 #?       (write-buffered Stderr "=> {")
2085 #?       (print-int32-buffered Stderr %eax)
2086 #?       (write-buffered Stderr ", 0}\n")
2087 #?       (flush Stderr)
2088       89/<- *edx 0/r32/eax  # Tree-left
2089       e9/jump $parse-type:return-edx/disp32
2090     }
2091     # otherwise s == "("
2092     # result->left = parse-type(ad, in)
2093     (parse-type *(ebp+8) *(ebp+0xc))
2094 #?     (write-buffered Stderr "=> {")
2095 #?     (print-int32-buffered Stderr %eax)
2096     89/<- *edx 0/r32/eax  # Tree-left
2097     # result->right = parse-type-tree(ad, in)
2098     (parse-type-tree *(ebp+8) *(ebp+0xc))
2099 #?     (write-buffered Stderr Space)
2100 #?     (print-int32-buffered Stderr %eax)
2101 #?     (write-buffered Stderr "}\n")
2102 #?     (flush Stderr)
2103     89/<- *(edx+4) 0/r32/eax  # Tree-right
2104 $parse-type:return-edx:
2105     89/<- %eax 2/r32/edx
2106 $parse-type:end:
2107     # . reclaim locals
2108     81 0/subop/add %esp 8/imm32
2109     # . restore registers
2110     5a/pop-to-edx
2111     59/pop-to-ecx
2112     # . epilogue
2113     89/<- %esp 5/r32/ebp
2114     5d/pop-to-ebp
2115     c3/return
2116 
2117 $parse-type:abort:
2118     # error("unexpected token when parsing type: '" s "'\n")
2119     (write-buffered Stderr "unexpected token when parsing type: '")
2120     (write-slice-buffered Stderr %ecx)
2121     (write-buffered Stderr "'\n")
2122     (flush Stderr)
2123     # . syscall(exit, 1)
2124     bb/copy-to-ebx  1/imm32
2125     b8/copy-to-eax  1/imm32/exit
2126     cd/syscall  0x80/imm8
2127     # never gets here
2128 
2129 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2130     # pseudocode:
2131     #   var tmp: (handle tree type-id) = parse-type(ad, in)
2132     #   if tmp == 0
2133     #     return 0
2134     #   result = allocate(Tree)
2135     #   zero-out(result, *Tree-size)
2136     #   result->left = tmp
2137     #   result->right = parse-type-tree(ad, in)
2138     #
2139     # . prologue
2140     55/push-ebp
2141     89/<- %ebp 4/r32/esp
2142     # . save registers
2143     51/push-ecx
2144     52/push-edx
2145     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
2146     (parse-type *(ebp+8) *(ebp+0xc))
2147     # if (tmp == 0) return tmp
2148     3d/compare-eax-and 0/imm32
2149     74/jump-if-= $parse-type-tree:end/disp8
2150     # var tmp2/ecx = tmp
2151     89/<- %ecx 0/r32/eax
2152     # var result/edx: (handle tree type-id)
2153     (allocate *(ebp+8) *Tree-size)  # => eax
2154     (zero-out %eax *Tree-size)
2155     89/<- %edx 0/r32/eax
2156     # result->left = tmp2
2157     89/<- *edx 1/r32/ecx  # Tree-left
2158     # result->right = parse-type-tree(ad, in)
2159     (parse-type-tree *(ebp+8) *(ebp+0xc))
2160     89/<- *(edx+4) 0/r32/eax  # Tree-right
2161 $parse-type-tree:return-edx:
2162     89/<- %eax 2/r32/edx
2163 $parse-type-tree:end:
2164     # . restore registers
2165     5a/pop-to-edx
2166     59/pop-to-ecx
2167     # . epilogue
2168     89/<- %esp 5/r32/ebp
2169     5d/pop-to-ebp
2170     c3/return
2171 
2172 next-mu-token:  # in: (addr stream byte), out: (addr slice)
2173     # pseudocode:
2174     # start:
2175     #   skip-chars-matching-whitespace(in)
2176     #   if in->read >= in->write              # end of in
2177     #     out = {0, 0}
2178     #     return
2179     #   out->start = &in->data[in->read]
2180     #   var curr-byte/eax: byte = in->data[in->read]
2181     #   if curr->byte == ':'                  # comment token
2182     #     ++in->read
2183     #     goto start
2184     #   if curr->byte == ','                  # comment token
2185     #     ++in->read
2186     #     goto start
2187     #   if curr-byte == '#'                   # comment
2188     #     in->read = in->write                # skip to end of in
2189     #     goto done
2190     #   if curr-byte == '"'                   # string literal
2191     #     skip-string(in)
2192     #     goto done                           # no metadata
2193     #   if curr-byte == '('
2194     #     ++in->read
2195     #     goto done
2196     #   if curr-byte == ')'
2197     #     ++in->read
2198     #     goto done
2199     #   # read a word
2200     #   while true
2201     #     if in->read >= in->write
2202     #       break
2203     #     curr-byte = in->data[in->read]
2204     #     if curr-byte == ' '
2205     #       break
2206     #     if curr-byte == '\r'
2207     #       break
2208     #     if curr-byte == '\n'
2209     #       break
2210     #     if curr-byte == '('
2211     #       break
2212     #     if curr-byte == ')'
2213     #       break
2214     #     if curr-byte == ':'
2215     #       break
2216     #     if curr-byte == ','
2217     #       break
2218     #     ++in->read
2219     # done:
2220     #   out->end = &in->data[in->read]
2221     #   # hack: skip a few trailing delimiters, because we don't always use
2222     #   # this correct tokenizer in later tokens
2223     #   while true
2224     #     if in->read >= in->write
2225     #       break
2226     #     curr-byte = in->data[in->read]
2227     #     if curr-byte == ':'
2228     #       ++in->read
2229     #     else if curr-byte == ','
2230     #       ++in->read
2231     #     else
2232     #       break
2233     #
2234     # . prologue
2235     55/push-ebp
2236     89/<- %ebp 4/r32/esp
2237     # . save registers
2238     50/push-eax
2239     51/push-ecx
2240     56/push-esi
2241     57/push-edi
2242     # esi = in
2243     8b/-> *(ebp+8) 6/r32/esi
2244     # edi = out
2245     8b/-> *(ebp+0xc) 7/r32/edi
2246 $next-mu-token:start:
2247     (skip-chars-matching-whitespace %esi)
2248 $next-mu-token:check0:
2249     # if (in->read >= in->write) return out = {0, 0}
2250     # . ecx = in->read
2251     8b/-> *(esi+4) 1/r32/ecx
2252     # . if (ecx >= in->write) return out = {0, 0}
2253     3b/compare 1/r32/ecx *esi
2254     c7 0/subop/copy *edi 0/imm32
2255     c7 0/subop/copy *(edi+4) 0/imm32
2256     0f 8d/jump-if->= $next-mu-token:end/disp32
2257     # out->start = &in->data[in->read]
2258     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2259     89/<- *edi 0/r32/eax
2260     # var curr-byte/eax: byte = in->data[in->read]
2261     31/xor %eax 0/r32/eax
2262     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2263     {
2264 $next-mu-token:check-for-colon:
2265       # if (curr-byte != ':') break
2266       3d/compare-eax-and 0x3a/imm32/colon
2267       75/jump-if-!= break/disp8
2268       # ++in->read
2269       ff 0/subop/increment *(esi+4)
2270       # restart
2271       e9/jump $next-mu-token:start/disp32
2272     }
2273     {
2274 $next-mu-token:check-for-comma:
2275       # if (curr-byte != ',') break
2276       3d/compare-eax-and 0x2c/imm32/comma
2277       75/jump-if-!= break/disp8
2278       # ++in->read
2279       ff 0/subop/increment *(esi+4)
2280       # restart
2281       e9/jump $next-mu-token:start/disp32
2282     }
2283     {
2284 $next-mu-token:check-for-comment:
2285       # if (curr-byte != '#') break
2286       3d/compare-eax-and 0x23/imm32/pound
2287       75/jump-if-!= break/disp8
2288       # in->read = in->write  # skip rest of in
2289       8b/-> *esi 0/r32/eax
2290       89/<- *(esi+4) 0/r32/eax
2291       # return
2292       e9/jump $next-mu-token:done/disp32
2293     }
2294     {
2295 $next-mu-token:check-for-string-literal:
2296       # if (curr-byte != '"') break
2297       3d/compare-eax-and 0x22/imm32/dquote
2298       75/jump-if-!= break/disp8
2299       (skip-string %esi)
2300       # return
2301       e9/jump $next-mu-token:done/disp32
2302     }
2303     {
2304 $next-mu-token:check-for-open-paren:
2305       # if (curr-byte != '(') break
2306       3d/compare-eax-and 0x28/imm32/open-paren
2307       75/jump-if-!= break/disp8
2308       # ++in->read
2309       ff 0/subop/increment *(esi+4)
2310       # return
2311       e9/jump $next-mu-token:done/disp32
2312     }
2313     {
2314 $next-mu-token:check-for-close-paren:
2315       # if (curr-byte != ')') break
2316       3d/compare-eax-and 0x29/imm32/close-paren
2317       75/jump-if-!= break/disp8
2318       # ++in->read
2319       ff 0/subop/increment *(esi+4)
2320       # return
2321       e9/jump $next-mu-token:done/disp32
2322     }
2323     {
2324 $next-mu-token:regular-word-without-metadata:
2325       # if (in->read >= in->write) break
2326       # . ecx = in->read
2327       8b/-> *(esi+4) 1/r32/ecx
2328       # . if (ecx >= in->write) break
2329       3b/compare *esi 1/r32/ecx
2330       7d/jump-if->= break/disp8
2331       # var c/eax: byte = in->data[in->read]
2332       31/xor %eax 0/r32/eax
2333       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2334       # if (c == ' ') break
2335       3d/compare-eax-and 0x20/imm32/space
2336       74/jump-if-= break/disp8
2337       # if (c == '\r') break
2338       3d/compare-eax-and 0xd/imm32/carriage-return
2339       74/jump-if-= break/disp8
2340       # if (c == '\n') break
2341       3d/compare-eax-and 0xa/imm32/newline
2342       74/jump-if-= break/disp8
2343       # if (c == '(') break
2344       3d/compare-eax-and 0x28/imm32/open-paren
2345       0f 84/jump-if-= break/disp32
2346       # if (c == ')') break
2347       3d/compare-eax-and 0x29/imm32/close-paren
2348       0f 84/jump-if-= break/disp32
2349       # if (c == ':') break
2350       3d/compare-eax-and 0x3a/imm32/colon
2351       0f 84/jump-if-= break/disp32
2352       # if (c == ',') break
2353       3d/compare-eax-and 0x2c/imm32/comma
2354       0f 84/jump-if-= break/disp32
2355       # ++in->read
2356       ff 0/subop/increment *(esi+4)
2357       #
2358       e9/jump loop/disp32
2359     }
2360 $next-mu-token:done:
2361     # out->end = &in->data[in->read]
2362     8b/-> *(esi+4) 1/r32/ecx
2363     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2364     89/<- *(edi+4) 0/r32/eax
2365     {
2366 $next-mu-token:skip-trailing-delimiters:
2367       # if (in->read >= in->write) break
2368       # . ecx = in->read
2369       8b/-> *(esi+4) 1/r32/ecx
2370       # . if (ecx >= in->write) break
2371       3b/compare *esi 1/r32/ecx
2372       7d/jump-if->= break/disp8
2373       # var c/eax: byte = in->data[in->read]
2374       31/xor %eax 0/r32/eax
2375       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2376       # if (c == ':') ++in->read and loop
2377       {
2378         3d/compare-eax-and 0x3a/imm32/colon
2379         75/jump-if-!= break/disp8
2380         # ++in->read
2381         ff 0/subop/increment *(esi+4)
2382         #
2383         eb/jump $next-mu-token:skip-trailing-delimiters/disp8
2384       }
2385       # if (c == ',') ++in->read and loop
2386       {
2387         3d/compare-eax-and 0x2c/imm32/comma
2388         75/jump-if-!= break/disp8
2389         # ++in->read
2390         ff 0/subop/increment *(esi+4)
2391         #
2392         eb/jump $next-mu-token:skip-trailing-delimiters/disp8
2393       }
2394       # else break
2395     }
2396 $next-mu-token:end:
2397     # . restore registers
2398     5f/pop-to-edi
2399     5e/pop-to-esi
2400     59/pop-to-ecx
2401     58/pop-to-eax
2402     # . epilogue
2403     89/<- %esp 5/r32/ebp
2404     5d/pop-to-ebp
2405     c3/return
2406 
2407 # return the index in an array of strings matching 's'
2408 # index is denominated in elements, not bytes
2409 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
2410     # . prologue
2411     55/push-ebp
2412     89/<- %ebp 4/r32/esp
2413     # . save registers
2414     51/push-ecx
2415     52/push-edx
2416     53/push-ebx
2417     56/push-esi
2418 #?     (write-buffered Stderr "pos-slice: ")
2419 #?     (write-slice-buffered Stderr *(ebp+0xc))
2420 #?     (write-buffered Stderr "\n")
2421 #?     (flush Stderr)
2422     # esi = arr
2423     8b/-> *(ebp+8) 6/r32/esi
2424     # var index/ecx: int = 0
2425     b9/copy-to-ecx 0/imm32
2426     # var curr/edx: (addr (addr array byte)) = arr->data
2427     8d/copy-address *(esi+0xc) 2/r32/edx
2428     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
2429     8b/-> *esi 3/r32/ebx
2430     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
2431     {
2432 #?       (write-buffered Stderr "  ")
2433 #?       (print-int32-buffered Stderr %ecx)
2434 #?       (write-buffered Stderr "\n")
2435 #?       (flush Stderr)
2436       # if (curr >= max) return -1
2437       39/compare %edx 3/r32/ebx
2438       b8/copy-to-eax -1/imm32
2439       73/jump-if-addr>= $pos-slice:end/disp8
2440       # if (slice-equal?(s, *curr)) break
2441       (slice-equal? *(ebp+0xc) *edx)  # => eax
2442       3d/compare-eax-and 0/imm32
2443       75/jump-if-!= break/disp8
2444       # ++index
2445       41/increment-ecx
2446       # curr += 4
2447       81 0/subop/add %edx 4/imm32
2448       #
2449       eb/jump loop/disp8
2450     }
2451     # return index
2452     89/<- %eax 1/r32/ecx
2453 $pos-slice:end:
2454 #?     (write-buffered Stderr "=> ")
2455 #?     (print-int32-buffered Stderr %eax)
2456 #?     (write-buffered Stderr "\n")
2457     # . restore registers
2458     5e/pop-to-esi
2459     5b/pop-to-ebx
2460     5a/pop-to-edx
2461     59/pop-to-ecx
2462     # . epilogue
2463     89/<- %esp 5/r32/ebp
2464     5d/pop-to-ebp
2465     c3/return
2466 
2467 == data
2468 
2469 Type-id:  # (stream (address array byte))
2470   0x18/imm32/write
2471   0/imm32/read
2472   0x100/imm32/length
2473   # data
2474   "literal"/imm32  # 0
2475   "int"/imm32  # 1
2476   "addr"/imm32  # 2
2477   "array"/imm32  # 3
2478   "handle"/imm32  # 4
2479   "bool"/imm32  # 5
2480   0/imm32
2481   0/imm32
2482   # 0x20
2483   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2484   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2485   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2486   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2487   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2488   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2489   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
2490 
2491 == code
2492 
2493 test-parse-var-with-type:
2494     # . prologue
2495     55/push-ebp
2496     89/<- %ebp 4/r32/esp
2497     # (eax..ecx) = "x:"
2498     b8/copy-to-eax "x:"/imm32
2499     8b/-> *eax 1/r32/ecx
2500     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2501     05/add-to-eax 4/imm32
2502     # var slice/ecx: slice = {eax, ecx}
2503     51/push-ecx
2504     50/push-eax
2505     89/<- %ecx 4/r32/esp
2506     # _test-input-stream contains "int"
2507     (clear-stream _test-input-stream)
2508     (write _test-input-stream "int")
2509     #
2510     (parse-var-with-type %ecx _test-input-stream)
2511     8b/-> *eax 2/r32/edx  # Var-name
2512     (check-strings-equal %edx "x" "F - test-var-with-type/name")
2513     8b/-> *(eax+4) 2/r32/edx  # Var-type
2514     (check-ints-equal *edx 1 "F - test-var-with-type/type")
2515     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
2516     # . epilogue
2517     89/<- %esp 5/r32/ebp
2518     5d/pop-to-ebp
2519     c3/return
2520 
2521 test-parse-var-with-type-and-register:
2522     # . prologue
2523     55/push-ebp
2524     89/<- %ebp 4/r32/esp
2525     # (eax..ecx) = "x/eax"
2526     b8/copy-to-eax "x/eax"/imm32
2527     8b/-> *eax 1/r32/ecx
2528     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2529     05/add-to-eax 4/imm32
2530     # var slice/ecx: slice = {eax, ecx}
2531     51/push-ecx
2532     50/push-eax
2533     89/<- %ecx 4/r32/esp
2534     # _test-input-stream contains ": int"
2535     (clear-stream _test-input-stream)
2536     (write _test-input-stream ": int")
2537     #
2538     (parse-var-with-type %ecx _test-input-stream)
2539     8b/-> *eax 2/r32/edx  # Var-name
2540     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
2541     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2542     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
2543     8b/-> *(eax+4) 2/r32/edx  # Var-type
2544     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
2545     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
2546     # . epilogue
2547     89/<- %esp 5/r32/ebp
2548     5d/pop-to-ebp
2549     c3/return
2550 
2551 test-parse-var-with-trailing-characters:
2552     # . prologue
2553     55/push-ebp
2554     89/<- %ebp 4/r32/esp
2555     # (eax..ecx) = "x:"
2556     b8/copy-to-eax "x:"/imm32
2557     8b/-> *eax 1/r32/ecx
2558     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2559     05/add-to-eax 4/imm32
2560     # var slice/ecx: slice = {eax, ecx}
2561     51/push-ecx
2562     50/push-eax
2563     89/<- %ecx 4/r32/esp
2564     # _test-input-stream contains "int,"
2565     (clear-stream _test-input-stream)
2566     (write _test-input-stream "int,")
2567     #
2568     (parse-var-with-type %ecx _test-input-stream)
2569     8b/-> *eax 2/r32/edx  # Var-name
2570     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
2571     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2572     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
2573     8b/-> *(eax+4) 2/r32/edx  # Var-type
2574     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
2575     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
2576     # . epilogue
2577     89/<- %esp 5/r32/ebp
2578     5d/pop-to-ebp
2579     c3/return
2580 
2581 test-parse-var-with-register-and-trailing-characters:
2582     # . prologue
2583     55/push-ebp
2584     89/<- %ebp 4/r32/esp
2585     # (eax..ecx) = "x/eax:"
2586     b8/copy-to-eax "x/eax:"/imm32
2587     8b/-> *eax 1/r32/ecx
2588     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2589     05/add-to-eax 4/imm32
2590     # var slice/ecx: slice = {eax, ecx}
2591     51/push-ecx
2592     50/push-eax
2593     89/<- %ecx 4/r32/esp
2594     # _test-input-stream contains "int,"
2595     (clear-stream _test-input-stream)
2596     (write _test-input-stream "int,")
2597     #
2598     (parse-var-with-type %ecx _test-input-stream)
2599     8b/-> *eax 2/r32/edx  # Var-name
2600     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
2601     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2602     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
2603     8b/-> *(eax+4) 2/r32/edx  # Var-type
2604     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
2605     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
2606     # . epilogue
2607     89/<- %esp 5/r32/ebp
2608     5d/pop-to-ebp
2609     c3/return
2610 
2611 test-parse-var-with-compound-type:
2612     # . prologue
2613     55/push-ebp
2614     89/<- %ebp 4/r32/esp
2615     # (eax..ecx) = "x:"
2616     b8/copy-to-eax "x:"/imm32
2617     8b/-> *eax 1/r32/ecx
2618     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2619     05/add-to-eax 4/imm32
2620     # var slice/ecx: slice = {eax, ecx}
2621     51/push-ecx
2622     50/push-eax
2623     89/<- %ecx 4/r32/esp
2624     # _test-input-stream contains "(addr int)"
2625     (clear-stream _test-input-stream)
2626     (write _test-input-stream "(addr int)")
2627     #
2628     (parse-var-with-type %ecx _test-input-stream)
2629     8b/-> *eax 2/r32/edx  # Var-name
2630     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
2631     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
2632     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
2633     # var type/edx: (handle tree type-id) = var->type
2634     8b/-> *(eax+4) 2/r32/edx  # Var-type
2635     # type->left == atom(addr)
2636     8b/-> *edx 0/r32/eax  # Atom-value
2637     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
2638     # type->right->left == atom(int)
2639     8b/-> *(edx+4) 2/r32/edx  # Tree-right
2640     8b/-> *edx 0/r32/eax  # Tree-left
2641     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
2642     # type->right->right == null
2643     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
2644     # . epilogue
2645     89/<- %esp 5/r32/ebp
2646     5d/pop-to-ebp
2647     c3/return
2648 
2649 # identifier starts with a letter or '$' or '_'
2650 # no constraints at the moment on later letters
2651 # all we really want to do so far is exclude '{', '}' and '->'
2652 is-identifier?:  # in: (addr slice) -> result/eax: boolean
2653     # . prologue
2654     55/push-ebp
2655     89/<- %ebp 4/r32/esp
2656     # if (slice-empty?(in)) return false
2657     (slice-empty? *(ebp+8))  # => eax
2658     3d/compare-eax-and 0/imm32
2659     75/jump-if-!= $is-identifier?:false/disp8
2660     # var c/eax: byte = *in->start
2661     8b/-> *(ebp+8) 0/r32/eax
2662     8b/-> *eax 0/r32/eax
2663     8a/copy-byte *eax 0/r32/AL
2664     81 4/subop/and %eax 0xff/imm32
2665     # if (c == '$') return true
2666     3d/compare-eax-and 0x24/imm32/$
2667     74/jump-if-= $is-identifier?:true/disp8
2668     # if (c == '_') return true
2669     3d/compare-eax-and 0x5f/imm32/_
2670     74/jump-if-= $is-identifier?:true/disp8
2671     # drop case
2672     25/and-eax-with 0x5f/imm32
2673     # if (c < 'A') return false
2674     3d/compare-eax-and 0x41/imm32/A
2675     7c/jump-if-< $is-identifier?:false/disp8
2676     # if (c > 'Z') return false
2677     3d/compare-eax-and 0x5a/imm32/Z
2678     7f/jump-if-> $is-identifier?:false/disp8
2679     # otherwise return true
2680 $is-identifier?:true:
2681     b8/copy-to-eax 1/imm32/true
2682     eb/jump $is-identifier?:end/disp8
2683 $is-identifier?:false:
2684     b8/copy-to-eax 0/imm32/false
2685 $is-identifier?:end:
2686     # . epilogue
2687     89/<- %esp 5/r32/ebp
2688     5d/pop-to-ebp
2689     c3/return
2690 
2691 test-is-identifier-dollar:
2692     # . prologue
2693     55/push-ebp
2694     89/<- %ebp 4/r32/esp
2695     # (eax..ecx) = "$a"
2696     b8/copy-to-eax "$a"/imm32
2697     8b/-> *eax 1/r32/ecx
2698     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2699     05/add-to-eax 4/imm32
2700     # var slice/ecx: slice = {eax, ecx}
2701     51/push-ecx
2702     50/push-eax
2703     89/<- %ecx 4/r32/esp
2704     #
2705     (is-identifier? %ecx)
2706     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
2707     # . epilogue
2708     89/<- %esp 5/r32/ebp
2709     5d/pop-to-ebp
2710     c3/return
2711 
2712 test-is-identifier-underscore:
2713     # . prologue
2714     55/push-ebp
2715     89/<- %ebp 4/r32/esp
2716     # (eax..ecx) = "_a"
2717     b8/copy-to-eax "_a"/imm32
2718     8b/-> *eax 1/r32/ecx
2719     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2720     05/add-to-eax 4/imm32
2721     # var slice/ecx: slice = {eax, ecx}
2722     51/push-ecx
2723     50/push-eax
2724     89/<- %ecx 4/r32/esp
2725     #
2726     (is-identifier? %ecx)
2727     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
2728     # . epilogue
2729     89/<- %esp 5/r32/ebp
2730     5d/pop-to-ebp
2731     c3/return
2732 
2733 test-is-identifier-a:
2734     # . prologue
2735     55/push-ebp
2736     89/<- %ebp 4/r32/esp
2737     # (eax..ecx) = "a$"
2738     b8/copy-to-eax "a$"/imm32
2739     8b/-> *eax 1/r32/ecx
2740     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2741     05/add-to-eax 4/imm32
2742     # var slice/ecx: slice = {eax, ecx}
2743     51/push-ecx
2744     50/push-eax
2745     89/<- %ecx 4/r32/esp
2746     #
2747     (is-identifier? %ecx)
2748     (check-ints-equal %eax 1 "F - test-is-identifier-a")
2749     # . epilogue
2750     89/<- %esp 5/r32/ebp
2751     5d/pop-to-ebp
2752     c3/return
2753 
2754 test-is-identifier-z:
2755     # . prologue
2756     55/push-ebp
2757     89/<- %ebp 4/r32/esp
2758     # (eax..ecx) = "z$"
2759     b8/copy-to-eax "z$"/imm32
2760     8b/-> *eax 1/r32/ecx
2761     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2762     05/add-to-eax 4/imm32
2763     # var slice/ecx: slice = {eax, ecx}
2764     51/push-ecx
2765     50/push-eax
2766     89/<- %ecx 4/r32/esp
2767     #
2768     (is-identifier? %ecx)
2769     (check-ints-equal %eax 1 "F - test-is-identifier-z")
2770     # . epilogue
2771     89/<- %esp 5/r32/ebp
2772     5d/pop-to-ebp
2773     c3/return
2774 
2775 test-is-identifier-A:
2776     # . prologue
2777     55/push-ebp
2778     89/<- %ebp 4/r32/esp
2779     # (eax..ecx) = "A$"
2780     b8/copy-to-eax "A$"/imm32
2781     8b/-> *eax 1/r32/ecx
2782     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2783     05/add-to-eax 4/imm32
2784     # var slice/ecx: slice = {eax, ecx}
2785     51/push-ecx
2786     50/push-eax
2787     89/<- %ecx 4/r32/esp
2788     #
2789     (is-identifier? %ecx)
2790     (check-ints-equal %eax 1 "F - test-is-identifier-A")
2791     # . epilogue
2792     89/<- %esp 5/r32/ebp
2793     5d/pop-to-ebp
2794     c3/return
2795 
2796 test-is-identifier-Z:
2797     # . prologue
2798     55/push-ebp
2799     89/<- %ebp 4/r32/esp
2800     # (eax..ecx) = "Z$"
2801     b8/copy-to-eax "Z$"/imm32
2802     8b/-> *eax 1/r32/ecx
2803     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2804     05/add-to-eax 4/imm32
2805     # var slice/ecx: slice = {eax, ecx}
2806     51/push-ecx
2807     50/push-eax
2808     89/<- %ecx 4/r32/esp
2809     #
2810     (is-identifier? %ecx)
2811     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
2812     # . epilogue
2813     89/<- %esp 5/r32/ebp
2814     5d/pop-to-ebp
2815     c3/return
2816 
2817 test-is-identifier-@:
2818     # character before 'A' is invalid
2819     # . prologue
2820     55/push-ebp
2821     89/<- %ebp 4/r32/esp
2822     # (eax..ecx) = "@a"
2823     b8/copy-to-eax "@a"/imm32
2824     8b/-> *eax 1/r32/ecx
2825     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2826     05/add-to-eax 4/imm32
2827     # var slice/ecx: slice = {eax, ecx}
2828     51/push-ecx
2829     50/push-eax
2830     89/<- %ecx 4/r32/esp
2831     #
2832     (is-identifier? %ecx)
2833     (check-ints-equal %eax 0 "F - test-is-identifier-@")
2834     # . epilogue
2835     89/<- %esp 5/r32/ebp
2836     5d/pop-to-ebp
2837     c3/return
2838 
2839 test-is-identifier-square-bracket:
2840     # character after 'Z' is invalid
2841     # . prologue
2842     55/push-ebp
2843     89/<- %ebp 4/r32/esp
2844     # (eax..ecx) = "[a"
2845     b8/copy-to-eax "[a"/imm32
2846     8b/-> *eax 1/r32/ecx
2847     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2848     05/add-to-eax 4/imm32
2849     # var slice/ecx: slice = {eax, ecx}
2850     51/push-ecx
2851     50/push-eax
2852     89/<- %ecx 4/r32/esp
2853     #
2854     (is-identifier? %ecx)
2855     (check-ints-equal %eax 0 "F - test-is-identifier-@")
2856     # . epilogue
2857     89/<- %esp 5/r32/ebp
2858     5d/pop-to-ebp
2859     c3/return
2860 
2861 test-is-identifier-backtick:
2862     # character before 'a' is invalid
2863     # . prologue
2864     55/push-ebp
2865     89/<- %ebp 4/r32/esp
2866     # (eax..ecx) = "`a"
2867     b8/copy-to-eax "`a"/imm32
2868     8b/-> *eax 1/r32/ecx
2869     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2870     05/add-to-eax 4/imm32
2871     # var slice/ecx: slice = {eax, ecx}
2872     51/push-ecx
2873     50/push-eax
2874     89/<- %ecx 4/r32/esp
2875     #
2876     (is-identifier? %ecx)
2877     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
2878     # . epilogue
2879     89/<- %esp 5/r32/ebp
2880     5d/pop-to-ebp
2881     c3/return
2882 
2883 test-is-identifier-curly-brace-open:
2884     # character after 'z' is invalid; also used for blocks
2885     # . prologue
2886     55/push-ebp
2887     89/<- %ebp 4/r32/esp
2888     # (eax..ecx) = "{a"
2889     b8/copy-to-eax "{a"/imm32
2890     8b/-> *eax 1/r32/ecx
2891     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2892     05/add-to-eax 4/imm32
2893     # var slice/ecx: slice = {eax, ecx}
2894     51/push-ecx
2895     50/push-eax
2896     89/<- %ecx 4/r32/esp
2897     #
2898     (is-identifier? %ecx)
2899     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
2900     # . epilogue
2901     89/<- %esp 5/r32/ebp
2902     5d/pop-to-ebp
2903     c3/return
2904 
2905 test-is-identifier-curly-brace-close:
2906     # . prologue
2907     55/push-ebp
2908     89/<- %ebp 4/r32/esp
2909     # (eax..ecx) = "}a"
2910     b8/copy-to-eax "}a"/imm32
2911     8b/-> *eax 1/r32/ecx
2912     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2913     05/add-to-eax 4/imm32
2914     # var slice/ecx: slice = {eax, ecx}
2915     51/push-ecx
2916     50/push-eax
2917     89/<- %ecx 4/r32/esp
2918     #
2919     (is-identifier? %ecx)
2920     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
2921     # . epilogue
2922     89/<- %esp 5/r32/ebp
2923     5d/pop-to-ebp
2924     c3/return
2925 
2926 test-is-identifier-hyphen:
2927     # disallow leading '-' since '->' has special meaning
2928     # . prologue
2929     55/push-ebp
2930     89/<- %ebp 4/r32/esp
2931     # (eax..ecx) = "-a"
2932     b8/copy-to-eax "-a"/imm32
2933     8b/-> *eax 1/r32/ecx
2934     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2935     05/add-to-eax 4/imm32
2936     # var slice/ecx: slice = {eax, ecx}
2937     51/push-ecx
2938     50/push-eax
2939     89/<- %ecx 4/r32/esp
2940     #
2941     (is-identifier? %ecx)
2942     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
2943     # . epilogue
2944     89/<- %esp 5/r32/ebp
2945     5d/pop-to-ebp
2946     c3/return
2947 
2948 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
2949     # . prologue
2950     55/push-ebp
2951     89/<- %ebp 4/r32/esp
2952     # . save registers
2953     50/push-eax
2954     56/push-esi
2955     57/push-edi
2956     # esi = in
2957     8b/-> *(ebp+8) 6/r32/esi
2958     # edi = out
2959     8b/-> *(ebp+0xc) 7/r32/edi
2960     # initialize some global state
2961     c7 0/subop/copy *Curr-block-depth 0/imm32
2962     c7 0/subop/copy *Next-local-stack-offset -4/imm32
2963     # var eax: (handle block) = parse-mu-block(in, vars)
2964     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
2965     # out->body = eax
2966     89/<- *(edi+0x10) 0/r32/eax  # Function-body
2967 $populate-mu-function-body:end:
2968     # . restore registers
2969     5f/pop-to-edi
2970     5e/pop-to-esi
2971     58/pop-to-eax
2972     # . epilogue
2973     89/<- %esp 5/r32/ebp
2974     5d/pop-to-ebp
2975     c3/return
2976 
2977 == data
2978 
2979 # Global state when parsing a function
2980 Curr-block-depth:  # (addr int)
2981     0/imm32
2982 Next-local-stack-offset:  # (addr int)
2983     -4/imm32
2984 
2985 == code
2986 
2987 # parses a block, assuming that the leading '{' has already been read by the caller
2988 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
2989     # pseudocode:
2990     #   var line: (stream byte 512)
2991     #   var word-slice: slice
2992     #   result/eax = allocate(Heap, Stmt-size)
2993     #   result->tag = 0/Block
2994     #   while true                                  # line loop
2995     #     clear-stream(line)
2996     #     read-line-buffered(in, line)
2997     #     if (line->write == 0) break               # end of file
2998     #     word-slice = next-word(line)
2999     #     if slice-empty?(word-slice)               # end of line
3000     #       continue
3001     #     else if slice-starts-with?(word-slice, "#")
3002     #       continue
3003     #     else if slice-equal?(word-slice, "{")
3004     #       assert(no-tokens-in(line))
3005     #       block = parse-mu-block(in, vars, fn)
3006     #       append-to-block(result, block)
3007     #     else if slice-equal?(word-slice, "}")
3008     #       break
3009     #     else if slice-ends-with?(word-slice, ":")
3010     #       # TODO: check the rest of line
3011     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
3012     #       append-to-block(result, named-block)
3013     #     else if slice-equal?(word-slice, "var")
3014     #       var-def = parse-mu-var-def(line, vars)
3015     #       append-to-block(result, var-def)
3016     #     else
3017     #       stmt = parse-mu-stmt(line, vars, fn)
3018     #       append-to-block(result, stmt)
3019     #   return result
3020     #
3021     # . prologue
3022     55/push-ebp
3023     89/<- %ebp 4/r32/esp
3024     # . save registers
3025     51/push-ecx
3026     52/push-edx
3027     53/push-ebx
3028     57/push-edi
3029     # var line/ecx: (stream byte 512)
3030     81 5/subop/subtract %esp 0x200/imm32
3031     68/push 0x200/imm32/length
3032     68/push 0/imm32/read
3033     68/push 0/imm32/write
3034     89/<- %ecx 4/r32/esp
3035     # var word-slice/edx: slice
3036     68/push 0/imm32/end
3037     68/push 0/imm32/start
3038     89/<- %edx 4/r32/esp
3039     # edi = result
3040     (allocate Heap *Stmt-size)  # => eax
3041     (zero-out %eax *Stmt-size)
3042     89/<- %edi 0/r32/eax
3043     # result->tag is implicitly block (0)
3044     { # line loop
3045 $parse-mu-block:line-loop:
3046       # line = read-line-buffered(in)
3047       (clear-stream %ecx)
3048       (read-line-buffered *(ebp+8) %ecx)
3049 #?       (write-buffered Stderr "line: ")
3050 #?       (write-stream-data Stderr %ecx)
3051 #?       (write-buffered Stderr Newline)
3052 #?       (flush Stderr)
3053       # if (line->write == 0) break
3054       81 7/subop/compare *ecx 0/imm32
3055       0f 84/jump-if-= break/disp32
3056       # word-slice = next-word(line)
3057       (next-word %ecx %edx)
3058 #?       (write-buffered Stderr "word: ")
3059 #?       (write-slice-buffered Stderr %edx)
3060 #?       (write-buffered Stderr Newline)
3061 #?       (flush Stderr)
3062       # if slice-empty?(word-slice) continue
3063       (slice-empty? %edx)
3064       3d/compare-eax-and 0/imm32
3065       0f 85/jump-if-!= loop/disp32
3066       # if (slice-starts-with?(word-slice, '#') continue
3067       # . eax = *word-slice->start
3068       8b/-> *edx 0/r32/eax
3069       8a/copy-byte *eax 0/r32/AL
3070       81 4/subop/and %eax 0xff/imm32
3071       # . if (eax == '#') continue
3072       3d/compare-eax-and 0x23/imm32/hash
3073       0f 84/jump-if-= loop/disp32
3074       # if slice-equal?(word-slice, "{")
3075       {
3076 $parse-mu-block:check-for-block:
3077         (slice-equal? %edx "{")
3078         3d/compare-eax-and 0/imm32
3079         74/jump-if-= break/disp8
3080         (check-no-tokens-left %ecx)
3081         # parse new block and append
3082         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3083         (append-to-block Heap %edi %eax)
3084         e9/jump $parse-mu-block:line-loop/disp32
3085       }
3086       # if slice-equal?(word-slice, "}") break
3087 $parse-mu-block:check-for-end:
3088       (slice-equal? %edx "}")
3089       3d/compare-eax-and 0/imm32
3090       0f 85/jump-if-!= break/disp32
3091       # if slice-ends-with?(word-slice, ":") parse named block and append
3092       {
3093 $parse-mu-block:check-for-named-block:
3094         # . eax = *(word-slice->end-1)
3095         8b/-> *(edx+4) 0/r32/eax
3096         48/decrement-eax
3097         8a/copy-byte *eax 0/r32/AL
3098         81 4/subop/and %eax 0xff/imm32
3099         # . if (eax != ':') break
3100         3d/compare-eax-and 0x3a/imm32/colon
3101         0f 85/jump-if-!= break/disp32
3102         #
3103         # TODO: error-check the rest of 'line'
3104         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3105         (append-to-block Heap %edi %eax)
3106         e9/jump $parse-mu-block:line-loop/disp32
3107       }
3108       # if slice-equal?(word-slice, "var")
3109       {
3110 $parse-mu-block:check-for-var:
3111         (slice-equal? %edx "var")
3112         3d/compare-eax-and 0/imm32
3113         74/jump-if-= break/disp8
3114         #
3115         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
3116         (append-to-block Heap %edi %eax)
3117         e9/jump $parse-mu-block:line-loop/disp32
3118       }
3119 $parse-mu-block:regular-stmt:
3120       # otherwise
3121       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3122       (append-to-block Heap %edi %eax)
3123       e9/jump loop/disp32
3124     } # end line loop
3125     # return result
3126     89/<- %eax 7/r32/edi
3127 $parse-mu-block:end:
3128     # . reclaim locals
3129     81 0/subop/add %esp 0x214/imm32
3130     # . restore registers
3131     5f/pop-to-edi
3132     5b/pop-to-ebx
3133     5a/pop-to-edx
3134     59/pop-to-ecx
3135     # . epilogue
3136     89/<- %esp 5/r32/ebp
3137     5d/pop-to-ebp
3138     c3/return
3139 
3140 $parse-mu-block:abort:
3141     # error("'{' or '}' should be on its own line, but got '")
3142     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3143     (rewind-stream %ecx)
3144     (write-stream 2 %ecx)
3145     (write-buffered Stderr "'\n")
3146     (flush Stderr)
3147     # . syscall(exit, 1)
3148     bb/copy-to-ebx  1/imm32
3149     b8/copy-to-eax  1/imm32/exit
3150     cd/syscall  0x80/imm8
3151     # never gets here
3152 
3153 check-no-tokens-left:  # line: (addr stream byte)
3154     # . prologue
3155     55/push-ebp
3156     89/<- %ebp 4/r32/esp
3157     # . save registers
3158     50/push-eax
3159     51/push-ecx
3160     # var s/ecx: slice
3161     68/push 0/imm32/end
3162     68/push 0/imm32/start
3163     89/<- %ecx 4/r32/esp
3164     #
3165     (next-word *(ebp+8) %ecx)
3166     # if slice-empty?(s) return
3167     (slice-empty? %ecx)
3168     3d/compare-eax-and 0/imm32
3169     75/jump-if-!= $check-no-tokens-left:end/disp8
3170     # if (slice-starts-with?(s, '#') return
3171     # . eax = *s->start
3172     8b/-> *edx 0/r32/eax
3173     8a/copy-byte *eax 0/r32/AL
3174     81 4/subop/and %eax 0xff/imm32
3175     # . if (eax == '#') continue
3176     3d/compare-eax-and 0x23/imm32/hash
3177     74/jump-if-= $check-no-tokens-left:end/disp8
3178     # abort
3179     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3180     (rewind-stream %ecx)
3181     (write-stream 2 %ecx)
3182     (write-buffered Stderr "'\n")
3183     (flush Stderr)
3184     # . syscall(exit, 1)
3185     bb/copy-to-ebx  1/imm32
3186     b8/copy-to-eax  1/imm32/exit
3187     cd/syscall  0x80/imm8
3188     # never gets here
3189 $check-no-tokens-left:end:
3190     # . reclaim locals
3191     81 0/subop/add %esp 8/imm32
3192     # . restore registers
3193     59/pop-to-ecx
3194     58/pop-to-eax
3195     # . epilogue
3196     89/<- %esp 5/r32/ebp
3197     5d/pop-to-ebp
3198     c3/return
3199 
3200 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3201     # pseudocode:
3202     #   result = parse-mu-block(in, vars, fn)
3203     #   result->tag = named-block
3204     #   result->name = slice-to-string(name)
3205     #   return result
3206     #
3207     # . prologue
3208     55/push-ebp
3209     89/<- %ebp 4/r32/esp
3210     # . save registers
3211     51/push-ecx
3212     # var s/ecx: (addr array byte) = slice-to-string(name)
3213     (slice-to-string Heap *(ebp+8))  # => eax
3214     89/<- %ecx 0/r32/eax
3215     #
3216     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
3217     # result->tag = named-block
3218     c7 0/subop/copy *eax 4/imm32/named-block  # Stmt-tag
3219     # result->name = slice-to-string(name)
3220     89/<- *(eax+8) 1/r32/ecx  # Named-block-name
3221 $parse-mu-named-block:end:
3222     # . restore registers
3223     59/pop-to-ecx
3224     # . epilogue
3225     89/<- %esp 5/r32/ebp
3226     5d/pop-to-ebp
3227     c3/return
3228 
3229 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
3230     # . prologue
3231     55/push-ebp
3232     89/<- %ebp 4/r32/esp
3233     # . save registers
3234     51/push-ecx
3235     52/push-edx
3236     # var word-slice/ecx: slice
3237     68/push 0/imm32/end
3238     68/push 0/imm32/start
3239     89/<- %ecx 4/r32/esp
3240     # var v/edx: (handle var) = parse-var-with-type(line)
3241     (next-word *(ebp+8) %ecx)
3242     (parse-var-with-type %ecx *(ebp+8))  # => eax
3243     89/<- %edx 0/r32/eax
3244     # v->stack-offset = *Next-local-stack-offset
3245     8b/-> *Next-local-stack-offset 0/r32/eax
3246     89/<- *(edx+0xc) 0/r32/eax  # Var-stack-offset
3247     # *Next-local-stack-offset -= size-of(v)
3248     (size-of %edx)
3249     29/subtract-from *Next-local-stack-offset 0/r32/eax
3250     #
3251     (push *(ebp+0xc) %edx)
3252     # either v has no register and there's no more to this line
3253     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
3254     3d/compare-eax-and 0/imm32
3255     {
3256       75/jump-if-!= break/disp8
3257       # TODO: ensure that there's nothing else on this line
3258       (new-vardef Heap %edx)  # => eax
3259       eb/jump $parse-mu-var-def:end/disp8
3260     }
3261     # or v has a register and there's more to this line
3262     {
3263       74/jump-if-= break/disp8
3264       # ensure that the next word is '<-'
3265       (next-word *(ebp+8) %ecx)
3266       (slice-equal? %ecx "<-")  # => eax
3267       3d/compare-eax-and 0/imm32
3268       74/jump-if-= $parse-mu-var-def:abort/disp8
3269       #
3270       (new-regvardef Heap %edx)  # => eax
3271       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
3272     }
3273 $parse-mu-var-def:end:
3274     # . reclaim locals
3275     81 0/subop/add %esp 8/imm32
3276     # . restore registers
3277     5a/pop-to-edx
3278     59/pop-to-ecx
3279     # . epilogue
3280     89/<- %esp 5/r32/ebp
3281     5d/pop-to-ebp
3282     c3/return
3283 
3284 $parse-mu-var-def:abort:
3285     (rewind-stream *(ebp+8))
3286     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
3287     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
3288     (flush Stderr)
3289     (write-stream 2 *(ebp+8))
3290     (write-buffered Stderr "'\n")
3291     (flush Stderr)
3292     # . syscall(exit, 1)
3293     bb/copy-to-ebx  1/imm32
3294     b8/copy-to-eax  1/imm32/exit
3295     cd/syscall  0x80/imm8
3296     # never gets here
3297 
3298 test-parse-mu-var-def:
3299     # 'var n: int'
3300     # . prologue
3301     55/push-ebp
3302     89/<- %ebp 4/r32/esp
3303     # setup
3304     (clear-stream _test-input-stream)
3305     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
3306     # var vars/ecx: (stack (addr var) 4)
3307     81 5/subop/subtract %esp 0x10/imm32
3308     68/push 0x10/imm32/length
3309     68/push 0/imm32/top
3310     89/<- %ecx 4/r32/esp
3311     (clear-stack %ecx)
3312     # convert
3313     (parse-mu-var-def _test-input-stream %ecx)  # => eax
3314     # check result
3315     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is vardef
3316     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
3317     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
3318     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
3319     # TODO: ensure stack-offset is -4
3320     # TODO: ensure block-depth is 1
3321     # ensure type is int
3322     8b/-> *(eax+4) 0/r32/eax  # Var-type
3323     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
3324     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
3325     # . epilogue
3326     89/<- %esp 5/r32/ebp
3327     5d/pop-to-ebp
3328     c3/return
3329 
3330 test-parse-mu-reg-var-def:
3331     # 'var n/eax: int <- copy 0'
3332     # . prologue
3333     55/push-ebp
3334     89/<- %ebp 4/r32/esp
3335     # setup
3336     (clear-stream _test-input-stream)
3337     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
3338     # var vars/ecx: (stack (addr var) 4)
3339     81 5/subop/subtract %esp 0x10/imm32
3340     68/push 0x10/imm32/length
3341     68/push 0/imm32/top
3342     89/<- %ecx 4/r32/esp
3343     (clear-stack %ecx)
3344     # convert
3345     (parse-mu-var-def _test-input-stream %ecx)  # => eax
3346     # check result
3347     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is regvardef
3348     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
3349     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
3350     8b/-> *eax 0/r32/eax  # List-value
3351     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
3352     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
3353     # TODO: ensure stack-offset is -4
3354     # TODO: ensure block-depth is 1
3355     # ensure type is int
3356     8b/-> *(eax+4) 0/r32/eax  # Var-type
3357     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
3358     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
3359     # . epilogue
3360     89/<- %esp 5/r32/ebp
3361     5d/pop-to-ebp
3362     c3/return
3363 
3364 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3365     # pseudocode:
3366     #   var name: slice
3367     #   result = allocate(Heap, Stmt-size)
3368     #   if stmt-has-outputs?(line)
3369     #     while true
3370     #       name = next-mu-token(line)
3371     #       if (name == '<-') break
3372     #       assert(is-identifier?(name))
3373     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
3374     #       result->outputs = append(result->outputs, v)
3375     #   add-operation-and-inputs-to-stmt(result, line, vars)
3376     #
3377     # . prologue
3378     55/push-ebp
3379     89/<- %ebp 4/r32/esp
3380     # . save registers
3381     51/push-ecx
3382     57/push-edi
3383     # var name/ecx: slice
3384     68/push 0/imm32/end
3385     68/push 0/imm32/start
3386     89/<- %ecx 4/r32/esp
3387     # result/edi: (handle stmt)
3388     (allocate Heap *Stmt-size)  # => eax
3389     (zero-out %eax *Stmt-size)
3390     89/<- %edi 0/r32/eax
3391     # result->tag = 1/stmt
3392     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
3393     {
3394       (stmt-has-outputs? *(ebp+8))
3395       3d/compare-eax-and 0/imm32
3396       0f 84/jump-if-= break/disp32
3397       {
3398 $parse-mu-stmt:read-outputs:
3399         # name = next-mu-token(line)
3400         (next-mu-token *(ebp+8) %ecx)
3401         # if slice-empty?(word-slice) break
3402         (slice-empty? %ecx)
3403         3d/compare-eax-and 0/imm32
3404         0f 85/jump-if-!= break/disp32
3405         # if (name == "<-") break
3406         (slice-equal? %ecx "<-")
3407         3d/compare-eax-and 0/imm32
3408         75/jump-if-!= break/disp8
3409         # assert(is-identifier?(name))
3410         (is-identifier? %ecx)
3411         3d/compare-eax-and 0/imm32
3412         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
3413         #
3414         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3415         (append-list Heap %eax *(edi+0xc))  # Stmt1-outputs => eax
3416         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
3417         e9/jump loop/disp32
3418       }
3419     }
3420     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
3421 $parse-mu-stmt:end:
3422     # return result
3423     89/<- %eax 7/r32/edi
3424     # . reclaim locals
3425     81 0/subop/add %esp 8/imm32
3426     # . restore registers
3427     5f/pop-to-edi
3428     59/pop-to-ecx
3429     # . epilogue
3430     89/<- %esp 5/r32/ebp
3431     5d/pop-to-ebp
3432     c3/return
3433 
3434 $parse-mu-stmt:abort:
3435     # error("invalid identifier '" name "'\n")
3436     (write-buffered Stderr "invalid identifier '")
3437     (write-slice-buffered Stderr %ecx)
3438     (write-buffered Stderr "'\n")
3439     (flush Stderr)
3440     # . syscall(exit, 1)
3441     bb/copy-to-ebx  1/imm32
3442     b8/copy-to-eax  1/imm32/exit
3443     cd/syscall  0x80/imm8
3444     # never gets here
3445 
3446 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte)
3447     # pseudocode:
3448     #   stmt->name = slice-to-string(next-mu-token(line))
3449     #   while true
3450     #     name = next-mu-token(line)
3451     #     v = lookup-var-or-literal(name)
3452     #     stmt->inouts = append(stmt->inouts, v)
3453     #
3454     # . prologue
3455     55/push-ebp
3456     89/<- %ebp 4/r32/esp
3457     # . save registers
3458     50/push-eax
3459     51/push-ecx
3460     57/push-edi
3461     # edi = stmt
3462     8b/-> *(ebp+8) 7/r32/edi
3463     # var name/ecx: slice
3464     68/push 0/imm32/end
3465     68/push 0/imm32/start
3466     89/<- %ecx 4/r32/esp
3467 $add-operation-and-inputs-to-stmt:read-operation:
3468     (next-mu-token *(ebp+0xc) %ecx)
3469     (slice-to-string Heap %ecx)  # => eax
3470     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
3471     {
3472 $add-operation-and-inputs-to-stmt:read-inouts:
3473       # name = next-mu-token(line)
3474       (next-mu-token *(ebp+0xc) %ecx)
3475       # if slice-empty?(word-slice) break
3476       (slice-empty? %ecx)  # => eax
3477       3d/compare-eax-and 0/imm32
3478       0f 85/jump-if-!= break/disp32
3479       # if (name == "<-") abort
3480       (slice-equal? %ecx "<-")
3481       3d/compare-eax-and 0/imm32
3482       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
3483       # if (name starts with "$") treat it as a literal
3484       {
3485         # var eax: byte = name[0]
3486         8b/-> *ecx 0/r32/eax
3487         8a/copy-byte *eax 0/r32/AL
3488         81 4/subop/and %eax 0xff/imm32
3489         # if (eax != '$') goto next condition
3490         3d/compare-eax-and 0x24/imm32/dollar
3491         75/jump-if-!= break/disp8
3492         # var eax: (handle var)
3493         (new-label Heap %ecx)  # => eax
3494         # stmt->inouts = append(eax, stmt->inouts)
3495         (append-list Heap %eax *(edi+8))  # Stmt1-inouts => eax
3496         89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts
3497         # continue
3498         e9/jump $add-operation-and-inputs-to-stmt:read-inouts/disp32
3499       }
3500 $add-operation-and-inputs-to-stmt:regular-inout:
3501       # otherwise
3502       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
3503       (append-list Heap %eax *(edi+8))  # Stmt1-inouts or Regvardef-inouts => eax
3504       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
3505       e9/jump loop/disp32
3506     }
3507 $add-operation-and-inputs-to-stmt:end:
3508     # . reclaim locals
3509     81 0/subop/add %esp 8/imm32
3510     # . restore registers
3511     5f/pop-to-edi
3512     59/pop-to-ecx
3513     58/pop-to-eax
3514     # . epilogue
3515     89/<- %esp 5/r32/ebp
3516     5d/pop-to-ebp
3517     c3/return
3518 
3519 $add-operation-and-inputs-to-stmt:abort:
3520     # error("invalid statement '" line "'\n")
3521     (rewind-stream *(ebp+8))
3522     (write-buffered Stderr "invalid identifier '")
3523     (flush Stderr)
3524     (write-stream 2 *(ebp+8))
3525     (write-buffered Stderr "'\n")
3526     (flush Stderr)
3527     # . syscall(exit, 1)
3528     bb/copy-to-ebx  1/imm32
3529     b8/copy-to-eax  1/imm32/exit
3530     cd/syscall  0x80/imm8
3531     # never gets here
3532 
3533 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
3534     # . prologue
3535     55/push-ebp
3536     89/<- %ebp 4/r32/esp
3537     # . save registers
3538     51/push-ecx
3539     # var word-slice/ecx: slice
3540     68/push 0/imm32/end
3541     68/push 0/imm32/start
3542     89/<- %ecx 4/r32/esp
3543     # result = false
3544     b8/copy-to-eax 0/imm32/false
3545     (rewind-stream *(ebp+8))
3546     {
3547       (next-mu-token *(ebp+8) %ecx)
3548       # if slice-empty?(word-slice) break
3549       (slice-empty? %ecx)
3550       3d/compare-eax-and 0/imm32
3551       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
3552       0f 85/jump-if-!= break/disp32
3553       # if slice-starts-with?(word-slice, '#') break
3554       # . eax = *word-slice->start
3555       8b/-> *ecx 0/r32/eax
3556       8a/copy-byte *eax 0/r32/AL
3557       81 4/subop/and %eax 0xff/imm32
3558       # . if (eax == '#') break
3559       3d/compare-eax-and 0x23/imm32/hash
3560       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
3561       0f 84/jump-if-= break/disp32
3562       # if slice-equal?(word-slice, '<-') return true
3563       (slice-equal? %ecx "<-")
3564       3d/compare-eax-and 0/imm32
3565       74/jump-if-= loop/disp8
3566       b8/copy-to-eax 1/imm32/true
3567     }
3568 $stmt-has-outputs:end:
3569     (rewind-stream *(ebp+8))
3570     # . reclaim locals
3571     81 0/subop/add %esp 8/imm32
3572     # . restore registers
3573     59/pop-to-ecx
3574     # . epilogue
3575     89/<- %esp 5/r32/ebp
3576     5d/pop-to-ebp
3577     c3/return
3578 
3579 # if 'name' starts with a digit, create a new literal var for it
3580 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
3581 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
3582     # . prologue
3583     55/push-ebp
3584     89/<- %ebp 4/r32/esp
3585     # . save registers
3586     51/push-ecx
3587     56/push-esi
3588     # esi = name
3589     8b/-> *(ebp+8) 6/r32/esi
3590     # if slice-empty?(name) abort
3591     (slice-empty? %esi)  # => eax
3592     3d/compare-eax-and 0/imm32
3593     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
3594     # var c/ecx: byte = *name->start
3595     8b/-> *esi 1/r32/ecx
3596     8a/copy-byte *ecx 1/r32/CL
3597     81 4/subop/and %ecx 0xff/imm32
3598     # if is-decimal-digit?(c) return new var(name)
3599     {
3600       (is-decimal-digit? %ecx)  # => eax
3601       81 7/subop/compare %eax 0/imm32
3602       74/jump-if-= break/disp8
3603       (new-literal-integer Heap %esi)  # => eax
3604       eb/jump $lookup-var-or-literal:end/disp8
3605     }
3606     # else if (c == '"') return new var(name)
3607     {
3608       81 7/subop/compare %ecx 0x22/imm32/dquote
3609       75/jump-if-!= break/disp8
3610       (new-literal-string Heap %esi)  # => eax
3611       eb/jump $lookup-var-or-literal:end/disp8
3612     }
3613     # otherwise return lookup-var(name, vars)
3614     {
3615       (lookup-var %esi *(ebp+0xc))  # => eax
3616     }
3617 $lookup-var-or-literal:end:
3618     # . restore registers
3619     5e/pop-to-esi
3620     59/pop-to-ecx
3621     # . epilogue
3622     89/<- %esp 5/r32/ebp
3623     5d/pop-to-ebp
3624     c3/return
3625 
3626 $lookup-var-or-literal:abort:
3627     (write-buffered Stderr "empty variable!")
3628     (flush Stderr)
3629     # . syscall(exit, 1)
3630     bb/copy-to-ebx  1/imm32
3631     b8/copy-to-eax  1/imm32/exit
3632     cd/syscall  0x80/imm8
3633     # never gets here
3634 
3635 # return first 'name' from the top (back) of 'vars' and abort if not found
3636 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
3637     # . prologue
3638     55/push-ebp
3639     89/<- %ebp 4/r32/esp
3640     # var target/eax: (handle array byte) = slice-to-string(name)
3641     (slice-to-string Heap *(ebp+8))  # => eax
3642     #
3643     (lookup-var-helper %eax *(ebp+0xc))  # => eax
3644     # if (result == 0) abort
3645     3d/compare-eax-and 0/imm32
3646     74/jump-if-= $lookup-var:abort/disp8
3647 $lookup-var:end:
3648     # . epilogue
3649     89/<- %esp 5/r32/ebp
3650     5d/pop-to-ebp
3651     c3/return
3652 
3653 $lookup-var:abort:
3654     (write-buffered Stderr "unknown variable '")
3655     (write-slice-buffered Stderr *(ebp+8))
3656     (write-buffered Stderr "'\n")
3657     (flush Stderr)
3658     # . syscall(exit, 1)
3659     bb/copy-to-ebx  1/imm32
3660     b8/copy-to-eax  1/imm32/exit
3661     cd/syscall  0x80/imm8
3662     # never gets here
3663 
3664 # return first 'name' from the top (back) of 'vars', and 0/null if not found
3665 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
3666     # pseudocode:
3667     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
3668     #   var min = vars->data
3669     #   while curr >= min
3670     #     var v: (handle var) = *curr
3671     #     if v->name == name
3672     #       return v
3673     #   return 0
3674     #
3675     # . prologue
3676     55/push-ebp
3677     89/<- %ebp 4/r32/esp
3678     # . save registers
3679     52/push-edx
3680     53/push-ebx
3681     56/push-esi
3682     # esi = vars
3683     8b/-> *(ebp+0xc) 6/r32/esi
3684     # ebx = vars->top
3685     8b/-> *esi 3/r32/ebx
3686     # if (vars->top > vars->length) abort
3687     3b/compare 0/r32/eax *(esi+4)
3688     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
3689     # var min/edx: (addr handle var) = vars->data
3690     8d/copy-address *(esi+8) 2/r32/edx
3691     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
3692     81 5/subop/subtract %ebx 4/imm32
3693     8d/copy-address *(esi+ebx+8) 3/r32/ebx
3694     {
3695       # if (curr < min) return 0
3696       39/compare %ebx 2/r32/edx
3697       b8/copy-to-eax 0/imm32
3698       0f 82/jump-if-addr< break/disp32
3699       # var v/eax: (handle var) = *curr
3700       8b/-> *ebx 0/r32/eax
3701       # if (v->name == name) return v
3702       (string-equal? *eax *(ebp+8))  # Var-name
3703       3d/compare-eax-and 0/imm32
3704       8b/-> *ebx 0/r32/eax
3705       75/jump-if-!= break/disp8
3706       # curr -= 4
3707       81 5/subop/subtract %ebx 4/imm32
3708       e9/jump loop/disp32
3709     }
3710 $lookup-var-helper:end:
3711     # . restore registers
3712     5e/pop-to-esi
3713     5b/pop-to-ebx
3714     5a/pop-to-edx
3715     # . epilogue
3716     89/<- %esp 5/r32/ebp
3717     5d/pop-to-ebp
3718     c3/return
3719 
3720 $lookup-var-helper:error1:
3721     (write-buffered Stderr "malformed stack when looking up '")
3722     (write-slice-buffered Stderr *(ebp+8))
3723     (write-buffered Stderr "'\n")
3724     (flush Stderr)
3725     # . syscall(exit, 1)
3726     bb/copy-to-ebx  1/imm32
3727     b8/copy-to-eax  1/imm32/exit
3728     cd/syscall  0x80/imm8
3729     # never gets here
3730 
3731 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
3732 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
3733     # . prologue
3734     55/push-ebp
3735     89/<- %ebp 4/r32/esp
3736     # . save registers
3737     51/push-ecx
3738     # var target/ecx: (handle array byte) = slice-to-string(name)
3739     (slice-to-string Heap *(ebp+8))  # => eax
3740     89/<- %ecx 0/r32/eax
3741     #
3742     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
3743     {
3744       # if (result != 0) return
3745       3d/compare-eax-and 0/imm32
3746       75/jump-if-!= break/disp8
3747       # if name is one of fn's outputs, return it
3748       {
3749         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
3750         3d/compare-eax-and 0/imm32
3751         # otherwise abort
3752         0f 84/jump-if-!= $lookup-var:abort/disp32
3753       }
3754     }
3755 $lookup-or-define-var:end:
3756     # . restore registers
3757     59/pop-to-ecx
3758     # . epilogue
3759     89/<- %esp 5/r32/ebp
3760     5d/pop-to-ebp
3761     c3/return
3762 
3763 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) => result/eax: (handle var)
3764     # . prologue
3765     55/push-ebp
3766     89/<- %ebp 4/r32/esp
3767     # . save registers
3768     51/push-ecx
3769     # var curr/ecx: (handle list var) = fn->outputs
3770     8b/-> *(ebp+8) 1/r32/ecx
3771     8b/-> *(ecx+0xc) 1/r32/ecx
3772     # while curr != null
3773     {
3774       81 7/subop/compare %ecx 0/imm32
3775       74/jump-if-= break/disp8
3776       # var v: (handle var) = *curr
3777       8b/-> *ecx 0/r32/eax  # List-value
3778       # if (curr->name == name) return curr
3779       50/push-eax
3780       (string-equal? *eax *(ebp+0xc))
3781       3d/compare-eax-and 0/imm32
3782       58/pop-to-eax
3783       75/jump-if-!= $find-in-function-outputs:end/disp8
3784       # curr = curr->next
3785       8b/-> *(ecx+4) 1/r32/ecx  # List-next
3786       eb/jump loop/disp8
3787     }
3788     b8/copy-to-eax 0/imm32
3789 $find-in-function-outputs:end:
3790     # . restore registers
3791     59/pop-to-ecx
3792     # . epilogue
3793     89/<- %esp 5/r32/ebp
3794     5d/pop-to-ebp
3795     c3/return
3796 
3797 test-parse-mu-stmt:
3798     # 'increment n'
3799     # . prologue
3800     55/push-ebp
3801     89/<- %ebp 4/r32/esp
3802     # setup
3803     (clear-stream _test-input-stream)
3804     (write _test-input-stream "increment n\n")
3805     # var vars/ecx: (stack (addr var) 4)
3806     81 5/subop/subtract %esp 0x10/imm32
3807     68/push 0x10/imm32/length
3808     68/push 0/imm32/top
3809     89/<- %ecx 4/r32/esp
3810     (clear-stack %ecx)
3811     # var v/edx: var
3812     81 5/subop/subtract %esp 0x14/imm32  # Var-size
3813     89/<- %edx 4/r32/esp
3814     (zero-out %edx 0x14)
3815     # v->name = "n"
3816     c7 0/subop/copy *edx "n"/imm32  # Var-name
3817     #
3818     (push %ecx %edx)
3819     # convert
3820     (parse-mu-stmt _test-input-stream %ecx)  # => eax
3821     # check result
3822     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
3823     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
3824     # edx: (handle list var) = result->inouts
3825     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
3826     # ebx: (handle var) = result->inouts->value
3827     8b/-> *edx 3/r32/ebx  # List-value
3828     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
3829     # . epilogue
3830     89/<- %esp 5/r32/ebp
3831     5d/pop-to-ebp
3832     c3/return
3833 
3834 test-parse-mu-stmt-with-comma:
3835     # 'increment n'
3836     # . prologue
3837     55/push-ebp
3838     89/<- %ebp 4/r32/esp
3839     # setup
3840     (clear-stream _test-input-stream)
3841     (write _test-input-stream "copy-to n, 3\n")
3842     # var vars/ecx: (stack (addr var) 4)
3843     81 5/subop/subtract %esp 0x10/imm32
3844     68/push 0x10/imm32/length
3845     68/push 0/imm32/top
3846     89/<- %ecx 4/r32/esp
3847     (clear-stack %ecx)
3848     # var v/edx: var
3849     81 5/subop/subtract %esp 0x14/imm32  # Var-size
3850     89/<- %edx 4/r32/esp
3851     (zero-out %edx 0x14)
3852     # v->name = "n"
3853     c7 0/subop/copy *edx "n"/imm32  # Var-name
3854     #
3855     (push %ecx %edx)
3856     # convert
3857     (parse-mu-stmt _test-input-stream %ecx)  # => eax
3858     # check result
3859     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
3860     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
3861     # edx: (handle list var) = result->inouts
3862     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
3863     # ebx: (handle var) = result->inouts->value
3864     8b/-> *edx 3/r32/ebx  # List-value
3865     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
3866     # . epilogue
3867     89/<- %esp 5/r32/ebp
3868     5d/pop-to-ebp
3869     c3/return
3870 
3871 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)
3872     # . prologue
3873     55/push-ebp
3874     89/<- %ebp 4/r32/esp
3875     # . save registers
3876     51/push-ecx
3877     #
3878     (allocate *(ebp+8) *Function-size)  # => eax
3879     8b/-> *(ebp+0xc) 1/r32/ecx
3880     89/<- *eax 1/r32/ecx  # Function-name
3881     8b/-> *(ebp+0x10) 1/r32/ecx
3882     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
3883     8b/-> *(ebp+0x14) 1/r32/ecx
3884     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
3885     8b/-> *(ebp+0x18) 1/r32/ecx
3886     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
3887     8b/-> *(ebp+0x1c) 1/r32/ecx
3888     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
3889     8b/-> *(ebp+0x20) 1/r32/ecx
3890     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
3891 $new-function:end:
3892     # . restore registers
3893     59/pop-to-ecx
3894     # . epilogue
3895     89/<- %esp 5/r32/ebp
3896     5d/pop-to-ebp
3897     c3/return
3898 
3899 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte), type: int, block: int, stack-offset: int, register: (addr array byte) -> result/eax: (handle var)
3900     # . prologue
3901     55/push-ebp
3902     89/<- %ebp 4/r32/esp
3903     # . save registers
3904     51/push-ecx
3905     #
3906     (allocate *(ebp+8) *Var-size)  # => eax
3907     8b/-> *(ebp+0xc) 1/r32/ecx
3908     89/<- *eax 1/r32/ecx  # Var-name
3909     8b/-> *(ebp+0x10) 1/r32/ecx
3910     89/<- *(eax+4) 1/r32/ecx  # Var-type
3911     8b/-> *(ebp+0x14) 1/r32/ecx
3912     89/<- *(eax+8) 1/r32/ecx  # Var-block
3913     8b/-> *(ebp+0x18) 1/r32/ecx
3914     89/<- *(eax+0xc) 1/r32/ecx  # Var-stack-offset
3915     8b/-> *(ebp+0x1c) 1/r32/ecx
3916     89/<- *(eax+0x10) 1/r32/ecx  # Var-register
3917 $new-var:end:
3918     # . restore registers
3919     59/pop-to-ecx
3920     # . epilogue
3921     89/<- %esp 5/r32/ebp
3922     5d/pop-to-ebp
3923     c3/return
3924 
3925 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
3926     # . prologue
3927     55/push-ebp
3928     89/<- %ebp 4/r32/esp
3929     # . save registers
3930     51/push-ecx
3931     # if (!is-hex-int?(name)) abort
3932     (is-hex-int? *(ebp+0xc))  # => eax
3933     3d/compare-eax-and 0/imm32
3934     0f 84/jump-if-= $new-literal-integer:abort/disp32
3935     # var s/ecx: (addr array byte)
3936     (slice-to-string Heap *(ebp+0xc))  # => eax
3937     89/<- %ecx 0/r32/eax
3938     # result/ecx = new var(s)
3939     (allocate *(ebp+8) *Var-size)  # => eax
3940     (zero-out %eax *Var-size)
3941     89/<- *eax 1/r32/ecx  # Var-name
3942     89/<- %ecx 0/r32/eax
3943     # result->type = new type()
3944     (allocate *(ebp+8) *Tree-size)  # => eax
3945     (zero-out %eax *Tree-size)  # default type is 'literal'
3946     89/<- *(ecx+4) 0/r32/eax  # Var-type
3947     # move result to eax
3948     89/<- %eax 1/r32/ecx
3949 $new-literal-integer:end:
3950     # . restore registers
3951     59/pop-to-ecx
3952     # . epilogue
3953     89/<- %esp 5/r32/ebp
3954     5d/pop-to-ebp
3955     c3/return
3956 
3957 $new-literal-integer:abort:
3958     (write-buffered Stderr "variable cannot begin with a digit '")
3959     (write-slice-buffered Stderr *(ebp+0xc))
3960     (write-buffered Stderr "'\n")
3961     (flush Stderr)
3962     # . syscall(exit, 1)
3963     bb/copy-to-ebx  1/imm32
3964     b8/copy-to-eax  1/imm32/exit
3965     cd/syscall  0x80/imm8
3966     # never gets here
3967 
3968 new-literal-string:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
3969     # . prologue
3970     55/push-ebp
3971     89/<- %ebp 4/r32/esp
3972     # . save registers
3973     51/push-ecx
3974     # var s/ecx: (addr array byte)
3975     (slice-to-string Heap *(ebp+0xc))  # => eax
3976     89/<- %ecx 0/r32/eax
3977     # result/ecx = new var(s)
3978     (allocate *(ebp+8) *Var-size)  # => eax
3979     (zero-out %eax)
3980     89/<- *eax 1/r32/ecx  # Var-name
3981     89/<- %ecx 0/r32/eax
3982     # result->type = new type()
3983     (allocate *(ebp+8) *Tree-size)  # => eax
3984     (zero-out %eax)  # default type is 'literal'
3985     89/<- *(ecx+4) 0/r32/eax  # Var-type
3986     # move result to eax
3987     89/<- %eax 1/r32/ecx
3988 $new-literal-string:end:
3989     # . restore registers
3990     59/pop-to-ecx
3991     # . epilogue
3992     89/<- %esp 5/r32/ebp
3993     5d/pop-to-ebp
3994     c3/return
3995 
3996 new-label:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
3997     # . prologue
3998     55/push-ebp
3999     89/<- %ebp 4/r32/esp
4000     # . save registers
4001     51/push-ecx
4002     # var s/ecx: (addr array byte)
4003     (slice-to-string Heap *(ebp+0xc))  # => eax
4004     89/<- %ecx 0/r32/eax
4005     #
4006     (allocate *(ebp+8) *Var-size)  # => eax
4007     89/<- *eax 1/r32/ecx  # Var-name
4008     89/<- %ecx 0/r32/eax
4009     (allocate *(ebp+8) *Tree-size)  # => eax
4010     89/<- *(ecx+4) 0/r32/eax  # Var-type
4011     89/<- %eax 1/r32/ecx
4012     c7 0/subop/copy *(eax+8) 0/imm32  # Var-block
4013     c7 0/subop/copy *(eax+0xc) 0/imm32  # Var-stack-offset
4014     c7 0/subop/copy *(eax+0x10) 0/imm32  # Var-register
4015 $new-label:end:
4016     # . restore registers
4017     59/pop-to-ecx
4018     # . epilogue
4019     89/<- %esp 5/r32/ebp
4020     5d/pop-to-ebp
4021     c3/return
4022 
4023 new-block:  # ad: (addr allocation-descriptor), data: (handle list statement) -> result/eax: (handle statement)
4024     # . prologue
4025     55/push-ebp
4026     89/<- %ebp 4/r32/esp
4027     # . save registers
4028     51/push-ecx
4029     #
4030     (allocate *(ebp+8) *Stmt-size)  # => eax
4031     (zero-out %eax *Stmt-size)
4032     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
4033     8b/-> *(ebp+0xc) 1/r32/ecx
4034     89/<- *(eax+4) 1/r32/ecx  # Block-statements
4035 $new-block: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-vardef:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement)
4044     # . prologue
4045     55/push-ebp
4046     89/<- %ebp 4/r32/esp
4047     # . save registers
4048     51/push-ecx
4049     #
4050     (allocate *(ebp+8) *Stmt-size)  # => eax
4051     (zero-out %eax *Stmt-size)
4052     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
4053     # result->var = var
4054     8b/-> *(ebp+0xc) 1/r32/ecx
4055     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
4056 $new-vardef:end:
4057     # . restore registers
4058     59/pop-to-ecx
4059     # . epilogue
4060     89/<- %esp 5/r32/ebp
4061     5d/pop-to-ebp
4062     c3/return
4063 
4064 new-regvardef:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement)
4065     # . prologue
4066     55/push-ebp
4067     89/<- %ebp 4/r32/esp
4068     # . save registers
4069     51/push-ecx
4070     57/push-edi
4071     # ecx = var
4072     8b/-> *(ebp+0xc) 1/r32/ecx
4073     # edi = result
4074     (allocate *(ebp+8) *Stmt-size)  # => eax
4075     89/<- %edi 0/r32/eax
4076     (zero-out %edi *Stmt-size)
4077     # set tag
4078     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
4079     # set output
4080     (append-list Heap %ecx *(edi+0xc))  # Regvardef-outputs => eax
4081     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
4082 $new-regvardef:end:
4083     89/<- %eax 7/r32/edi
4084     # . restore registers
4085     5f/pop-to-edi
4086     59/pop-to-ecx
4087     # . epilogue
4088     89/<- %esp 5/r32/ebp
4089     5d/pop-to-ebp
4090     c3/return
4091 
4092 new-named-block:  # ad: (addr allocation-descriptor), name: (addr array byte), data: (handle list statement) -> result/eax: (handle statement)
4093     # . prologue
4094     55/push-ebp
4095     89/<- %ebp 4/r32/esp
4096     # . save registers
4097     51/push-ecx
4098     #
4099     (allocate *(ebp+8) *Stmt-size)  # => eax
4100     (zero-out %eax *Stmt-size)
4101     c7 0/subop/copy *eax 4/imm32/tag/named-block
4102     8b/-> *(ebp+0xc) 1/r32/ecx
4103     89/<- *(eax+8) 1/r32/ecx  # Named-block-name
4104     8b/-> *(ebp+0x10) 1/r32/ecx
4105     89/<- *(eax+4) 1/r32/ecx  # Named-block-statements
4106 $new-named-block:end:
4107     # . restore registers
4108     59/pop-to-ecx
4109     # . epilogue
4110     89/<- %esp 5/r32/ebp
4111     5d/pop-to-ebp
4112     c3/return
4113 
4114 new-list:  # ad: (addr allocation-descriptor), value: _type, next: (handle list _type) -> result/eax: (handle list _type)
4115     # . prologue
4116     55/push-ebp
4117     89/<- %ebp 4/r32/esp
4118     # . save registers
4119     51/push-ecx
4120     #
4121     (allocate *(ebp+8) *List-size)  # => eax
4122     8b/-> *(ebp+0xc) 1/r32/ecx
4123     89/<- *eax 1/r32/ecx  # List-value
4124     8b/-> *(ebp+0x10) 1/r32/ecx
4125     89/<- *(eax+4) 1/r32/ecx  # List-next
4126 $new-list:end:
4127     # . restore registers
4128     59/pop-to-ecx
4129     # . epilogue
4130     89/<- %esp 5/r32/ebp
4131     5d/pop-to-ebp
4132     c3/return
4133 
4134 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
4135     # . prologue
4136     55/push-ebp
4137     89/<- %ebp 4/r32/esp
4138     # . save registers
4139     51/push-ecx
4140     #
4141     (allocate *(ebp+8) *List-size)  # => eax
4142     8b/-> *(ebp+0xc) 1/r32/ecx
4143     89/<- *eax 1/r32/ecx  # List-value
4144     # if (list == null) return result
4145     81 7/subop/compare *(ebp+0x10) 0/imm32
4146     74/jump-if-= $new-list:end/disp8
4147     # otherwise append
4148     # var curr/ecx = list
4149     8b/-> *(ebp+0x10) 1/r32/ecx
4150     # while (curr->next != null) curr = curr->next
4151     {
4152       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
4153       74/jump-if-= break/disp8
4154       # curr = curr->next
4155       8b/-> *(ecx+4) 1/r32/ecx
4156       eb/jump loop/disp8
4157     }
4158     # curr->next = result
4159     89/<- *(ecx+4) 0/r32/eax
4160     # return list
4161     8b/-> *(ebp+0x10) 0/r32/eax
4162 $append-list:end:
4163     # . restore registers
4164     59/pop-to-ecx
4165     # . epilogue
4166     89/<- %esp 5/r32/ebp
4167     5d/pop-to-ebp
4168     c3/return
4169 
4170 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
4171     # . prologue
4172     55/push-ebp
4173     89/<- %ebp 4/r32/esp
4174     # . save registers
4175     56/push-esi
4176     # esi = block
4177     8b/-> *(ebp+0xc) 6/r32/esi
4178     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-statements
4179     89/<- *(esi+4) 0/r32/eax  # Block-statements
4180 $append-to-block:end:
4181     # . restore registers
4182     5e/pop-to-esi
4183     # . epilogue
4184     89/<- %esp 5/r32/ebp
4185     5d/pop-to-ebp
4186     c3/return
4187 
4188 #######################################################
4189 # Type-checking
4190 #######################################################
4191 
4192 check-mu-types:
4193     # . prologue
4194     55/push-ebp
4195     89/<- %ebp 4/r32/esp
4196     #
4197 $check-mu-types:end:
4198     # . epilogue
4199     89/<- %esp 5/r32/ebp
4200     5d/pop-to-ebp
4201     c3/return
4202 
4203 size-of:  # n: (addr var) -> result/eax: int
4204     # . prologue
4205     55/push-ebp
4206     89/<- %ebp 4/r32/esp
4207     # hard-coded since we only support 'int' types for now
4208     b8/copy-to-eax 4/imm32
4209 $size-of:end:
4210     # . epilogue
4211     89/<- %esp 5/r32/ebp
4212     5d/pop-to-ebp
4213     c3/return
4214 
4215 == data
4216 
4217 # not yet used, but it will be
4218 Type-size:  # (stream int)
4219   0x18/imm32/write
4220   0/imm32/read
4221   0x100/imm32/length
4222   # data
4223   4/imm32  # literal
4224   4/imm32  # int
4225   4/imm32  # addr
4226   0/imm32  # array (logic elsewhere)
4227   8/imm32  # handle (fat pointer)
4228   4/imm32  # bool
4229   0/imm32
4230   0/imm32
4231   # 0x20
4232   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4233   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4234   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4235   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4236   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4237   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4238   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
4239 
4240 == code
4241 
4242 #######################################################
4243 # Code-generation
4244 #######################################################
4245 
4246 emit-subx:  # out: (addr buffered-file)
4247     # . prologue
4248     55/push-ebp
4249     89/<- %ebp 4/r32/esp
4250     # . save registers
4251     50/push-eax
4252     51/push-ecx
4253     57/push-edi
4254     # edi = out
4255     8b/-> *(ebp+8) 7/r32/edi
4256     # var curr/ecx: (handle function) = *Program
4257     8b/-> *Program 1/r32/ecx
4258     {
4259       # if (curr == null) break
4260       81 7/subop/compare %ecx 0/imm32
4261       0f 84/jump-if-= break/disp32
4262       (emit-subx-function %edi %ecx)
4263       # curr = curr->next
4264       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
4265       e9/jump loop/disp32
4266     }
4267 $emit-subx:end:
4268     # . restore registers
4269     5f/pop-to-edi
4270     59/pop-to-ecx
4271     58/pop-to-eax
4272     # . epilogue
4273     89/<- %esp 5/r32/ebp
4274     5d/pop-to-ebp
4275     c3/return
4276 
4277 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
4278     # . prologue
4279     55/push-ebp
4280     89/<- %ebp 4/r32/esp
4281     # . save registers
4282     50/push-eax
4283     51/push-ecx
4284     52/push-edx
4285     57/push-edi
4286     # edi = out
4287     8b/-> *(ebp+8) 7/r32/edi
4288     # ecx = f
4289     8b/-> *(ebp+0xc) 1/r32/ecx
4290     # var vars/edx: (stack (addr var) 256)
4291     81 5/subop/subtract %esp 0x400/imm32
4292     68/push 0x400/imm32/length
4293     68/push 0/imm32/top
4294     89/<- %edx 4/r32/esp
4295     #
4296     (write-buffered %edi *ecx)
4297     (write-buffered %edi ":\n")
4298     (emit-subx-prologue %edi)
4299     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
4300     (emit-subx-epilogue %edi)
4301 $emit-subx-function:end:
4302     # . reclaim locals
4303     81 0/subop/add %esp 408/imm32
4304     # . restore registers
4305     5f/pop-to-edi
4306     5a/pop-to-edx
4307     59/pop-to-ecx
4308     58/pop-to-eax
4309     # . epilogue
4310     89/<- %esp 5/r32/ebp
4311     5d/pop-to-ebp
4312     c3/return
4313 
4314 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
4315     # . prologue
4316     55/push-ebp
4317     89/<- %ebp 4/r32/esp
4318     # . save registers
4319     56/push-esi
4320     # var stmts/esi: (handle list statement) = block->statements
4321     8b/-> *(ebp+0xc) 6/r32/esi
4322     8b/-> *(esi+4) 6/r32/esi  # Block-statements
4323     #
4324     {
4325 $emit-subx-block:check-empty:
4326       81 7/subop/compare %esi 0/imm32
4327       0f 84/jump-if-= break/disp32
4328       (write-buffered *(ebp+8) "{\n")
4329       (emit-subx-stmt-list *(ebp+8) %esi *(ebp+0x10))
4330       (write-buffered *(ebp+8) "}\n")
4331     }
4332 $emit-subx-block:end:
4333     # . restore registers
4334     5e/pop-to-esi
4335     # . epilogue
4336     89/<- %esp 5/r32/ebp
4337     5d/pop-to-ebp
4338     c3/return
4339 
4340 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
4341     # . prologue
4342     55/push-ebp
4343     89/<- %ebp 4/r32/esp
4344     # . save registers
4345     50/push-eax
4346     51/push-ecx
4347     56/push-esi
4348     # esi = stmts
4349     8b/-> *(ebp+0xc) 6/r32/esi
4350     #
4351     {
4352 $emit-subx-stmt-list:loop:
4353       81 7/subop/compare %esi 0/imm32
4354       0f 84/jump-if-= break/disp32
4355       # var curr-stmt/ecx = stmts->value
4356       8b/-> *esi 1/r32/ecx  # List-value
4357       {
4358 $emit-subx-stmt-list:check-for-block:
4359         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
4360         75/jump-if-!= break/disp8
4361 $emit-subx-stmt-list:block:
4362         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
4363       }
4364       {
4365 $emit-subx-stmt-list:check-for-stmt:
4366         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
4367         75/jump-if-!= break/disp8
4368 $emit-subx-stmt-list:stmt:
4369         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
4370       }
4371       {
4372 $emit-subx-stmt-list:check-for-vardef:
4373         81 7/subop/compare *ecx 2/imm32/vardef  # Stmt-tag
4374         75/jump-if-!= break/disp8
4375 $emit-subx-stmt-list:vardef:
4376         (emit-subx-var-def *(ebp+8) %ecx)
4377         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
4378       }
4379       {
4380 $emit-subx-stmt-list:check-for-regvardef:
4381         81 7/subop/compare *ecx 3/imm32/regvardef  # Stmt-tag
4382         0f 85/jump-if-!= break/disp32
4383 $emit-subx-stmt-list:regvardef:
4384         # TODO: ensure that there's exactly one output
4385         # var output/eax: (handle var) = curr-stmt->outputs->value
4386         8b/-> *(ecx+0xc) 0/r32/eax
4387         8b/-> *eax 0/r32/eax
4388         # ensure that output is in a register
4389         81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
4390         0f 84/jump-if-= $emit-subx-stmt-list:abort-regvardef-without-register/disp32
4391         # emit spill
4392         (write-buffered *(ebp+8) "ff 6/subop/push %")
4393         (write-buffered *(ebp+8) *(eax+0x10))
4394         (write-buffered *(ebp+8) Newline)
4395         # register variable definition
4396         (push *(ebp+0x10) %eax)
4397         # emit the instruction as usual
4398         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
4399       }
4400       {
4401 $emit-subx-stmt-list:check-for-named-block:
4402         81 7/subop/compare *ecx 4/imm32/named-block  # Stmt-tag
4403         75/jump-if-!= break/disp8
4404 $emit-subx-stmt-list:named-block:
4405         (emit-subx-named-block *(ebp+8) %ecx *(ebp+0x10))
4406       }
4407       8b/-> *(esi+4) 6/r32/esi  # List-next
4408       e9/jump loop/disp32
4409     }
4410     # reclaim locals
4411     # TODO: support nested blocks; take block-ids into account
4412     {
4413 $emit-subx-stmt-list:reclaim-loop:
4414       8b/-> *(ebp+0x10) 0/r32/eax
4415       81 7/subop/compare *eax 0/imm32  # Stack-top
4416       0f 84/jump-if-= break/disp32
4417       # var v/ecx : (handle var) = top(vars)
4418       (top %eax)  # => eax
4419       89/<- %ecx 0/r32/eax
4420       # if v is in a register
4421       81 7/subop/compare *(ecx+0x10) 0/imm32  # Var-register
4422       {
4423         74/jump-if-= break/disp8
4424 $emit-subx-stmt-list:reclaim-var-in-register:
4425         (write-buffered *(ebp+8) "8f 0/subop/pop %")
4426         (write-buffered *(ebp+8) *(ecx+0x10))
4427         (write-buffered *(ebp+8) Newline)
4428       }
4429       # if v is on the stack
4430       {
4431         75/jump-if-!= break/disp8
4432 $emit-subx-stmt-list:reclaim-var-on-stack:
4433         (size-of %ecx)  # => eax
4434         01/add *Next-local-stack-offset 0/r32/eax
4435         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
4436         (print-int32-buffered *(ebp+8) %eax)
4437         (write-buffered *(ebp+8) "/imm32\n")
4438       }
4439       #
4440       (pop *(ebp+0x10))
4441       e9/jump loop/disp32
4442     }
4443 $emit-subx-stmt-list:end:
4444     # . restore registers
4445     5e/pop-to-esi
4446     59/pop-to-ecx
4447     58/pop-to-eax
4448     # . epilogue
4449     89/<- %esp 5/r32/ebp
4450     5d/pop-to-ebp
4451     c3/return
4452 
4453 $emit-subx-stmt-list:abort-regvardef-without-register:
4454     # error("var '" var->name "' initialized from an instruction must live in a register\n")
4455     (write-buffered Stderr "var '")
4456     (write-buffered Stderr *eax)  # Var-name
4457     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
4458     (flush Stderr)
4459     # . syscall(exit, 1)
4460     bb/copy-to-ebx  1/imm32
4461     b8/copy-to-eax  1/imm32/exit
4462     cd/syscall  0x80/imm8
4463     # never gets here
4464 
4465 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle statement)
4466     # . prologue
4467     55/push-ebp
4468     89/<- %ebp 4/r32/esp
4469     # . save registers
4470     50/push-eax
4471     51/push-ecx
4472     # eax = stmt
4473     8b/-> *(ebp+0xc) 0/r32/eax
4474     # var n/eax: int = size-of(stmt->var)
4475     (size-of *(eax+4))  # Vardef-var => eax
4476     # while n > 0
4477     {
4478       3d/compare-eax-with 0/imm32
4479       7e/jump-if-<= break/disp8
4480       (write-buffered *(ebp+8) "68/push 0/imm32\n")
4481       # n -= 4
4482       2d/subtract-from-eax 4/imm32
4483       #
4484       eb/jump loop/disp8
4485     }
4486 $emit-subx-var-def:end:
4487     # . restore registers
4488     59/pop-to-ecx
4489     58/pop-to-eax
4490     # . epilogue
4491     89/<- %esp 5/r32/ebp
4492     5d/pop-to-ebp
4493     c3/return
4494 
4495 emit-subx-statement:  # out: (addr buffered-file), stmt: (handle statement), primitives: (handle primitive), functions: (handle function)
4496     # . prologue
4497     55/push-ebp
4498     89/<- %ebp 4/r32/esp
4499     # . save registers
4500     50/push-eax
4501     51/push-ecx
4502     # if stmt matches a primitive, emit it
4503     {
4504 $emit-subx-statement:check-for-primitive:
4505       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
4506       3d/compare-eax-and 0/imm32
4507       74/jump-if-= break/disp8
4508 $emit-subx-statement:primitive:
4509       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
4510       e9/jump $emit-subx-statement:end/disp32
4511     }
4512     # else if stmt matches a function, emit a call to it
4513     {
4514 $emit-subx-statement:check-for-call:
4515       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
4516       3d/compare-eax-and 0/imm32
4517       74/jump-if-= break/disp8
4518 $emit-subx-statement:call:
4519       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
4520       e9/jump $emit-subx-statement:end/disp32
4521     }
4522     # else abort
4523     e9/jump $emit-subx-statement:abort/disp32
4524 $emit-subx-statement:end:
4525     # . restore registers
4526     59/pop-to-ecx
4527     58/pop-to-eax
4528     # . epilogue
4529     89/<- %esp 5/r32/ebp
4530     5d/pop-to-ebp
4531     c3/return
4532 
4533 $emit-subx-statement:abort:
4534     # error("couldn't translate '" stmt "'\n")
4535     (write-buffered Stderr "couldn't translate an instruction with operation '")
4536     8b/-> *(ebp+0xc) 0/r32/eax
4537     (write-buffered Stderr *(eax+4))  # Stmt1-operation
4538     (write-buffered Stderr "'\n")
4539     (flush Stderr)
4540     # . syscall(exit, 1)
4541     bb/copy-to-ebx  1/imm32
4542     b8/copy-to-eax  1/imm32/exit
4543     cd/syscall  0x80/imm8
4544     # never gets here
4545 
4546 emit-subx-named-block:  # out: (addr buffered-file), named-block: (handle named-block), vars: (addr stack (handle var))
4547     # . prologue
4548     55/push-ebp
4549     89/<- %ebp 4/r32/esp
4550     # . save registers
4551     50/push-eax
4552     51/push-ecx
4553     56/push-esi
4554     # esi = block
4555     8b/-> *(ebp+0xc) 6/r32/esi
4556     # var stmts/ecx: (handle list statement) = block->statements
4557     8b/-> *(esi+4) 0/r32/eax  # Block-statements
4558     #
4559     {
4560 $emit-subx-named-block:check-empty:
4561       81 7/subop/compare %eax 0/imm32
4562       0f 84/jump-if-= break/disp32
4563       (write-buffered *(ebp+8) "{\n")
4564       (write-buffered *(ebp+8) *(esi+8))  # Named-block-name
4565       (write-buffered *(ebp+8) "loop:\n")
4566       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
4567       (write-buffered *(ebp+8) "}\n")
4568       (write-buffered *(ebp+8) *(esi+8))  # Named-block-name
4569       (write-buffered *(ebp+8) "break:\n")
4570     }
4571 $emit-subx-named-block:end:
4572     # . restore registers
4573     5e/pop-to-esi
4574     59/pop-to-ecx
4575     58/pop-to-eax
4576     # . epilogue
4577     89/<- %esp 5/r32/ebp
4578     5d/pop-to-ebp
4579     c3/return
4580 
4581 # Primitives supported
4582 # For each operation, put variants with hard-coded registers before flexible ones.
4583 == data
4584 Primitives:
4585 # - increment/decrement
4586 _Primitive-inc-eax:
4587     # var/eax <- increment => 40/increment-eax
4588     "increment"/imm32/name
4589     0/imm32/no-inouts
4590     Single-int-var-in-eax/imm32/outputs
4591     "40/increment-eax"/imm32/subx-name
4592     0/imm32/no-rm32
4593     0/imm32/no-r32
4594     0/imm32/no-imm32
4595     0/imm32/no-disp32
4596     0/imm32/output-is-write-only
4597     _Primitive-inc-ecx/imm32/next
4598 _Primitive-inc-ecx:
4599     # var/ecx <- increment => 41/increment-ecx
4600     "increment"/imm32/name
4601     0/imm32/no-inouts
4602     Single-int-var-in-ecx/imm32/outputs
4603     "41/increment-ecx"/imm32/subx-name
4604     0/imm32/no-rm32
4605     0/imm32/no-r32
4606     0/imm32/no-imm32
4607     0/imm32/no-disp32
4608     0/imm32/output-is-write-only
4609     _Primitive-inc-edx/imm32/next
4610 _Primitive-inc-edx:
4611     # var/edx <- increment => 42/increment-edx
4612     "increment"/imm32/name
4613     0/imm32/no-inouts
4614     Single-int-var-in-edx/imm32/outputs
4615     "42/increment-edx"/imm32/subx-name
4616     0/imm32/no-rm32
4617     0/imm32/no-r32
4618     0/imm32/no-imm32
4619     0/imm32/no-disp32
4620     0/imm32/output-is-write-only
4621     _Primitive-inc-ebx/imm32/next
4622 _Primitive-inc-ebx:
4623     # var/ebx <- increment => 43/increment-ebx
4624     "increment"/imm32/name
4625     0/imm32/no-inouts
4626     Single-int-var-in-ebx/imm32/outputs
4627     "43/increment-ebx"/imm32/subx-name
4628     0/imm32/no-rm32
4629     0/imm32/no-r32
4630     0/imm32/no-imm32
4631     0/imm32/no-disp32
4632     0/imm32/output-is-write-only
4633     _Primitive-inc-esi/imm32/next
4634 _Primitive-inc-esi:
4635     # var/esi <- increment => 46/increment-esi
4636     "increment"/imm32/name
4637     0/imm32/no-inouts
4638     Single-int-var-in-esi/imm32/outputs
4639     "46/increment-esi"/imm32/subx-name
4640     0/imm32/no-rm32
4641     0/imm32/no-r32
4642     0/imm32/no-imm32
4643     0/imm32/no-disp32
4644     0/imm32/output-is-write-only
4645     _Primitive-inc-edi/imm32/next
4646 _Primitive-inc-edi:
4647     # var/edi <- increment => 47/increment-edi
4648     "increment"/imm32/name
4649     0/imm32/no-inouts
4650     Single-int-var-in-edi/imm32/outputs
4651     "47/increment-edi"/imm32/subx-name
4652     0/imm32/no-rm32
4653     0/imm32/no-r32
4654     0/imm32/no-imm32
4655     0/imm32/no-disp32
4656     0/imm32/output-is-write-only
4657     _Primitive-dec-eax/imm32/next
4658 _Primitive-dec-eax:
4659     # var/eax <- decrement => 48/decrement-eax
4660     "decrement"/imm32/name
4661     0/imm32/no-inouts
4662     Single-int-var-in-eax/imm32/outputs
4663     "48/decrement-eax"/imm32/subx-name
4664     0/imm32/no-rm32
4665     0/imm32/no-r32
4666     0/imm32/no-imm32
4667     0/imm32/no-disp32
4668     0/imm32/output-is-write-only
4669     _Primitive-dec-ecx/imm32/next
4670 _Primitive-dec-ecx:
4671     # var/ecx <- decrement => 49/decrement-ecx
4672     "decrement"/imm32/name
4673     0/imm32/no-inouts
4674     Single-int-var-in-ecx/imm32/outputs
4675     "49/decrement-ecx"/imm32/subx-name
4676     0/imm32/no-rm32
4677     0/imm32/no-r32
4678     0/imm32/no-imm32
4679     0/imm32/no-disp32
4680     0/imm32/output-is-write-only
4681     _Primitive-dec-edx/imm32/next
4682 _Primitive-dec-edx:
4683     # var/edx <- decrement => 4a/decrement-edx
4684     "decrement"/imm32/name
4685     0/imm32/no-inouts
4686     Single-int-var-in-edx/imm32/outputs
4687     "4a/decrement-edx"/imm32/subx-name
4688     0/imm32/no-rm32
4689     0/imm32/no-r32
4690     0/imm32/no-imm32
4691     0/imm32/no-disp32
4692     0/imm32/output-is-write-only
4693     _Primitive-dec-ebx/imm32/next
4694 _Primitive-dec-ebx:
4695     # var/ebx <- decrement => 4b/decrement-ebx
4696     "decrement"/imm32/name
4697     0/imm32/no-inouts
4698     Single-int-var-in-ebx/imm32/outputs
4699     "4b/decrement-ebx"/imm32/subx-name
4700     0/imm32/no-rm32
4701     0/imm32/no-r32
4702     0/imm32/no-imm32
4703     0/imm32/no-disp32
4704     0/imm32/output-is-write-only
4705     _Primitive-dec-esi/imm32/next
4706 _Primitive-dec-esi:
4707     # var/esi <- decrement => 4e/decrement-esi
4708     "decrement"/imm32/name
4709     0/imm32/no-inouts
4710     Single-int-var-in-esi/imm32/outputs
4711     "4e/decrement-esi"/imm32/subx-name
4712     0/imm32/no-rm32
4713     0/imm32/no-r32
4714     0/imm32/no-imm32
4715     0/imm32/no-disp32
4716     0/imm32/output-is-write-only
4717     _Primitive-dec-edi/imm32/next
4718 _Primitive-dec-edi:
4719     # var/edi <- decrement => 4f/decrement-edi
4720     "decrement"/imm32/name
4721     0/imm32/no-inouts
4722     Single-int-var-in-edi/imm32/outputs
4723     "4f/decrement-edi"/imm32/subx-name
4724     0/imm32/no-rm32
4725     0/imm32/no-r32
4726     0/imm32/no-imm32
4727     0/imm32/no-disp32
4728     0/imm32/output-is-write-only
4729     _Primitive-inc-mem/imm32/next
4730 _Primitive-inc-mem:
4731     # increment var => ff 0/subop/increment *(ebp+__)
4732     "increment"/imm32/name
4733     Single-int-var-on-stack/imm32/inouts
4734     0/imm32/no-outputs
4735     "ff 0/subop/increment"/imm32/subx-name
4736     1/imm32/rm32-is-first-inout
4737     0/imm32/no-r32
4738     0/imm32/no-imm32
4739     0/imm32/no-disp32
4740     0/imm32/output-is-write-only
4741     _Primitive-inc-reg/imm32/next
4742 _Primitive-inc-reg:
4743     # var/reg <- increment => ff 0/subop/increment %__
4744     "increment"/imm32/name
4745     0/imm32/no-inouts
4746     Single-int-var-in-some-register/imm32/outputs
4747     "ff 0/subop/increment"/imm32/subx-name
4748     3/imm32/rm32-is-first-output
4749     0/imm32/no-r32
4750     0/imm32/no-imm32
4751     0/imm32/no-disp32
4752     0/imm32/output-is-write-only
4753     _Primitive-dec-mem/imm32/next
4754 _Primitive-dec-mem:
4755     # decrement var => ff 1/subop/decrement *(ebp+__)
4756     "decrement"/imm32/name
4757     Single-int-var-on-stack/imm32/inouts
4758     0/imm32/no-outputs
4759     "ff 1/subop/decrement"/imm32/subx-name
4760     1/imm32/rm32-is-first-inout
4761     0/imm32/no-r32
4762     0/imm32/no-imm32
4763     0/imm32/no-disp32
4764     0/imm32/output-is-write-only
4765     _Primitive-dec-reg/imm32/next
4766 _Primitive-dec-reg:
4767     # var/reg <- decrement => ff 1/subop/decrement %__
4768     "decrement"/imm32/name
4769     0/imm32/no-inouts
4770     Single-int-var-in-some-register/imm32/outputs
4771     "ff 1/subop/decrement"/imm32/subx-name
4772     3/imm32/rm32-is-first-output
4773     0/imm32/no-r32
4774     0/imm32/no-imm32
4775     0/imm32/no-disp32
4776     0/imm32/output-is-write-only
4777     _Primitive-add-to-eax/imm32/next
4778 # - add
4779 _Primitive-add-to-eax:
4780     # var/eax <- add lit => 05/add-to-eax lit/imm32
4781     "add"/imm32/name
4782     Single-lit-var/imm32/inouts
4783     Single-int-var-in-eax/imm32/outputs
4784     "05/add-to-eax"/imm32/subx-name
4785     0/imm32/no-rm32
4786     0/imm32/no-r32
4787     1/imm32/imm32-is-first-inout
4788     0/imm32/no-disp32
4789     0/imm32/output-is-write-only
4790     _Primitive-add-reg-to-reg/imm32/next
4791 _Primitive-add-reg-to-reg:
4792     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
4793     "add"/imm32/name
4794     Single-int-var-in-some-register/imm32/inouts
4795     Single-int-var-in-some-register/imm32/outputs
4796     "01/add-to"/imm32/subx-name
4797     3/imm32/rm32-is-first-output
4798     1/imm32/r32-is-first-inout
4799     0/imm32/no-imm32
4800     0/imm32/no-disp32
4801     0/imm32/output-is-write-only
4802     _Primitive-add-reg-to-mem/imm32/next
4803 _Primitive-add-reg-to-mem:
4804     # add-to var1 var2/reg => 01/add-to var1 var2/r32
4805     "add-to"/imm32/name
4806     Two-args-int-stack-int-reg/imm32/inouts
4807     0/imm32/outputs
4808     "01/add-to"/imm32/subx-name
4809     1/imm32/rm32-is-first-inout
4810     2/imm32/r32-is-second-inout
4811     0/imm32/no-imm32
4812     0/imm32/no-disp32
4813     0/imm32/output-is-write-only
4814     _Primitive-add-mem-to-reg/imm32/next
4815 _Primitive-add-mem-to-reg:
4816     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
4817     "add"/imm32/name
4818     Single-int-var-on-stack/imm32/inouts
4819     Single-int-var-in-some-register/imm32/outputs
4820     "03/add"/imm32/subx-name
4821     1/imm32/rm32-is-first-inout
4822     3/imm32/r32-is-first-output
4823     0/imm32/no-imm32
4824     0/imm32/no-disp32
4825     0/imm32/output-is-write-only
4826     _Primitive-add-lit-to-reg/imm32/next
4827 _Primitive-add-lit-to-reg:
4828     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
4829     "add"/imm32/name
4830     Single-lit-var/imm32/inouts
4831     Single-int-var-in-some-register/imm32/outputs
4832     "81 0/subop/add"/imm32/subx-name
4833     3/imm32/rm32-is-first-output
4834     0/imm32/no-r32
4835     1/imm32/imm32-is-first-inout
4836     0/imm32/no-disp32
4837     0/imm32/output-is-write-only
4838     _Primitive-add-lit-to-mem/imm32/next
4839 _Primitive-add-lit-to-mem:
4840     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
4841     "add-to"/imm32/name
4842     Int-var-and-literal/imm32/inouts
4843     0/imm32/outputs
4844     "81 0/subop/add"/imm32/subx-name
4845     1/imm32/rm32-is-first-inout
4846     0/imm32/no-r32
4847     2/imm32/imm32-is-second-inout
4848     0/imm32/no-disp32
4849     0/imm32/output-is-write-only
4850     _Primitive-subtract-from-eax/imm32/next
4851 # - subtract
4852 _Primitive-subtract-from-eax:
4853     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
4854     "subtract"/imm32/name
4855     Single-lit-var/imm32/inouts
4856     Single-int-var-in-eax/imm32/outputs
4857     "2d/subtract-from-eax"/imm32/subx-name
4858     0/imm32/no-rm32
4859     0/imm32/no-r32
4860     1/imm32/imm32-is-first-inout
4861     0/imm32/no-disp32
4862     0/imm32/output-is-write-only
4863     _Primitive-subtract-reg-from-reg/imm32/next
4864 _Primitive-subtract-reg-from-reg:
4865     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
4866     "subtract"/imm32/name
4867     Single-int-var-in-some-register/imm32/inouts
4868     Single-int-var-in-some-register/imm32/outputs
4869     "29/subtract-from"/imm32/subx-name
4870     3/imm32/rm32-is-first-output
4871     1/imm32/r32-is-first-inout
4872     0/imm32/no-imm32
4873     0/imm32/no-disp32
4874     0/imm32/output-is-write-only
4875     _Primitive-subtract-reg-from-mem/imm32/next
4876 _Primitive-subtract-reg-from-mem:
4877     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
4878     "subtract-from"/imm32/name
4879     Two-args-int-stack-int-reg/imm32/inouts
4880     0/imm32/outputs
4881     "29/subtract-from"/imm32/subx-name
4882     1/imm32/rm32-is-first-inout
4883     2/imm32/r32-is-second-inout
4884     0/imm32/no-imm32
4885     0/imm32/no-disp32
4886     0/imm32/output-is-write-only
4887     _Primitive-subtract-mem-from-reg/imm32/next
4888 _Primitive-subtract-mem-from-reg:
4889     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
4890     "subtract"/imm32/name
4891     Single-int-var-on-stack/imm32/inouts
4892     Single-int-var-in-some-register/imm32/outputs
4893     "2b/subtract"/imm32/subx-name
4894     1/imm32/rm32-is-first-inout
4895     3/imm32/r32-is-first-output
4896     0/imm32/no-imm32
4897     0/imm32/no-disp32
4898     0/imm32/output-is-write-only
4899     _Primitive-subtract-lit-from-reg/imm32/next
4900 _Primitive-subtract-lit-from-reg:
4901     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
4902     "subtract"/imm32/name
4903     Single-lit-var/imm32/inouts
4904     Single-int-var-in-some-register/imm32/outputs
4905     "81 5/subop/subtract"/imm32/subx-name
4906     3/imm32/rm32-is-first-output
4907     0/imm32/no-r32
4908     1/imm32/imm32-is-first-inout
4909     0/imm32/no-disp32
4910     0/imm32/output-is-write-only
4911     _Primitive-subtract-lit-from-mem/imm32/next
4912 _Primitive-subtract-lit-from-mem:
4913     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
4914     "subtract-from"/imm32/name
4915     Int-var-and-literal/imm32/inouts
4916     0/imm32/outputs
4917     "81 5/subop/subtract"/imm32/subx-name
4918     1/imm32/rm32-is-first-inout
4919     0/imm32/no-r32
4920     2/imm32/imm32-is-first-inout
4921     0/imm32/no-disp32
4922     0/imm32/output-is-write-only
4923     _Primitive-and-with-eax/imm32/next
4924 # - and
4925 _Primitive-and-with-eax:
4926     # var/eax <- and lit => 25/and-with-eax lit/imm32
4927     "and"/imm32/name
4928     Single-lit-var/imm32/inouts
4929     Single-int-var-in-eax/imm32/outputs
4930     "25/and-with-eax"/imm32/subx-name
4931     0/imm32/no-rm32
4932     0/imm32/no-r32
4933     1/imm32/imm32-is-first-inout
4934     0/imm32/no-disp32
4935     0/imm32/output-is-write-only
4936     _Primitive-and-reg-with-reg/imm32/next
4937 _Primitive-and-reg-with-reg:
4938     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
4939     "and"/imm32/name
4940     Single-int-var-in-some-register/imm32/inouts
4941     Single-int-var-in-some-register/imm32/outputs
4942     "21/and-with"/imm32/subx-name
4943     3/imm32/rm32-is-first-output
4944     1/imm32/r32-is-first-inout
4945     0/imm32/no-imm32
4946     0/imm32/no-disp32
4947     0/imm32/output-is-write-only
4948     _Primitive-and-reg-with-mem/imm32/next
4949 _Primitive-and-reg-with-mem:
4950     # and-with var1 var2/reg => 21/and-with var1 var2/r32
4951     "and-with"/imm32/name
4952     Two-args-int-stack-int-reg/imm32/inouts
4953     0/imm32/outputs
4954     "21/and-with"/imm32/subx-name
4955     1/imm32/rm32-is-first-inout
4956     2/imm32/r32-is-second-inout
4957     0/imm32/no-imm32
4958     0/imm32/no-disp32
4959     0/imm32/output-is-write-only
4960     _Primitive-and-mem-with-reg/imm32/next
4961 _Primitive-and-mem-with-reg:
4962     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
4963     "and"/imm32/name
4964     Single-int-var-on-stack/imm32/inouts
4965     Single-int-var-in-some-register/imm32/outputs
4966     "23/and"/imm32/subx-name
4967     1/imm32/rm32-is-first-inout
4968     3/imm32/r32-is-first-output
4969     0/imm32/no-imm32
4970     0/imm32/no-disp32
4971     0/imm32/output-is-write-only
4972     _Primitive-and-lit-with-reg/imm32/next
4973 _Primitive-and-lit-with-reg:
4974     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
4975     "and"/imm32/name
4976     Single-lit-var/imm32/inouts
4977     Single-int-var-in-some-register/imm32/outputs
4978     "81 4/subop/and"/imm32/subx-name
4979     3/imm32/rm32-is-first-output
4980     0/imm32/no-r32
4981     1/imm32/imm32-is-first-inout
4982     0/imm32/no-disp32
4983     0/imm32/output-is-write-only
4984     _Primitive-and-lit-with-mem/imm32/next
4985 _Primitive-and-lit-with-mem:
4986     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
4987     "and-with"/imm32/name
4988     Int-var-and-literal/imm32/inouts
4989     0/imm32/outputs
4990     "81 4/subop/and"/imm32/subx-name
4991     1/imm32/rm32-is-first-inout
4992     0/imm32/no-r32
4993     2/imm32/imm32-is-first-inout
4994     0/imm32/no-disp32
4995     0/imm32/output-is-write-only
4996     _Primitive-or-with-eax/imm32/next
4997 # - or
4998 _Primitive-or-with-eax:
4999     # var/eax <- or lit => 0d/or-with-eax lit/imm32
5000     "or"/imm32/name
5001     Single-lit-var/imm32/inouts
5002     Single-int-var-in-eax/imm32/outputs
5003     "0d/or-with-eax"/imm32/subx-name
5004     0/imm32/no-rm32
5005     0/imm32/no-r32
5006     1/imm32/imm32-is-first-inout
5007     0/imm32/no-disp32
5008     0/imm32/output-is-write-only
5009     _Primitive-or-reg-with-reg/imm32/next
5010 _Primitive-or-reg-with-reg:
5011     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
5012     "or"/imm32/name
5013     Single-int-var-in-some-register/imm32/inouts
5014     Single-int-var-in-some-register/imm32/outputs
5015     "09/or-with"/imm32/subx-name
5016     3/imm32/rm32-is-first-output
5017     1/imm32/r32-is-first-inout
5018     0/imm32/no-imm32
5019     0/imm32/no-disp32
5020     0/imm32/output-is-write-only
5021     _Primitive-or-reg-with-mem/imm32/next
5022 _Primitive-or-reg-with-mem:
5023     # or-with var1 var2/reg => 09/or-with var1 var2/r32
5024     "or-with"/imm32/name
5025     Two-args-int-stack-int-reg/imm32/inouts
5026     0/imm32/outputs
5027     "09/or-with"/imm32/subx-name
5028     1/imm32/rm32-is-first-inout
5029     2/imm32/r32-is-second-inout
5030     0/imm32/no-imm32
5031     0/imm32/no-disp32
5032     0/imm32/output-is-write-only
5033     _Primitive-or-mem-with-reg/imm32/next
5034 _Primitive-or-mem-with-reg:
5035     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
5036     "or"/imm32/name
5037     Single-int-var-on-stack/imm32/inouts
5038     Single-int-var-in-some-register/imm32/outputs
5039     "0b/or"/imm32/subx-name
5040     1/imm32/rm32-is-first-inout
5041     3/imm32/r32-is-first-output
5042     0/imm32/no-imm32
5043     0/imm32/no-disp32
5044     0/imm32/output-is-write-only
5045     _Primitive-or-lit-with-reg/imm32/next
5046 _Primitive-or-lit-with-reg:
5047     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
5048     "or"/imm32/name
5049     Single-lit-var/imm32/inouts
5050     Single-int-var-in-some-register/imm32/outputs
5051     "81 1/subop/or"/imm32/subx-name
5052     3/imm32/rm32-is-first-output
5053     0/imm32/no-r32
5054     1/imm32/imm32-is-first-inout
5055     0/imm32/no-disp32
5056     0/imm32/output-is-write-only
5057     _Primitive-or-lit-with-mem/imm32/next
5058 _Primitive-or-lit-with-mem:
5059     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
5060     "or-with"/imm32/name
5061     Int-var-and-literal/imm32/inouts
5062     0/imm32/outputs
5063     "81 1/subop/or"/imm32/subx-name
5064     1/imm32/rm32-is-first-inout
5065     0/imm32/no-r32
5066     2/imm32/imm32-is-second-inout
5067     0/imm32/no-disp32
5068     0/imm32/output-is-write-only
5069     _Primitive-xor-with-eax/imm32/next
5070 # - xor
5071 _Primitive-xor-with-eax:
5072     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
5073     "xor"/imm32/name
5074     Single-lit-var/imm32/inouts
5075     Single-int-var-in-eax/imm32/outputs
5076     "35/xor-with-eax"/imm32/subx-name
5077     0/imm32/no-rm32
5078     0/imm32/no-r32
5079     1/imm32/imm32-is-first-inout
5080     0/imm32/no-disp32
5081     0/imm32/output-is-write-only
5082     _Primitive-xor-reg-with-reg/imm32/next
5083 _Primitive-xor-reg-with-reg:
5084     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
5085     "xor"/imm32/name
5086     Single-int-var-in-some-register/imm32/inouts
5087     Single-int-var-in-some-register/imm32/outputs
5088     "31/xor-with"/imm32/subx-name
5089     3/imm32/rm32-is-first-output
5090     1/imm32/r32-is-first-inout
5091     0/imm32/no-imm32
5092     0/imm32/no-disp32
5093     0/imm32/output-is-write-only
5094     _Primitive-xor-reg-with-mem/imm32/next
5095 _Primitive-xor-reg-with-mem:
5096     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
5097     "xor-with"/imm32/name
5098     Two-args-int-stack-int-reg/imm32/inouts
5099     0/imm32/outputs
5100     "31/xor-with"/imm32/subx-name
5101     1/imm32/rm32-is-first-inout
5102     2/imm32/r32-is-second-inout
5103     0/imm32/no-imm32
5104     0/imm32/no-disp32
5105     0/imm32/output-is-write-only
5106     _Primitive-xor-mem-with-reg/imm32/next
5107 _Primitive-xor-mem-with-reg:
5108     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
5109     "xor"/imm32/name
5110     Single-int-var-on-stack/imm32/inouts
5111     Single-int-var-in-some-register/imm32/outputs
5112     "33/xor"/imm32/subx-name
5113     1/imm32/rm32-is-first-inout
5114     3/imm32/r32-is-first-output
5115     0/imm32/no-imm32
5116     0/imm32/no-disp32
5117     0/imm32/output-is-write-only
5118     _Primitive-xor-lit-with-reg/imm32/next
5119 _Primitive-xor-lit-with-reg:
5120     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
5121     "xor"/imm32/name
5122     Single-lit-var/imm32/inouts
5123     Single-int-var-in-some-register/imm32/outputs
5124     "81 6/subop/xor"/imm32/subx-name
5125     3/imm32/rm32-is-first-output
5126     0/imm32/no-r32
5127     1/imm32/imm32-is-first-inout
5128     0/imm32/no-disp32
5129     0/imm32/output-is-write-only
5130     _Primitive-xor-lit-with-mem/imm32/next
5131 _Primitive-xor-lit-with-mem:
5132     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
5133     "xor-with"/imm32/name
5134     Int-var-and-literal/imm32/inouts
5135     0/imm32/outputs
5136     "81 6/subop/xor"/imm32/subx-name
5137     1/imm32/rm32-is-first-inout
5138     0/imm32/no-r32
5139     2/imm32/imm32-is-first-inout
5140     0/imm32/no-disp32
5141     0/imm32/output-is-write-only
5142     _Primitive-copy-to-eax/imm32/next
5143 # - copy
5144 _Primitive-copy-to-eax:
5145     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
5146     "copy"/imm32/name
5147     Single-lit-var/imm32/inouts
5148     Single-int-var-in-eax/imm32/outputs
5149     "b8/copy-to-eax"/imm32/subx-name
5150     0/imm32/no-rm32
5151     0/imm32/no-r32
5152     1/imm32/imm32-is-first-inout
5153     0/imm32/no-disp32
5154     1/imm32/output-is-write-only
5155     _Primitive-copy-to-ecx/imm32/next
5156 _Primitive-copy-to-ecx:
5157     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
5158     "copy"/imm32/name
5159     Single-lit-var/imm32/inouts
5160     Single-int-var-in-ecx/imm32/outputs
5161     "b9/copy-to-ecx"/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     1/imm32/output-is-write-only
5167     _Primitive-copy-to-edx/imm32/next
5168 _Primitive-copy-to-edx:
5169     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
5170     "copy"/imm32/name
5171     Single-lit-var/imm32/inouts
5172     Single-int-var-in-edx/imm32/outputs
5173     "ba/copy-to-edx"/imm32/subx-name
5174     0/imm32/no-rm32
5175     0/imm32/no-r32
5176     1/imm32/imm32-is-first-inout
5177     0/imm32/no-disp32
5178     1/imm32/output-is-write-only
5179     _Primitive-copy-to-ebx/imm32/next
5180 _Primitive-copy-to-ebx:
5181     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
5182     "copy"/imm32/name
5183     Single-lit-var/imm32/inouts
5184     Single-int-var-in-ebx/imm32/outputs
5185     "bb/copy-to-ebx"/imm32/subx-name
5186     0/imm32/no-rm32
5187     0/imm32/no-r32
5188     1/imm32/imm32-is-first-inout
5189     0/imm32/no-disp32
5190     1/imm32/output-is-write-only
5191     _Primitive-copy-to-esi/imm32/next
5192 _Primitive-copy-to-esi:
5193     # var/esi <- copy lit => be/copy-to-esi lit/imm32
5194     "copy"/imm32/name
5195     Single-lit-var/imm32/inouts
5196     Single-int-var-in-esi/imm32/outputs
5197     "be/copy-to-esi"/imm32/subx-name
5198     0/imm32/no-rm32
5199     0/imm32/no-r32
5200     1/imm32/imm32-is-first-inout
5201     0/imm32/no-disp32
5202     1/imm32/output-is-write-only
5203     _Primitive-copy-to-edi/imm32/next
5204 _Primitive-copy-to-edi:
5205     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
5206     "copy"/imm32/name
5207     Single-lit-var/imm32/inouts
5208     Single-int-var-in-edi/imm32/outputs
5209     "bf/copy-to-edi"/imm32/subx-name
5210     0/imm32/no-rm32
5211     0/imm32/no-r32
5212     1/imm32/imm32-is-first-inout
5213     0/imm32/no-disp32
5214     1/imm32/output-is-write-only
5215     _Primitive-copy-reg-to-reg/imm32/next
5216 _Primitive-copy-reg-to-reg:
5217     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
5218     "copy"/imm32/name
5219     Single-int-var-in-some-register/imm32/inouts
5220     Single-int-var-in-some-register/imm32/outputs
5221     "89/copy-to"/imm32/subx-name
5222     3/imm32/rm32-is-first-output
5223     1/imm32/r32-is-first-inout
5224     0/imm32/no-imm32
5225     0/imm32/no-disp32
5226     1/imm32/output-is-write-only
5227     _Primitive-copy-reg-to-mem/imm32/next
5228 _Primitive-copy-reg-to-mem:
5229     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
5230     "copy-to"/imm32/name
5231     Two-args-int-stack-int-reg/imm32/inouts
5232     0/imm32/outputs
5233     "89/copy-to"/imm32/subx-name
5234     1/imm32/rm32-is-first-inout
5235     2/imm32/r32-is-second-inout
5236     0/imm32/no-imm32
5237     0/imm32/no-disp32
5238     1/imm32/output-is-write-only
5239     _Primitive-copy-mem-to-reg/imm32/next
5240 _Primitive-copy-mem-to-reg:
5241     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
5242     "copy"/imm32/name
5243     Single-int-var-on-stack/imm32/inouts
5244     Single-int-var-in-some-register/imm32/outputs
5245     "8b/copy-from"/imm32/subx-name
5246     1/imm32/rm32-is-first-inout
5247     3/imm32/r32-is-first-output
5248     0/imm32/no-imm32
5249     0/imm32/no-disp32
5250     1/imm32/output-is-write-only
5251     _Primitive-copy-lit-to-reg/imm32/next
5252 _Primitive-copy-lit-to-reg:
5253     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
5254     "copy"/imm32/name
5255     Single-lit-var/imm32/inouts
5256     Single-int-var-in-some-register/imm32/outputs
5257     "c7 0/subop/copy"/imm32/subx-name
5258     3/imm32/rm32-is-first-output
5259     0/imm32/no-r32
5260     1/imm32/imm32-is-first-inout
5261     0/imm32/no-disp32
5262     1/imm32/output-is-write-only
5263     _Primitive-copy-lit-to-mem/imm32/next
5264 _Primitive-copy-lit-to-mem:
5265     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
5266     "copy-to"/imm32/name
5267     Int-var-and-literal/imm32/inouts
5268     0/imm32/outputs
5269     "c7 0/subop/copy"/imm32/subx-name
5270     1/imm32/rm32-is-first-inout
5271     0/imm32/no-r32
5272     2/imm32/imm32-is-first-inout
5273     0/imm32/no-disp32
5274     1/imm32/output-is-write-only
5275     _Primitive-compare-mem-with-reg/imm32/next
5276 # - compare
5277 _Primitive-compare-mem-with-reg:
5278     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
5279     "compare"/imm32/name
5280     Two-args-int-stack-int-reg/imm32/inouts
5281     0/imm32/outputs
5282     "39/compare->"/imm32/subx-name
5283     1/imm32/rm32-is-first-inout
5284     2/imm32/r32-is-second-inout
5285     0/imm32/no-imm32
5286     0/imm32/no-disp32
5287     0/imm32/output-is-write-only
5288     _Primitive-compare-reg-with-mem/imm32/next
5289 _Primitive-compare-reg-with-mem:
5290     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
5291     "compare"/imm32/name
5292     Two-args-int-reg-int-stack/imm32/inouts
5293     0/imm32/outputs
5294     "3b/compare<-"/imm32/subx-name
5295     2/imm32/rm32-is-second-inout
5296     1/imm32/r32-is-first-inout
5297     0/imm32/no-imm32
5298     0/imm32/no-disp32
5299     0/imm32/output-is-write-only
5300     _Primitive-compare-eax-with-literal/imm32/next
5301 _Primitive-compare-eax-with-literal:
5302     # compare var1/eax n => 3d/compare-eax-with n/imm32
5303     "compare"/imm32/name
5304     Two-args-int-eax-int-literal/imm32/inouts
5305     0/imm32/outputs
5306     "3d/compare-eax-with"/imm32/subx-name
5307     0/imm32/no-rm32
5308     0/imm32/no-r32
5309     2/imm32/imm32-is-second-inout
5310     0/imm32/no-disp32
5311     0/imm32/output-is-write-only
5312     _Primitive-compare-regmem-with-literal/imm32/next
5313 _Primitive-compare-regmem-with-literal:
5314     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
5315     "compare"/imm32/name
5316     Int-var-and-literal/imm32/inouts
5317     0/imm32/outputs
5318     "81 7/subop/compare"/imm32/subx-name
5319     1/imm32/rm32-is-first-inout
5320     0/imm32/no-r32
5321     2/imm32/imm32-is-second-inout
5322     0/imm32/no-disp32
5323     0/imm32/output-is-write-only
5324     _Primitive-multiply-reg-by-mem/imm32/next
5325 # - multiply
5326 _Primitive-multiply-reg-by-mem:
5327     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
5328     "multiply"/imm32/name
5329     Single-int-var-on-stack/imm32/inouts
5330     Single-int-var-in-some-register/imm32/outputs
5331     "0f af/multiply"/imm32/subx-name
5332     1/imm32/rm32-is-first-inout
5333     3/imm32/r32-is-first-output
5334     0/imm32/no-imm32
5335     0/imm32/no-disp32
5336     0/imm32/output-is-write-only
5337     _Primitive-break-if-addr</imm32/next
5338 # - branches
5339 _Primitive-break-if-addr<:
5340     "break-if-addr<"/imm32/name
5341     0/imm32/inouts
5342     0/imm32/outputs
5343     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
5344     0/imm32/no-rm32
5345     0/imm32/no-r32
5346     0/imm32/no-imm32
5347     0/imm32/no-disp32
5348     0/imm32/no-output
5349     _Primitive-break-if-addr>=/imm32/next
5350 _Primitive-break-if-addr>=:
5351     "break-if-addr>="/imm32/name
5352     0/imm32/inouts
5353     0/imm32/outputs
5354     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
5355     0/imm32/no-rm32
5356     0/imm32/no-r32
5357     0/imm32/no-imm32
5358     0/imm32/no-disp32
5359     0/imm32/no-output
5360     _Primitive-break-if-=/imm32/next
5361 _Primitive-break-if-=:
5362     "break-if-="/imm32/name
5363     0/imm32/inouts
5364     0/imm32/outputs
5365     "0f 84/jump-if-= break/disp32"/imm32/subx-name
5366     0/imm32/no-rm32
5367     0/imm32/no-r32
5368     0/imm32/no-imm32
5369     0/imm32/no-disp32
5370     0/imm32/no-output
5371     _Primitive-break-if-!=/imm32/next
5372 _Primitive-break-if-!=:
5373     "break-if-!="/imm32/name
5374     0/imm32/inouts
5375     0/imm32/outputs
5376     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
5377     0/imm32/no-rm32
5378     0/imm32/no-r32
5379     0/imm32/no-imm32
5380     0/imm32/no-disp32
5381     0/imm32/no-output
5382     _Primitive-break-if-addr<=/imm32/next
5383 _Primitive-break-if-addr<=:
5384     "break-if-addr<="/imm32/name
5385     0/imm32/inouts
5386     0/imm32/outputs
5387     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
5388     0/imm32/no-rm32
5389     0/imm32/no-r32
5390     0/imm32/no-imm32
5391     0/imm32/no-disp32
5392     0/imm32/no-output
5393     _Primitive-break-if-addr>/imm32/next
5394 _Primitive-break-if-addr>:
5395     "break-if-addr>"/imm32/name
5396     0/imm32/inouts
5397     0/imm32/outputs
5398     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
5399     0/imm32/no-rm32
5400     0/imm32/no-r32
5401     0/imm32/no-imm32
5402     0/imm32/no-disp32
5403     0/imm32/no-output
5404     _Primitive-break-if-</imm32/next
5405 _Primitive-break-if-<:
5406     "break-if-<"/imm32/name
5407     0/imm32/inouts
5408     0/imm32/outputs
5409     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
5410     0/imm32/no-rm32
5411     0/imm32/no-r32
5412     0/imm32/no-imm32
5413     0/imm32/no-disp32
5414     0/imm32/no-output
5415     _Primitive-break-if->=/imm32/next
5416 _Primitive-break-if->=:
5417     "break-if->="/imm32/name
5418     0/imm32/inouts
5419     0/imm32/outputs
5420     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
5421     0/imm32/no-rm32
5422     0/imm32/no-r32
5423     0/imm32/no-imm32
5424     0/imm32/no-disp32
5425     0/imm32/no-output
5426     _Primitive-break-if-<=/imm32/next
5427 _Primitive-break-if-<=:
5428     "break-if-<="/imm32/name
5429     0/imm32/inouts
5430     0/imm32/outputs
5431     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
5432     0/imm32/no-rm32
5433     0/imm32/no-r32
5434     0/imm32/no-imm32
5435     0/imm32/no-disp32
5436     0/imm32/no-output
5437     _Primitive-break-if->/imm32/next
5438 _Primitive-break-if->:
5439     "break-if->"/imm32/name
5440     0/imm32/inouts
5441     0/imm32/outputs
5442     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
5443     0/imm32/no-rm32
5444     0/imm32/no-r32
5445     0/imm32/no-imm32
5446     0/imm32/no-disp32
5447     0/imm32/no-output
5448     _Primitive-loop-if-addr</imm32/next
5449 _Primitive-loop-if-addr<:
5450     "loop-if-addr<"/imm32/name
5451     0/imm32/inouts
5452     0/imm32/outputs
5453     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
5454     0/imm32/no-rm32
5455     0/imm32/no-r32
5456     0/imm32/no-imm32
5457     0/imm32/no-disp32
5458     0/imm32/no-output
5459     _Primitive-loop-if-addr>=/imm32/next
5460 _Primitive-loop-if-addr>=:
5461     "loop-if-addr>="/imm32/name
5462     0/imm32/inouts
5463     0/imm32/outputs
5464     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
5465     0/imm32/no-rm32
5466     0/imm32/no-r32
5467     0/imm32/no-imm32
5468     0/imm32/no-disp32
5469     0/imm32/no-output
5470     _Primitive-loop-if-=/imm32/next
5471 _Primitive-loop-if-=:
5472     "loop-if-="/imm32/name
5473     0/imm32/inouts
5474     0/imm32/outputs
5475     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
5476     0/imm32/no-rm32
5477     0/imm32/no-r32
5478     0/imm32/no-imm32
5479     0/imm32/no-disp32
5480     0/imm32/no-output
5481     _Primitive-loop-if-!=/imm32/next
5482 _Primitive-loop-if-!=:
5483     "loop-if-!="/imm32/name
5484     0/imm32/inouts
5485     0/imm32/outputs
5486     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
5487     0/imm32/no-rm32
5488     0/imm32/no-r32
5489     0/imm32/no-imm32
5490     0/imm32/no-disp32
5491     0/imm32/no-output
5492     _Primitive-loop-if-addr<=/imm32/next
5493 _Primitive-loop-if-addr<=:
5494     "loop-if-addr<="/imm32/name
5495     0/imm32/inouts
5496     0/imm32/outputs
5497     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
5498     0/imm32/no-rm32
5499     0/imm32/no-r32
5500     0/imm32/no-imm32
5501     0/imm32/no-disp32
5502     0/imm32/no-output
5503     _Primitive-loop-if-addr>/imm32/next
5504 _Primitive-loop-if-addr>:
5505     "loop-if-addr>"/imm32/name
5506     0/imm32/inouts
5507     0/imm32/outputs
5508     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
5509     0/imm32/no-rm32
5510     0/imm32/no-r32
5511     0/imm32/no-imm32
5512     0/imm32/no-disp32
5513     0/imm32/no-output
5514     _Primitive-loop-if-</imm32/next
5515 _Primitive-loop-if-<:
5516     "loop-if-<"/imm32/name
5517     0/imm32/inouts
5518     0/imm32/outputs
5519     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
5520     0/imm32/no-rm32
5521     0/imm32/no-r32
5522     0/imm32/no-imm32
5523     0/imm32/no-disp32
5524     0/imm32/no-output
5525     _Primitive-loop-if->=/imm32/next
5526 _Primitive-loop-if->=:
5527     "loop-if->="/imm32/name
5528     0/imm32/inouts
5529     0/imm32/outputs
5530     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
5531     0/imm32/no-rm32
5532     0/imm32/no-r32
5533     0/imm32/no-imm32
5534     0/imm32/no-disp32
5535     0/imm32/no-output
5536     _Primitive-loop-if-<=/imm32/next
5537 _Primitive-loop-if-<=:
5538     "loop-if-<="/imm32/name
5539     0/imm32/inouts
5540     0/imm32/outputs
5541     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
5542     0/imm32/no-rm32
5543     0/imm32/no-r32
5544     0/imm32/no-imm32
5545     0/imm32/no-disp32
5546     0/imm32/no-output
5547     _Primitive-loop-if->/imm32/next
5548 _Primitive-loop-if->:
5549     "loop-if->"/imm32/name
5550     0/imm32/inouts
5551     0/imm32/outputs
5552     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
5553     0/imm32/no-rm32
5554     0/imm32/no-r32
5555     0/imm32/no-imm32
5556     0/imm32/no-disp32
5557     0/imm32/no-output
5558     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
5559 _Primitive-loop:
5560     "loop"/imm32/name
5561     0/imm32/inouts
5562     0/imm32/outputs
5563     "e9/jump loop/disp32"/imm32/subx-name
5564     0/imm32/no-rm32
5565     0/imm32/no-r32
5566     0/imm32/no-imm32
5567     0/imm32/no-disp32
5568     0/imm32/no-output
5569     _Primitive-break-if-addr<-named/imm32/next
5570 # - branches to named blocks
5571 _Primitive-break-if-addr<-named:
5572     "break-if-addr<"/imm32/name
5573     Single-lit-var/imm32/inouts
5574     0/imm32/outputs
5575     "0f 82/jump-if-addr<"/imm32/subx-name
5576     0/imm32/no-rm32
5577     0/imm32/no-r32
5578     0/imm32/no-imm32
5579     1/imm32/disp32-is-first-inout
5580     0/imm32/no-output
5581     _Primitive-break-if-addr>=-named/imm32/next
5582 _Primitive-break-if-addr>=-named:
5583     "break-if-addr>="/imm32/name
5584     Single-lit-var/imm32/inouts
5585     0/imm32/outputs
5586     "0f 83/jump-if-addr>="/imm32/subx-name
5587     0/imm32/no-rm32
5588     0/imm32/no-r32
5589     0/imm32/no-imm32
5590     1/imm32/disp32-is-first-inout
5591     0/imm32/no-output
5592     _Primitive-break-if-=-named/imm32/next
5593 _Primitive-break-if-=-named:
5594     "break-if-="/imm32/name
5595     Single-lit-var/imm32/inouts
5596     0/imm32/outputs
5597     "0f 84/jump-if-="/imm32/subx-name
5598     0/imm32/no-rm32
5599     0/imm32/no-r32
5600     0/imm32/no-imm32
5601     1/imm32/disp32-is-first-inout
5602     0/imm32/no-output
5603     _Primitive-break-if-!=-named/imm32/next
5604 _Primitive-break-if-!=-named:
5605     "break-if-!="/imm32/name
5606     Single-lit-var/imm32/inouts
5607     0/imm32/outputs
5608     "0f 85/jump-if-!="/imm32/subx-name
5609     0/imm32/no-rm32
5610     0/imm32/no-r32
5611     0/imm32/no-imm32
5612     1/imm32/disp32-is-first-inout
5613     0/imm32/no-output
5614     _Primitive-break-if-addr<=-named/imm32/next
5615 _Primitive-break-if-addr<=-named:
5616     "break-if-addr<="/imm32/name
5617     Single-lit-var/imm32/inouts
5618     0/imm32/outputs
5619     "0f 86/jump-if-addr<="/imm32/subx-name
5620     0/imm32/no-rm32
5621     0/imm32/no-r32
5622     0/imm32/no-imm32
5623     1/imm32/disp32-is-first-inout
5624     0/imm32/no-output
5625     _Primitive-break-if-addr>-named/imm32/next
5626 _Primitive-break-if-addr>-named:
5627     "break-if-addr>"/imm32/name
5628     Single-lit-var/imm32/inouts
5629     0/imm32/outputs
5630     "0f 87/jump-if-addr>"/imm32/subx-name
5631     0/imm32/no-rm32
5632     0/imm32/no-r32
5633     0/imm32/no-imm32
5634     1/imm32/disp32-is-first-inout
5635     0/imm32/no-output
5636     _Primitive-break-if-<-named/imm32/next
5637 _Primitive-break-if-<-named:
5638     "break-if-<"/imm32/name
5639     Single-lit-var/imm32/inouts
5640     0/imm32/outputs
5641     "0f 8c/jump-if-<"/imm32/subx-name
5642     0/imm32/no-rm32
5643     0/imm32/no-r32
5644     0/imm32/no-imm32
5645     1/imm32/disp32-is-first-inout
5646     0/imm32/no-output
5647     _Primitive-break-if->=-named/imm32/next
5648 _Primitive-break-if->=-named:
5649     "break-if->="/imm32/name
5650     Single-lit-var/imm32/inouts
5651     0/imm32/outputs
5652     "0f 8d/jump-if->="/imm32/subx-name
5653     0/imm32/no-rm32
5654     0/imm32/no-r32
5655     0/imm32/no-imm32
5656     1/imm32/disp32-is-first-inout
5657     0/imm32/no-output
5658     _Primitive-break-if-<=-named/imm32/next
5659 _Primitive-break-if-<=-named:
5660     "break-if-<="/imm32/name
5661     Single-lit-var/imm32/inouts
5662     0/imm32/outputs
5663     "0f 8e/jump-if-<="/imm32/subx-name
5664     0/imm32/no-rm32
5665     0/imm32/no-r32
5666     0/imm32/no-imm32
5667     1/imm32/disp32-is-first-inout
5668     0/imm32/no-output
5669     _Primitive-break-if->-named/imm32/next
5670 _Primitive-break-if->-named:
5671     "break-if->"/imm32/name
5672     Single-lit-var/imm32/inouts
5673     0/imm32/outputs
5674     "0f 8f/jump-if->"/imm32/subx-name
5675     0/imm32/no-rm32
5676     0/imm32/no-r32
5677     0/imm32/no-imm32
5678     1/imm32/disp32-is-first-inout
5679     0/imm32/no-output
5680     _Primitive-loop-if-addr<-named/imm32/next
5681 _Primitive-loop-if-addr<-named:
5682     "loop-if-addr<"/imm32/name
5683     Single-lit-var/imm32/inouts
5684     0/imm32/outputs
5685     "0f 82/jump-if-addr<"/imm32/subx-name
5686     0/imm32/no-rm32
5687     0/imm32/no-r32
5688     0/imm32/no-imm32
5689     1/imm32/disp32-is-first-inout
5690     0/imm32/no-output
5691     _Primitive-loop-if-addr>=-named/imm32/next
5692 _Primitive-loop-if-addr>=-named:
5693     "loop-if-addr>="/imm32/name
5694     Single-lit-var/imm32/inouts
5695     0/imm32/outputs
5696     "0f 83/jump-if-addr>="/imm32/subx-name
5697     0/imm32/no-rm32
5698     0/imm32/no-r32
5699     0/imm32/no-imm32
5700     1/imm32/disp32-is-first-inout
5701     0/imm32/no-output
5702     _Primitive-loop-if-=-named/imm32/next
5703 _Primitive-loop-if-=-named:
5704     "loop-if-="/imm32/name
5705     Single-lit-var/imm32/inouts
5706     0/imm32/outputs
5707     "0f 84/jump-if-="/imm32/subx-name
5708     0/imm32/no-rm32
5709     0/imm32/no-r32
5710     0/imm32/no-imm32
5711     1/imm32/disp32-is-first-inout
5712     0/imm32/no-output
5713     _Primitive-loop-if-!=-named/imm32/next
5714 _Primitive-loop-if-!=-named:
5715     "loop-if-!="/imm32/name
5716     Single-lit-var/imm32/inouts
5717     0/imm32/outputs
5718     "0f 85/jump-if-!="/imm32/subx-name
5719     0/imm32/no-rm32
5720     0/imm32/no-r32
5721     0/imm32/no-imm32
5722     1/imm32/disp32-is-first-inout
5723     0/imm32/no-output
5724     _Primitive-loop-if-addr<=-named/imm32/next
5725 _Primitive-loop-if-addr<=-named:
5726     "loop-if-addr<="/imm32/name
5727     Single-lit-var/imm32/inouts
5728     0/imm32/outputs
5729     "0f 86/jump-if-addr<="/imm32/subx-name
5730     0/imm32/no-rm32
5731     0/imm32/no-r32
5732     0/imm32/no-imm32
5733     1/imm32/disp32-is-first-inout
5734     0/imm32/no-output
5735     _Primitive-loop-if-addr>-named/imm32/next
5736 _Primitive-loop-if-addr>-named:
5737     "loop-if-addr>"/imm32/name
5738     Single-lit-var/imm32/inouts
5739     0/imm32/outputs
5740     "0f 87/jump-if-addr>"/imm32/subx-name
5741     0/imm32/no-rm32
5742     0/imm32/no-r32
5743     0/imm32/no-imm32
5744     1/imm32/disp32-is-first-inout
5745     0/imm32/no-output
5746     _Primitive-loop-if-<-named/imm32/next
5747 _Primitive-loop-if-<-named:
5748     "loop-if-<"/imm32/name
5749     Single-lit-var/imm32/inouts
5750     0/imm32/outputs
5751     "0f 8c/jump-if-<"/imm32/subx-name
5752     0/imm32/no-rm32
5753     0/imm32/no-r32
5754     0/imm32/no-imm32
5755     1/imm32/disp32-is-first-inout
5756     0/imm32/no-output
5757     _Primitive-loop-if->=-named/imm32/next
5758 _Primitive-loop-if->=-named:
5759     "loop-if->="/imm32/name
5760     Single-lit-var/imm32/inouts
5761     0/imm32/outputs
5762     "0f 8d/jump-if->="/imm32/subx-name
5763     0/imm32/no-rm32
5764     0/imm32/no-r32
5765     0/imm32/no-imm32
5766     1/imm32/disp32-is-first-inout
5767     0/imm32/no-output
5768     _Primitive-loop-if-<=-named/imm32/next
5769 _Primitive-loop-if-<=-named:
5770     "loop-if-<="/imm32/name
5771     Single-lit-var/imm32/inouts
5772     0/imm32/outputs
5773     "0f 8e/jump-if-<="/imm32/subx-name
5774     0/imm32/no-rm32
5775     0/imm32/no-r32
5776     0/imm32/no-imm32
5777     1/imm32/disp32-is-first-inout
5778     0/imm32/no-output
5779     _Primitive-loop-if->-named/imm32/next
5780 _Primitive-loop-if->-named:
5781     "loop-if->"/imm32/name
5782     Single-lit-var/imm32/inouts
5783     0/imm32/outputs
5784     "0f 8f/jump-if->"/imm32/subx-name
5785     0/imm32/no-rm32
5786     0/imm32/no-r32
5787     0/imm32/no-imm32
5788     1/imm32/disp32-is-first-inout
5789     0/imm32/no-output
5790     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
5791 _Primitive-loop-named:
5792     "loop"/imm32/name
5793     Single-lit-var/imm32/inouts
5794     0/imm32/outputs
5795     "e9/jump"/imm32/subx-name
5796     0/imm32/no-rm32
5797     0/imm32/no-r32
5798     0/imm32/no-imm32
5799     1/imm32/disp32-is-first-inout
5800     0/imm32/no-output
5801     0/imm32/next
5802 
5803 Single-int-var-on-stack:
5804     Int-var-on-stack/imm32
5805     0/imm32/next
5806 
5807 Int-var-on-stack:
5808     "arg1"/imm32/name
5809     Type-int/imm32
5810     1/imm32/some-block-depth
5811     1/imm32/some-stack-offset
5812     0/imm32/no-register
5813 
5814 Two-args-int-stack-int-reg:
5815     Int-var-on-stack/imm32
5816     Single-int-var-in-some-register/imm32/next
5817 
5818 Two-args-int-reg-int-stack:
5819     Int-var-in-some-register/imm32
5820     Single-int-var-on-stack/imm32/next
5821 
5822 Two-args-int-eax-int-literal:
5823     Int-var-in-eax/imm32
5824     Single-lit-var/imm32/next
5825 
5826 Int-var-and-literal:
5827     Int-var-on-stack/imm32
5828     Single-lit-var/imm32/next
5829 
5830 Single-int-var-in-some-register:
5831     Int-var-in-some-register/imm32
5832     0/imm32/next
5833 
5834 Int-var-in-some-register:
5835     "arg1"/imm32/name
5836     Type-int/imm32
5837     1/imm32/some-block-depth
5838     0/imm32/no-stack-offset
5839     "*"/imm32/register
5840 
5841 Single-int-var-in-eax:
5842     Int-var-in-eax/imm32
5843     0/imm32/next
5844 
5845 Int-var-in-eax:
5846     "arg1"/imm32/name
5847     Type-int/imm32
5848     1/imm32/some-block-depth
5849     0/imm32/no-stack-offset
5850     "eax"/imm32/register
5851 
5852 Single-int-var-in-ecx:
5853     Int-var-in-ecx/imm32
5854     0/imm32/next
5855 
5856 Int-var-in-ecx:
5857     "arg1"/imm32/name
5858     Type-int/imm32
5859     1/imm32/some-block-depth
5860     0/imm32/no-stack-offset
5861     "ecx"/imm32/register
5862 
5863 Single-int-var-in-edx:
5864     Int-var-in-edx/imm32
5865     0/imm32/next
5866 
5867 Int-var-in-edx:
5868     "arg1"/imm32/name
5869     Type-int/imm32
5870     1/imm32/some-block-depth
5871     0/imm32/no-stack-offset
5872     "edx"/imm32/register
5873 
5874 Single-int-var-in-ebx:
5875     Int-var-in-ebx/imm32
5876     0/imm32/next
5877 
5878 Int-var-in-ebx:
5879     "arg1"/imm32/name
5880     Type-int/imm32
5881     1/imm32/some-block-depth
5882     0/imm32/no-stack-offset
5883     "ebx"/imm32/register
5884 
5885 Single-int-var-in-esi:
5886     Int-var-in-esi/imm32
5887     0/imm32/next
5888 
5889 Int-var-in-esi:
5890     "arg1"/imm32/name
5891     Type-int/imm32
5892     1/imm32/some-block-depth
5893     0/imm32/no-stack-offset
5894     "esi"/imm32/register
5895 
5896 Single-int-var-in-edi:
5897     Int-var-in-edi/imm32
5898     0/imm32/next
5899 
5900 Int-var-in-edi:
5901     "arg1"/imm32/name
5902     Type-int/imm32
5903     1/imm32/some-block-depth
5904     0/imm32/no-stack-offset
5905     "edi"/imm32/register
5906 
5907 Single-lit-var:
5908     Lit-var/imm32
5909     0/imm32/next
5910 
5911 Lit-var:
5912     "literal"/imm32/name
5913     Type-literal/imm32
5914     1/imm32/some-block-depth
5915     0/imm32/no-stack-offset
5916     0/imm32/no-register
5917 
5918 Type-int:
5919     1/imm32/left/int
5920     0/imm32/right/null
5921 
5922 Type-literal:
5923     0/imm32/left/literal
5924     0/imm32/right/null
5925 
5926 == code
5927 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle statement), primitive: (handle function)
5928     # . prologue
5929     55/push-ebp
5930     89/<- %ebp 4/r32/esp
5931     # . save registers
5932     50/push-eax
5933     51/push-ecx
5934     # ecx = primitive
5935     8b/-> *(ebp+0x10) 1/r32/ecx
5936     # emit primitive name
5937     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
5938     # emit rm32 if necessary
5939     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
5940     # emit r32 if necessary
5941     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
5942     # emit imm32 if necessary
5943     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
5944     # emit disp32 if necessary
5945     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
5946     (write-buffered *(ebp+8) Newline)
5947 $emit-subx-primitive:end:
5948     # . restore registers
5949     59/pop-to-ecx
5950     58/pop-to-eax
5951     # . epilogue
5952     89/<- %esp 5/r32/ebp
5953     5d/pop-to-ebp
5954     c3/return
5955 
5956 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
5957     # . prologue
5958     55/push-ebp
5959     89/<- %ebp 4/r32/esp
5960     # . save registers
5961     50/push-eax
5962     # if (l == 0) return
5963     81 7/subop/compare *(ebp+0xc) 0/imm32
5964     74/jump-if-= $emit-subx-rm32:end/disp8
5965     #
5966     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
5967     (emit-subx-var-as-rm32 *(ebp+8) %eax)  # out, var
5968 $emit-subx-rm32:end:
5969     # . restore registers
5970     58/pop-to-eax
5971     # . epilogue
5972     89/<- %esp 5/r32/ebp
5973     5d/pop-to-ebp
5974     c3/return
5975 
5976 get-stmt-operand-from-arg-location:  # stmt: (handle statement), l: arg-location -> var/eax: (handle variable)
5977     # . prologue
5978     55/push-ebp
5979     89/<- %ebp 4/r32/esp
5980     # . save registers
5981     51/push-ecx
5982     # eax = l
5983     8b/-> *(ebp+0xc) 0/r32/eax
5984     # ecx = stmt
5985     8b/-> *(ebp+8) 1/r32/ecx
5986     # if (l == 1) return stmt->inouts->var
5987     {
5988       3d/compare-eax-and 1/imm32
5989       75/jump-if-!= break/disp8
5990 $get-stmt-operand-from-arg-location:1:
5991       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
5992       8b/-> *eax 0/r32/eax  # Operand-var
5993       eb/jump $get-stmt-operand-from-arg-location:end/disp8
5994     }
5995     # if (l == 2) return stmt->inouts->next->var
5996     {
5997       3d/compare-eax-and 2/imm32
5998       75/jump-if-!= break/disp8
5999 $get-stmt-operand-from-arg-location:2:
6000       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
6001       8b/-> *(eax+4) 0/r32/eax  # Operand-next
6002       8b/-> *eax 0/r32/eax  # Operand-var
6003       eb/jump $get-stmt-operand-from-arg-location:end/disp8
6004     }
6005     # if (l == 3) return stmt->outputs
6006     {
6007       3d/compare-eax-and 3/imm32
6008       75/jump-if-!= break/disp8
6009 $get-stmt-operand-from-arg-location:3:
6010       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
6011       8b/-> *eax 0/r32/eax  # Operand-var
6012       eb/jump $get-stmt-operand-from-arg-location:end/disp8
6013     }
6014     # abort
6015     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
6016 $get-stmt-operand-from-arg-location:end:
6017     # . restore registers
6018     59/pop-to-ecx
6019     # . epilogue
6020     89/<- %esp 5/r32/ebp
6021     5d/pop-to-ebp
6022     c3/return
6023 
6024 $get-stmt-operand-from-arg-location:abort:
6025     # error("invalid arg-location " eax)
6026     (write-buffered Stderr "invalid arg-location ")
6027     (print-int32-buffered Stderr %eax)
6028     (write-buffered Stderr Newline)
6029     (flush Stderr)
6030     # . syscall(exit, 1)
6031     bb/copy-to-ebx  1/imm32
6032     b8/copy-to-eax  1/imm32/exit
6033     cd/syscall  0x80/imm8
6034     # never gets here
6035 
6036 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6037     # . prologue
6038     55/push-ebp
6039     89/<- %ebp 4/r32/esp
6040     # . save registers
6041     50/push-eax
6042     51/push-ecx
6043     # if (location == 0) return
6044     81 7/subop/compare *(ebp+0xc) 0/imm32
6045     0f 84/jump-if-= $emit-subx-r32:end/disp32
6046     #
6047     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6048     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
6049     (write-buffered *(ebp+8) Space)
6050     (print-int32-buffered *(ebp+8) *eax)
6051     (write-buffered *(ebp+8) "/r32")
6052 $emit-subx-r32:end:
6053     # . restore registers
6054     59/pop-to-ecx
6055     58/pop-to-eax
6056     # . epilogue
6057     89/<- %esp 5/r32/ebp
6058     5d/pop-to-ebp
6059     c3/return
6060 
6061 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6062     # . prologue
6063     55/push-ebp
6064     89/<- %ebp 4/r32/esp
6065     # . save registers
6066     50/push-eax
6067     51/push-ecx
6068     # if (location == 0) return
6069     81 7/subop/compare *(ebp+0xc) 0/imm32
6070     74/jump-if-= $emit-subx-imm32:end/disp8
6071     #
6072     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6073     (write-buffered *(ebp+8) Space)
6074     (write-buffered *(ebp+8) *eax)  # Var-name
6075     (write-buffered *(ebp+8) "/imm32")
6076 $emit-subx-imm32:end:
6077     # . restore registers
6078     59/pop-to-ecx
6079     58/pop-to-eax
6080     # . epilogue
6081     89/<- %esp 5/r32/ebp
6082     5d/pop-to-ebp
6083     c3/return
6084 
6085 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle statement)
6086     # . prologue
6087     55/push-ebp
6088     89/<- %ebp 4/r32/esp
6089     # . save registers
6090     50/push-eax
6091     51/push-ecx
6092     # if (location == 0) return
6093     81 7/subop/compare *(ebp+0xc) 0/imm32
6094     0f 84/jump-if-= $emit-subx-disp32:end/disp32
6095     #
6096     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
6097     (write-buffered *(ebp+8) Space)
6098     (write-buffered *(ebp+8) *eax)  # Var-name
6099     # hack: if instruction operation starts with "break", emit ":break"
6100     # var name/ecx: (addr array byte) = stmt->operation
6101     8b/-> *(ebp+0x10) 0/r32/eax
6102     8b/-> *(eax+4) 1/r32/ecx
6103     {
6104       (string-starts-with? %ecx "break")  # => eax
6105       3d/compare-eax-and 0/imm32
6106       74/jump-if-= break/disp8
6107       (write-buffered *(ebp+8) ":break")
6108     }
6109     # hack: if instruction operation starts with "loop", emit ":loop"
6110     {
6111       (string-starts-with? %ecx "loop")  # => eax
6112       3d/compare-eax-and 0/imm32
6113       74/jump-if-= break/disp8
6114       (write-buffered *(ebp+8) ":loop")
6115     }
6116     (write-buffered *(ebp+8) "/disp32")
6117 $emit-subx-disp32:end:
6118     # . restore registers
6119     59/pop-to-ecx
6120     58/pop-to-eax
6121     # . epilogue
6122     89/<- %esp 5/r32/ebp
6123     5d/pop-to-ebp
6124     c3/return
6125 
6126 emit-subx-call:  # out: (addr buffered-file), stmt: (handle statement), callee: (handle function)
6127     # . prologue
6128     55/push-ebp
6129     89/<- %ebp 4/r32/esp
6130     # . save registers
6131     50/push-eax
6132     51/push-ecx
6133     #
6134     (write-buffered *(ebp+8) "(")
6135     # - emit function name
6136     8b/-> *(ebp+0x10) 1/r32/ecx
6137     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
6138     # - emit arguments
6139     # var curr/ecx: (handle list var) = stmt->inouts
6140     8b/-> *(ebp+0xc) 1/r32/ecx
6141     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
6142     {
6143       # if (curr == null) break
6144       81 7/subop/compare %ecx 0/imm32
6145       74/jump-if-= break/disp8
6146       #
6147       (emit-subx-call-operand *(ebp+8) *ecx)
6148       # curr = curr->next
6149       8b/-> *(ecx+4) 1/r32/ecx
6150       eb/jump loop/disp8
6151     }
6152     #
6153     (write-buffered *(ebp+8) ")\n")
6154 $emit-subx-call:end:
6155     # . restore registers
6156     59/pop-to-ecx
6157     58/pop-to-eax
6158     # . epilogue
6159     89/<- %esp 5/r32/ebp
6160     5d/pop-to-ebp
6161     c3/return
6162 
6163 emit-subx-call-operand:  # out: (addr buffered-file), operand: (handle variable)
6164     # shares code with emit-subx-var-as-rm32
6165     # . prologue
6166     55/push-ebp
6167     89/<- %ebp 4/r32/esp
6168     # . save registers
6169     50/push-eax
6170     # eax = operand
6171     8b/-> *(ebp+0xc) 0/r32/eax
6172     # if (operand->register) emit "%__"
6173     {
6174       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
6175       74/jump-if-= break/disp8
6176 $emit-subx-call-operand:register:
6177       (write-buffered *(ebp+8) " %")
6178       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
6179       e9/jump $emit-subx-call-operand:end/disp32
6180     }
6181     # else if (operand->stack-offset) emit "*(ebp+__)"
6182     {
6183       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
6184       74/jump-if-= break/disp8
6185 $emit-subx-call-operand:stack:
6186       (write-buffered *(ebp+8) Space)
6187       (write-buffered *(ebp+8) "*(ebp+")
6188       8b/-> *(ebp+0xc) 0/r32/eax
6189       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
6190       (write-buffered *(ebp+8) ")")
6191       e9/jump $emit-subx-call-operand:end/disp32
6192     }
6193     # else if (operand->type == literal) emit "__"
6194     {
6195       50/push-eax
6196       8b/-> *(eax+4) 0/r32/eax  # Var-type
6197       81 7/subop/compare *eax 0/imm32  # Tree-left
6198       58/pop-to-eax
6199       75/jump-if-!= break/disp8
6200 $emit-subx-call-operand:literal:
6201       (write-buffered *(ebp+8) Space)
6202       (write-buffered *(ebp+8) *eax)
6203     }
6204 $emit-subx-call-operand:end:
6205     # . restore registers
6206     58/pop-to-eax
6207     # . epilogue
6208     89/<- %esp 5/r32/ebp
6209     5d/pop-to-ebp
6210     c3/return
6211 
6212 emit-subx-var-as-rm32:  # out: (addr buffered-file), operand: (handle variable)
6213     # . prologue
6214     55/push-ebp
6215     89/<- %ebp 4/r32/esp
6216     # . save registers
6217     50/push-eax
6218     # eax = operand
6219     8b/-> *(ebp+0xc) 0/r32/eax
6220     # if (operand->register) emit "%__"
6221     {
6222       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
6223       74/jump-if-= break/disp8
6224 $emit-subx-var-as-rm32:register:
6225       (write-buffered *(ebp+8) " %")
6226       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
6227     }
6228     # else if (operand->stack-offset) emit "*(ebp+__)"
6229     {
6230       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
6231       74/jump-if-= break/disp8
6232 $emit-subx-var-as-rm32:stack:
6233       (write-buffered *(ebp+8) Space)
6234       (write-buffered *(ebp+8) "*(ebp+")
6235       8b/-> *(ebp+0xc) 0/r32/eax
6236       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
6237       (write-buffered *(ebp+8) ")")
6238     }
6239 $emit-subx-var-as-rm32:end:
6240     # . restore registers
6241     58/pop-to-eax
6242     # . epilogue
6243     89/<- %esp 5/r32/ebp
6244     5d/pop-to-ebp
6245     c3/return
6246 
6247 find-matching-function:  # functions: (addr function), stmt: (handle statement) -> result/eax: (handle function)
6248     # . prologue
6249     55/push-ebp
6250     89/<- %ebp 4/r32/esp
6251     # . save registers
6252     51/push-ecx
6253     # var curr/ecx: (handle function) = functions
6254     8b/-> *(ebp+8) 1/r32/ecx
6255     {
6256       # if (curr == null) break
6257       81 7/subop/compare %ecx 0/imm32
6258       74/jump-if-= break/disp8
6259       # if match(stmt, curr) return curr
6260       {
6261         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
6262         3d/compare-eax-and 0/imm32
6263         74/jump-if-= break/disp8
6264         89/<- %eax 1/r32/ecx
6265         eb/jump $find-matching-function:end/disp8
6266       }
6267       # curr = curr->next
6268       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
6269       eb/jump loop/disp8
6270     }
6271     # return null
6272     b8/copy-to-eax 0/imm32
6273 $find-matching-function:end:
6274     # . restore registers
6275     59/pop-to-ecx
6276     # . epilogue
6277     89/<- %esp 5/r32/ebp
6278     5d/pop-to-ebp
6279     c3/return
6280 
6281 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle statement) -> result/eax: (handle primitive)
6282     # . prologue
6283     55/push-ebp
6284     89/<- %ebp 4/r32/esp
6285     # . save registers
6286     51/push-ecx
6287     # var curr/ecx: (handle primitive) = primitives
6288     8b/-> *(ebp+8) 1/r32/ecx
6289     {
6290 $find-matching-primitive:loop:
6291       # if (curr == null) break
6292       81 7/subop/compare %ecx 0/imm32
6293       0f 84/jump-if-= break/disp32
6294 #?       (write-buffered Stderr "prim: ")
6295 #?       (write-buffered Stderr *ecx)  # Primitive-name
6296 #?       (write-buffered Stderr " => ")
6297 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
6298 #?       (write-buffered Stderr Newline)
6299 #?       (flush Stderr)
6300       # if match(curr, stmt) return curr
6301       {
6302         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
6303         3d/compare-eax-and 0/imm32
6304         74/jump-if-= break/disp8
6305         89/<- %eax 1/r32/ecx
6306         eb/jump $find-matching-primitive:end/disp8
6307       }
6308 $find-matching-primitive:next-primitive:
6309       # curr = curr->next
6310       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
6311       e9/jump loop/disp32
6312     }
6313     # return null
6314     b8/copy-to-eax 0/imm32
6315 $find-matching-primitive:end:
6316     # . restore registers
6317     59/pop-to-ecx
6318     # . epilogue
6319     89/<- %esp 5/r32/ebp
6320     5d/pop-to-ebp
6321     c3/return
6322 
6323 mu-stmt-matches-function?:  # stmt: (handle statement), function: (handle function) => result/eax: boolean
6324     # . prologue
6325     55/push-ebp
6326     89/<- %ebp 4/r32/esp
6327     # . save registers
6328     51/push-ecx
6329     # return function->name == stmt->operation
6330     8b/-> *(ebp+8) 1/r32/ecx
6331     8b/-> *(ebp+0xc) 0/r32/eax
6332     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
6333 $mu-stmt-matches-function?:end:
6334     # . restore registers
6335     59/pop-to-ecx
6336     # . epilogue
6337     89/<- %esp 5/r32/ebp
6338     5d/pop-to-ebp
6339     c3/return
6340 
6341 mu-stmt-matches-primitive?:  # stmt: (handle statement), primitive: (handle primitive) => result/eax: boolean
6342     # A mu stmt matches a primitive if the name matches, all the inout vars
6343     # match, and all the output vars match.
6344     # Vars match if types match and registers match.
6345     # In addition, a stmt output matches a primitive's output if types match
6346     # and the primitive has a wildcard register.
6347     # . prologue
6348     55/push-ebp
6349     89/<- %ebp 4/r32/esp
6350     # . save registers
6351     51/push-ecx
6352     52/push-edx
6353     53/push-ebx
6354     56/push-esi
6355     57/push-edi
6356     # ecx = stmt
6357     8b/-> *(ebp+8) 1/r32/ecx
6358     # edx = primitive
6359     8b/-> *(ebp+0xc) 2/r32/edx
6360     {
6361 $mu-stmt-matches-primitive?:check-name:
6362       # if (primitive->name != stmt->operation) return false
6363       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
6364       3d/compare-eax-and 0/imm32
6365       75/jump-if-!= break/disp8
6366       b8/copy-to-eax 0/imm32
6367       e9/jump $mu-stmt-matches-primitive?:end/disp32
6368     }
6369 $mu-stmt-matches-primitive?:check-inouts:
6370     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
6371     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts
6372     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
6373     {
6374       # if (curr == 0 && curr2 == 0) move on to check outputs
6375       {
6376         81 7/subop/compare %esi 0/imm32
6377         75/jump-if-!= break/disp8
6378 $mu-stmt-matches-primitive?:stmt-inout-is-null:
6379         {
6380           81 7/subop/compare %edi 0/imm32
6381           75/jump-if-!= break/disp8
6382           #
6383           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
6384         }
6385         # return false
6386         b8/copy-to-eax 0/imm32/false
6387         e9/jump $mu-stmt-matches-primitive?:end/disp32
6388       }
6389       # if (curr2 == 0) return false
6390       {
6391         81 7/subop/compare %edi 0/imm32
6392         75/jump-if-!= break/disp8
6393 $mu-stmt-matches-primitive?:prim-inout-is-null:
6394         b8/copy-to-eax 0/imm32/false
6395         e9/jump $mu-stmt-matches-primitive?:end/disp32
6396       }
6397       # if (curr != curr2) return false
6398       {
6399         (operand-matches-primitive? *esi *edi)  # => eax
6400         3d/compare-eax-and 0/imm32
6401         75/jump-if-!= break/disp8
6402         b8/copy-to-eax 0/imm32/false
6403         e9/jump $mu-stmt-matches-primitive?:end/disp32
6404       }
6405       # curr=curr->next
6406       8b/-> *(esi+4) 6/r32/esi  # Operand-next
6407       # curr2=curr2->next
6408       8b/-> *(edi+4) 7/r32/edi  # Operand-next
6409       eb/jump loop/disp8
6410     }
6411 $mu-stmt-matches-primitive?:check-outputs:
6412     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
6413     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
6414     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
6415     {
6416       # if (curr == 0) return (curr2 == 0)
6417       {
6418 $mu-stmt-matches-primitive?:check-output:
6419         81 7/subop/compare %esi 0/imm32
6420         75/jump-if-!= break/disp8
6421         {
6422           81 7/subop/compare %edi 0/imm32
6423           75/jump-if-!= break/disp8
6424           # return true
6425           b8/copy-to-eax 1/imm32
6426           e9/jump $mu-stmt-matches-primitive?:end/disp32
6427         }
6428         # return false
6429         b8/copy-to-eax 0/imm32
6430         e9/jump $mu-stmt-matches-primitive?:end/disp32
6431       }
6432       # if (curr2 == 0) return false
6433       {
6434         81 7/subop/compare %edi 0/imm32
6435         75/jump-if-!= break/disp8
6436         b8/copy-to-eax 0/imm32
6437         e9/jump $mu-stmt-matches-primitive?:end/disp32
6438       }
6439       # if (curr != curr2) return false
6440       {
6441         (operand-matches-primitive? *esi *edi)  # List-value List-value => eax
6442         3d/compare-eax-and 0/imm32
6443         75/jump-if-!= break/disp8
6444         b8/copy-to-eax 0/imm32
6445         e9/jump $mu-stmt-matches-primitive?:end/disp32
6446       }
6447       # curr=curr->next
6448       8b/-> *(esi+4) 6/r32/esi  # Operand-next
6449       # curr2=curr2->next
6450       8b/-> *(edi+4) 7/r32/edi  # Operand-next
6451       eb/jump loop/disp8
6452     }
6453 $mu-stmt-matches-primitive?:return-true:
6454     b8/copy-to-eax 1/imm32
6455 $mu-stmt-matches-primitive?:end:
6456     # . restore registers
6457     5f/pop-to-edi
6458     5e/pop-to-esi
6459     5b/pop-to-ebx
6460     5a/pop-to-edx
6461     59/pop-to-ecx
6462     # . epilogue
6463     89/<- %esp 5/r32/ebp
6464     5d/pop-to-ebp
6465     c3/return
6466 
6467 operand-matches-primitive?:  # var: (handle var), prim-var: (handle var) => result/eax: boolean
6468     # . prologue
6469     55/push-ebp
6470     89/<- %ebp 4/r32/esp
6471     # . save registers
6472     56/push-esi
6473     57/push-edi
6474     # esi = var
6475     8b/-> *(ebp+8) 6/r32/esi
6476     # edi = prim-var
6477     8b/-> *(ebp+0xc) 7/r32/edi
6478     # if (var->type != prim-var->type) return false
6479     (type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
6480     3d/compare-eax-and 0/imm32
6481     b8/copy-to-eax 0/imm32/false
6482     74/jump-if-= $operand-matches-primitive?:end/disp8
6483     # return false if var->register doesn't match prim-var->register
6484     {
6485       # if addresses are equal, don't return here
6486       8b/-> *(esi+0x10) 0/r32/eax
6487       39/compare *(edi+0x10) 0/r32/eax
6488       74/jump-if-= break/disp8
6489       # if either address is 0, return false
6490       3d/compare-eax-and 0/imm32
6491       74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
6492       81 7/subop/compare *(edi+0x10) 0/imm32
6493       74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
6494       # if prim-var->register is "*", return true
6495       (string-equal? *(edi+0x10) "*")  # Var-register
6496       3d/compare-eax-and 0/imm32
6497       b8/copy-to-eax 1/imm32/true
6498       75/jump-if-!= $operand-matches-primitive?:end/disp8
6499       # if string contents don't match, return false
6500       (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register
6501       3d/compare-eax-and 0/imm32
6502       b8/copy-to-eax 0/imm32/false
6503       74/jump-if-= $operand-matches-primitive?:end/disp8
6504     }
6505     # return true
6506     b8/copy-to-eax 1/imm32/true
6507 $operand-matches-primitive?:end:
6508     # . restore registers
6509     5f/pop-to-edi
6510     5e/pop-to-esi
6511     # . epilogue
6512     89/<- %esp 5/r32/ebp
6513     5d/pop-to-ebp
6514     c3/return
6515 
6516 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) => result/eax: boolean
6517     # . prologue
6518     55/push-ebp
6519     89/<- %ebp 4/r32/esp
6520     # . save registers
6521     51/push-ecx
6522     52/push-edx
6523     # ecx = a
6524     8b/-> *(ebp+8) 1/r32/ecx
6525     # edx = b
6526     8b/-> *(ebp+0xc) 2/r32/edx
6527     # if (a == b) return true
6528     8b/-> %ecx 0/r32/eax  # Var-type
6529     39/compare %edx 0/r32/eax  # Var-type
6530     b8/copy-to-eax 1/imm32/true
6531     74/jump-if-= $type-equal?:end/disp8
6532     # if (a < MAX_TYPE_ID) return false
6533     81 7/subop/compare %ecx 0x10000/imm32
6534     b8/copy-to-eax 0/imm32/false
6535     72/jump-if-addr< $type-equal?:end/disp8
6536     # if (b < MAX_TYPE_ID) return false
6537     81 7/subop/compare %edx 0x10000/imm32
6538     b8/copy-to-eax 0/imm32/false
6539     72/jump-if-addr< $type-equal?:end/disp8
6540     # if (!type-equal?(a->left, b->left)) return false
6541     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
6542     3d/compare-eax-and 0/imm32
6543     74/jump-if-= $type-equal?:end/disp8
6544     # return type-equal?(a->right, b->right)
6545     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
6546 $type-equal?:end:
6547     # . restore registers
6548     5a/pop-to-edx
6549     59/pop-to-ecx
6550     # . epilogue
6551     89/<- %esp 5/r32/ebp
6552     5d/pop-to-ebp
6553     c3/return
6554 
6555 test-emit-subx-statement-primitive:
6556     # Primitive operation on a variable on the stack.
6557     #   increment foo
6558     # =>
6559     #   ff 0/subop/increment *(ebp-8)
6560     #
6561     # There's a variable on the var stack as follows:
6562     #   name: 'foo'
6563     #   type: int
6564     #   stack-offset: -8
6565     #
6566     # There's a primitive with this info:
6567     #   name: 'increment'
6568     #   inouts: int/mem
6569     #   value: 'ff 0/subop/increment'
6570     #
6571     # There's nothing in functions.
6572     #
6573     # . prologue
6574     55/push-ebp
6575     89/<- %ebp 4/r32/esp
6576     # setup
6577     (clear-stream _test-output-stream)
6578     (clear-stream $_test-output-buffered-file->buffer)
6579     # var type/ecx: (handle tree type-id) = int
6580     68/push 0/imm32/right/null
6581     68/push 1/imm32/left/int
6582     89/<- %ecx 4/r32/esp
6583     # var var-foo/ecx: var
6584     68/push 0/imm32/no-register
6585     68/push -8/imm32/stack-offset
6586     68/push 1/imm32/block-depth
6587     51/push-ecx
6588     68/push "foo"/imm32
6589     89/<- %ecx 4/r32/esp
6590     # var operand/ebx: (list var)
6591     68/push 0/imm32/next
6592     51/push-ecx/var-foo
6593     89/<- %ebx 4/r32/esp
6594     # var stmt/esi: statement
6595     68/push 0/imm32/next
6596     68/push 0/imm32/outputs
6597     53/push-ebx/operands
6598     68/push "increment"/imm32/operation
6599     68/push 1/imm32
6600     89/<- %esi 4/r32/esp
6601     # var primitives/ebx: primitive
6602     68/push 0/imm32/next
6603     68/push 0/imm32/output-is-write-only
6604     68/push 0/imm32/no-disp32
6605     68/push 0/imm32/no-imm32
6606     68/push 0/imm32/no-r32
6607     68/push 1/imm32/rm32-is-first-inout
6608     68/push "ff 0/subop/increment"/imm32/subx-name
6609     68/push 0/imm32/outputs
6610     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
6611     68/push "increment"/imm32/name
6612     89/<- %ebx 4/r32/esp
6613     # convert
6614     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
6615     (flush _test-output-buffered-file)
6616 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
6622     # check output
6623     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive")
6624     # . epilogue
6625     89/<- %esp 5/r32/ebp
6626     5d/pop-to-ebp
6627     c3/return
6628 
6629 test-emit-subx-statement-primitive-register:
6630     # Primitive operation on a variable in a register.
6631     #   foo <- increment
6632     # =>
6633     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
6634     #
6635     # There's a variable on the var stack as follows:
6636     #   name: 'foo'
6637     #   type: int
6638     #   register: 'eax'
6639     #
6640     # There's a primitive with this info:
6641     #   name: 'increment'
6642     #   out: int/reg
6643     #   value: 'ff 0/subop/increment'
6644     #
6645     # There's nothing in functions.
6646     #
6647     # . prologue
6648     55/push-ebp
6649     89/<- %ebp 4/r32/esp
6650     # setup
6651     (clear-stream _test-output-stream)
6652     (clear-stream $_test-output-buffered-file->buffer)
6653     # var type/ecx: (handle tree type-id) = int
6654     68/push 0/imm32/right/null
6655     68/push 1/imm32/left/int
6656     89/<- %ecx 4/r32/esp
6657     # var var-foo/ecx: var in eax
6658     68/push "eax"/imm32/register
6659     68/push 0/imm32/no-stack-offset
6660     68/push 1/imm32/block-depth
6661     51/push-ecx
6662     68/push "foo"/imm32
6663     89/<- %ecx 4/r32/esp
6664     # var operand/ebx: (list var)
6665     68/push 0/imm32/next
6666     51/push-ecx/var-foo
6667     89/<- %ebx 4/r32/esp
6668     # var stmt/esi: statement
6669     68/push 0/imm32/next
6670     53/push-ebx/outputs
6671     68/push 0/imm32/inouts
6672     68/push "increment"/imm32/operation
6673     68/push 1/imm32
6674     89/<- %esi 4/r32/esp
6675     # var formal-var/ebx: var in any register
6676     68/push Any-register/imm32
6677     68/push 0/imm32/no-stack-offset
6678     68/push 1/imm32/block-depth
6679     ff 6/subop/push *(ecx+4)  # Var-type
6680     68/push "dummy"/imm32
6681     89/<- %ebx 4/r32/esp
6682     # var operand/ebx: (list var)
6683     68/push 0/imm32/next
6684     53/push-ebx/formal-var
6685     89/<- %ebx 4/r32/esp
6686     # var primitives/ebx: primitive
6687     68/push 0/imm32/next
6688     68/push 0/imm32/output-is-write-only
6689     68/push 0/imm32/no-disp32
6690     68/push 0/imm32/no-imm32
6691     68/push 0/imm32/no-r32
6692     68/push 3/imm32/rm32-in-first-output
6693     68/push "ff 0/subop/increment"/imm32/subx-name
6694     53/push-ebx/outputs
6695     68/push 0/imm32/inouts
6696     68/push "increment"/imm32/name
6697     89/<- %ebx 4/r32/esp
6698     # convert
6699     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
6700     (flush _test-output-buffered-file)
6701 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
6707     # check output
6708     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register")
6709     # . epilogue
6710     89/<- %esp 5/r32/ebp
6711     5d/pop-to-ebp
6712     c3/return
6713 
6714 test-emit-subx-statement-select-primitive:
6715     # Select the right primitive between overloads.
6716     #   foo <- increment
6717     # =>
6718     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
6719     #
6720     # There's a variable on the var stack as follows:
6721     #   name: 'foo'
6722     #   type: int
6723     #   register: 'eax'
6724     #
6725     # There's two primitives, as follows:
6726     #   - name: 'increment'
6727     #     out: int/reg
6728     #     value: 'ff 0/subop/increment'
6729     #   - name: 'increment'
6730     #     inout: int/mem
6731     #     value: 'ff 0/subop/increment'
6732     #
6733     # There's nothing in functions.
6734     #
6735     # . prologue
6736     55/push-ebp
6737     89/<- %ebp 4/r32/esp
6738     # setup
6739     (clear-stream _test-output-stream)
6740     (clear-stream $_test-output-buffered-file->buffer)
6741     # var type/ecx: (handle tree type-id) = int
6742     68/push 0/imm32/right/null
6743     68/push 1/imm32/left/int
6744     89/<- %ecx 4/r32/esp
6745     # var var-foo/ecx: var in eax
6746     68/push "eax"/imm32/register
6747     68/push 0/imm32/no-stack-offset
6748     68/push 1/imm32/block-depth
6749     51/push-ecx
6750     68/push "foo"/imm32
6751     89/<- %ecx 4/r32/esp
6752     # var real-outputs/edi: (list var)
6753     68/push 0/imm32/next
6754     51/push-ecx/var-foo
6755     89/<- %edi 4/r32/esp
6756     # var stmt/esi: statement
6757     68/push 0/imm32/next
6758     57/push-edi/outputs
6759     68/push 0/imm32/inouts
6760     68/push "increment"/imm32/operation
6761     68/push 1/imm32
6762     89/<- %esi 4/r32/esp
6763     # var formal-var/ebx: var in any register
6764     68/push Any-register/imm32
6765     68/push 0/imm32/no-stack-offset
6766     68/push 1/imm32/block-depth
6767     ff 6/subop/push *(ecx+4)  # Var-type
6768     68/push "dummy"/imm32
6769     89/<- %ebx 4/r32/esp
6770     # var formal-outputs/ebx: (list var) = {formal-var, 0}
6771     68/push 0/imm32/next
6772     53/push-ebx/formal-var
6773     89/<- %ebx 4/r32/esp
6774     # var primitive1/ebx: primitive
6775     68/push 0/imm32/next
6776     68/push 0/imm32/output-is-write-only
6777     68/push 0/imm32/no-disp32
6778     68/push 0/imm32/no-imm32
6779     68/push 0/imm32/no-r32
6780     68/push 3/imm32/rm32-in-first-output
6781     68/push "ff 0/subop/increment"/imm32/subx-name
6782     53/push-ebx/outputs/formal-outputs
6783     68/push 0/imm32/inouts
6784     68/push "increment"/imm32/name
6785     89/<- %ebx 4/r32/esp
6786     # var primitives/ebx: primitive
6787     53/push-ebx/next
6788     68/push 0/imm32/output-is-write-only
6789     68/push 0/imm32/no-disp32
6790     68/push 0/imm32/no-imm32
6791     68/push 0/imm32/no-r32
6792     68/push 1/imm32/rm32-is-first-inout
6793     68/push "ff 0/subop/increment"/imm32/subx-name
6794     68/push 0/imm32/outputs
6795     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
6796     68/push "increment"/imm32/name
6797     89/<- %ebx 4/r32/esp
6798     # convert
6799     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
6800     (flush _test-output-buffered-file)
6801 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
6807     # check output
6808     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive")
6809     # . epilogue
6810     89/<- %esp 5/r32/ebp
6811     5d/pop-to-ebp
6812     c3/return
6813 
6814 test-emit-subx-statement-select-primitive-2:
6815     # Select the right primitive between overloads.
6816     #   foo <- increment
6817     # =>
6818     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
6819     #
6820     # There's a variable on the var stack as follows:
6821     #   name: 'foo'
6822     #   type: int
6823     #   register: 'eax'
6824     #
6825     # There's two primitives, as follows:
6826     #   - name: 'increment'
6827     #     out: int/reg
6828     #     value: 'ff 0/subop/increment'
6829     #   - name: 'increment'
6830     #     inout: int/mem
6831     #     value: 'ff 0/subop/increment'
6832     #
6833     # There's nothing in functions.
6834     #
6835     # . prologue
6836     55/push-ebp
6837     89/<- %ebp 4/r32/esp
6838     # setup
6839     (clear-stream _test-output-stream)
6840     (clear-stream $_test-output-buffered-file->buffer)
6841     # var type/ecx: (handle tree type-id) = int
6842     68/push 0/imm32/right/null
6843     68/push 1/imm32/left/int
6844     89/<- %ecx 4/r32/esp
6845     # var var-foo/ecx: var in eax
6846     68/push "eax"/imm32/register
6847     68/push 0/imm32/no-stack-offset
6848     68/push 1/imm32/block-depth
6849     51/push-ecx
6850     68/push "foo"/imm32
6851     89/<- %ecx 4/r32/esp
6852     # var inouts/edi: (list var)
6853     68/push 0/imm32/next
6854     51/push-ecx/var-foo
6855     89/<- %edi 4/r32/esp
6856     # var stmt/esi: statement
6857     68/push 0/imm32/next
6858     68/push 0/imm32/outputs
6859     57/push-edi/inouts
6860     68/push "increment"/imm32/operation
6861     68/push 1/imm32
6862     89/<- %esi 4/r32/esp
6863     # var formal-var/ebx: var in any register
6864     68/push Any-register/imm32
6865     68/push 0/imm32/no-stack-offset
6866     68/push 1/imm32/block-depth
6867     ff 6/subop/push *(ecx+4)  # Var-type
6868     68/push "dummy"/imm32
6869     89/<- %ebx 4/r32/esp
6870     # var operand/ebx: (list var)
6871     68/push 0/imm32/next
6872     53/push-ebx/formal-var
6873     89/<- %ebx 4/r32/esp
6874     # var primitive1/ebx: primitive
6875     68/push 0/imm32/next
6876     68/push 0/imm32/output-is-write-only
6877     68/push 0/imm32/no-disp32
6878     68/push 0/imm32/no-imm32
6879     68/push 0/imm32/no-r32
6880     68/push 3/imm32/rm32-in-first-output
6881     68/push "ff 0/subop/increment"/imm32/subx-name
6882     53/push-ebx/outputs/formal-outputs
6883     68/push 0/imm32/inouts
6884     68/push "increment"/imm32/name
6885     89/<- %ebx 4/r32/esp
6886     # var primitives/ebx: primitive
6887     53/push-ebx/next
6888     68/push 0/imm32/output-is-write-only
6889     68/push 0/imm32/no-disp32
6890     68/push 0/imm32/no-imm32
6891     68/push 0/imm32/no-r32
6892     68/push 1/imm32/rm32-is-first-inout
6893     68/push "ff 0/subop/increment"/imm32/subx-name
6894     68/push 0/imm32/outputs
6895     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
6896     68/push "increment"/imm32/name
6897     89/<- %ebx 4/r32/esp
6898     # convert
6899     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
6900     (flush _test-output-buffered-file)
6901 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
6907     # check output
6908     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2")
6909     # . epilogue
6910     89/<- %esp 5/r32/ebp
6911     5d/pop-to-ebp
6912     c3/return
6913 
6914 test-increment-register:
6915     # Select the right primitive between overloads.
6916     #   foo <- increment
6917     # =>
6918     #   50/increment-eax
6919     #
6920     # There's a variable on the var stack as follows:
6921     #   name: 'foo'
6922     #   type: int
6923     #   register: 'eax'
6924     #
6925     # Primitives are the global definitions.
6926     #
6927     # There are no functions defined.
6928     #
6929     # . prologue
6930     55/push-ebp
6931     89/<- %ebp 4/r32/esp
6932     # setup
6933     (clear-stream _test-output-stream)
6934     (clear-stream $_test-output-buffered-file->buffer)
6935     # var type/ecx: (handle tree type-id) = int
6936     68/push 0/imm32/right/null
6937     68/push 1/imm32/left/int
6938     89/<- %ecx 4/r32/esp
6939     # var var-foo/ecx: var in eax
6940     68/push "eax"/imm32/register
6941     68/push 0/imm32/no-stack-offset
6942     68/push 1/imm32/block-depth
6943     51/push-ecx
6944     68/push "foo"/imm32
6945     89/<- %ecx 4/r32/esp
6946     # var real-outputs/edi: (list var)
6947     68/push 0/imm32/next
6948     51/push-ecx/var-foo
6949     89/<- %edi 4/r32/esp
6950     # var stmt/esi: statement
6951     68/push 0/imm32/next
6952     57/push-edi/outputs
6953     68/push 0/imm32/inouts
6954     68/push "increment"/imm32/operation
6955     68/push 1/imm32/regular-statement
6956     89/<- %esi 4/r32/esp
6957     # convert
6958     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
6959     (flush _test-output-buffered-file)
6960 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
6966     # check output
6967     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
6968     # . epilogue
6969     89/<- %esp 5/r32/ebp
6970     5d/pop-to-ebp
6971     c3/return
6972 
6973 test-increment-var:
6974     # Select the right primitive between overloads.
6975     #   foo <- increment
6976     # =>
6977     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
6978     #
6979     # There's a variable on the var stack as follows:
6980     #   name: 'foo'
6981     #   type: int
6982     #   register: 'eax'
6983     #
6984     # Primitives are the global definitions.
6985     #
6986     # There are no functions defined.
6987     #
6988     # . prologue
6989     55/push-ebp
6990     89/<- %ebp 4/r32/esp
6991     # setup
6992     (clear-stream _test-output-stream)
6993     (clear-stream $_test-output-buffered-file->buffer)
6994     # var type/ecx: (handle tree type-id) = int
6995     68/push 0/imm32/right/null
6996     68/push 1/imm32/left/int
6997     89/<- %ecx 4/r32/esp
6998     # var var-foo/ecx: var in eax
6999     68/push "eax"/imm32/register
7000     68/push 0/imm32/no-stack-offset
7001     68/push 1/imm32/block-depth
7002     51/push-ecx
7003     68/push "foo"/imm32
7004     89/<- %ecx 4/r32/esp
7005     # var inouts/edi: (list var)
7006     68/push 0/imm32/next
7007     51/push-ecx/var-foo
7008     89/<- %edi 4/r32/esp
7009     # var stmt/esi: statement
7010     68/push 0/imm32/next
7011     68/push 0/imm32/outputs
7012     57/push-edi/inouts
7013     68/push "increment"/imm32/operation
7014     68/push 1/imm32
7015     89/<- %esi 4/r32/esp
7016     # convert
7017     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7018     (flush _test-output-buffered-file)
7019 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7025     # check output
7026     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-var")
7027     # . epilogue
7028     89/<- %esp 5/r32/ebp
7029     5d/pop-to-ebp
7030     c3/return
7031 
7032 test-add-reg-to-reg:
7033     #   var1/reg <- add var2/reg
7034     # =>
7035     #   01/add %var1 var2
7036     #
7037     # . prologue
7038     55/push-ebp
7039     89/<- %ebp 4/r32/esp
7040     # setup
7041     (clear-stream _test-output-stream)
7042     (clear-stream $_test-output-buffered-file->buffer)
7043     # var type/ecx: (handle tree type-id) = int
7044     68/push 0/imm32/right/null
7045     68/push 1/imm32/left/int
7046     89/<- %ecx 4/r32/esp
7047     # var var-var1/ecx: var in eax
7048     68/push "eax"/imm32/register
7049     68/push 0/imm32/no-stack-offset
7050     68/push 1/imm32/block-depth
7051     51/push-ecx
7052     68/push "var1"/imm32
7053     89/<- %ecx 4/r32/esp
7054     # var var-var2/edx: var in ecx
7055     68/push "ecx"/imm32/register
7056     68/push 0/imm32/no-stack-offset
7057     68/push 1/imm32/block-depth
7058     ff 6/subop/push *(ecx+4)  # Var-type
7059     68/push "var2"/imm32
7060     89/<- %edx 4/r32/esp
7061     # var inouts/esi: (list var2)
7062     68/push 0/imm32/next
7063     52/push-edx/var-var2
7064     89/<- %esi 4/r32/esp
7065     # var outputs/edi: (list var1)
7066     68/push 0/imm32/next
7067     51/push-ecx/var-var1
7068     89/<- %edi 4/r32/esp
7069     # var stmt/esi: statement
7070     68/push 0/imm32/next
7071     57/push-edi/outputs
7072     56/push-esi/inouts
7073     68/push "add"/imm32/operation
7074     68/push 1/imm32
7075     89/<- %esi 4/r32/esp
7076     # convert
7077     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7078     (flush _test-output-buffered-file)
7079 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7085     # check output
7086     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
7087     # . epilogue
7088     89/<- %esp 5/r32/ebp
7089     5d/pop-to-ebp
7090     c3/return
7091 
7092 test-add-reg-to-mem:
7093     #   add-to var1 var2/reg
7094     # =>
7095     #   01/add *(ebp+__) var2
7096     #
7097     # . prologue
7098     55/push-ebp
7099     89/<- %ebp 4/r32/esp
7100     # setup
7101     (clear-stream _test-output-stream)
7102     (clear-stream $_test-output-buffered-file->buffer)
7103     # var type/ecx: (handle tree type-id) = int
7104     68/push 0/imm32/right/null
7105     68/push 1/imm32/left/int
7106     89/<- %ecx 4/r32/esp
7107     # var var-var1/ecx: var
7108     68/push 0/imm32/no-register
7109     68/push 8/imm32/stack-offset
7110     68/push 1/imm32/block-depth
7111     51/push-ecx
7112     68/push "var1"/imm32
7113     89/<- %ecx 4/r32/esp
7114     # var var-var2/edx: var in ecx
7115     68/push "ecx"/imm32/register
7116     68/push 0/imm32/no-stack-offset
7117     68/push 1/imm32/block-depth
7118     ff 6/subop/push *(ecx+4)  # Var-type
7119     68/push "var2"/imm32
7120     89/<- %edx 4/r32/esp
7121     # var inouts/esi: (list var2)
7122     68/push 0/imm32/next
7123     52/push-edx/var-var2
7124     89/<- %esi 4/r32/esp
7125     # var inouts = (list var1 var2)
7126     56/push-esi/next
7127     51/push-ecx/var-var1
7128     89/<- %esi 4/r32/esp
7129     # var stmt/esi: statement
7130     68/push 0/imm32/next
7131     68/push 0/imm32/outputs
7132     56/push-esi/inouts
7133     68/push "add-to"/imm32/operation
7134     68/push 1/imm32
7135     89/<- %esi 4/r32/esp
7136     # convert
7137     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7138     (flush _test-output-buffered-file)
7139 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7145     # check output
7146     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
7147     # . epilogue
7148     89/<- %esp 5/r32/ebp
7149     5d/pop-to-ebp
7150     c3/return
7151 
7152 test-add-mem-to-reg:
7153     #   var1/reg <- add var2
7154     # =>
7155     #   03/add *(ebp+__) var1
7156     #
7157     # . prologue
7158     55/push-ebp
7159     89/<- %ebp 4/r32/esp
7160     # setup
7161     (clear-stream _test-output-stream)
7162     (clear-stream $_test-output-buffered-file->buffer)
7163     # var type/ecx: (handle tree type-id) = int
7164     68/push 0/imm32/right/null
7165     68/push 1/imm32/left/int
7166     89/<- %ecx 4/r32/esp
7167     # var var-var1/ecx: var in eax
7168     68/push "eax"/imm32/register
7169     68/push 0/imm32/no-stack-offset
7170     68/push 1/imm32/block-depth
7171     51/push-ecx
7172     68/push "var1"/imm32
7173     89/<- %ecx 4/r32/esp
7174     # var var-var2/edx: var
7175     68/push 0/imm32/no-register
7176     68/push 8/imm32/stack-offset
7177     68/push 1/imm32/block-depth
7178     ff 6/subop/push *(ecx+4)  # Var-type
7179     68/push "var2"/imm32
7180     89/<- %edx 4/r32/esp
7181     # var inouts/esi: (list var2)
7182     68/push 0/imm32/next
7183     52/push-edx/var-var2
7184     89/<- %esi 4/r32/esp
7185     # var outputs/edi: (list var1)
7186     68/push 0/imm32/next
7187     51/push-ecx/var-var1
7188     89/<- %edi 4/r32/esp
7189     # var stmt/esi: statement
7190     68/push 0/imm32/next
7191     57/push-edi/outputs
7192     56/push-esi/inouts
7193     68/push "add"/imm32/operation
7194     68/push 1/imm32
7195     89/<- %esi 4/r32/esp
7196     # convert
7197     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7198     (flush _test-output-buffered-file)
7199 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7205     # check output
7206     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
7207     # . epilogue
7208     89/<- %esp 5/r32/ebp
7209     5d/pop-to-ebp
7210     c3/return
7211 
7212 test-add-literal-to-eax:
7213     #   var1/eax <- add 0x34
7214     # =>
7215     #   05/add-to-eax 0x34/imm32
7216     #
7217     # . prologue
7218     55/push-ebp
7219     89/<- %ebp 4/r32/esp
7220     # setup
7221     (clear-stream _test-output-stream)
7222     (clear-stream $_test-output-buffered-file->buffer)
7223     # var type/ecx: (handle tree type-id) = int
7224     68/push 0/imm32/right/null
7225     68/push 1/imm32/left/int
7226     89/<- %ecx 4/r32/esp
7227     # var var-var1/ecx: var in eax
7228     68/push "eax"/imm32/register
7229     68/push 0/imm32/no-stack-offset
7230     68/push 1/imm32/block-depth
7231     51/push-ecx
7232     68/push "var1"/imm32
7233     89/<- %ecx 4/r32/esp
7234     # var type/edx: (handle tree type-id) = literal
7235     68/push 0/imm32/right/null
7236     68/push 0/imm32/left/literal
7237     89/<- %edx 4/r32/esp
7238     # var var-var2/edx: var literal
7239     68/push 0/imm32/no-register
7240     68/push 0/imm32/no-stack-offset
7241     68/push 1/imm32/block-depth
7242     52/push-edx
7243     68/push "0x34"/imm32
7244     89/<- %edx 4/r32/esp
7245     # var inouts/esi: (list var2)
7246     68/push 0/imm32/next
7247     52/push-edx/var-var2
7248     89/<- %esi 4/r32/esp
7249     # var outputs/edi: (list var1)
7250     68/push 0/imm32/next
7251     51/push-ecx/var-var1
7252     89/<- %edi 4/r32/esp
7253     # var stmt/esi: statement
7254     68/push 0/imm32/next
7255     57/push-edi/outputs
7256     56/push-esi/inouts
7257     68/push "add"/imm32/operation
7258     68/push 1/imm32
7259     89/<- %esi 4/r32/esp
7260     # convert
7261     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7262     (flush _test-output-buffered-file)
7263 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7269     # check output
7270     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
7271     # . epilogue
7272     89/<- %esp 5/r32/ebp
7273     5d/pop-to-ebp
7274     c3/return
7275 
7276 test-add-literal-to-reg:
7277     #   var1/ecx <- add 0x34
7278     # =>
7279     #   81 0/subop/add %ecx 0x34/imm32
7280     #
7281     # . prologue
7282     55/push-ebp
7283     89/<- %ebp 4/r32/esp
7284     # setup
7285     (clear-stream _test-output-stream)
7286     (clear-stream $_test-output-buffered-file->buffer)
7287     # var type/ecx: (handle tree type-id) = int
7288     68/push 0/imm32/right/null
7289     68/push 1/imm32/left/int
7290     89/<- %ecx 4/r32/esp
7291     # var var-var1/ecx: var in ecx
7292     68/push "ecx"/imm32/register
7293     68/push 0/imm32/no-stack-offset
7294     68/push 1/imm32/block-depth
7295     51/push-ecx
7296     68/push "var1"/imm32
7297     89/<- %ecx 4/r32/esp
7298     # var type/edx: (handle tree type-id) = literal
7299     68/push 0/imm32/right/null
7300     68/push 0/imm32/left/literal
7301     89/<- %edx 4/r32/esp
7302     # var var-var2/edx: var literal
7303     68/push 0/imm32/no-register
7304     68/push 0/imm32/no-stack-offset
7305     68/push 1/imm32/block-depth
7306     52/push-edx
7307     68/push "0x34"/imm32
7308     89/<- %edx 4/r32/esp
7309     # var inouts/esi: (list var2)
7310     68/push 0/imm32/next
7311     52/push-edx/var-var2
7312     89/<- %esi 4/r32/esp
7313     # var outputs/edi: (list var1)
7314     68/push 0/imm32/next
7315     51/push-ecx/var-var1
7316     89/<- %edi 4/r32/esp
7317     # var stmt/esi: statement
7318     68/push 0/imm32/next
7319     57/push-edi/outputs
7320     56/push-esi/inouts
7321     68/push "add"/imm32/operation
7322     68/push 1/imm32
7323     89/<- %esi 4/r32/esp
7324     # convert
7325     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7326     (flush _test-output-buffered-file)
7327 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7333     # check output
7334     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
7335     # . epilogue
7336     89/<- %esp 5/r32/ebp
7337     5d/pop-to-ebp
7338     c3/return
7339 
7340 test-add-literal-to-mem:
7341     #   add-to var1, 0x34
7342     # =>
7343     #   81 0/subop/add %eax 0x34/imm32
7344     #
7345     # . prologue
7346     55/push-ebp
7347     89/<- %ebp 4/r32/esp
7348     # setup
7349     (clear-stream _test-output-stream)
7350     (clear-stream $_test-output-buffered-file->buffer)
7351     # var type/ecx: (handle tree type-id) = int
7352     68/push 0/imm32/right/null
7353     68/push 1/imm32/left/int
7354     89/<- %ecx 4/r32/esp
7355     # var var-var1/ecx: var
7356     68/push 0/imm32/no-register
7357     68/push 8/imm32/stack-offset
7358     68/push 1/imm32/block-depth
7359     51/push-ecx
7360     68/push "var1"/imm32
7361     89/<- %ecx 4/r32/esp
7362     # var type/edx: (handle tree type-id) = literal
7363     68/push 0/imm32/right/null
7364     68/push 0/imm32/left/literal
7365     89/<- %edx 4/r32/esp
7366     # var var-var2/edx: var literal
7367     68/push 0/imm32/no-register
7368     68/push 0/imm32/no-stack-offset
7369     68/push 1/imm32/block-depth
7370     52/push-edx
7371     68/push "0x34"/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 inouts = (list var1 inouts)
7378     56/push-esi/next
7379     51/push-ecx/var-var1
7380     89/<- %esi 4/r32/esp
7381     # var stmt/esi: statement
7382     68/push 0/imm32/next
7383     68/push 0/imm32/outputs
7384     56/push-esi/inouts
7385     68/push "add-to"/imm32/operation
7386     68/push 1/imm32
7387     89/<- %esi 4/r32/esp
7388     # convert
7389     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7390     (flush _test-output-buffered-file)
7391 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7397     # check output
7398     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
7399     # . epilogue
7400     89/<- %esp 5/r32/ebp
7401     5d/pop-to-ebp
7402     c3/return
7403 
7404 test-compare-mem-with-reg:
7405     #   compare var1, var2/eax
7406     # =>
7407     #   39/compare *(ebp+___) 0/r32/eax
7408     #
7409     # . prologue
7410     55/push-ebp
7411     89/<- %ebp 4/r32/esp
7412     # setup
7413     (clear-stream _test-output-stream)
7414     (clear-stream $_test-output-buffered-file->buffer)
7415     # var type/ecx: (handle tree type-id) = int
7416     68/push 0/imm32/right/null
7417     68/push 1/imm32/left/int
7418     89/<- %ecx 4/r32/esp
7419     # var var-var2/ecx: var in eax
7420     68/push "eax"/imm32/register
7421     68/push 0/imm32/no-stack-offset
7422     68/push 1/imm32/block-depth
7423     51/push-ecx
7424     68/push "var2"/imm32
7425     89/<- %ecx 4/r32/esp
7426     # var var-var1/edx: var
7427     68/push 0/imm32/no-register
7428     68/push 8/imm32/stack-offset
7429     68/push 1/imm32/block-depth
7430     ff 6/subop/push *(ecx+4)  # Var-type
7431     68/push "var1"/imm32
7432     89/<- %edx 4/r32/esp
7433     # var inouts/esi: (list var1 var2)
7434     68/push 0/imm32/next
7435     51/push-ecx/var-var2
7436     89/<- %esi 4/r32/esp
7437     56/push-esi
7438     52/push-edx/var-var1
7439     89/<- %esi 4/r32/esp
7440     # var stmt/esi: statement
7441     68/push 0/imm32/next
7442     68/push 0/imm32/outputs
7443     56/push-esi/inouts
7444     68/push "compare"/imm32/operation
7445     68/push 1/imm32
7446     89/<- %esi 4/r32/esp
7447     # convert
7448     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7449     (flush _test-output-buffered-file)
7450 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7456     # check output
7457     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
7458     # . epilogue
7459     89/<- %esp 5/r32/ebp
7460     5d/pop-to-ebp
7461     c3/return
7462 
7463 test-compare-reg-with-mem:
7464     #   compare var1/eax, var2
7465     # =>
7466     #   3b/compare *(ebp+___) 0/r32/eax
7467     #
7468     # . prologue
7469     55/push-ebp
7470     89/<- %ebp 4/r32/esp
7471     # setup
7472     (clear-stream _test-output-stream)
7473     (clear-stream $_test-output-buffered-file->buffer)
7474     # var type/ecx: (handle tree type-id) = int
7475     68/push 0/imm32/right/null
7476     68/push 1/imm32/left/int
7477     89/<- %ecx 4/r32/esp
7478     # var var-var1/ecx: var in eax
7479     68/push "eax"/imm32/register
7480     68/push 0/imm32/no-stack-offset
7481     68/push 1/imm32/block-depth
7482     51/push-ecx
7483     68/push "var1"/imm32
7484     89/<- %ecx 4/r32/esp
7485     # var var-var2/edx: var
7486     68/push 0/imm32/no-register
7487     68/push 8/imm32/stack-offset
7488     68/push 1/imm32/block-depth
7489     ff 6/subop/push *(ecx+4)  # Var-type
7490     68/push "var2"/imm32
7491     89/<- %edx 4/r32/esp
7492     # var inouts/esi: (list var1 var2)
7493     68/push 0/imm32/next
7494     52/push-edx/var-var2
7495     89/<- %esi 4/r32/esp
7496     56/push-esi
7497     51/push-ecx/var-var1
7498     89/<- %esi 4/r32/esp
7499     # var stmt/esi: statement
7500     68/push 0/imm32/next
7501     68/push 0/imm32/outputs
7502     56/push-esi/inouts
7503     68/push "compare"/imm32/operation
7504     68/push 1/imm32
7505     89/<- %esi 4/r32/esp
7506     # convert
7507     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7508     (flush _test-output-buffered-file)
7509 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7515     # check output
7516     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
7517     # . epilogue
7518     89/<- %esp 5/r32/ebp
7519     5d/pop-to-ebp
7520     c3/return
7521 
7522 test-compare-mem-with-literal:
7523     #   compare var1, 0x34
7524     # =>
7525     #   81 7/subop/compare *(ebp+___) 0x34/imm32
7526     #
7527     # . prologue
7528     55/push-ebp
7529     89/<- %ebp 4/r32/esp
7530     # setup
7531     (clear-stream _test-output-stream)
7532     (clear-stream $_test-output-buffered-file->buffer)
7533     # var type/ecx: (handle tree type-id) = int
7534     68/push 0/imm32/right/null
7535     68/push 1/imm32/left/int
7536     89/<- %ecx 4/r32/esp
7537     # var var-var1/ecx: var
7538     68/push 0/imm32/no-register
7539     68/push 8/imm32/stack-offset
7540     68/push 1/imm32/block-depth
7541     51/push-ecx
7542     68/push "var1"/imm32
7543     89/<- %ecx 4/r32/esp
7544     # var type/edx: (handle tree type-id) = literal
7545     68/push 0/imm32/right/null
7546     68/push 0/imm32/left/literal
7547     89/<- %edx 4/r32/esp
7548     # var var-var2/edx: var literal
7549     68/push 0/imm32/no-register
7550     68/push 0/imm32/no-stack-offset
7551     68/push 1/imm32/block-depth
7552     52/push-edx
7553     68/push "0x34"/imm32
7554     89/<- %edx 4/r32/esp
7555     # var inouts/esi: (list var2)
7556     68/push 0/imm32/next
7557     52/push-edx/var-var2
7558     89/<- %esi 4/r32/esp
7559     # var inouts = (list var1 inouts)
7560     56/push-esi/next
7561     51/push-ecx/var-var1
7562     89/<- %esi 4/r32/esp
7563     # var stmt/esi: statement
7564     68/push 0/imm32/next
7565     68/push 0/imm32/outputs
7566     56/push-esi/inouts
7567     68/push "compare"/imm32/operation
7568     68/push 1/imm32
7569     89/<- %esi 4/r32/esp
7570     # convert
7571     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7572     (flush _test-output-buffered-file)
7573 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7579     # check output
7580     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
7581     # . epilogue
7582     89/<- %esp 5/r32/ebp
7583     5d/pop-to-ebp
7584     c3/return
7585 
7586 test-compare-eax-with-literal:
7587     #   compare var1/eax 0x34
7588     # =>
7589     #   3d/compare-eax-with 0x34/imm32
7590     #
7591     # . prologue
7592     55/push-ebp
7593     89/<- %ebp 4/r32/esp
7594     # setup
7595     (clear-stream _test-output-stream)
7596     (clear-stream $_test-output-buffered-file->buffer)
7597     # var type/ecx: (handle tree type-id) = int
7598     68/push 0/imm32/right/null
7599     68/push 1/imm32/left/int
7600     89/<- %ecx 4/r32/esp
7601     # var var-var1/ecx: var in eax
7602     68/push "eax"/imm32/register
7603     68/push 0/imm32/no-stack-offset
7604     68/push 1/imm32/block-depth
7605     51/push-ecx
7606     68/push "var1"/imm32
7607     89/<- %ecx 4/r32/esp
7608     # var type/edx: (handle tree type-id) = literal
7609     68/push 0/imm32/right/null
7610     68/push 0/imm32/left/literal
7611     89/<- %edx 4/r32/esp
7612     # var var-var2/edx: var literal
7613     68/push 0/imm32/no-register
7614     68/push 0/imm32/no-stack-offset
7615     68/push 1/imm32/block-depth
7616     52/push-edx
7617     68/push "0x34"/imm32
7618     89/<- %edx 4/r32/esp
7619     # var inouts/esi: (list var2)
7620     68/push 0/imm32/next
7621     52/push-edx/var-var2
7622     89/<- %esi 4/r32/esp
7623     # var inouts = (list var1 inouts)
7624     56/push-esi/next
7625     51/push-ecx/var-var1
7626     89/<- %esi 4/r32/esp
7627     # var stmt/esi: statement
7628     68/push 0/imm32/next
7629     68/push 0/imm32/outputs
7630     56/push-esi/inouts
7631     68/push "compare"/imm32/operation
7632     68/push 1/imm32/regular-stmt
7633     89/<- %esi 4/r32/esp
7634     # convert
7635     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7636     (flush _test-output-buffered-file)
7637 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7643     # check output
7644     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
7645     # . epilogue
7646     89/<- %esp 5/r32/ebp
7647     5d/pop-to-ebp
7648     c3/return
7649 
7650 test-compare-reg-with-literal:
7651     #   compare var1/ecx 0x34
7652     # =>
7653     #   81 7/subop/compare %ecx 0x34/imm32
7654     #
7655     # . prologue
7656     55/push-ebp
7657     89/<- %ebp 4/r32/esp
7658     # setup
7659     (clear-stream _test-output-stream)
7660     (clear-stream $_test-output-buffered-file->buffer)
7661     # var type/ecx: (handle tree type-id) = int
7662     68/push 0/imm32/right/null
7663     68/push 1/imm32/left/int
7664     89/<- %ecx 4/r32/esp
7665     # var var-var1/ecx: var in ecx
7666     68/push "ecx"/imm32/register
7667     68/push 0/imm32/no-stack-offset
7668     68/push 1/imm32/block-depth
7669     51/push-ecx
7670     68/push "var1"/imm32
7671     89/<- %ecx 4/r32/esp
7672     # var type/edx: (handle tree type-id) = literal
7673     68/push 0/imm32/right/null
7674     68/push 0/imm32/left/literal
7675     89/<- %edx 4/r32/esp
7676     # var var-var2/edx: var literal
7677     68/push 0/imm32/no-register
7678     68/push 0/imm32/no-stack-offset
7679     68/push 1/imm32/block-depth
7680     52/push-edx
7681     68/push "0x34"/imm32
7682     89/<- %edx 4/r32/esp
7683     # var inouts/esi: (list var2)
7684     68/push 0/imm32/next
7685     52/push-edx/var-var2
7686     89/<- %esi 4/r32/esp
7687     # var inouts = (list var1 inouts)
7688     56/push-esi/next
7689     51/push-ecx/var-var1
7690     89/<- %esi 4/r32/esp
7691     # var stmt/esi: statement
7692     68/push 0/imm32/next
7693     68/push 0/imm32/outputs
7694     56/push-esi/inouts
7695     68/push "compare"/imm32/operation
7696     68/push 1/imm32/regular-stmt
7697     89/<- %esi 4/r32/esp
7698     # convert
7699     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
7700     (flush _test-output-buffered-file)
7701 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7707     # check output
7708     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
7709     # . epilogue
7710     89/<- %esp 5/r32/ebp
7711     5d/pop-to-ebp
7712     c3/return
7713 
7714 test-emit-subx-statement-function-call:
7715     # Call a function on a variable on the stack.
7716     #   f foo
7717     # =>
7718     #   (f2 *(ebp-8))
7719     # (Changing the function name supports overloading in general, but here it
7720     # just serves to help disambiguate things.)
7721     #
7722     # There's a variable on the var stack as follows:
7723     #   name: 'foo'
7724     #   type: int
7725     #   stack-offset: -8
7726     #
7727     # There's nothing in primitives.
7728     #
7729     # There's a function with this info:
7730     #   name: 'f'
7731     #   inout: int/mem
7732     #   value: 'f2'
7733     #
7734     # . prologue
7735     55/push-ebp
7736     89/<- %ebp 4/r32/esp
7737     # setup
7738     (clear-stream _test-output-stream)
7739     (clear-stream $_test-output-buffered-file->buffer)
7740     # var type/ecx: (handle tree type-id) = int
7741     68/push 0/imm32/right/null
7742     68/push 1/imm32/left/int
7743     89/<- %ecx 4/r32/esp
7744     # var var-foo/ecx: var
7745     68/push 0/imm32/no-register
7746     68/push -8/imm32/stack-offset
7747     68/push 0/imm32/block-depth
7748     51/push-ecx
7749     68/push "foo"/imm32
7750     89/<- %ecx 4/r32/esp
7751     # var operands/esi: (list var)
7752     68/push 0/imm32/next
7753     51/push-ecx/var-foo
7754     89/<- %esi 4/r32/esp
7755     # var stmt/esi: statement
7756     68/push 0/imm32/next
7757     68/push 0/imm32/outputs
7758     56/push-esi/inouts
7759     68/push "f"/imm32/operation
7760     68/push 1/imm32
7761     89/<- %esi 4/r32/esp
7762     # var functions/ebx: function
7763     68/push 0/imm32/next
7764     68/push 0/imm32/body
7765     68/push 0/imm32/outputs
7766     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
7767     68/push "f2"/imm32/subx-name
7768     68/push "f"/imm32/name
7769     89/<- %ebx 4/r32/esp
7770     # convert
7771     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
7772     (flush _test-output-buffered-file)
7773 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7779     # check output
7780     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call")
7781     # . epilogue
7782     89/<- %esp 5/r32/ebp
7783     5d/pop-to-ebp
7784     c3/return
7785 
7786 test-emit-subx-statement-function-call-with-literal-arg:
7787     # Call a function on a literal.
7788     #   f 34
7789     # =>
7790     #   (f2 34)
7791     #
7792     # . prologue
7793     55/push-ebp
7794     89/<- %ebp 4/r32/esp
7795     # setup
7796     (clear-stream _test-output-stream)
7797     (clear-stream $_test-output-buffered-file->buffer)
7798     # var type/ecx: (handle tree type-id) = literal
7799     68/push 0/imm32/right/null
7800     68/push 0/imm32/left/literal
7801     89/<- %ecx 4/r32/esp
7802     # var var-foo/ecx: var literal
7803     68/push 0/imm32/no-register
7804     68/push 0/imm32/no-stack-offset
7805     68/push 0/imm32/block-depth
7806     51/push-ecx
7807     68/push "34"/imm32
7808     89/<- %ecx 4/r32/esp
7809     # var operands/esi: (list var)
7810     68/push 0/imm32/next
7811     51/push-ecx/var-foo
7812     89/<- %esi 4/r32/esp
7813     # var stmt/esi: statement
7814     68/push 0/imm32/next
7815     68/push 0/imm32/outputs
7816     56/push-esi/inouts
7817     68/push "f"/imm32/operation
7818     68/push 1/imm32
7819     89/<- %esi 4/r32/esp
7820     # var functions/ebx: function
7821     68/push 0/imm32/next
7822     68/push 0/imm32/body
7823     68/push 0/imm32/outputs
7824     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
7825     68/push "f2"/imm32/subx-name
7826     68/push "f"/imm32/name
7827     89/<- %ebx 4/r32/esp
7828     # convert
7829     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
7830     (flush _test-output-buffered-file)
7831 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7837     # check output
7838     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg")
7839     # . epilogue
7840     89/<- %esp 5/r32/ebp
7841     5d/pop-to-ebp
7842     c3/return
7843 
7844 emit-subx-prologue:  # out: (addr buffered-file)
7845     # . prologue
7846     55/push-ebp
7847     89/<- %ebp 4/r32/esp
7848     #
7849     (write-buffered *(ebp+8) "# . prologue\n")
7850     (write-buffered *(ebp+8) "55/push-ebp\n")
7851     (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n")
7852 $emit-subx-prologue:end:
7853     # . epilogue
7854     89/<- %esp 5/r32/ebp
7855     5d/pop-to-ebp
7856     c3/return
7857 
7858 emit-subx-epilogue:  # out: (addr buffered-file)
7859     # . prologue
7860     55/push-ebp
7861     89/<- %ebp 4/r32/esp
7862     #
7863     (write-buffered *(ebp+8) "# . epilogue\n")
7864     (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n")
7865     (write-buffered *(ebp+8) "5d/pop-to-ebp\n")
7866     (write-buffered *(ebp+8) "c3/return\n")
7867 $emit-subx-epilogue:end:
7868     # . epilogue
7869     89/<- %esp 5/r32/ebp
7870     5d/pop-to-ebp
7871     c3/return