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*.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)
 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 #     output-is-write-only: boolean
 225 #   arg-location: enum
 226 #     0 means none
 227 #     1 means first inout
 228 #     2 means second inout
 229 #     3 means first output
 230 
 231 # == Translating a block
 232 # Emit block name if necessary
 233 # Emit '{'
 234 # When you encounter a statement, emit it as above
 235 # When you encounter a variable declaration
 236 #   emit any code needed for it (bzeros)
 237 #   push it on the var stack
 238 #   update register dict if necessary
 239 # When you encounter '}'
 240 #   While popping variables off the var stack until block id changes
 241 #     Emit code needed to clean up the stack
 242 #       either increment esp
 243 #       or pop into appropriate register
 244 
 245 # The rest is straightforward.
 246 
 247 == data
 248 
 249 Program:  # (handle function)
 250   0/imm32
 251 
 252 Function-name:
 253   0/imm32
 254 Function-subx-name:
 255   4/imm32
 256 Function-inouts:  # (handle list var)
 257   8/imm32
 258 Function-outputs:  # (handle list var)
 259   0xc/imm32
 260 Function-body:  # (handle block)
 261   0x10/imm32
 262 Function-next:  # (handle function)
 263   0x14/imm32
 264 Function-size:
 265   0x18/imm32/24
 266 
 267 Primitive-name:
 268   0/imm32
 269 Primitive-inouts:  # (handle list var)
 270   4/imm32
 271 Primitive-outputs:  # (handle list var)
 272   8/imm32
 273 Primitive-subx-name:  # (handle array byte)
 274   0xc/imm32
 275 Primitive-subx-rm32:  # enum arg-location
 276   0x10/imm32
 277 Primitive-subx-r32:  # enum arg-location
 278   0x14/imm32
 279 Primitive-subx-imm32:  # enum arg-location
 280   0x18/imm32
 281 Primitive-write-only-output:  # boolean
 282   0x1c/imm32
 283 Primitive-next:  # (handle function)
 284   0x20/imm32
 285 Primitive-size:
 286   0x24/imm32/36
 287 
 288 Stmt-tag:
 289   0/imm32
 290 
 291 Block-statements:  # (handle list statement)
 292   4/imm32
 293 
 294 Stmt1-operation:  # (handle array byte)
 295   4/imm32
 296 Stmt1-inouts:  # (handle list var)
 297   8/imm32
 298 Stmt1-outputs:  # (handle list var)
 299   0xc/imm32
 300 
 301 Vardef-name:  # (handle array byte)
 302   4/imm32
 303 Vardef-type:  # (handle tree type-id)
 304   8/imm32
 305 
 306 Regvardef-name:  # (handle array byte)
 307   4/imm32
 308 Regvardef-type:  # (handle tree type-id)
 309   8/imm32
 310 Regvardef-register:  # (handle array byte)
 311   0xc/imm32
 312 Regvardef-operation:  # (handle array byte)
 313   0x10/imm32
 314 Regvardef-inputs:  # (handle list var)
 315   0x14/imm32
 316 
 317 Named-block-name:
 318   4/imm32
 319 Named-block-statements:  # (handle list statement)
 320   8/imm32
 321 
 322 Stmt-size:
 323   0x18/imm32
 324 
 325 Var-name:
 326   0/imm32
 327 Var-type:
 328   4/imm32
 329 Var-block:
 330   8/imm32
 331 Var-stack-offset:
 332   0xc/imm32
 333 Var-register:
 334   0x10/imm32
 335 Var-size:
 336   0x14/imm32
 337 
 338 Any-register:  # "*"
 339   # size
 340   1/imm32
 341   # data
 342   2a/asterisk
 343 
 344 List-value:
 345   0/imm32
 346 List-next:
 347   4/imm32
 348 List-size:
 349   8/imm32
 350 
 351 # Types are expressed as trees (s-expressions) of type-ids (ints).
 352 # However, there's no need for singletons, so we can assume (int) == int
 353 #   - if x->right == nil, x is an atom
 354 #   - x->left contains either a pointer to a pair, or an atomic type-id directly.
 355 #     type ids will be less than 0x10000.
 356 
 357 Tree-left:  # either type-id or (addr tree type-id)
 358   0/imm32
 359 Tree-right:  # (addr tree type-id)
 360   4/imm32
 361 Tree-size:
 362   8/imm32
 363 
 364 Max-type-id:
 365   0x10000/imm32
 366 
 367 == code
 368 
 369 Entry:
 370     # . prologue
 371     89/<- %ebp 4/r32/esp
 372     (new-segment *Heap-size Heap)
 373     # if (argv[1] == "test') run-tests()
 374     {
 375       # if (argc <= 1) break
 376       81 7/subop/compare *ebp 1/imm32
 377       7e/jump-if-lesser-or-equal break/disp8
 378       # if (argv[1] != "test") break
 379       (kernel-string-equal? *(ebp+8) "test")  # => eax
 380       3d/compare-eax-and 0/imm32
 381       74/jump-if-equal break/disp8
 382       #
 383       (run-tests)
 384       # syscall(exit, *Num-test-failures)
 385       8b/-> *Num-test-failures 3/r32/ebx
 386       eb/jump $mu-main:end/disp8
 387     }
 388     # otherwise convert Stdin
 389     (convert-mu Stdin Stdout)
 390     (flush Stdout)
 391     # syscall(exit, 0)
 392     bb/copy-to-ebx 0/imm32
 393 $mu-main:end:
 394     b8/copy-to-eax 1/imm32/exit
 395     cd/syscall 0x80/imm8
 396 
 397 convert-mu:  # in : (addr buffered-file), out : (addr buffered-file)
 398     # . prologue
 399     55/push-ebp
 400     89/<- %ebp 4/r32/esp
 401     #
 402     (parse-mu *(ebp+8))
 403     (check-mu-types)
 404     (emit-subx *(ebp+0xc))
 405 $convert-mu:end:
 406     # . epilogue
 407     89/<- %esp 5/r32/ebp
 408     5d/pop-to-ebp
 409     c3/return
 410 
 411 test-convert-empty-input:
 412     # empty input => empty output
 413     # . prologue
 414     55/push-ebp
 415     89/<- %ebp 4/r32/esp
 416     # setup
 417     (clear-stream _test-input-stream)
 418     (clear-stream $_test-input-buffered-file->buffer)
 419     (clear-stream _test-output-stream)
 420     (clear-stream $_test-output-buffered-file->buffer)
 421     #
 422     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 423     (flush _test-output-buffered-file)
 424     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 425     # . epilogue
 426     89/<- %esp 5/r32/ebp
 427     5d/pop-to-ebp
 428     c3/return
 429 
 430 test-convert-function-skeleton:
 431     # empty function decl => function prologue and epilogue
 432     #   fn foo {
 433     #   }
 434     # =>
 435     #   foo:
 436     #     # . prologue
 437     #     55/push-ebp
 438     #     89/<- %ebp 4/r32/esp
 439     #     # . epilogue
 440     #     89/<- %esp 5/r32/ebp
 441     #     5d/pop-to-ebp
 442     #     c3/return
 443     # . prologue
 444     55/push-ebp
 445     89/<- %ebp 4/r32/esp
 446     # setup
 447     (clear-stream _test-input-stream)
 448     (clear-stream $_test-input-buffered-file->buffer)
 449     (clear-stream _test-output-stream)
 450     (clear-stream $_test-output-buffered-file->buffer)
 451     #
 452     (write _test-input-stream "fn foo {\n")
 453     (write _test-input-stream "}\n")
 454     # convert
 455     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 456     (flush _test-output-buffered-file)
 457 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 463     # check output
 464     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-skeleton/0")
 465     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-skeleton/1")
 466     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-skeleton/2")
 467     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 468     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-skeleton/4")
 469     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 470     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 471     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-skeleton/7")
 472     # . epilogue
 473     89/<- %esp 5/r32/ebp
 474     5d/pop-to-ebp
 475     c3/return
 476 
 477 test-convert-multiple-function-skeletons:
 478     # multiple functions correctly organized into a linked list
 479     #   fn foo {
 480     #   }
 481     #   fn bar {
 482     #   }
 483     # =>
 484     #   foo:
 485     #     # . prologue
 486     #     55/push-ebp
 487     #     89/<- %ebp 4/r32/esp
 488     #     # . epilogue
 489     #     89/<- %esp 5/r32/ebp
 490     #     5d/pop-to-ebp
 491     #     c3/return
 492     #   bar:
 493     #     # . prologue
 494     #     55/push-ebp
 495     #     89/<- %ebp 4/r32/esp
 496     #     # . epilogue
 497     #     89/<- %esp 5/r32/ebp
 498     #     5d/pop-to-ebp
 499     #     c3/return
 500     # . prologue
 501     55/push-ebp
 502     89/<- %ebp 4/r32/esp
 503     # setup
 504     (clear-stream _test-input-stream)
 505     (clear-stream $_test-input-buffered-file->buffer)
 506     (clear-stream _test-output-stream)
 507     (clear-stream $_test-output-buffered-file->buffer)
 508     #
 509     (write _test-input-stream "fn foo {\n")
 510     (write _test-input-stream "}\n")
 511     (write _test-input-stream "fn bar {\n")
 512     (write _test-input-stream "}\n")
 513     # convert
 514     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 515     (flush _test-output-buffered-file)
 516 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 522     # check first function
 523     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-multiple-function-skeletons/0")
 524     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/1")
 525     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 526     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 527     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 528     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 529     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 530     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/7")
 531     # check second function
 532     (check-next-stream-line-equal _test-output-stream "bar:"                  "F - test-convert-multiple-function-skeletons/10")
 533     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/11")
 534     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 535     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 536     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 537     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 538     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 539     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/17")
 540     # . epilogue
 541     89/<- %esp 5/r32/ebp
 542     5d/pop-to-ebp
 543     c3/return
 544 
 545 test-convert-function-with-arg:
 546     # function with one arg
 547     #   fn foo n : int {
 548     #   }
 549     # =>
 550     #   foo:
 551     #     # . prologue
 552     #     55/push-ebp
 553     #     89/<- %ebp 4/r32/esp
 554     #     # . epilogue
 555     #     89/<- %esp 5/r32/ebp
 556     #     5d/pop-to-ebp
 557     #     c3/return
 558     # . prologue
 559     55/push-ebp
 560     89/<- %ebp 4/r32/esp
 561     # setup
 562     (clear-stream _test-input-stream)
 563     (clear-stream $_test-input-buffered-file->buffer)
 564     (clear-stream _test-output-stream)
 565     (clear-stream $_test-output-buffered-file->buffer)
 566     #
 567     (write _test-input-stream "fn foo n : int {\n")
 568     (write _test-input-stream "}\n")
 569     # convert
 570     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 571     (flush _test-output-buffered-file)
 572 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 578     # check output
 579     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-arg/0")
 580     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-arg/1")
 581     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-arg/2")
 582     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
 583     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-arg/4")
 584     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
 585     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
 586     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-arg/7")
 587     # . epilogue
 588     89/<- %esp 5/r32/ebp
 589     5d/pop-to-ebp
 590     c3/return
 591 
 592 test-convert-function-with-arg-and-body:
 593     # function with one arg and one instruction in the body
 594     #   fn foo n : int {
 595     #     increment n
 596     #   }
 597     # =>
 598     #   foo:
 599     #     # . prologue
 600     #     55/push-ebp
 601     #     89/<- %ebp 4/r32/esp
 602     #     {
 603     #       ff 0/subop/increment *(ebp+8)
 604     #     }
 605     #     # . epilogue
 606     #     89/<- %esp 5/r32/ebp
 607     #     5d/pop-to-ebp
 608     #     c3/return
 609     # . prologue
 610     55/push-ebp
 611     89/<- %ebp 4/r32/esp
 612     # setup
 613     (clear-stream _test-input-stream)
 614     (clear-stream $_test-input-buffered-file->buffer)
 615     (clear-stream _test-output-stream)
 616     (clear-stream $_test-output-buffered-file->buffer)
 617     #
 618     (write _test-input-stream "fn foo n : int {\n")
 619     (write _test-input-stream "  increment n\n")
 620     (write _test-input-stream "}\n")
 621     # convert
 622     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 623     (flush _test-output-buffered-file)
 624 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 630     # check output
 631     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-with-arg-and-body/0")
 632     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-with-arg-and-body/1")
 633     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
 634     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
 635     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-with-arg-and-body/4")
 636     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/5")
 637     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-with-arg-and-body/6")
 638     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-with-arg-and-body/7")
 639     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/8")
 640     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/9")
 641     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-with-arg-and-body/10")
 642     # . epilogue
 643     89/<- %esp 5/r32/ebp
 644     5d/pop-to-ebp
 645     c3/return
 646 
 647 test-convert-function-distinguishes-args:
 648     # function with two args refers to second one in body
 649     #   fn foo a: int, b: int {
 650     #     increment b
 651     #   }
 652     # =>
 653     #   foo:
 654     #     # . prologue
 655     #     55/push-ebp
 656     #     89/<- %ebp 4/r32/esp
 657     #     {
 658     #       ff 0/subop/increment *(ebp+0xc)
 659     #     }
 660     #     # . epilogue
 661     #     89/<- %esp 5/r32/ebp
 662     #     5d/pop-to-ebp
 663     #     c3/return
 664     # . prologue
 665     55/push-ebp
 666     89/<- %ebp 4/r32/esp
 667     # setup
 668     (clear-stream _test-input-stream)
 669     (clear-stream $_test-input-buffered-file->buffer)
 670     (clear-stream _test-output-stream)
 671     (clear-stream $_test-output-buffered-file->buffer)
 672     #
 673     (write _test-input-stream "fn foo a: int, b: int {\n")
 674     (write _test-input-stream "  increment b\n")
 675     (write _test-input-stream "}\n")
 676     # convert
 677     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 678     (flush _test-output-buffered-file)
 679 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 685     # check output
 686     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-distinguishes-args/0")
 687     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-distinguishes-args/1")
 688     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
 689     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
 690     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-distinguishes-args/4")
 691     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/5")
 692     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-distinguishes-args/6")
 693     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-distinguishes-args/7")
 694     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/8")
 695     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/9")
 696     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-distinguishes-args/10")
 697     # . epilogue
 698     89/<- %esp 5/r32/ebp
 699     5d/pop-to-ebp
 700     c3/return
 701 
 702 test-convert-function-returns-result:
 703     # function writes to output
 704     #   fn foo a: int, b: int -> result/eax: int {
 705     #     result <- copy a
 706     #     result <- increment
 707     #   }
 708     # =>
 709     #   foo:
 710     #     # . prologue
 711     #     55/push-ebp
 712     #     89/<- %ebp 4/r32/esp
 713     #     {
 714     #       89/-> *(ebp+8) 0/r32/eax
 715     #       40/increment-eax
 716     #     }
 717     #     # . epilogue
 718     #     89/<- %esp 5/r32/ebp
 719     #     5d/pop-to-ebp
 720     #     c3/return
 721     # . prologue
 722     55/push-ebp
 723     89/<- %ebp 4/r32/esp
 724     # setup
 725     (clear-stream _test-input-stream)
 726     (clear-stream $_test-input-buffered-file->buffer)
 727     (clear-stream _test-output-stream)
 728     (clear-stream $_test-output-buffered-file->buffer)
 729     #
 730     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 731     (write _test-input-stream "  result <- copy a\n")
 732     (write _test-input-stream "  result <- increment\n")
 733     (write _test-input-stream "}\n")
 734     # convert
 735     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 736     (flush _test-output-buffered-file)
 737 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 743     # check output
 744     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-returns-result/0")
 745     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-returns-result/1")
 746     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-returns-result/2")
 747     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
 748     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-returns-result/4")
 749     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/5")
 750     (check-next-stream-line-equal _test-output-stream "40/increment-eax"      "F - test-convert-function-returns-result/6")
 751     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-returns-result/7")
 752     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-returns-result/8")
 753     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/9")
 754     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-returns-result/10")
 755     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-returns-result/11")
 756     # . epilogue
 757     89/<- %esp 5/r32/ebp
 758     5d/pop-to-ebp
 759     c3/return
 760 
 761 test-convert-function-literal-arg:
 762     # function writes to output
 763     #   fn foo a: int, b: int -> result/eax: int {
 764     #     result <- copy a
 765     #     result <- add 1
 766     #   }
 767     # =>
 768     #   foo:
 769     #     # . prologue
 770     #     55/push-ebp
 771     #     89/<- %ebp 4/r32/esp
 772     #     {
 773     #       89/-> *(ebp+8) 0/r32/eax
 774     #       05/add-to-eax 1/imm32
 775     #     }
 776     #     # . epilogue
 777     #     89/<- %esp 5/r32/ebp
 778     #     5d/pop-to-ebp
 779     #     c3/return
 780     # . prologue
 781     55/push-ebp
 782     89/<- %ebp 4/r32/esp
 783     # setup
 784     (clear-stream _test-input-stream)
 785     (clear-stream $_test-input-buffered-file->buffer)
 786     (clear-stream _test-output-stream)
 787     (clear-stream $_test-output-buffered-file->buffer)
 788     #
 789     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 790     (write _test-input-stream "  result <- copy a\n")
 791     (write _test-input-stream "  result <- add 1\n")
 792     (write _test-input-stream "}\n")
 793     # convert
 794     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 795     (flush _test-output-buffered-file)
 796 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 802     # check output
 803     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-literal-arg/0")
 804     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-literal-arg/1")
 805     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-literal-arg/2")
 806     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg/3")
 807     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-literal-arg/4")
 808     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-literal-arg/5")
 809     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 1/imm32"  "F - test-convert-function-literal-arg/6")
 810     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-literal-arg/7")
 811     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-literal-arg/8")
 812     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg/9")
 813     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-literal-arg/10")
 814     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-literal-arg/11")
 815     # . epilogue
 816     89/<- %esp 5/r32/ebp
 817     5d/pop-to-ebp
 818     c3/return
 819 
 820 test-convert-function-literal-arg-2:
 821     # function writes to output
 822     #   fn foo a: int, b: int -> result/ebx: int {
 823     #     result <- copy a
 824     #     result <- add 1
 825     #   }
 826     # =>
 827     #   foo:
 828     #     # . prologue
 829     #     55/push-ebp
 830     #     89/<- %ebp 4/r32/esp
 831     #     {
 832     #       89/-> *(ebp+8) 3/r32/ebx
 833     #       81 0/subop/add %ebx 1/imm32
 834     #     }
 835     #     # . epilogue
 836     #     89/<- %esp 5/r32/ebp
 837     #     5d/pop-to-ebp
 838     #     c3/return
 839     # . prologue
 840     55/push-ebp
 841     89/<- %ebp 4/r32/esp
 842     # setup
 843     (clear-stream _test-input-stream)
 844     (clear-stream $_test-input-buffered-file->buffer)
 845     (clear-stream _test-output-stream)
 846     (clear-stream $_test-output-buffered-file->buffer)
 847     #
 848     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
 849     (write _test-input-stream "  result <- copy a\n")
 850     (write _test-input-stream "  result <- add 1\n")
 851     (write _test-input-stream "}\n")
 852     # convert
 853     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 854     (flush _test-output-buffered-file)
 855 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 861     # check output
 862     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-literal-arg-2/0")
 863     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-literal-arg-2/1")
 864     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-literal-arg-2/2")
 865     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg-2/3")
 866     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-literal-arg-2/4")
 867     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-literal-arg-2/5")
 868     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-literal-arg-2/6")
 869     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-literal-arg-2/7")
 870     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-literal-arg-2/8")
 871     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg-2/9")
 872     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-literal-arg-2/10")
 873     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-literal-arg-2/11")
 874     # . epilogue
 875     89/<- %esp 5/r32/ebp
 876     5d/pop-to-ebp
 877     c3/return
 878 
 879 test-convert-function-call-with-literal-arg:
 880     # function writes to output
 881     #   fn main -> result/ebx: int {
 882     #     result <- do-add 3 4
 883     #   }
 884     #
 885     #   fn do-add a: int, b: int -> result/ebx: int {
 886     #     result <- copy a
 887     #     result <- add b
 888     #   }
 889     # =>
 890     #   main:
 891     #     # . prologue
 892     #     55/push-ebp
 893     #     89/<- %ebp 4/r32/esp
 894     #     {
 895     #       (do-add 3 4)
 896     #     }
 897     #     # . epilogue
 898     #     89/<- %esp 5/r32/ebp
 899     #     5d/pop-to-ebp
 900     #     c3/return
 901     #   do-add:
 902     #     # . prologue
 903     #     55/push-ebp
 904     #     89/<- %ebp 4/r32/esp
 905     #     {
 906     #       8b/-> *(ebp+8) 3/r32/ebx
 907     #       03/add-to 3/r32/ebx *(ebp+0xc)
 908     #     }
 909     #     # . epilogue
 910     #     89/<- %esp 5/r32/ebp
 911     #     5d/pop-to-ebp
 912     #     c3/return
 913     # . prologue
 914     55/push-ebp
 915     89/<- %ebp 4/r32/esp
 916     # setup
 917     (clear-stream _test-input-stream)
 918     (clear-stream $_test-input-buffered-file->buffer)
 919     (clear-stream _test-output-stream)
 920     (clear-stream $_test-output-buffered-file->buffer)
 921     #
 922     (write _test-input-stream "fn main -> result/ebx: int {\n")
 923     (write _test-input-stream "  result <- do-add 3 4\n")
 924     (write _test-input-stream "}\n")
 925     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
 926     (write _test-input-stream "  result <- copy a\n")
 927     (write _test-input-stream "  result <- add b\n")
 928     (write _test-input-stream "}\n")
 929     # convert
 930     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 931     (flush _test-output-buffered-file)
 932 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 938     # check output
 939     (check-next-stream-line-equal _test-output-stream "main:"                 "F - test-convert-function-call-with-literal-arg/0")
 940     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-call-with-literal-arg/1")
 941     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
 942     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
 943     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-call-with-literal-arg/4")
 944     (check-next-stream-line-equal _test-output-stream "(do-add 3 4)"          "F - test-convert-function-call-with-literal-arg/5")
 945     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-call-with-literal-arg/6")
 946     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-call-with-literal-arg/7")
 947     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/8")
 948     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/9")
 949     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-call-with-literal-arg/10")
 950     (check-next-stream-line-equal _test-output-stream "do-add:"               "F - test-convert-function-call-with-literal-arg/11")
 951     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-call-with-literal-arg/12")
 952     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-call-with-literal-arg/13")
 953     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/14")
 954     (check-next-stream-line-equal _test-output-stream "{"                     "F - test-convert-function-call-with-literal-arg/15")
 955     (check-next-stream-line-equal _test-output-stream "8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/16")
 956     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/17")
 957     (check-next-stream-line-equal _test-output-stream "}"                     "F - test-convert-function-call-with-literal-arg/18")
 958     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-call-with-literal-arg/19")
 959     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/20")
 960     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/21")
 961     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-call-with-literal-arg/22")
 962     # . epilogue
 963     89/<- %esp 5/r32/ebp
 964     5d/pop-to-ebp
 965     c3/return
 966 
 967 #######################################################
 968 # Parsing
 969 #######################################################
 970 
 971 parse-mu:  # in : (addr buffered-file)
 972     # pseudocode
 973     #   var curr-function : (addr (handle function)) = Program
 974     #   var line : (stream byte 512)
 975     #   var word-slice : slice
 976     #   while true                                  # line loop
 977     #     clear-stream(line)
 978     #     read-line-buffered(in, line)
 979     #     if (line->write == 0) break               # end of file
 980     #     word-slice = next-word-or-string(line)
 981     #     if slice-empty?(word-slice)               # end of line
 982     #       continue
 983     #     else if slice-starts-with?(word-slice, "#")  # comment
 984     #       continue                                # end of line
 985     #     else if slice-equal(word-slice, "fn")
 986     #       var new-function : (handle function) = allocate(function)
 987     #       var vars : (stack (addr var) 256)
 988     #       populate-mu-function-header(in, new-function, vars)
 989     #       populate-mu-function-body(in, new-function, vars)
 990     #       assert(vars->top == 0)
 991     #       *curr-function = new-function
 992     #       curr-function = &new-function->next
 993     #     else
 994     #       abort()
 995     #
 996     # . prologue
 997     55/push-ebp
 998     89/<- %ebp 4/r32/esp
 999     # . save registers
1000     50/push-eax
1001     51/push-ecx
1002     52/push-edx
1003     53/push-ebx
1004     57/push-edi
1005     # var line/ecx : (stream byte 512)
1006     81 5/subop/subtract %esp 0x200/imm32
1007     68/push 0x200/imm32/length
1008     68/push 0/imm32/read
1009     68/push 0/imm32/write
1010     89/<- %ecx 4/r32/esp
1011     # var word-slice/edx : slice
1012     68/push 0/imm32/end
1013     68/push 0/imm32/start
1014     89/<- %edx 4/r32/esp
1015     # var curr-function/edi : (addr (handle function)) = Program
1016     bf/copy-to-edi Program/imm32
1017     # var vars/ebx : (stack (addr var) 256)
1018     81 5/subop/subtract %esp 0x400/imm32
1019     68/push 0x400/imm32/length
1020     68/push 0/imm32/top
1021     89/<- %ebx 4/r32/esp
1022     {
1023 $parse-mu:line-loop:
1024       (clear-stream %ecx)
1025       (read-line-buffered *(ebp+8) %ecx)
1026       # if (line->write == 0) break
1027       81 7/subop/compare *ecx 0/imm32
1028       0f 84/jump-if-equal break/disp32
1029 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------
1035       (next-word-or-string %ecx %edx)
1036       # if slice-empty?(word-slice) continue
1037       (slice-empty? %edx)
1038       3d/compare-eax-and 0/imm32
1039       0f 85/jump-if-not-equal loop/disp32
1040       # if (*word-slice->start == "#") continue
1041       # . eax = *word-slice->start
1042       8b/-> *edx 0/r32/eax
1043       8a/copy-byte *eax 0/r32/AL
1044       81 4/subop/and %eax 0xff/imm32
1045       # . if (eax == '#') continue
1046       3d/compare-eax-and 0x23/imm32/hash
1047       0f 84/jump-if-equal loop/disp32
1048       # if (slice-equal?(word-slice, "fn")) parse a function
1049       {
1050 $parse-mu:fn:
1051         (slice-equal? %edx "fn")
1052         3d/compare-eax-and 0/imm32
1053         0f 84/jump-if-equal break/disp32
1054         # var new-function/eax : (handle function) = populate-mu-function(in, new-function, vars)
1055         (allocate Heap *Function-size)  # => eax
1056         (zero-out %eax *Function-size)
1057         (clear-stack %ebx)
1058         (populate-mu-function-header %ecx %eax %ebx)
1059         (populate-mu-function-body *(ebp+8) %eax %ebx)
1060         # *curr-function = new-function
1061         89/<- *edi 0/r32/eax
1062         # curr-function = &new-function->next
1063         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
1064         e9/jump $parse-mu:line-loop/disp32
1065       }
1066       # otherwise abort
1067       e9/jump $parse-mu:error1/disp32
1068     } # end line loop
1069 $parse-mu:end:
1070     # . reclaim locals
1071     81 0/subop/add %esp 0x630/imm32
1072     # . restore registers
1073     5f/pop-to-edi
1074     5b/pop-to-ebx
1075     5a/pop-to-edx
1076     59/pop-to-ecx
1077     58/pop-to-eax
1078     # . epilogue
1079     89/<- %esp 5/r32/ebp
1080     5d/pop-to-ebp
1081     c3/return
1082 
1083 $parse-mu:error1:
1084     # error("unexpected top-level command: " word-slice "\n")
1085     (write-buffered Stderr "unexpected top-level command: ")
1086     (write-slice-buffered Stderr %edx)
1087     (write-buffered Stderr "\n")
1088     (flush Stderr)
1089     # . syscall(exit, 1)
1090     bb/copy-to-ebx  1/imm32
1091     b8/copy-to-eax  1/imm32/exit
1092     cd/syscall  0x80/imm8
1093     # never gets here
1094 
1095 $parse-mu:error2:
1096     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
1097     (print-int32-buffered Stderr *ebx)
1098     (write-buffered Stderr " vars not reclaimed after fn '")
1099     (write-slice-buffered Stderr *eax)  # Function-name
1100     (write-buffered Stderr "'\n")
1101     (flush Stderr)
1102     # . syscall(exit, 1)
1103     bb/copy-to-ebx  1/imm32
1104     b8/copy-to-eax  1/imm32/exit
1105     cd/syscall  0x80/imm8
1106     # never gets here
1107 
1108 # scenarios considered:
1109 # ✗ fn foo  # no block
1110 # ✓ fn foo {
1111 # ✗ fn foo { {
1112 # ✗ fn foo { }
1113 # ✗ fn foo { } {
1114 # ✗ fn foo x {
1115 # ✗ fn foo x : {
1116 # ✓ fn foo x : int {
1117 # ✓ fn foo x: int {
1118 # ✓ fn foo x: int -> y/eax: int {
1119 populate-mu-function-header:  # first-line : (addr stream byte), out : (handle function), vars : (addr stack (handle var))
1120     # pseudocode:
1121     #   var name : slice
1122     #   next-word(first-line, name)
1123     #   assert(name not in '{' '}' '->')
1124     #   out->name = slice-to-string(name)
1125     #   var next-offset : int = 8
1126     #   ## inouts
1127     #   while true
1128     #     ## name
1129     #     name = next-word(first-line)
1130     #     if (name == '{') goto done
1131     #     if (name == '->') break
1132     #     assert(name != '}')
1133     #     var v : (handle var) = parse-var-with-type(name, first-line)
1134     #     assert(v->register == null)
1135     #     v->stack-offset = next-offset
1136     #     next-offset += size-of(v)
1137     #     out->inouts = append(out->inouts, v)
1138     #     push(vars, v)
1139     #   ## outputs
1140     #   while true
1141     #     ## name
1142     #     name = next-word(first-line)
1143     #     assert(name not in '{' '}' '->')
1144     #     var v : (handle var) = parse-var-with-type(name, first-line)
1145     #     assert(v->register != null)
1146     #     out->outputs = append(out->outputs, v)
1147     #   done:
1148     #
1149     # . prologue
1150     55/push-ebp
1151     89/<- %ebp 4/r32/esp
1152     # . save registers
1153     50/push-eax
1154     51/push-ecx
1155     52/push-edx
1156     53/push-ebx
1157     57/push-edi
1158     # edi = out
1159     8b/-> *(ebp+0xc) 7/r32/edi
1160     # var word-slice/ecx : slice
1161     68/push 0/imm32/end
1162     68/push 0/imm32/start
1163     89/<- %ecx 4/r32/esp
1164     # var next-offset/edx = 8
1165     ba/copy-to-edx 8/imm32
1166     # read function name
1167     (next-word *(ebp+8) %ecx)
1168     # error checking
1169     # if (word-slice == '{') abort
1170     (slice-equal? %ecx "{")   # => eax
1171     3d/compare-eax-and 0/imm32
1172     0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32
1173     # if (word-slice == '->') abort
1174     (slice-equal? %ecx "->")   # => eax
1175     3d/compare-eax-and 0/imm32
1176     0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32
1177     # if (word-slice == '}') abort
1178     (slice-equal? %ecx "}")   # => eax
1179     3d/compare-eax-and 0/imm32
1180     0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32
1181     # save function name
1182     (slice-to-string Heap %ecx)  # => eax
1183     89/<- *edi 0/r32/eax  # Function-name
1184     # initialize default subx-name as well
1185     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
1186     # save function inouts
1187     {
1188 $populate-mu-function-header:check-for-inout:
1189       (next-word *(ebp+8) %ecx)
1190       # if (word-slice == '{') goto done
1191       (slice-equal? %ecx "{")   # => eax
1192       3d/compare-eax-and 0/imm32
1193       0f 85/jump-if-not-equal $populate-mu-function-header:done/disp32
1194       # if (word-slice == '->') break
1195       (slice-equal? %ecx "->")   # => eax
1196       3d/compare-eax-and 0/imm32
1197       0f 85/jump-if-not-equal break/disp32
1198       # if (word-slice == '}') abort
1199       (slice-equal? %ecx "}")   # => eax
1200       3d/compare-eax-and 0/imm32
1201       0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32
1202       # var v/ebx : (handle var) = parse-var-with-type(word-slice, first-line)
1203       (parse-var-with-type %ecx *(ebp+8))  # => eax
1204       89/<- %ebx 0/r32/eax
1205       # assert(v->register == null)
1206       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
1207       0f 85/jump-if-not-equal $populate-mu-function-header:error2/disp32
1208       # v->stack-offset = next-offset
1209       89/<- *(ebx+0xc) 2/r32/edx  # Var-stack-offset
1210       # next-offset += size-of(v)
1211       (size-of %ebx)  # => eax
1212       01/add %edx 0/r32/eax
1213       #
1214       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
1215       89/<- *(edi+8) 0/r32/eax  # Function-inouts
1216       (push *(ebp+0x10) %ebx)
1217       #
1218       e9/jump loop/disp32
1219     }
1220     # save function outputs
1221     {
1222 $parse-var-with-type:check-for-out:
1223       (next-word *(ebp+8) %ecx)
1224       # if (word-slice == '{') break
1225       (slice-equal? %ecx "{")   # => eax
1226       3d/compare-eax-and 0/imm32
1227       0f 85/jump-if-not-equal break/disp32
1228       # if (word-slice == '->') abort
1229       (slice-equal? %ecx "->")   # => eax
1230       3d/compare-eax-and 0/imm32
1231       0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32
1232       # if (word-slice == '}') abort
1233       (slice-equal? %ecx "}")   # => eax
1234       3d/compare-eax-and 0/imm32
1235       0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32
1236       #
1237       (parse-var-with-type %ecx *(ebp+8))  # => eax
1238       89/<- %ebx 0/r32/eax
1239       # assert(var->register != null)
1240       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
1241       0f 84/jump-if-equal $populate-mu-function-header:error3/disp32
1242       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
1243       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
1244       e9/jump loop/disp32
1245     }
1246 $populate-mu-function-header:done:
1247     (check-no-tokens-left *(ebp+8))
1248 $populate-mu-function-header:end:
1249     # . reclaim locals
1250     81 0/subop/add %esp 8/imm32
1251     # . restore registers
1252     5f/pop-to-edi
1253     5b/pop-to-ebx
1254     5a/pop-to-edx
1255     59/pop-to-ecx
1256     58/pop-to-eax
1257     # . epilogue
1258     89/<- %esp 5/r32/ebp
1259     5d/pop-to-ebp
1260     c3/return
1261 
1262 $populate-mu-function-header:error1:
1263     # error("function header not in form 'fn <name> {'")
1264     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
1265     (flush Stderr)
1266     (rewind-stream *(ebp+8))
1267     (write-stream 2 *(ebp+8))
1268     (write-buffered Stderr "'\n")
1269     (flush Stderr)
1270     # . syscall(exit, 1)
1271     bb/copy-to-ebx  1/imm32
1272     b8/copy-to-eax  1/imm32/exit
1273     cd/syscall  0x80/imm8
1274     # never gets here
1275 
1276 $populate-mu-function-header:error2:
1277     # error("function input '" var "' cannot be in a register")
1278     (write-buffered Stderr "function input '")
1279     (write-buffered Stderr *ebx)  # Var-name
1280     (write-buffered Stderr "' cannot be in a register")
1281     (flush Stderr)
1282     # . syscall(exit, 1)
1283     bb/copy-to-ebx  1/imm32
1284     b8/copy-to-eax  1/imm32/exit
1285     cd/syscall  0x80/imm8
1286     # never gets here
1287 
1288 $populate-mu-function-header:error3:
1289     # error("function input '" var "' must be in a register")
1290     (write-buffered Stderr "function input '")
1291     (write-buffered Stderr *eax)  # Var-name
1292     (write-buffered Stderr " must be in a register'")
1293     (flush Stderr)
1294     (rewind-stream *(ebp+8))
1295     (write-stream 2 *(ebp+8))
1296     (write-buffered Stderr "'\n")
1297     (flush Stderr)
1298     # . syscall(exit, 1)
1299     bb/copy-to-ebx  1/imm32
1300     b8/copy-to-eax  1/imm32/exit
1301     cd/syscall  0x80/imm8
1302     # never gets here
1303 
1304 test-function-header-with-arg:
1305     # 'foo n : int {'
1306     # . prologue
1307     55/push-ebp
1308     89/<- %ebp 4/r32/esp
1309     # setup
1310     (clear-stream _test-input-stream)
1311     (write _test-input-stream "foo n : int {\n")
1312     # result/ecx : function
1313     2b/subtract-> *Function-size 4/r32/esp
1314     89/<- %ecx 4/r32/esp
1315     (zero-out %ecx *Function-size)
1316     # var vars/ebx : (stack (addr var) 16)
1317     81 5/subop/subtract %esp 0x10/imm32
1318     68/push 0x10/imm32/length
1319     68/push 0/imm32/top
1320     89/<- %ebx 4/r32/esp
1321     # convert
1322     (populate-mu-function-header _test-input-stream %ecx %ebx)
1323     # check result
1324     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
1325     # edx : (handle list var) = result->inouts
1326     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1327     # ebx : (handle var) = result->inouts->value
1328     8b/-> *edx 3/r32/ebx  # List-value
1329     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
1330     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1331     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
1332     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
1333     # . epilogue
1334     89/<- %esp 5/r32/ebp
1335     5d/pop-to-ebp
1336     c3/return
1337 
1338 test-function-header-with-multiple-args:
1339     # 'fn foo a: int, b: int, c: int {'
1340     # . prologue
1341     55/push-ebp
1342     89/<- %ebp 4/r32/esp
1343     # setup
1344     (clear-stream _test-input-stream)
1345     (write _test-input-stream "foo a: int, b: int c: int {\n")
1346     # result/ecx : (handle function)
1347     2b/subtract-> *Function-size 4/r32/esp
1348     89/<- %ecx 4/r32/esp
1349     (zero-out %ecx *Function-size)
1350     # var vars/ebx : (stack (addr var) 16)
1351     81 5/subop/subtract %esp 0x10/imm32
1352     68/push 0x10/imm32/length
1353     68/push 0/imm32/top
1354     89/<- %ebx 4/r32/esp
1355     # convert
1356     (populate-mu-function-header _test-input-stream %ecx %ebx)
1357     # check result
1358     (check-strings-equal *ecx "foo")  # Function-name
1359     # edx : (handle list var) = result->inouts
1360     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1361 $test-function-header-with-multiple-args:inout0:
1362     # ebx : (handle var) = result->inouts->value
1363     8b/-> *edx 3/r32/ebx  # List-value
1364     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
1365     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1366     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
1367     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
1368     # edx = result->inouts->next
1369     8b/-> *(edx+4) 2/r32/edx  # List-next
1370 $test-function-header-with-multiple-args:inout1:
1371     # ebx = result->inouts->next->value
1372     8b/-> *edx 3/r32/ebx  # List-value
1373     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
1374     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1375     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
1376     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
1377     # edx = result->inouts->next->next
1378     8b/-> *(edx+4) 2/r32/edx  # List-next
1379 $test-function-header-with-multiple-args:inout2:
1380     # ebx = result->inouts->next->next->value
1381     8b/-> *edx 3/r32/ebx  # List-value
1382     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
1383     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1384     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
1385     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
1386     # . epilogue
1387     89/<- %esp 5/r32/ebp
1388     5d/pop-to-ebp
1389     c3/return
1390 
1391 test-function-with-multiple-args-and-outputs:
1392     # fn foo a: int, b: int, c: int -> x: int, y: int {
1393     # . prologue
1394     55/push-ebp
1395     89/<- %ebp 4/r32/esp
1396     # setup
1397     (clear-stream _test-input-stream)
1398     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx : int {\n")
1399     # result/ecx : (handle function)
1400     2b/subtract-> *Function-size 4/r32/esp
1401     89/<- %ecx 4/r32/esp
1402     (zero-out %ecx *Function-size)
1403     # var vars/ebx : (stack (addr var) 16)
1404     81 5/subop/subtract %esp 0x10/imm32
1405     68/push 0x10/imm32/length
1406     68/push 0/imm32/top
1407     89/<- %ebx 4/r32/esp
1408     # convert
1409     (populate-mu-function-header _test-input-stream %ecx %ebx)
1410     # check result
1411     (check-strings-equal *ecx "foo")  # Function-name
1412     # edx : (handle list var) = result->inouts
1413     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
1414     # ebx : (handle var) = result->inouts->value
1415     8b/-> *edx 3/r32/ebx  # List-value
1416     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
1417     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1418     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
1419     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
1420     # edx = result->inouts->next
1421     8b/-> *(edx+4) 2/r32/edx  # List-next
1422     # ebx = result->inouts->next->value
1423     8b/-> *edx 3/r32/ebx  # List-value
1424     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
1425     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1426     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
1427     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
1428     # edx = result->inouts->next->next
1429     8b/-> *(edx+4) 2/r32/edx  # List-next
1430     # ebx = result->inouts->next->next->value
1431     8b/-> *edx 3/r32/ebx  # List-value
1432     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
1433     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1434     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
1435     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
1436     # edx : (handle list var) = result->outputs
1437     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
1438     # ebx : (handle var) = result->outputs->value
1439     8b/-> *edx 3/r32/ebx  # List-value
1440     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
1441     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
1442     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1443     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
1444     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
1445     # edx = result->outputs->next
1446     8b/-> *(edx+4) 2/r32/edx  # List-next
1447     # ebx = result->outputs->next->value
1448     8b/-> *edx 3/r32/ebx  # List-value
1449     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
1450     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
1451     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
1452     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
1453     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
1454     # . epilogue
1455     89/<- %esp 5/r32/ebp
1456     5d/pop-to-ebp
1457     c3/return
1458 
1459 # format for variables with types
1460 #   x : int
1461 #   x: int
1462 #   x: int,
1463 # ignores at most one trailing colon or comma
1464 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
1465     # pseudocode:
1466     #   var v : (handle var) = allocate(Heap, Var-size)
1467     #   var s : slice
1468     #   next-token-from-slice(name->start, name->end, '/', s)
1469     #   var end : (addr byte) = s->end
1470     #   if (slice-ends-with(s, ":"))
1471     #     decrement s->end
1472     #   if (slice-ends-with(s, ","))
1473     #     decrement s->end
1474     #   v->name = slice-to-string(s)
1475     #   ## register
1476     #   next-token-from-slice(end, name->end, '/', s)
1477     #   if (slice-ends-with(s, ":"))
1478     #     decrement s->end
1479     #   if (slice-ends-with(s, ","))
1480     #     decrement s->end
1481     #   if (!slice-empty?(s))
1482     #     v->register = slice-to-string(s)
1483     #   ## type
1484     #   s = next-mu-token(first-line)
1485     #   assert(s not in '{' '}' '->')
1486     #   if (slice-empty?(s)) {
1487     #     s = next-mu-token(first-line)
1488     #     assert(type not in '{' '}' '->')
1489     #   }
1490     #   type = type-for(s)
1491     #   v->type = type
1492     #   return v
1493     #
1494     # . prologue
1495     55/push-ebp
1496     89/<- %ebp 4/r32/esp
1497     # . save registers
1498     51/push-ecx
1499     52/push-edx
1500     53/push-ebx
1501     56/push-esi
1502     57/push-edi
1503     # var result/edi : (handle var) = allocate(Heap, Var-size)
1504     (allocate Heap *Var-size)  # => eax
1505     (zero-out %eax *Var-size)
1506     89/<- %edi 0/r32/eax
1507     # esi = name
1508     8b/-> *(ebp+8) 6/r32/esi
1509     # var s/ecx : slice
1510     68/push 0/imm32/end
1511     68/push 0/imm32/start
1512     89/<- %ecx 4/r32/esp
1513 $parse-var-with-type:save-name:
1514     # save v->name
1515     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
1516     # . end/edx = s->end
1517     8b/-> *(ecx+4) 2/r32/edx
1518     # . if s ends with ':', decrement s->end
1519     {
1520       8b/-> *(ecx+4) 0/r32/eax
1521       48/decrement-eax
1522       8a/copy-byte *eax 3/r32/BL
1523       81 4/subop/and %ebx 0xff/imm32
1524       81 7/subop/compare %ebx 0x3a/imm32/colon
1525       75/jump-if-not-equal break/disp8
1526       89/<- *(ecx+4) 0/r32/eax
1527     }
1528     # . if s ends with ',', decrement s->end
1529     {
1530       8b/-> *(ecx+4) 0/r32/eax
1531       48/decrement-eax
1532       8a/copy-byte *eax 3/r32/BL
1533       81 4/subop/and %ebx 0xff/imm32
1534       81 7/subop/compare %ebx 0x2c/imm32/comma
1535       75/jump-if-not-equal break/disp8
1536       89/<- *(ecx+4) 0/r32/eax
1537     }
1538 $parse-var-with-type:write-name:
1539     (slice-to-string Heap %ecx)  # => eax
1540     89/<- *edi 0/r32/eax  # Var-name
1541     # save v->register
1542 $parse-var-with-type:save-register:
1543     (next-token-from-slice %edx *(esi+4) 0x2f %ecx)  # end, name->end, '/'
1544     # . if s ends with ':', decrement s->end
1545     {
1546       8b/-> *(ecx+4) 0/r32/eax
1547       48/decrement-eax
1548       8a/copy-byte *eax 3/r32/BL
1549       81 4/subop/and %ebx 0xff/imm32
1550       81 7/subop/compare %ebx 0x3a/imm32/colon
1551       75/jump-if-not-equal break/disp8
1552       89/<- *(ecx+4) 0/r32/eax
1553     }
1554     # . if s ends with ',', decrement s->end
1555     {
1556       8b/-> *(ecx+4) 0/r32/eax
1557       48/decrement-eax
1558       8a/copy-byte *eax 3/r32/BL
1559       81 4/subop/and %ebx 0xff/imm32
1560       81 7/subop/compare %ebx 0x2c/imm32/comma
1561       75/jump-if-not-equal break/disp8
1562       89/<- *(ecx+4) 0/r32/eax
1563     }
1564     # if (!slice-empty?(s)) v->register = slice-to-string(s)
1565     {
1566 $parse-var-with-type:write-register:
1567       # HACK: s->end can be less than s->start with all the decrements above
1568       # That's probably a sign we have the wrong algorithm for this function.
1569       8b/-> *ecx 0/r32/eax
1570       39/compare 0/r32/eax *(ecx+4)
1571       76/jump-if-lesser-or-equal break/disp8
1572       (slice-to-string Heap %ecx)
1573       89/<- *(edi+0x10) 0/r32/eax  # Var-register
1574     }
1575     # save v->type
1576     (next-mu-token *(ebp+0xc) %ecx)
1577     # if (word-slice == '{') abort
1578     (slice-equal? %ecx "{")   # => eax
1579     3d/compare-eax-and 0/imm32
1580     0f 85/jump-if-not-equal $parse-var-with-type:abort/disp32
1581     # if (word-slice == '->') abort
1582     (slice-equal? %ecx "->")   # => eax
1583     3d/compare-eax-and 0/imm32
1584     0f 85/jump-if-not-equal $parse-var-with-type:abort/disp32
1585     # if (word-slice == '}') abort
1586     (slice-equal? %ecx "}")   # => eax
1587     3d/compare-eax-and 0/imm32
1588     0f 85/jump-if-not-equal $parse-var-with-type:abort/disp32
1589     # if (slice-empty?(type)) skip
1590     (slice-empty? %ecx)
1591     {
1592       3d/compare-eax-and 0/imm32
1593       0f 84/jump-if-equal break/disp32
1594       (next-mu-token *(ebp+0xc) %ecx)
1595       # if (word-slice == '{') abort
1596       (slice-equal? %ecx "{")   # => eax
1597       3d/compare-eax-and 0/imm32
1598       0f 85/jump-if-not-equal $parse-var-with-type:abort/disp32
1599       # if (word-slice == '->') abort
1600       (slice-equal? %ecx "->")   # => eax
1601       3d/compare-eax-and 0/imm32
1602       0f 85/jump-if-not-equal $parse-var-with-type:abort/disp32
1603       # if (word-slice == '}') abort
1604       (slice-equal? %ecx "}")   # => eax
1605       3d/compare-eax-and 0/imm32
1606       0f 85/jump-if-not-equal $parse-var-with-type:abort/disp32
1607     }
1608     (type-for %ecx)  # => eax
1609     89/<- *(edi+4) 0/r32/eax  # Var-type
1610 $parse-var-with-type:end:
1611     # return result
1612     89/<- %eax 7/r32/edi
1613     # . reclaim locals
1614     81 0/subop/add %esp 8/imm32
1615     # . restore registers
1616     5f/pop-to-edi
1617     5e/pop-to-esi
1618     5b/pop-to-ebx
1619     5a/pop-to-edx
1620     59/pop-to-ecx
1621     # . epilogue
1622     89/<- %esp 5/r32/ebp
1623     5d/pop-to-ebp
1624     c3/return
1625 
1626 $parse-var-with-type:abort:
1627     # error("function header not in form 'fn <name> {'")
1628     (write-buffered Stderr "var should have form 'name: type' in '")
1629     (flush Stderr)
1630     (rewind-stream *(ebp+0xc))
1631     (write-stream 2 *(ebp+0xc))
1632     (write-buffered Stderr "'\n")
1633     (flush Stderr)
1634     # . syscall(exit, 1)
1635     bb/copy-to-ebx  1/imm32
1636     b8/copy-to-eax  1/imm32/exit
1637     cd/syscall  0x80/imm8
1638     # never gets here
1639 
1640 next-mu-token:  # in: (addr stream byte), out: (addr slice)
1641     # . prologue
1642     55/push-ebp
1643     89/<- %ebp 4/r32/esp
1644     # . save registers
1645     50/push-eax
1646     57/push-edi
1647     # edi = out
1648     8b/-> *(ebp+0xc) 7/r32/edi
1649     #
1650     (next-word *(ebp+8) %edi)  # TODO: support s-expressions
1651     # if out ends with ':', decrement out->end
1652     {
1653       8b/-> *(edi+4) 0/r32/eax
1654       48/decrement-eax
1655       8a/copy-byte *eax 3/r32/BL
1656       81 4/subop/and %ebx 0xff/imm32
1657       81 7/subop/compare %ebx 0x3a/imm32/colon
1658       75/jump-if-not-equal break/disp8
1659       89/<- *(edi+4) 0/r32/eax
1660     }
1661     # if out ends with ',', decrement out->end
1662     {
1663       8b/-> *(edi+4) 0/r32/eax
1664       48/decrement-eax
1665       8a/copy-byte *eax 3/r32/BL
1666       81 4/subop/and %ebx 0xff/imm32
1667       81 7/subop/compare %ebx 0x2c/imm32/comma
1668       75/jump-if-not-equal break/disp8
1669       89/<- *(edi+4) 0/r32/eax
1670     }
1671 $next-mu-token:end:
1672     b8/copy-to-eax 1/imm32/int
1673     # . restore registers
1674     5f/pop-to-edi
1675     58/pop-to-eax
1676     # . epilogue
1677     89/<- %esp 5/r32/ebp
1678     5d/pop-to-ebp
1679     c3/return
1680 
1681 type-for:  # name: (addr slice) -> result/eax: (handle tree type-id)
1682     # . prologue
1683     55/push-ebp
1684     89/<- %ebp 4/r32/esp
1685     # . save registers
1686     51/push-ecx
1687     #
1688     (pos-slice Type-id *(ebp+8))  # => eax
1689     89/<- %ecx 0/r32/eax
1690     (allocate Heap *Tree-size)  # => eax
1691     (zero-out %eax *Tree-size)
1692     89/<- *eax 1/r32/ecx  # Tree-left
1693 $type-for:end:
1694     # . restore registers
1695     59/pop-to-ecx
1696     # . epilogue
1697     89/<- %esp 5/r32/ebp
1698     5d/pop-to-ebp
1699     c3/return
1700 
1701 # return the index in an array of strings matching 's'
1702 # index is denominated in elements, not bytes
1703 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
1704     # . prologue
1705     55/push-ebp
1706     89/<- %ebp 4/r32/esp
1707     # . save registers
1708     51/push-ecx
1709     52/push-edx
1710     53/push-ebx
1711     56/push-esi
1712     # esi = arr
1713     8b/-> *(ebp+8) 6/r32/esi
1714     # var index/ecx: int = 0
1715     b9/copy-to-ecx 0/imm32
1716     # var curr/edx: (addr (addr array byte)) = arr->data
1717     8d/copy-address *(esi+0xc) 2/r32/edx
1718     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
1719     8b/-> *esi 3/r32/ebx
1720     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
1721     {
1722       # if (curr >= max) return -1
1723       39/compare %edx 3/r32/ebx
1724       {
1725         72/jump-if-lesser-unsigned break/disp8
1726         b8/copy-to-eax 1/imm32
1727         eb/jump $pos-slice:end/disp8
1728       }
1729       # if (slice-equal?(s, *curr)) break
1730       (slice-equal? *(ebp+0xc) *edx)  # => eax
1731       3d/compare-eax-and 0/imm32
1732       75/jump-if-not-equal break/disp8
1733       # ++index
1734       41/increment-ecx
1735       # curr += 4
1736       81 0/subop/add %edx 4/imm32
1737     }
1738     89/<- %eax 1/r32/ecx
1739 $pos-slice:end:
1740     # . restore registers
1741     5e/pop-to-esi
1742     5b/pop-to-ebx
1743     5a/pop-to-edx
1744     59/pop-to-ecx
1745     # . epilogue
1746     89/<- %esp 5/r32/ebp
1747     5d/pop-to-ebp
1748     c3/return
1749 
1750 == data
1751 
1752 Type-id:  # (stream (address array byte))
1753   0x1c/imm32/write
1754   0/imm32/read
1755   0x100/imm32/length
1756   # data
1757   "literal"/imm32  # 0
1758   "int"/imm32  # 1
1759   "addr"/imm32  # 2
1760   "array"/imm32  # 3
1761   "handle"/imm32  # 4
1762   "bool"/imm32  # 5
1763   0/imm32
1764   0/imm32
1765   # 0x20
1766   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1767   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1768   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1769   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1770   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1771   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1772   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
1773 
1774 == code
1775 
1776 test-parse-var-with-type:
1777     # . prologue
1778     55/push-ebp
1779     89/<- %ebp 4/r32/esp
1780     # (eax..ecx) = "x:"
1781     b8/copy-to-eax "x:"/imm32
1782     8b/-> *eax 1/r32/ecx
1783     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1784     05/add-to-eax 4/imm32
1785     # var slice/ecx : slice = {eax, ecx}
1786     51/push-ecx
1787     50/push-eax
1788     89/<- %ecx 4/r32/esp
1789     # _test-input-stream contains "int"
1790     (clear-stream _test-input-stream)
1791     (write _test-input-stream "int")
1792     #
1793     (parse-var-with-type %ecx _test-input-stream)
1794     8b/-> *eax 2/r32/edx  # Var-name
1795     (check-strings-equal %edx "x" "F - test-var-with-type/name")
1796     8b/-> *(eax+4) 2/r32/edx  # Var-type
1797     (check-ints-equal *edx 1 "F - test-var-with-type/type")
1798     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
1799     # . epilogue
1800     89/<- %esp 5/r32/ebp
1801     5d/pop-to-ebp
1802     c3/return
1803 
1804 test-parse-var-with-type-and-register:
1805     # . prologue
1806     55/push-ebp
1807     89/<- %ebp 4/r32/esp
1808     # (eax..ecx) = "x/eax"
1809     b8/copy-to-eax "x/eax"/imm32
1810     8b/-> *eax 1/r32/ecx
1811     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1812     05/add-to-eax 4/imm32
1813     # var slice/ecx : slice = {eax, ecx}
1814     51/push-ecx
1815     50/push-eax
1816     89/<- %ecx 4/r32/esp
1817     # _test-input-stream contains ": int"
1818     (clear-stream _test-input-stream)
1819     (write _test-input-stream ": int")
1820     #
1821     (parse-var-with-type %ecx _test-input-stream)
1822     8b/-> *eax 2/r32/edx  # Var-name
1823     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
1824     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
1825     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
1826     8b/-> *(eax+4) 2/r32/edx  # Var-type
1827     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
1828     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
1829     # . epilogue
1830     89/<- %esp 5/r32/ebp
1831     5d/pop-to-ebp
1832     c3/return
1833 
1834 test-parse-var-with-trailing-characters:
1835     # . prologue
1836     55/push-ebp
1837     89/<- %ebp 4/r32/esp
1838     # (eax..ecx) = "x:"
1839     b8/copy-to-eax "x:"/imm32
1840     8b/-> *eax 1/r32/ecx
1841     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1842     05/add-to-eax 4/imm32
1843     # var slice/ecx : slice = {eax, ecx}
1844     51/push-ecx
1845     50/push-eax
1846     89/<- %ecx 4/r32/esp
1847     # _test-input-stream contains "int,"
1848     (clear-stream _test-input-stream)
1849     (write _test-input-stream "int,")
1850     #
1851     (parse-var-with-type %ecx _test-input-stream)
1852     8b/-> *eax 2/r32/edx  # Var-name
1853     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
1854     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
1855     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
1856     8b/-> *(eax+4) 2/r32/edx  # Var-type
1857     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
1858     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
1859     # . epilogue
1860     89/<- %esp 5/r32/ebp
1861     5d/pop-to-ebp
1862     c3/return
1863 
1864 test-parse-var-with-register-and-trailing-characters:
1865     # . prologue
1866     55/push-ebp
1867     89/<- %ebp 4/r32/esp
1868     # (eax..ecx) = "x/eax:"
1869     b8/copy-to-eax "x/eax:"/imm32
1870     8b/-> *eax 1/r32/ecx
1871     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1872     05/add-to-eax 4/imm32
1873     # var slice/ecx : slice = {eax, ecx}
1874     51/push-ecx
1875     50/push-eax
1876     89/<- %ecx 4/r32/esp
1877     # _test-input-stream contains "int,"
1878     (clear-stream _test-input-stream)
1879     (write _test-input-stream "int,")
1880     #
1881     (parse-var-with-type %ecx _test-input-stream)
1882     8b/-> *eax 2/r32/edx  # Var-name
1883     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
1884     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
1885     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
1886     8b/-> *(eax+4) 2/r32/edx  # Var-type
1887     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
1888     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
1889     # . epilogue
1890     89/<- %esp 5/r32/ebp
1891     5d/pop-to-ebp
1892     c3/return
1893 
1894 # identifier starts with a letter or '$' or '_'
1895 # no constraints at the moment on later letters
1896 # all we really want to do so far is exclude '{', '}' and '->'
1897 is-identifier?:  # in : (addr slice) -> result/eax : boolean
1898     # . prologue
1899     55/push-ebp
1900     89/<- %ebp 4/r32/esp
1901     # if (slice-empty?(in)) return false
1902     (slice-empty? *(ebp+8))  # => eax
1903     3d/compare-eax-and 0/imm32
1904     75/jump-if-not-equal $is-identifier?:false/disp8
1905     # var c/eax : byte = *in->start
1906     8b/-> *(ebp+8) 0/r32/eax
1907     8b/-> *eax 0/r32/eax
1908     8a/copy-byte *eax 0/r32/AL
1909     81 4/subop/and %eax 0xff/imm32
1910     # if (c == '$') return true
1911     3d/compare-eax-and 0x24/imm32/$
1912     74/jump-if-equal $is-identifier?:true/disp8
1913     # if (c == '_') return true
1914     3d/compare-eax-and 0x5f/imm32/_
1915     74/jump-if-equal $is-identifier?:true/disp8
1916     # drop case
1917     25/and-eax-with 0x5f/imm32
1918     # if (c < 'A') return false
1919     3d/compare-eax-and 0x41/imm32/A
1920     7c/jump-if-lesser $is-identifier?:false/disp8
1921     # if (c > 'Z') return false
1922     3d/compare-eax-and 0x5a/imm32/Z
1923     7f/jump-if-greater $is-identifier?:false/disp8
1924     # otherwise return true
1925 $is-identifier?:true:
1926     b8/copy-to-eax 1/imm32/true
1927     eb/jump $is-identifier?:end/disp8
1928 $is-identifier?:false:
1929     b8/copy-to-eax 0/imm32/false
1930 $is-identifier?:end:
1931     # . epilogue
1932     89/<- %esp 5/r32/ebp
1933     5d/pop-to-ebp
1934     c3/return
1935 
1936 test-is-identifier-dollar:
1937     # . prologue
1938     55/push-ebp
1939     89/<- %ebp 4/r32/esp
1940     # (eax..ecx) = "$a"
1941     b8/copy-to-eax "$a"/imm32
1942     8b/-> *eax 1/r32/ecx
1943     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1944     05/add-to-eax 4/imm32
1945     # var slice/ecx : slice = {eax, ecx}
1946     51/push-ecx
1947     50/push-eax
1948     89/<- %ecx 4/r32/esp
1949     #
1950     (is-identifier? %ecx)
1951     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
1952     # . epilogue
1953     89/<- %esp 5/r32/ebp
1954     5d/pop-to-ebp
1955     c3/return
1956 
1957 test-is-identifier-underscore:
1958     # . prologue
1959     55/push-ebp
1960     89/<- %ebp 4/r32/esp
1961     # (eax..ecx) = "_a"
1962     b8/copy-to-eax "_a"/imm32
1963     8b/-> *eax 1/r32/ecx
1964     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1965     05/add-to-eax 4/imm32
1966     # var slice/ecx : slice = {eax, ecx}
1967     51/push-ecx
1968     50/push-eax
1969     89/<- %ecx 4/r32/esp
1970     #
1971     (is-identifier? %ecx)
1972     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
1973     # . epilogue
1974     89/<- %esp 5/r32/ebp
1975     5d/pop-to-ebp
1976     c3/return
1977 
1978 test-is-identifier-a:
1979     # . prologue
1980     55/push-ebp
1981     89/<- %ebp 4/r32/esp
1982     # (eax..ecx) = "a$"
1983     b8/copy-to-eax "a$"/imm32
1984     8b/-> *eax 1/r32/ecx
1985     8d/copy-address *(eax+ecx+4) 1/r32/ecx
1986     05/add-to-eax 4/imm32
1987     # var slice/ecx : slice = {eax, ecx}
1988     51/push-ecx
1989     50/push-eax
1990     89/<- %ecx 4/r32/esp
1991     #
1992     (is-identifier? %ecx)
1993     (check-ints-equal %eax 1 "F - test-is-identifier-a")
1994     # . epilogue
1995     89/<- %esp 5/r32/ebp
1996     5d/pop-to-ebp
1997     c3/return
1998 
1999 test-is-identifier-z:
2000     # . prologue
2001     55/push-ebp
2002     89/<- %ebp 4/r32/esp
2003     # (eax..ecx) = "z$"
2004     b8/copy-to-eax "z$"/imm32
2005     8b/-> *eax 1/r32/ecx
2006     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2007     05/add-to-eax 4/imm32
2008     # var slice/ecx : slice = {eax, ecx}
2009     51/push-ecx
2010     50/push-eax
2011     89/<- %ecx 4/r32/esp
2012     #
2013     (is-identifier? %ecx)
2014     (check-ints-equal %eax 1 "F - test-is-identifier-z")
2015     # . epilogue
2016     89/<- %esp 5/r32/ebp
2017     5d/pop-to-ebp
2018     c3/return
2019 
2020 test-is-identifier-A:
2021     # . prologue
2022     55/push-ebp
2023     89/<- %ebp 4/r32/esp
2024     # (eax..ecx) = "A$"
2025     b8/copy-to-eax "A$"/imm32
2026     8b/-> *eax 1/r32/ecx
2027     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2028     05/add-to-eax 4/imm32
2029     # var slice/ecx : slice = {eax, ecx}
2030     51/push-ecx
2031     50/push-eax
2032     89/<- %ecx 4/r32/esp
2033     #
2034     (is-identifier? %ecx)
2035     (check-ints-equal %eax 1 "F - test-is-identifier-A")
2036     # . epilogue
2037     89/<- %esp 5/r32/ebp
2038     5d/pop-to-ebp
2039     c3/return
2040 
2041 test-is-identifier-Z:
2042     # . prologue
2043     55/push-ebp
2044     89/<- %ebp 4/r32/esp
2045     # (eax..ecx) = "Z$"
2046     b8/copy-to-eax "Z$"/imm32
2047     8b/-> *eax 1/r32/ecx
2048     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2049     05/add-to-eax 4/imm32
2050     # var slice/ecx : slice = {eax, ecx}
2051     51/push-ecx
2052     50/push-eax
2053     89/<- %ecx 4/r32/esp
2054     #
2055     (is-identifier? %ecx)
2056     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
2057     # . epilogue
2058     89/<- %esp 5/r32/ebp
2059     5d/pop-to-ebp
2060     c3/return
2061 
2062 test-is-identifier-@:
2063     # character before 'A' is invalid
2064     # . prologue
2065     55/push-ebp
2066     89/<- %ebp 4/r32/esp
2067     # (eax..ecx) = "@a"
2068     b8/copy-to-eax "@a"/imm32
2069     8b/-> *eax 1/r32/ecx
2070     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2071     05/add-to-eax 4/imm32
2072     # var slice/ecx : slice = {eax, ecx}
2073     51/push-ecx
2074     50/push-eax
2075     89/<- %ecx 4/r32/esp
2076     #
2077     (is-identifier? %ecx)
2078     (check-ints-equal %eax 0 "F - test-is-identifier-@")
2079     # . epilogue
2080     89/<- %esp 5/r32/ebp
2081     5d/pop-to-ebp
2082     c3/return
2083 
2084 test-is-identifier-square-bracket:
2085     # character after 'Z' is invalid
2086     # . prologue
2087     55/push-ebp
2088     89/<- %ebp 4/r32/esp
2089     # (eax..ecx) = "[a"
2090     b8/copy-to-eax "[a"/imm32
2091     8b/-> *eax 1/r32/ecx
2092     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2093     05/add-to-eax 4/imm32
2094     # var slice/ecx : slice = {eax, ecx}
2095     51/push-ecx
2096     50/push-eax
2097     89/<- %ecx 4/r32/esp
2098     #
2099     (is-identifier? %ecx)
2100     (check-ints-equal %eax 0 "F - test-is-identifier-@")
2101     # . epilogue
2102     89/<- %esp 5/r32/ebp
2103     5d/pop-to-ebp
2104     c3/return
2105 
2106 test-is-identifier-backtick:
2107     # character before 'a' is invalid
2108     # . prologue
2109     55/push-ebp
2110     89/<- %ebp 4/r32/esp
2111     # (eax..ecx) = "`a"
2112     b8/copy-to-eax "`a"/imm32
2113     8b/-> *eax 1/r32/ecx
2114     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2115     05/add-to-eax 4/imm32
2116     # var slice/ecx : slice = {eax, ecx}
2117     51/push-ecx
2118     50/push-eax
2119     89/<- %ecx 4/r32/esp
2120     #
2121     (is-identifier? %ecx)
2122     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
2123     # . epilogue
2124     89/<- %esp 5/r32/ebp
2125     5d/pop-to-ebp
2126     c3/return
2127 
2128 test-is-identifier-curly-brace-open:
2129     # character after 'z' is invalid; also used for blocks
2130     # . prologue
2131     55/push-ebp
2132     89/<- %ebp 4/r32/esp
2133     # (eax..ecx) = "{a"
2134     b8/copy-to-eax "{a"/imm32
2135     8b/-> *eax 1/r32/ecx
2136     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2137     05/add-to-eax 4/imm32
2138     # var slice/ecx : slice = {eax, ecx}
2139     51/push-ecx
2140     50/push-eax
2141     89/<- %ecx 4/r32/esp
2142     #
2143     (is-identifier? %ecx)
2144     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
2145     # . epilogue
2146     89/<- %esp 5/r32/ebp
2147     5d/pop-to-ebp
2148     c3/return
2149 
2150 test-is-identifier-curly-brace-close:
2151     # . prologue
2152     55/push-ebp
2153     89/<- %ebp 4/r32/esp
2154     # (eax..ecx) = "}a"
2155     b8/copy-to-eax "}a"/imm32
2156     8b/-> *eax 1/r32/ecx
2157     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2158     05/add-to-eax 4/imm32
2159     # var slice/ecx : slice = {eax, ecx}
2160     51/push-ecx
2161     50/push-eax
2162     89/<- %ecx 4/r32/esp
2163     #
2164     (is-identifier? %ecx)
2165     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
2166     # . epilogue
2167     89/<- %esp 5/r32/ebp
2168     5d/pop-to-ebp
2169     c3/return
2170 
2171 test-is-identifier-hyphen:
2172     # disallow leading '-' since '->' has special meaning
2173     # . prologue
2174     55/push-ebp
2175     89/<- %ebp 4/r32/esp
2176     # (eax..ecx) = "-a"
2177     b8/copy-to-eax "-a"/imm32
2178     8b/-> *eax 1/r32/ecx
2179     8d/copy-address *(eax+ecx+4) 1/r32/ecx
2180     05/add-to-eax 4/imm32
2181     # var slice/ecx : slice = {eax, ecx}
2182     51/push-ecx
2183     50/push-eax
2184     89/<- %ecx 4/r32/esp
2185     #
2186     (is-identifier? %ecx)
2187     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
2188     # . epilogue
2189     89/<- %esp 5/r32/ebp
2190     5d/pop-to-ebp
2191     c3/return
2192 
2193 populate-mu-function-body:  # in : (addr buffered-file), out : (handle function), vars : (addr stack (handle var))
2194     # . prologue
2195     55/push-ebp
2196     89/<- %ebp 4/r32/esp
2197     # . save registers
2198     50/push-eax
2199     56/push-esi
2200     57/push-edi
2201     # esi = in
2202     8b/-> *(ebp+8) 6/r32/esi
2203     # edi = out
2204     8b/-> *(ebp+0xc) 7/r32/edi
2205     # var eax : (handle block) = parse-mu-block(in, vars)
2206     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
2207     # out->body = eax
2208     89/<- *(edi+0x10) 0/r32/eax  # Function-body
2209 $populate-mu-function-body:end:
2210     # . restore registers
2211     5f/pop-to-edi
2212     5e/pop-to-esi
2213     58/pop-to-eax
2214     # . epilogue
2215     89/<- %esp 5/r32/ebp
2216     5d/pop-to-ebp
2217     c3/return
2218 
2219 # parses a block, assuming that the leading '{' has already been read by the caller
2220 parse-mu-block:  # in : (addr buffered-file), vars : (addr stack (handle var)), fn : (handle function) -> result/eax : (handle block)
2221     # pseudocode:
2222     #   var line : (stream byte 512)
2223     #   var word-slice : slice
2224     #   result/eax = allocate(Heap, Stmt-size)
2225     #   result->tag = 0/Block
2226     #   while true                                  # line loop
2227     #     clear-stream(line)
2228     #     read-line-buffered(in, line)
2229     #     if (line->write == 0) break               # end of file
2230     #     word-slice = next-word(line)
2231     #     if slice-empty?(word-slice)               # end of line
2232     #       continue
2233     #     else if slice-starts-with?(word-slice, "#")
2234     #       continue
2235     #     else if slice-equal?(word-slice, "{")
2236     #       assert(no-tokens-in(line))
2237     #       block = parse-mu-block(in, vars, fn)
2238     #       append-to-block(result, block)
2239     #     else if slice-equal?(word-slice, "}")
2240     #       break
2241     #     else if slice-ends-with?(word-slice, ":")
2242     #       named-block = parse-mu-named-block(word-slice, line, in, vars, fn)
2243     #       append-to-block(result, named-block)
2244     #     else if slice-equal?(word-slice, "var")
2245     #       var-def = parse-mu-var-def(line, vars)
2246     #       append-to-block(result, var-def)
2247     #     else
2248     #       stmt = parse-mu-stmt(line, vars, fn)
2249     #       append-to-block(result, stmt)
2250     #   return result
2251     #
2252     # . prologue
2253     55/push-ebp
2254     89/<- %ebp 4/r32/esp
2255     # . save registers
2256     51/push-ecx
2257     52/push-edx
2258     53/push-ebx
2259     57/push-edi
2260     # var line/ecx : (stream byte 512)
2261     81 5/subop/subtract %esp 0x200/imm32
2262     68/push 0x200/imm32/length
2263     68/push 0/imm32/read
2264     68/push 0/imm32/write
2265     89/<- %ecx 4/r32/esp
2266     # var word-slice/edx : slice
2267     68/push 0/imm32/end
2268     68/push 0/imm32/start
2269     89/<- %edx 4/r32/esp
2270     # edi = result
2271     (allocate Heap *Stmt-size)  # => eax
2272     (zero-out %eax *Stmt-size)
2273     89/<- %edi 0/r32/eax
2274     { # line loop
2275 $parse-mu-block:line-loop:
2276       # line = read-line-buffered(in)
2277       (clear-stream %ecx)
2278       (read-line-buffered *(ebp+8) %ecx)
2279 #?       (write-buffered Stderr "line: ")
2280 #?       (write-stream-data Stderr %ecx)
2281 #?       (write-buffered Stderr Newline)
2282 #?       (flush Stderr)
2283       # if (line->write == 0) break
2284       81 7/subop/compare *ecx 0/imm32
2285       0f 84/jump-if-equal break/disp32
2286       # word-slice = next-word(line)
2287       (next-word %ecx %edx)
2288 #?       (write-buffered Stderr "word: ")
2289 #?       (write-slice-buffered Stderr %edx)
2290 #?       (write-buffered Stderr Newline)
2291 #?       (flush Stderr)
2292       # if slice-empty?(word-slice) continue
2293       (slice-empty? %edx)
2294       3d/compare-eax-and 0/imm32
2295       0f 85/jump-if-not-equal loop/disp32
2296       # if (slice-starts-with?(word-slice, '#') continue
2297       # . eax = *word-slice->start
2298       8b/-> *edx 0/r32/eax
2299       8a/copy-byte *eax 0/r32/AL
2300       81 4/subop/and %eax 0xff/imm32
2301       # . if (eax == '#') continue
2302       3d/compare-eax-and 0x23/imm32/hash
2303       0f 84/jump-if-equal loop/disp32
2304       # if slice-equal?(word-slice, "{")
2305       {
2306 $parse-mu-block:check-for-block:
2307         (slice-equal? %edx "{")
2308         3d/compare-eax-and 0/imm32
2309         74/jump-if-equal break/disp8
2310         (check-no-tokens-left %ecx)
2311         # parse new block and append
2312         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
2313         (append-to-block %edi %eax)
2314         e9/jump $parse-mu-block:line-loop/disp32
2315       }
2316       # if slice-equal?(word-slice, "}") break
2317 $parse-mu-block:check-for-end:
2318       (slice-equal? %edx "}")
2319       3d/compare-eax-and 0/imm32
2320       0f 85/jump-if-not-equal break/disp32
2321       # if slice-ends-with?(word-slice, ":") parse named block and append
2322       {
2323 $parse-mu-block:check-for-named-block:
2324         # . eax = *word-slice->end
2325         8b/-> *(edx+4) 0/r32/eax
2326         8a/copy-byte *eax 0/r32/AL
2327         81 4/subop/and %eax 0xff/imm32
2328         # . if (eax != ':') break
2329         3d/compare-eax-and 0x23/imm32/hash
2330         0f 85/jump-if-not-equal break/disp32
2331         #
2332         (parse-mu-named-block %edx %ecx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
2333         (append-to-block %edi %eax)
2334         e9/jump $parse-mu-block:line-loop/disp32
2335       }
2336       # if slice-equal?(word-slice, "var")
2337       {
2338 $parse-mu-block:check-for-var:
2339         (slice-equal? %edx "var")
2340         3d/compare-eax-and 0/imm32
2341         74/jump-if-equal break/disp8
2342         #
2343         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
2344         (append-to-block %edi %eax)
2345         e9/jump $parse-mu-block:line-loop/disp32
2346       }
2347 $parse-mu-block:regular-stmt:
2348       # otherwise
2349       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
2350       (append-to-block Heap %edi %eax)
2351       e9/jump loop/disp32
2352     } # end line loop
2353     # return result
2354     89/<- %eax 7/r32/edi
2355 $parse-mu-block:end:
2356     # . reclaim locals
2357     81 0/subop/add %esp 0x214/imm32
2358     # . restore registers
2359     5f/pop-to-edi
2360     5b/pop-to-ebx
2361     5a/pop-to-edx
2362     59/pop-to-ecx
2363     # . epilogue
2364     89/<- %esp 5/r32/ebp
2365     5d/pop-to-ebp
2366     c3/return
2367 
2368 $parse-mu-block:abort:
2369     # error("'{' or '}' should be on its own line, but got '")
2370     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
2371     (rewind-stream %ecx)
2372     (write-stream 2 %ecx)
2373     (write-buffered Stderr "'\n")
2374     (flush Stderr)
2375     # . syscall(exit, 1)
2376     bb/copy-to-ebx  1/imm32
2377     b8/copy-to-eax  1/imm32/exit
2378     cd/syscall  0x80/imm8
2379     # never gets here
2380 
2381 check-no-tokens-left:  # line : (addr stream byte)
2382     # . prologue
2383     55/push-ebp
2384     89/<- %ebp 4/r32/esp
2385     # . save registers
2386     50/push-eax
2387     51/push-ecx
2388     # var s/ecx : slice
2389     68/push 0/imm32/end
2390     68/push 0/imm32/start
2391     89/<- %ecx 4/r32/esp
2392     #
2393     (next-word *(ebp+8) %ecx)
2394     # if slice-empty?(s) return
2395     (slice-empty? %ecx)
2396     3d/compare-eax-and 0/imm32
2397     75/jump-if-not-equal $check-no-tokens-left:end/disp8
2398     # if (slice-starts-with?(s, '#') return
2399     # . eax = *s->start
2400     8b/-> *edx 0/r32/eax
2401     8a/copy-byte *eax 0/r32/AL
2402     81 4/subop/and %eax 0xff/imm32
2403     # . if (eax == '#') continue
2404     3d/compare-eax-and 0x23/imm32/hash
2405     74/jump-if-equal $check-no-tokens-left:end/disp8
2406     # abort
2407     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
2408     (rewind-stream %ecx)
2409     (write-stream 2 %ecx)
2410     (write-buffered Stderr "'\n")
2411     (flush Stderr)
2412     # . syscall(exit, 1)
2413     bb/copy-to-ebx  1/imm32
2414     b8/copy-to-eax  1/imm32/exit
2415     cd/syscall  0x80/imm8
2416     # never gets here
2417 $check-no-tokens-left:end:
2418     # . reclaim locals
2419     81 0/subop/add %esp 8/imm32
2420     # . restore registers
2421     59/pop-to-ecx
2422     58/pop-to-eax
2423     # . epilogue
2424     89/<- %esp 5/r32/ebp
2425     5d/pop-to-ebp
2426     c3/return
2427 
2428 parse-mu-named-block:  # name : (addr slice), first-line : (addr stream byte), in : (addr buffered-file), vars : (addr stack (handle var)) -> result/eax : (handle stmt)
2429     # pseudocode:
2430     #   var line : (stream byte 512)
2431     #   var word-slice : slice
2432     #   result/eax = allocate(Heap, Stmt-size)
2433     #   result->tag = 4/Named-block
2434     #   result->name = name
2435     #   assert(next-word(first-line) == "{")
2436     #   assert(no-tokens-in(first-line))
2437     #   while true                                  # line loop
2438     #     clear-stream(line)
2439     #     read-line-buffered(in, line)
2440     #     if (line->write == 0) break               # end of file
2441     #     word-slice = next-word(line)
2442     #     if slice-empty?(word-slice)               # end of line
2443     #       break
2444     #     else if slice-equal?(word-slice, "{")
2445     #       block = parse-mu-block(in, vars)
2446     #       append-to-block(result, block)
2447     #     else if slice-equal?(word-slice, "}")
2448     #       break
2449     #     else if slice-ends-with?(word-slice, ":")
2450     #       named-block = parse-mu-named-block(word-slice, in, vars)
2451     #       append-to-block(result, named-block)
2452     #     else if slice-equal?(word-slice, "var")
2453     #       var-def = parse-mu-var-def(line, vars)
2454     #       append-to-block(result, var-def)
2455     #     else
2456     #       stmt = parse-mu-stmt(line, vars, fn)
2457     #       append-to-block(result, stmt)
2458     #   return result
2459     #
2460     # . prologue
2461     55/push-ebp
2462     89/<- %ebp 4/r32/esp
2463     # . save registers
2464 $parse-mu-named-block:end:
2465     # . reclaim locals
2466     # . restore registers
2467     # . epilogue
2468     89/<- %esp 5/r32/ebp
2469     5d/pop-to-ebp
2470     c3/return
2471 
2472 parse-mu-var-def:  # line : (addr stream byte), vars : (addr stack (handle var)) -> result/eax : (handle stmt)
2473     # pseudocode:
2474     #
2475     # . prologue
2476     55/push-ebp
2477     89/<- %ebp 4/r32/esp
2478     # . save registers
2479 $parse-mu-var-def:end:
2480     # . reclaim locals
2481     # . restore registers
2482     # . epilogue
2483     89/<- %esp 5/r32/ebp
2484     5d/pop-to-ebp
2485     c3/return
2486 
2487 parse-mu-stmt:  # line : (addr stream byte), vars : (addr stack (handle var)), fn : (handle function) -> result/eax : (handle stmt)
2488     # pseudocode:
2489     #   var name : slice
2490     #   result = allocate(Heap, Stmt-size)
2491     #   if stmt-has-outputs?(line)
2492     #     while true
2493     #       name = next-word(line)
2494     #       if (name == '<-') break
2495     #       assert(is-identifier?(name))
2496     #       var v : (handle var) = lookup-or-define-var(name, vars)
2497     #       result->outputs = append(result->outputs, v)
2498     #   result->name = slice-to-string(next-word(line))
2499     #   while true
2500     #     name = next-word-or-string(line)
2501     #     v = lookup-var-or-literal(name)
2502     #     result->inouts = append(result->inouts, v)
2503     #
2504     # . prologue
2505     55/push-ebp
2506     89/<- %ebp 4/r32/esp
2507     # . save registers
2508     51/push-ecx
2509     57/push-edi
2510     # var name/ecx : slice
2511     68/push 0/imm32/end
2512     68/push 0/imm32/start
2513     89/<- %ecx 4/r32/esp
2514     # result/edi : (handle stmt)
2515     (allocate Heap *Stmt-size)  # => eax
2516     (zero-out %eax *Stmt-size)
2517     89/<- %edi 0/r32/eax
2518     # result->tag = 1/stmt
2519     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
2520     {
2521       (stmt-has-outputs? *(ebp+8))
2522       3d/compare-eax-and 0/imm32
2523       0f 84/jump-if-equal break/disp32
2524       {
2525 $parse-mu-stmt:read-outputs:
2526         # name = next-word(line)
2527         (next-word *(ebp+8) %ecx)
2528         # if slice-empty?(word-slice) break
2529         (slice-empty? %ecx)
2530         3d/compare-eax-and 0/imm32
2531         0f 85/jump-if-not-equal break/disp32
2532         # if (name == "<-") break
2533         (slice-equal? %ecx "<-")
2534         3d/compare-eax-and 0/imm32
2535         75/jump-if-not-equal break/disp8
2536         # assert(is-identifier?(name))
2537         (is-identifier? %ecx)
2538         3d/compare-eax-and 0/imm32
2539         0f 84/jump-if-equal $parse-mu-stmt:abort/disp32
2540         #
2541         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
2542         (append-list Heap %eax *(edi+0xc))  # Stmt1-outputs => eax
2543         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
2544         e9/jump loop/disp32
2545       }
2546     }
2547 $parse-mu-stmt:read-operation:
2548     (next-word *(ebp+8) %ecx)
2549     (slice-to-string Heap %ecx)
2550     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation
2551     {
2552 $parse-mu-stmt:read-inouts:
2553       # name = next-word-or-string(line)
2554       (next-word-or-string *(ebp+8) %ecx)
2555       # if slice-empty?(word-slice) break
2556       (slice-empty? %ecx)
2557       3d/compare-eax-and 0/imm32
2558       0f 85/jump-if-not-equal break/disp32
2559       # if (name == "<-") abort
2560       (slice-equal? %ecx "<-")
2561       3d/compare-eax-and 0/imm32
2562       0f 85/jump-if-not-equal $parse-mu-stmt:abort2/disp32
2563       #
2564       (lookup-var-or-literal %ecx *(ebp+0xc))  # => eax
2565       (append-list Heap %eax *(edi+8))  # Stmt1-inouts => eax
2566       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts
2567       e9/jump loop/disp32
2568     }
2569 $parse-mu-stmt:end:
2570     # return result
2571     89/<- %eax 7/r32/edi
2572     # . reclaim locals
2573     81 0/subop/add %esp 8/imm32
2574     # . restore registers
2575     5f/pop-to-edi
2576     59/pop-to-ecx
2577     # . epilogue
2578     89/<- %esp 5/r32/ebp
2579     5d/pop-to-ebp
2580     c3/return
2581 
2582 $parse-mu-stmt:abort:
2583     # error("invalid identifier '" name "'\n")
2584     (write-buffered Stderr "invalid identifier '")
2585     (write-slice-buffered Stderr %ecx)
2586     (write-buffered Stderr "'\n")
2587     (flush Stderr)
2588     # . syscall(exit, 1)
2589     bb/copy-to-ebx  1/imm32
2590     b8/copy-to-eax  1/imm32/exit
2591     cd/syscall  0x80/imm8
2592     # never gets here
2593 
2594 $parse-mu-stmt:abort2:
2595     # error("invalid statement '" line "'\n")
2596     (rewind-stream *(ebp+8))
2597     (write-buffered Stderr "invalid identifier '")
2598     (write-stream Stderr *(ebp+8))
2599     (write-buffered Stderr "'\n")
2600     (flush Stderr)
2601     # . syscall(exit, 1)
2602     bb/copy-to-ebx  1/imm32
2603     b8/copy-to-eax  1/imm32/exit
2604     cd/syscall  0x80/imm8
2605     # never gets here
2606 
2607 stmt-has-outputs?:  # line : (addr stream byte) -> result/eax : boolean
2608     # . prologue
2609     55/push-ebp
2610     89/<- %ebp 4/r32/esp
2611     # . save registers
2612     51/push-ecx
2613     # var word-slice/ecx : slice
2614     68/push 0/imm32/end
2615     68/push 0/imm32/start
2616     89/<- %ecx 4/r32/esp
2617     # result = false
2618     b8/copy-to-eax 0/imm32/false
2619     (rewind-stream *(ebp+8))
2620     {
2621       (next-word-or-string *(ebp+8) %ecx)
2622       # if slice-empty?(word-slice) break
2623       (slice-empty? %ecx)
2624       3d/compare-eax-and 0/imm32
2625       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
2626       0f 85/jump-if-not-equal break/disp32
2627       # if slice-starts-with?(word-slice, '#') break
2628       # . eax = *word-slice->start
2629       8b/-> *ecx 0/r32/eax
2630       8a/copy-byte *eax 0/r32/AL
2631       81 4/subop/and %eax 0xff/imm32
2632       # . if (eax == '#') break
2633       3d/compare-eax-and 0x23/imm32/hash
2634       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
2635       0f 84/jump-if-equal break/disp32
2636       # if slice-equal?(word-slice, '<-') return true
2637       (slice-equal? %ecx "<-")
2638       3d/compare-eax-and 0/imm32
2639       74/jump-if-equal loop/disp8
2640       b8/copy-to-eax 1/imm32/true
2641     }
2642 $stmt-has-outputs:end:
2643     (rewind-stream *(ebp+8))
2644     # . reclaim locals
2645     81 0/subop/add %esp 8/imm32
2646     # . restore registers
2647     59/pop-to-ecx
2648     # . epilogue
2649     89/<- %esp 5/r32/ebp
2650     5d/pop-to-ebp
2651     c3/return
2652 
2653 # if 'name' starts with a digit, create a new literal var for it
2654 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
2655 lookup-var-or-literal:  # name: (addr slice), vars : (addr stack (handle var)) -> result/eax: (handle var)
2656     # . prologue
2657     55/push-ebp
2658     89/<- %ebp 4/r32/esp
2659     # . save registers
2660     51/push-ecx
2661     56/push-esi
2662     # esi = name
2663     8b/-> *(ebp+8) 6/r32/esi
2664     # if slice-empty?(name) abort
2665     (slice-empty? %esi)  # => eax
2666     3d/compare-eax-and 0/imm32
2667     0f 85/jump-if-not-equal $lookup-var-or-literal:abort/disp32
2668     # var ecx : byte = *name->start
2669     8b/-> *esi 1/r32/ecx
2670     8a/copy-byte *ecx 1/r32/CL
2671     81 4/subop/and %ecx 0xff/imm32
2672     # if is-decimal-digit?(*name->start) return new var(name)
2673     (is-decimal-digit? %ecx)  # => eax
2674     81 7/subop/compare %eax 0/imm32
2675     {
2676       74/jump-if-equal break/disp8
2677       (new-literal-integer Heap %esi)  # => eax
2678     }
2679     # otherwise return lookup-var(name, vars)
2680     {
2681       75/jump-if-not-equal break/disp8
2682       (lookup-var %esi *(ebp+0xc))  # => eax
2683     }
2684 $lookup-var-or-literal:end:
2685     # . restore registers
2686     5e/pop-to-esi
2687     59/pop-to-ecx
2688     # . epilogue
2689     89/<- %esp 5/r32/ebp
2690     5d/pop-to-ebp
2691     c3/return
2692 
2693 $lookup-var-or-literal:abort:
2694     (write-buffered Stderr "empty variable!")
2695     (flush Stderr)
2696     # . syscall(exit, 1)
2697     bb/copy-to-ebx  1/imm32
2698     b8/copy-to-eax  1/imm32/exit
2699     cd/syscall  0x80/imm8
2700     # never gets here
2701 
2702 # return first 'name' from the top (back) of 'vars' and abort if not found
2703 lookup-var:  # name: (addr slice), vars : (addr stack (handle var)) -> result/eax: (handle var)
2704     # . prologue
2705     55/push-ebp
2706     89/<- %ebp 4/r32/esp
2707     # var target/eax : (handle array byte) = slice-to-string(name)
2708     (slice-to-string Heap *(ebp+8))  # => eax
2709     #
2710     (lookup-var-helper %eax *(ebp+0xc))  # => eax
2711     # if (result == 0) abort
2712     3d/compare-eax-and 0/imm32
2713     74/jump-if-equal $lookup-var:abort/disp8
2714 $lookup-var:end:
2715     # . epilogue
2716     89/<- %esp 5/r32/ebp
2717     5d/pop-to-ebp
2718     c3/return
2719 
2720 $lookup-var:abort:
2721     (write-buffered Stderr "unknown variable '")
2722     (write-slice-buffered Stderr *(ebp+8))
2723     (write-buffered Stderr "'\n")
2724     (flush Stderr)
2725     # . syscall(exit, 1)
2726     bb/copy-to-ebx  1/imm32
2727     b8/copy-to-eax  1/imm32/exit
2728     cd/syscall  0x80/imm8
2729     # never gets here
2730 
2731 # return first 'name' from the top (back) of 'vars', and 0/null if not found
2732 lookup-var-helper:  # name: (addr array byte), vars : (addr stack (handle var)) -> result/eax: (handle var)
2733     # pseudocode:
2734     #   var curr : (addr handle var) = &vars->data[vars->top - 4]
2735     #   var min = vars->data
2736     #   while curr >= min
2737     #     var v : (handle var) = *curr
2738     #     if v->name == name
2739     #       return v
2740     #   return 0
2741     #
2742     # . prologue
2743     55/push-ebp
2744     89/<- %ebp 4/r32/esp
2745     # . save registers
2746     52/push-edx
2747     53/push-ebx
2748     56/push-esi
2749     # esi = vars
2750     8b/-> *(ebp+0xc) 6/r32/esi
2751     # ebx = vars->top
2752     8b/-> *esi 3/r32/ebx
2753     # if (vars->top > vars->length) abort
2754     3b/compare 0/r32/eax *(esi+4)
2755     0f 8f/jump-if-greater $lookup-var-helper:error1/disp32
2756     # var min/edx : (addr handle var) = vars->data
2757     8d/copy-address *(esi+8) 2/r32/edx
2758     # var curr/ebx : (addr handle var) = &vars->data[vars->top - 4]
2759     81 5/subop/subtract %ebx 4/imm32
2760     8d/copy-address *(esi+ebx+8) 3/r32/ebx
2761     {
2762       # if (curr < min) return 0
2763       39/compare %ebx 2/r32/edx
2764       b8/copy-to-eax 0/imm32
2765       0f 82/jump-if-lesser-unsigned break/disp32
2766       # var v/eax : (handle var) = *curr
2767       8b/-> *ebx 0/r32/eax
2768       # if (v->name == name) return v
2769       (string-equal? *eax *(ebp+8))  # Var-name
2770       3d/compare-eax-and 0/imm32
2771       8b/-> *ebx 0/r32/eax
2772       75/jump-if-not-equal break/disp8
2773       # curr -= 4
2774       81 5/subop/subtract %ebx 4/imm32
2775       e9/jump loop/disp32
2776     }
2777 $lookup-var-helper:end:
2778     # . restore registers
2779     5e/pop-to-esi
2780     5b/pop-to-ebx
2781     5a/pop-to-edx
2782     # . epilogue
2783     89/<- %esp 5/r32/ebp
2784     5d/pop-to-ebp
2785     c3/return
2786 
2787 $lookup-var-helper:error1:
2788     (write-buffered Stderr "malformed stack when looking up '")
2789     (write-slice-buffered Stderr *(ebp+8))
2790     (write-buffered Stderr "'\n")
2791     (flush Stderr)
2792     # . syscall(exit, 1)
2793     bb/copy-to-ebx  1/imm32
2794     b8/copy-to-eax  1/imm32/exit
2795     cd/syscall  0x80/imm8
2796     # never gets here
2797 
2798 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
2799 lookup-or-define-var:  # name: (addr slice), vars : (addr stack (handle var)), fn : (handle function) -> result/eax: (handle var)
2800     # . prologue
2801     55/push-ebp
2802     89/<- %ebp 4/r32/esp
2803     # . save registers
2804     51/push-ecx
2805     # var target/ecx : (handle array byte) = slice-to-string(name)
2806     (slice-to-string Heap *(ebp+8))  # => eax
2807     89/<- %ecx 0/r32/eax
2808     #
2809     (lookup-var-helper *(ebp+8) *(ebp+0xc))  # => eax
2810     {
2811       # if (result != 0) return
2812       3d/compare-eax-and 0/imm32
2813       75/jump-if-not-equal break/disp8
2814       # if name is one of fn's outputs, return it
2815       {
2816         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
2817         3d/compare-eax-and 0/imm32
2818         # otherwise abort
2819         0f 84/jump-if-not-equal $lookup-var:abort/disp32
2820       }
2821     }
2822 $lookup-or-define-var:end:
2823     # . restore registers
2824     59/pop-to-ecx
2825     # . epilogue
2826     89/<- %esp 5/r32/ebp
2827     5d/pop-to-ebp
2828     c3/return
2829 
2830 find-in-function-outputs:  # fn : (handle function), name : (handle array byte) => result/eax : (handle var)
2831     # . prologue
2832     55/push-ebp
2833     89/<- %ebp 4/r32/esp
2834     # . save registers
2835     51/push-ecx
2836     # var curr/ecx : (handle list var) = fn->outputs
2837     8b/-> *(ebp+8) 1/r32/ecx
2838     8b/-> *(ecx+0xc) 1/r32/ecx
2839     # while curr != null
2840     {
2841       81 7/subop/compare %ecx 0/imm32
2842       74/jump-if-equal break/disp8
2843       # var v : (handle var) = *curr
2844       8b/-> *ecx 0/r32/eax  # List-value
2845       # if (curr->name == name) return curr
2846       50/push-eax
2847       (string-equal? *eax *(ebp+0xc))
2848       3d/compare-eax-and 0/imm32
2849       58/pop-to-eax
2850       75/jump-if-not-equal $find-in-function-outputs:end/disp8
2851       # curr = curr->next
2852       8b/-> *(ecx+4) 1/r32/ecx  # List-next
2853       eb/jump loop/disp8
2854     }
2855     b8/copy-to-eax 0/imm32
2856 $find-in-function-outputs:end:
2857     # . restore registers
2858     59/pop-to-ecx
2859     # . epilogue
2860     89/<- %esp 5/r32/ebp
2861     5d/pop-to-ebp
2862     c3/return
2863 
2864 test-parse-mu-stmt:
2865     # 'increment n'
2866     # . prologue
2867     55/push-ebp
2868     89/<- %ebp 4/r32/esp
2869     # setup
2870     (clear-stream _test-input-stream)
2871     (write _test-input-stream "increment n\n")
2872     # var vars/ecx : (stack (addr var) 4)
2873     81 5/subop/subtract %esp 0x10/imm32
2874     68/push 0x10/imm32/length
2875     68/push 0/imm32/top
2876     89/<- %ecx 4/r32/esp
2877     (clear-stack %ecx)
2878     # var v/edx : var
2879     81 5/subop/subtract %esp 0x14/imm32  # Var-size
2880     89/<- %edx 4/r32/esp
2881     (zero-out %edx 0x14)
2882     # v->name = "n"
2883     c7 0/subop/copy *edx "n"/imm32  # Var-name
2884     #
2885     (push %ecx %edx)
2886     # convert
2887     (parse-mu-stmt _test-input-stream %ecx)
2888     # check result
2889     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
2890     # edx : (handle list var) = result->inouts
2891     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
2892     # ebx : (handle var) = result->inouts->value
2893     8b/-> *edx 3/r32/ebx  # List-value
2894     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
2895     # . epilogue
2896     89/<- %esp 5/r32/ebp
2897     5d/pop-to-ebp
2898     c3/return
2899 
2900 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)
2901     # . prologue
2902     55/push-ebp
2903     89/<- %ebp 4/r32/esp
2904     # . save registers
2905     51/push-ecx
2906     #
2907     (allocate *(ebp+8) *Function-size)  # => eax
2908     8b/-> *(ebp+0xc) 1/r32/ecx
2909     89/<- *eax 1/r32/ecx  # Function-name
2910     8b/-> *(ebp+0x10) 1/r32/ecx
2911     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
2912     8b/-> *(ebp+0x14) 1/r32/ecx
2913     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
2914     8b/-> *(ebp+0x18) 1/r32/ecx
2915     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
2916     8b/-> *(ebp+0x1c) 1/r32/ecx
2917     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
2918     8b/-> *(ebp+0x20) 1/r32/ecx
2919     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
2920 $new-function:end:
2921     # . restore registers
2922     59/pop-to-ecx
2923     # . epilogue
2924     89/<- %esp 5/r32/ebp
2925     5d/pop-to-ebp
2926     c3/return
2927 
2928 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)
2929     # . prologue
2930     55/push-ebp
2931     89/<- %ebp 4/r32/esp
2932     # . save registers
2933     51/push-ecx
2934     #
2935     (allocate *(ebp+8) *Var-size)  # => eax
2936     8b/-> *(ebp+0xc) 1/r32/ecx
2937     89/<- *eax 1/r32/ecx  # Var-name
2938     8b/-> *(ebp+0x10) 1/r32/ecx
2939     89/<- *(eax+4) 1/r32/ecx  # Var-type
2940     8b/-> *(ebp+0x14) 1/r32/ecx
2941     89/<- *(eax+8) 1/r32/ecx  # Var-block
2942     8b/-> *(ebp+0x18) 1/r32/ecx
2943     89/<- *(eax+0xc) 1/r32/ecx  # Var-stack-offset
2944     8b/-> *(ebp+0x1c) 1/r32/ecx
2945     89/<- *(eax+0x10) 1/r32/ecx  # Var-register
2946 $new-var:end:
2947     # . restore registers
2948     59/pop-to-ecx
2949     # . epilogue
2950     89/<- %esp 5/r32/ebp
2951     5d/pop-to-ebp
2952     c3/return
2953 
2954 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
2955     # . prologue
2956     55/push-ebp
2957     89/<- %ebp 4/r32/esp
2958     # . save registers
2959     51/push-ecx
2960     # if (!is-hex-int?(name)) abort
2961     (is-hex-int? *(ebp+0xc))  # => eax
2962     3d/compare-eax-and 0/imm32
2963     0f 84/jump-if-equal $new-literal-integer:abort/disp32
2964     # var s/ecx : (addr array byte)
2965     (slice-to-string Heap *(ebp+0xc))  # => eax
2966     89/<- %ecx 0/r32/eax
2967     #
2968     (allocate *(ebp+8) *Var-size)  # => eax
2969     89/<- *eax 1/r32/ecx  # Var-name
2970     89/<- %ecx 0/r32/eax
2971     (allocate *(ebp+8) *Tree-size)  # => eax
2972     89/<- *(ecx+4) 0/r32/eax  # Var-type
2973     89/<- %eax 1/r32/ecx
2974     c7 0/subop/copy *(eax+8) 0/imm32  # Var-block
2975     c7 0/subop/copy *(eax+0xc) 0/imm32  # Var-stack-offset
2976     c7 0/subop/copy *(eax+0x10) 0/imm32  # Var-register
2977 $new-literal-integer:end:
2978     # . restore registers
2979     59/pop-to-ecx
2980     # . epilogue
2981     89/<- %esp 5/r32/ebp
2982     5d/pop-to-ebp
2983     c3/return
2984 
2985 $new-literal-integer:abort:
2986     (write-buffered Stderr "variable cannot begin with a digit '")
2987     (write-slice-buffered Stderr *(ebp+0xc))
2988     (write-buffered Stderr "'\n")
2989     (flush Stderr)
2990     # . syscall(exit, 1)
2991     bb/copy-to-ebx  1/imm32
2992     b8/copy-to-eax  1/imm32/exit
2993     cd/syscall  0x80/imm8
2994     # never gets here
2995 
2996 new-block:  # ad: (addr allocation-descriptor), data: (handle list statement) -> result/eax: (handle statement)
2997     # . prologue
2998     55/push-ebp
2999     89/<- %ebp 4/r32/esp
3000     # . save registers
3001     51/push-ecx
3002     #
3003     (allocate *(ebp+8) *Stmt-size)  # => eax
3004     (zero-out %eax *Stmt-size)
3005     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
3006     8b/-> *(ebp+0xc) 1/r32/ecx
3007     89/<- *(eax+4) 1/r32/ecx  # Block-statements
3008 $new-block:end:
3009     # . restore registers
3010     59/pop-to-ecx
3011     # . epilogue
3012     89/<- %esp 5/r32/ebp
3013     5d/pop-to-ebp
3014     c3/return
3015 
3016 new-stmt:  # ad: (addr allocation-descriptor), operation: (addr array byte), inouts: (handle list var), outputs: (handle list var) -> result/eax: (handle statement)
3017     # . prologue
3018     55/push-ebp
3019     89/<- %ebp 4/r32/esp
3020     # . save registers
3021     51/push-ecx
3022     #
3023     (allocate *(ebp+8) *Stmt-size)  # => eax
3024     (zero-out %eax *Stmt-size)
3025     c7 0/subop/copy *eax 1/imm32/tag/regular-stmt  # Stmt-tag
3026     8b/-> *(ebp+0xc) 1/r32/ecx
3027     89/<- *(eax+4) 1/r32/ecx  # Stmt1-operation
3028     8b/-> *(ebp+0x10) 1/r32/ecx
3029     89/<- *(eax+8) 1/r32/ecx  # Stmt1-inouts
3030     8b/-> *(ebp+0x14) 1/r32/ecx
3031     89/<- *(eax+0xc) 1/r32/ecx  # Stmt1-outputs
3032 $new-stmt:end:
3033     # . restore registers
3034     59/pop-to-ecx
3035     # . epilogue
3036     89/<- %esp 5/r32/ebp
3037     5d/pop-to-ebp
3038     c3/return
3039 
3040 new-vardef:  # ad: (addr allocation-descriptor), name: (addr array byte), type: int -> result/eax: (handle statement)
3041     # . prologue
3042     55/push-ebp
3043     89/<- %ebp 4/r32/esp
3044     # . save registers
3045     51/push-ecx
3046     #
3047     (allocate *(ebp+8) *Stmt-size)  # => eax
3048     (zero-out %eax *Stmt-size)
3049     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
3050     8b/-> *(ebp+0xc) 1/r32/ecx
3051     89/<- *(eax+4) 1/r32/ecx  # Vardef-name
3052     8b/-> *(ebp+0x10) 1/r32/ecx
3053     89/<- *(eax+8) 1/r32/ecx  # Vardef-type
3054 $new-vardef:end:
3055     # . restore registers
3056     59/pop-to-ecx
3057     # . epilogue
3058     89/<- %esp 5/r32/ebp
3059     5d/pop-to-ebp
3060     c3/return
3061 
3062 new-regvardef:  # ad: (addr allocation-descriptor), name: (addr array byte), type: int, register: (addr array byte) -> result/eax: (handle statement)
3063     # . prologue
3064     55/push-ebp
3065     89/<- %ebp 4/r32/esp
3066     # . save registers
3067     51/push-ecx
3068     #
3069     (allocate *(ebp+8) *Stmt-size)  # => eax
3070     (zero-out %eax *Stmt-size)
3071     c7 0/subop/copy *eax 3/imm32/tag/var-in-register
3072     8b/-> *(ebp+0xc) 1/r32/ecx
3073     89/<- *(eax+4) 1/r32/ecx  # Regvardef-name
3074     8b/-> *(ebp+0x10) 1/r32/ecx
3075     89/<- *(eax+8) 1/r32/ecx  # Regvardef-type
3076     8b/-> *(ebp+0x14) 1/r32/ecx
3077     89/<- *(eax+0xc) 1/r32/ecx  # Regvardef-register
3078 $new-regvardef:end:
3079     # . restore registers
3080     59/pop-to-ecx
3081     # . epilogue
3082     89/<- %esp 5/r32/ebp
3083     5d/pop-to-ebp
3084     c3/return
3085 
3086 new-named-block:  # ad: (addr allocation-descriptor), name: (addr array byte), data: (handle list statement) -> result/eax: (handle statement)
3087     # . prologue
3088     55/push-ebp
3089     89/<- %ebp 4/r32/esp
3090     # . save registers
3091     51/push-ecx
3092     #
3093     (allocate *(ebp+8) *Stmt-size)  # => eax
3094     (zero-out %eax *Stmt-size)
3095     c7 0/subop/copy *eax 4/imm32/tag/named-block
3096     8b/-> *(ebp+0xc) 1/r32/ecx
3097     89/<- *(eax+4) 1/r32/ecx  # Named-block-name
3098     8b/-> *(ebp+0x10) 1/r32/ecx
3099     89/<- *(eax+8) 1/r32/ecx  # Named-block-statements
3100 $new-named-block:end:
3101     # . restore registers
3102     59/pop-to-ecx
3103     # . epilogue
3104     89/<- %esp 5/r32/ebp
3105     5d/pop-to-ebp
3106     c3/return
3107 
3108 new-list:  # ad: (addr allocation-descriptor), value: _type, next: (handle list _type) -> result/eax : (handle list _type)
3109     # . prologue
3110     55/push-ebp
3111     89/<- %ebp 4/r32/esp
3112     # . save registers
3113     51/push-ecx
3114     #
3115     (allocate *(ebp+8) *List-size)  # => eax
3116     8b/-> *(ebp+0xc) 1/r32/ecx
3117     89/<- *eax 1/r32/ecx  # List-value
3118     8b/-> *(ebp+0x10) 1/r32/ecx
3119     89/<- *(eax+4) 1/r32/ecx  # List-next
3120 $new-list:end:
3121     # . restore registers
3122     59/pop-to-ecx
3123     # . epilogue
3124     89/<- %esp 5/r32/ebp
3125     5d/pop-to-ebp
3126     c3/return
3127 
3128 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax : (handle list _type)
3129     # . prologue
3130     55/push-ebp
3131     89/<- %ebp 4/r32/esp
3132     # . save registers
3133     51/push-ecx
3134     #
3135     (allocate *(ebp+8) *List-size)  # => eax
3136     8b/-> *(ebp+0xc) 1/r32/ecx
3137     89/<- *eax 1/r32/ecx  # List-value
3138     # if (list == null) return result
3139     81 7/subop/compare *(ebp+0x10) 0/imm32
3140     74/jump-if-equal $new-list:end/disp8
3141     # otherwise append
3142     # var curr/ecx = list
3143     8b/-> *(ebp+0x10) 1/r32/ecx
3144     # while (curr->next != null) curr = curr->next
3145     {
3146       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
3147       74/jump-if-equal break/disp8
3148       # curr = curr->next
3149       8b/-> *(ecx+4) 1/r32/ecx
3150       eb/jump loop/disp8
3151     }
3152     # curr->next = result
3153     89/<- *(ecx+4) 0/r32/eax
3154     # return list
3155     8b/-> *(ebp+0x10) 0/r32/eax
3156 $append-list:end:
3157     # . restore registers
3158     59/pop-to-ecx
3159     # . epilogue
3160     89/<- %esp 5/r32/ebp
3161     5d/pop-to-ebp
3162     c3/return
3163 
3164 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
3165     # . prologue
3166     55/push-ebp
3167     89/<- %ebp 4/r32/esp
3168     # . save registers
3169     56/push-esi
3170     # esi = block
3171     8b/-> *(ebp+0xc) 6/r32/esi
3172     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-statements
3173     89/<- *(esi+4) 0/r32/eax  # Block-statements
3174 $append-to-block:end:
3175     # . restore registers
3176     5e/pop-to-esi
3177     # . epilogue
3178     89/<- %esp 5/r32/ebp
3179     5d/pop-to-ebp
3180     c3/return
3181 
3182 #######################################################
3183 # Type-checking
3184 #######################################################
3185 
3186 check-mu-types:
3187     # . prologue
3188     55/push-ebp
3189     89/<- %ebp 4/r32/esp
3190     #
3191 $check-mu-types:end:
3192     # . epilogue
3193     89/<- %esp 5/r32/ebp
3194     5d/pop-to-ebp
3195     c3/return
3196 
3197 size-of:  # n : (addr var)
3198     # . prologue
3199     55/push-ebp
3200     89/<- %ebp 4/r32/esp
3201     # hard-coded since we only support 'int' types for now
3202     b8/copy-to-eax 4/imm32
3203 $size-of:end:
3204     # . epilogue
3205     89/<- %esp 5/r32/ebp
3206     5d/pop-to-ebp
3207     c3/return
3208 
3209 #######################################################
3210 # Code-generation
3211 #######################################################
3212 
3213 emit-subx:  # out : (addr buffered-file)
3214     # . prologue
3215     55/push-ebp
3216     89/<- %ebp 4/r32/esp
3217     # . save registers
3218     50/push-eax
3219     51/push-ecx
3220     57/push-edi
3221     # edi = out
3222     8b/-> *(ebp+8) 7/r32/edi
3223     # var curr/ecx : (handle function) = *Program
3224     8b/-> *Program 1/r32/ecx
3225     {
3226       # if (curr == null) break
3227       81 7/subop/compare %ecx 0/imm32
3228       0f 84/jump-if-equal break/disp32
3229       (emit-subx-function %edi %ecx)
3230       # curr = curr->next
3231       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
3232       e9/jump loop/disp32
3233     }
3234 $emit-subx:end:
3235     # . restore registers
3236     5f/pop-to-edi
3237     59/pop-to-ecx
3238     58/pop-to-eax
3239     # . epilogue
3240     89/<- %esp 5/r32/ebp
3241     5d/pop-to-ebp
3242     c3/return
3243 
3244 emit-subx-function:  # out : (addr buffered-file), f : (handle function)
3245     # . prologue
3246     55/push-ebp
3247     89/<- %ebp 4/r32/esp
3248     # . save registers
3249     50/push-eax
3250     51/push-ecx
3251     57/push-edi
3252     # edi = out
3253     8b/-> *(ebp+8) 7/r32/edi
3254     # ecx = f
3255     8b/-> *(ebp+0xc) 1/r32/ecx
3256     #
3257     (write-buffered %edi *ecx)
3258     (write-buffered %edi ":\n")
3259     (emit-subx-prologue %edi)
3260     (emit-subx-block %edi *(ecx+0x10))  # Function-body
3261     (emit-subx-epilogue %edi)
3262 $emit-subx-function:end:
3263     # . restore registers
3264     5f/pop-to-edi
3265     59/pop-to-ecx
3266     58/pop-to-eax
3267     # . epilogue
3268     89/<- %esp 5/r32/ebp
3269     5d/pop-to-ebp
3270     c3/return
3271 
3272 emit-subx-block:  # out : (addr buffered-file), block : (handle block)
3273     # . prologue
3274     55/push-ebp
3275     89/<- %ebp 4/r32/esp
3276     # curr/esi : (handle list statement) = block->statements
3277     8b/-> *(ebp+0xc) 6/r32/esi
3278     8b/-> *(esi+4) 6/r32/esi  # Block-statements
3279     #
3280     {
3281 $emit-subx-block:check-empty:
3282       81 7/subop/compare %esi 0/imm32
3283       0f 84/jump-if-equal break/disp32
3284       (write-buffered *(ebp+8) "{\n")
3285       {
3286 $emit-subx-block:stmt:
3287         81 7/subop/compare %esi 0/imm32
3288         74/jump-if-equal break/disp8
3289         (emit-subx-statement *(ebp+8) *esi Primitives *Program)
3290         (write-buffered *(ebp+8) Newline)
3291         8b/-> *(esi+4) 6/r32/esi  # List-next
3292         eb/jump loop/disp8
3293       }
3294       (write-buffered *(ebp+8) "}\n")
3295     }
3296 $emit-subx-block:end:
3297     # . epilogue
3298     89/<- %esp 5/r32/ebp
3299     5d/pop-to-ebp
3300     c3/return
3301 
3302 emit-subx-statement:  # out : (addr buffered-file), stmt : (handle statement), primitives : (handle primitive), functions : (handle function)
3303     # . prologue
3304     55/push-ebp
3305     89/<- %ebp 4/r32/esp
3306     # . save registers
3307     50/push-eax
3308     51/push-ecx
3309     # if stmt matches a primitive, emit it
3310     {
3311 $emit-subx-statement:primitive:
3312       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
3313       3d/compare-eax-and 0/imm32
3314       74/jump-if-equal break/disp8
3315       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
3316       e9/jump $emit-subx-statement:end/disp32
3317     }
3318     # else if stmt matches a function, emit a call to it
3319     {
3320 $emit-subx-statement:call:
3321       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
3322       3d/compare-eax-and 0/imm32
3323       74/jump-if-equal break/disp8
3324       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
3325       e9/jump $emit-subx-statement:end/disp32
3326     }
3327     # else abort
3328     e9/jump $emit-subx-statement:abort/disp32
3329 $emit-subx-statement:end:
3330     # . restore registers
3331     59/pop-to-ecx
3332     58/pop-to-eax
3333     # . epilogue
3334     89/<- %esp 5/r32/ebp
3335     5d/pop-to-ebp
3336     c3/return
3337 
3338 $emit-subx-statement:abort:
3339     # error("couldn't translate '" stmt "'\n")
3340     (write-buffered Stderr "couldn't translate '")
3341 #?     (emit-string Stderr *(ebp+0xc))  # TODO
3342     (write-buffered Stderr "'\n")
3343     (flush Stderr)
3344     # . syscall(exit, 1)
3345     bb/copy-to-ebx  1/imm32
3346     b8/copy-to-eax  1/imm32/exit
3347     cd/syscall  0x80/imm8
3348     # never gets here
3349 
3350 # Primitives supported
3351 # For each operation, put variants with hard-coded registers before flexible ones.
3352 == data
3353 Primitives:
3354 # - increment/decrement
3355 _Primitive-inc-eax:
3356     # var/eax <- increment => 40/increment-eax
3357     "increment"/imm32/name
3358     0/imm32/no-inouts
3359     Single-int-var-in-eax/imm32/outputs
3360     "40/increment-eax"/imm32/subx-name
3361     0/imm32/no-rm32
3362     0/imm32/no-r32
3363     0/imm32/no-imm32
3364     0/imm32/output-is-write-only
3365     _Primitive-inc-ecx/imm32/next
3366 _Primitive-inc-ecx:
3367     # var/ecx <- increment => 41/increment-ecx
3368     "increment"/imm32/name
3369     0/imm32/no-inouts
3370     Single-int-var-in-ecx/imm32/outputs
3371     "41/increment-ecx"/imm32/subx-name
3372     0/imm32/no-rm32
3373     0/imm32/no-r32
3374     0/imm32/no-imm32
3375     0/imm32/output-is-write-only
3376     _Primitive-inc-edx/imm32/next
3377 _Primitive-inc-edx:
3378     # var/edx <- increment => 42/increment-edx
3379     "increment"/imm32/name
3380     0/imm32/no-inouts
3381     Single-int-var-in-edx/imm32/outputs
3382     "42/increment-edx"/imm32/subx-name
3383     0/imm32/no-rm32
3384     0/imm32/no-r32
3385     0/imm32/no-imm32
3386     0/imm32/output-is-write-only
3387     _Primitive-inc-ebx/imm32/next
3388 _Primitive-inc-ebx:
3389     # var/ebx <- increment => 43/increment-ebx
3390     "increment"/imm32/name
3391     0/imm32/no-inouts
3392     Single-int-var-in-ebx/imm32/outputs
3393     "43/increment-ebx"/imm32/subx-name
3394     0/imm32/no-rm32
3395     0/imm32/no-r32
3396     0/imm32/no-imm32
3397     0/imm32/output-is-write-only
3398     _Primitive-inc-esi/imm32/next
3399 _Primitive-inc-esi:
3400     # var/esi <- increment => 46/increment-esi
3401     "increment"/imm32/name
3402     0/imm32/no-inouts
3403     Single-int-var-in-esi/imm32/outputs
3404     "46/increment-esi"/imm32/subx-name
3405     0/imm32/no-rm32
3406     0/imm32/no-r32
3407     0/imm32/no-imm32
3408     0/imm32/output-is-write-only
3409     _Primitive-inc-edi/imm32/next
3410 _Primitive-inc-edi:
3411     # var/edi <- increment => 47/increment-edi
3412     "increment"/imm32/name
3413     0/imm32/no-inouts
3414     Single-int-var-in-edi/imm32/outputs
3415     "47/increment-edi"/imm32/subx-name
3416     0/imm32/no-rm32
3417     0/imm32/no-r32
3418     0/imm32/no-imm32
3419     0/imm32/output-is-write-only
3420     _Primitive-dec-eax/imm32/next
3421 _Primitive-dec-eax:
3422     # var/eax <- decrement => 48/decrement-eax
3423     "decrement"/imm32/name
3424     0/imm32/no-inouts
3425     Single-int-var-in-eax/imm32/outputs
3426     "48/decrement-eax"/imm32/subx-name
3427     0/imm32/no-rm32
3428     0/imm32/no-r32
3429     0/imm32/no-imm32
3430     0/imm32/output-is-write-only
3431     _Primitive-dec-ecx/imm32/next
3432 _Primitive-dec-ecx:
3433     # var/ecx <- decrement => 49/decrement-ecx
3434     "decrement"/imm32/name
3435     0/imm32/no-inouts
3436     Single-int-var-in-ecx/imm32/outputs
3437     "49/decrement-ecx"/imm32/subx-name
3438     0/imm32/no-rm32
3439     0/imm32/no-r32
3440     0/imm32/no-imm32
3441     0/imm32/output-is-write-only
3442     _Primitive-dec-edx/imm32/next
3443 _Primitive-dec-edx:
3444     # var/edx <- decrement => 4a/decrement-edx
3445     "decrement"/imm32/name
3446     0/imm32/no-inouts
3447     Single-int-var-in-edx/imm32/outputs
3448     "4a/decrement-edx"/imm32/subx-name
3449     0/imm32/no-rm32
3450     0/imm32/no-r32
3451     0/imm32/no-imm32
3452     0/imm32/output-is-write-only
3453     _Primitive-dec-ebx/imm32/next
3454 _Primitive-dec-ebx:
3455     # var/ebx <- decrement => 4b/decrement-ebx
3456     "decrement"/imm32/name
3457     0/imm32/no-inouts
3458     Single-int-var-in-ebx/imm32/outputs
3459     "4b/decrement-ebx"/imm32/subx-name
3460     0/imm32/no-rm32
3461     0/imm32/no-r32
3462     0/imm32/no-imm32
3463     0/imm32/output-is-write-only
3464     _Primitive-dec-esi/imm32/next
3465 _Primitive-dec-esi:
3466     # var/esi <- decrement => 4e/decrement-esi
3467     "decrement"/imm32/name
3468     0/imm32/no-inouts
3469     Single-int-var-in-esi/imm32/outputs
3470     "4e/decrement-esi"/imm32/subx-name
3471     0/imm32/no-rm32
3472     0/imm32/no-r32
3473     0/imm32/no-imm32
3474     0/imm32/output-is-write-only
3475     _Primitive-dec-edi/imm32/next
3476 _Primitive-dec-edi:
3477     # var/edi <- decrement => 4f/decrement-edi
3478     "decrement"/imm32/name
3479     0/imm32/no-inouts
3480     Single-int-var-in-edi/imm32/outputs
3481     "4f/decrement-edi"/imm32/subx-name
3482     0/imm32/no-rm32
3483     0/imm32/no-r32
3484     0/imm32/no-imm32
3485     0/imm32/output-is-write-only
3486     _Primitive-inc-mem/imm32/next
3487 _Primitive-inc-mem:
3488     # increment var => ff 0/subop/increment *(ebp+__)
3489     "increment"/imm32/name
3490     Single-int-var-on-stack/imm32/inouts
3491     0/imm32/no-outputs
3492     "ff 0/subop/increment"/imm32/subx-name
3493     1/imm32/rm32-is-first-inout
3494     0/imm32/no-r32
3495     0/imm32/no-imm32
3496     0/imm32/output-is-write-only
3497     _Primitive-inc-reg/imm32/next
3498 _Primitive-inc-reg:
3499     # var/reg <- increment => ff 0/subop/increment %__
3500     "increment"/imm32/name
3501     0/imm32/no-inouts
3502     Single-int-var-in-some-register/imm32/outputs
3503     "ff 0/subop/increment"/imm32/subx-name
3504     3/imm32/rm32-is-first-output
3505     0/imm32/no-r32
3506     0/imm32/no-imm32
3507     0/imm32/output-is-write-only
3508     _Primitive-dec-mem/imm32/next
3509 _Primitive-dec-mem:
3510     # decrement var => ff 1/subop/decrement *(ebp+__)
3511     "decrement"/imm32/name
3512     Single-int-var-on-stack/imm32/inouts
3513     0/imm32/no-outputs
3514     "ff 1/subop/decrement"/imm32/subx-name
3515     1/imm32/rm32-is-first-inout
3516     0/imm32/no-r32
3517     0/imm32/no-imm32
3518     0/imm32/output-is-write-only
3519     _Primitive-dec-reg/imm32/next
3520 _Primitive-dec-reg:
3521     # var/reg <- decrement => ff 1/subop/decrement %__
3522     "decrement"/imm32/name
3523     0/imm32/no-inouts
3524     Single-int-var-in-some-register/imm32/outputs
3525     "ff 1/subop/decrement"/imm32/subx-name
3526     3/imm32/rm32-is-first-output
3527     0/imm32/no-r32
3528     0/imm32/no-imm32
3529     0/imm32/output-is-write-only
3530     _Primitive-add-to-eax/imm32/next
3531 # - add
3532 _Primitive-add-to-eax:
3533     # var/eax <- add lit => 05/add-to-eax lit/imm32
3534     "add"/imm32/name
3535     Single-lit-var/imm32/inouts
3536     Single-int-var-in-eax/imm32/outputs
3537     "05/add-to-eax"/imm32/subx-name
3538     0/imm32/no-rm32
3539     0/imm32/no-r32
3540     1/imm32/imm32-is-first-inout
3541     0/imm32/output-is-write-only
3542     _Primitive-add-reg-to-reg/imm32/next
3543 _Primitive-add-reg-to-reg:
3544     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
3545     "add"/imm32/name
3546     Single-int-var-in-some-register/imm32/inouts
3547     Single-int-var-in-some-register/imm32/outputs
3548     "01/add-to"/imm32/subx-name
3549     3/imm32/rm32-is-first-output
3550     1/imm32/r32-is-first-inout
3551     0/imm32/no-imm32
3552     0/imm32/output-is-write-only
3553     _Primitive-add-reg-to-mem/imm32/next
3554 _Primitive-add-reg-to-mem:
3555     # add-to var1 var2/reg => 01/add-to var1 var2/r32
3556     "add-to"/imm32/name
3557     Int-var-and-second-int-var-in-some-register/imm32/inouts
3558     0/imm32/outputs
3559     "01/add-to"/imm32/subx-name
3560     1/imm32/rm32-is-first-inout
3561     2/imm32/r32-is-second-inout
3562     0/imm32/no-imm32
3563     0/imm32/output-is-write-only
3564     _Primitive-add-mem-to-reg/imm32/next
3565 _Primitive-add-mem-to-reg:
3566     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
3567     "add"/imm32/name
3568     Single-int-var-on-stack/imm32/inouts
3569     Single-int-var-in-some-register/imm32/outputs
3570     "03/add"/imm32/subx-name
3571     1/imm32/rm32-is-first-inout
3572     3/imm32/r32-is-first-output
3573     0/imm32/no-imm32
3574     0/imm32/output-is-write-only
3575     _Primitive-add-lit-to-reg/imm32/next
3576 _Primitive-add-lit-to-reg:
3577     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
3578     "add"/imm32/name
3579     Single-lit-var/imm32/inouts
3580     Single-int-var-in-some-register/imm32/outputs
3581     "81 0/subop/add"/imm32/subx-name
3582     3/imm32/rm32-is-first-output
3583     0/imm32/no-r32
3584     1/imm32/imm32-is-first-inout
3585     0/imm32/output-is-write-only
3586     _Primitive-add-lit-to-mem/imm32/next
3587 _Primitive-add-lit-to-mem:
3588     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
3589     "add-to"/imm32/name
3590     Int-var-and-literal/imm32/inouts
3591     0/imm32/outputs
3592     "81 0/subop/add"/imm32/subx-name
3593     1/imm32/rm32-is-first-inout
3594     0/imm32/no-r32
3595     2/imm32/imm32-is-first-inout
3596     0/imm32/output-is-write-only
3597     _Primitive-subtract-from-eax/imm32/next
3598 # - subtract
3599 _Primitive-subtract-from-eax:
3600     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
3601     "subtract"/imm32/name
3602     Single-lit-var/imm32/inouts
3603     Single-int-var-in-eax/imm32/outputs
3604     "2d/subtract-from-eax"/imm32/subx-name
3605     0/imm32/no-rm32
3606     0/imm32/no-r32
3607     1/imm32/imm32-is-first-inout
3608     0/imm32/output-is-write-only
3609     _Primitive-subtract-reg-from-reg/imm32/next
3610 _Primitive-subtract-reg-from-reg:
3611     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
3612     "subtract"/imm32/name
3613     Single-int-var-in-some-register/imm32/inouts
3614     Single-int-var-in-some-register/imm32/outputs
3615     "29/subtract-from"/imm32/subx-name
3616     3/imm32/rm32-is-first-output
3617     1/imm32/r32-is-first-inout
3618     0/imm32/no-imm32
3619     0/imm32/output-is-write-only
3620     _Primitive-subtract-reg-from-mem/imm32/next
3621 _Primitive-subtract-reg-from-mem:
3622     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
3623     "subtract-from"/imm32/name
3624     Int-var-and-second-int-var-in-some-register/imm32/inouts
3625     0/imm32/outputs
3626     "29/subtract-from"/imm32/subx-name
3627     1/imm32/rm32-is-first-inout
3628     2/imm32/r32-is-second-inout
3629     0/imm32/no-imm32
3630     0/imm32/output-is-write-only
3631     _Primitive-subtract-mem-from-reg/imm32/next
3632 _Primitive-subtract-mem-from-reg:
3633     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
3634     "subtract"/imm32/name
3635     Single-int-var-on-stack/imm32/inouts
3636     Single-int-var-in-some-register/imm32/outputs
3637     "2b/subtract"/imm32/subx-name
3638     1/imm32/rm32-is-first-inout
3639     3/imm32/r32-is-first-output
3640     0/imm32/no-imm32
3641     0/imm32/output-is-write-only
3642     _Primitive-subtract-lit-from-reg/imm32/next
3643 _Primitive-subtract-lit-from-reg:
3644     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
3645     "subtract"/imm32/name
3646     Single-lit-var/imm32/inouts
3647     Single-int-var-in-some-register/imm32/outputs
3648     "81 5/subop/subtract"/imm32/subx-name
3649     3/imm32/rm32-is-first-output
3650     0/imm32/no-r32
3651     1/imm32/imm32-is-first-inout
3652     0/imm32/output-is-write-only
3653     _Primitive-subtract-lit-from-mem/imm32/next
3654 _Primitive-subtract-lit-from-mem:
3655     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
3656     "subtract-from"/imm32/name
3657     Int-var-and-literal/imm32/inouts
3658     0/imm32/outputs
3659     "81 5/subop/subtract"/imm32/subx-name
3660     1/imm32/rm32-is-first-inout
3661     0/imm32/no-r32
3662     2/imm32/imm32-is-first-inout
3663     0/imm32/output-is-write-only
3664     _Primitive-and-with-eax/imm32/next
3665 # - and
3666 _Primitive-and-with-eax:
3667     # var/eax <- and lit => 25/and-with-eax lit/imm32
3668     "and"/imm32/name
3669     Single-lit-var/imm32/inouts
3670     Single-int-var-in-eax/imm32/outputs
3671     "25/and-with-eax"/imm32/subx-name
3672     0/imm32/no-rm32
3673     0/imm32/no-r32
3674     1/imm32/imm32-is-first-inout
3675     0/imm32/output-is-write-only
3676     _Primitive-and-reg-with-reg/imm32/next
3677 _Primitive-and-reg-with-reg:
3678     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
3679     "and"/imm32/name
3680     Single-int-var-in-some-register/imm32/inouts
3681     Single-int-var-in-some-register/imm32/outputs
3682     "21/and-with"/imm32/subx-name
3683     3/imm32/rm32-is-first-output
3684     1/imm32/r32-is-first-inout
3685     0/imm32/no-imm32
3686     0/imm32/output-is-write-only
3687     _Primitive-and-reg-with-mem/imm32/next
3688 _Primitive-and-reg-with-mem:
3689     # and-with var1 var2/reg => 21/and-with var1 var2/r32
3690     "and-with"/imm32/name
3691     Int-var-and-second-int-var-in-some-register/imm32/inouts
3692     0/imm32/outputs
3693     "21/and-with"/imm32/subx-name
3694     1/imm32/rm32-is-first-inout
3695     2/imm32/r32-is-second-inout
3696     0/imm32/no-imm32
3697     0/imm32/output-is-write-only
3698     _Primitive-and-mem-with-reg/imm32/next
3699 _Primitive-and-mem-with-reg:
3700     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
3701     "and"/imm32/name
3702     Single-int-var-on-stack/imm32/inouts
3703     Single-int-var-in-some-register/imm32/outputs
3704     "23/and"/imm32/subx-name
3705     1/imm32/rm32-is-first-inout
3706     3/imm32/r32-is-first-output
3707     0/imm32/no-imm32
3708     0/imm32/output-is-write-only
3709     _Primitive-and-lit-with-reg/imm32/next
3710 _Primitive-and-lit-with-reg:
3711     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
3712     "and"/imm32/name
3713     Single-lit-var/imm32/inouts
3714     Single-int-var-in-some-register/imm32/outputs
3715     "81 4/subop/and"/imm32/subx-name
3716     3/imm32/rm32-is-first-output
3717     0/imm32/no-r32
3718     1/imm32/imm32-is-first-inout
3719     0/imm32/output-is-write-only
3720     _Primitive-and-lit-with-mem/imm32/next
3721 _Primitive-and-lit-with-mem:
3722     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
3723     "and-with"/imm32/name
3724     Int-var-and-literal/imm32/inouts
3725     0/imm32/outputs
3726     "81 4/subop/and"/imm32/subx-name
3727     1/imm32/rm32-is-first-inout
3728     0/imm32/no-r32
3729     2/imm32/imm32-is-first-inout
3730     0/imm32/output-is-write-only
3731     _Primitive-or-with-eax/imm32/next
3732 # - or
3733 _Primitive-or-with-eax:
3734     # var/eax <- or lit => 0d/or-with-eax lit/imm32
3735     "or"/imm32/name
3736     Single-lit-var/imm32/inouts
3737     Single-int-var-in-eax/imm32/outputs
3738     "0d/or-with-eax"/imm32/subx-name
3739     0/imm32/no-rm32
3740     0/imm32/no-r32
3741     1/imm32/imm32-is-first-inout
3742     0/imm32/output-is-write-only
3743     _Primitive-or-reg-with-reg/imm32/next
3744 _Primitive-or-reg-with-reg:
3745     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
3746     "or"/imm32/name
3747     Single-int-var-in-some-register/imm32/inouts
3748     Single-int-var-in-some-register/imm32/outputs
3749     "09/or-with"/imm32/subx-name
3750     3/imm32/rm32-is-first-output
3751     1/imm32/r32-is-first-inout
3752     0/imm32/no-imm32
3753     0/imm32/output-is-write-only
3754     _Primitive-or-reg-with-mem/imm32/next
3755 _Primitive-or-reg-with-mem:
3756     # or-with var1 var2/reg => 09/or-with var1 var2/r32
3757     "or-with"/imm32/name
3758     Int-var-and-second-int-var-in-some-register/imm32/inouts
3759     0/imm32/outputs
3760     "09/or-with"/imm32/subx-name
3761     1/imm32/rm32-is-first-inout
3762     2/imm32/r32-is-second-inout
3763     0/imm32/no-imm32
3764     0/imm32/output-is-write-only
3765     _Primitive-or-mem-with-reg/imm32/next
3766 _Primitive-or-mem-with-reg:
3767     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
3768     "or"/imm32/name
3769     Single-int-var-on-stack/imm32/inouts
3770     Single-int-var-in-some-register/imm32/outputs
3771     "0b/or"/imm32/subx-name
3772     1/imm32/rm32-is-first-inout
3773     3/imm32/r32-is-first-output
3774     0/imm32/no-imm32
3775     0/imm32/output-is-write-only
3776     _Primitive-or-lit-with-reg/imm32/next
3777 _Primitive-or-lit-with-reg:
3778     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
3779     "or"/imm32/name
3780     Single-lit-var/imm32/inouts
3781     Single-int-var-in-some-register/imm32/outputs
3782     "81 4/subop/or"/imm32/subx-name
3783     3/imm32/rm32-is-first-output
3784     0/imm32/no-r32
3785     1/imm32/imm32-is-first-inout
3786     0/imm32/output-is-write-only
3787     _Primitive-or-lit-with-mem/imm32/next
3788 _Primitive-or-lit-with-mem:
3789     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
3790     "or-with"/imm32/name
3791     Int-var-and-literal/imm32/inouts
3792     0/imm32/outputs
3793     "81 4/subop/or"/imm32/subx-name
3794     1/imm32/rm32-is-first-inout
3795     0/imm32/no-r32
3796     2/imm32/imm32-is-first-inout
3797     0/imm32/output-is-write-only
3798     _Primitive-xor-with-eax/imm32/next
3799 # - xor
3800 _Primitive-xor-with-eax:
3801     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
3802     "xor"/imm32/name
3803     Single-lit-var/imm32/inouts
3804     Single-int-var-in-eax/imm32/outputs
3805     "35/xor-with-eax"/imm32/subx-name
3806     0/imm32/no-rm32
3807     0/imm32/no-r32
3808     1/imm32/imm32-is-first-inout
3809     0/imm32/output-is-write-only
3810     _Primitive-xor-reg-with-reg/imm32/next
3811 _Primitive-xor-reg-with-reg:
3812     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
3813     "xor"/imm32/name
3814     Single-int-var-in-some-register/imm32/inouts
3815     Single-int-var-in-some-register/imm32/outputs
3816     "31/xor-with"/imm32/subx-name
3817     3/imm32/rm32-is-first-output
3818     1/imm32/r32-is-first-inout
3819     0/imm32/no-imm32
3820     0/imm32/output-is-write-only
3821     _Primitive-xor-reg-with-mem/imm32/next
3822 _Primitive-xor-reg-with-mem:
3823     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
3824     "xor-with"/imm32/name
3825     Int-var-and-second-int-var-in-some-register/imm32/inouts
3826     0/imm32/outputs
3827     "31/xor-with"/imm32/subx-name
3828     1/imm32/rm32-is-first-inout
3829     2/imm32/r32-is-second-inout
3830     0/imm32/no-imm32
3831     0/imm32/output-is-write-only
3832     _Primitive-xor-mem-with-reg/imm32/next
3833 _Primitive-xor-mem-with-reg:
3834     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
3835     "xor"/imm32/name
3836     Single-int-var-on-stack/imm32/inouts
3837     Single-int-var-in-some-register/imm32/outputs
3838     "33/xor"/imm32/subx-name
3839     1/imm32/rm32-is-first-inout
3840     3/imm32/r32-is-first-output
3841     0/imm32/no-imm32
3842     0/imm32/output-is-write-only
3843     _Primitive-xor-lit-with-reg/imm32/next
3844 _Primitive-xor-lit-with-reg:
3845     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
3846     "xor"/imm32/name
3847     Single-lit-var/imm32/inouts
3848     Single-int-var-in-some-register/imm32/outputs
3849     "81 4/subop/xor"/imm32/subx-name
3850     3/imm32/rm32-is-first-output
3851     0/imm32/no-r32
3852     1/imm32/imm32-is-first-inout
3853     0/imm32/output-is-write-only
3854     _Primitive-xor-lit-with-mem/imm32/next
3855 _Primitive-xor-lit-with-mem:
3856     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
3857     "xor-with"/imm32/name
3858     Int-var-and-literal/imm32/inouts
3859     0/imm32/outputs
3860     "81 4/subop/xor"/imm32/subx-name
3861     1/imm32/rm32-is-first-inout
3862     0/imm32/no-r32
3863     2/imm32/imm32-is-first-inout
3864     0/imm32/output-is-write-only
3865     _Primitive-copy-to-eax/imm32/next
3866 # - copy
3867 _Primitive-copy-to-eax:
3868     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
3869     "copy"/imm32/name
3870     Single-lit-var/imm32/inouts
3871     Single-int-var-in-eax/imm32/outputs
3872     "b8/copy-to-eax"/imm32/subx-name
3873     0/imm32/no-rm32
3874     0/imm32/no-r32
3875     1/imm32/imm32-is-first-inout
3876     1/imm32/output-is-write-only
3877     _Primitive-copy-to-ecx/imm32/next
3878 _Primitive-copy-to-ecx:
3879     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
3880     "copy"/imm32/name
3881     Single-lit-var/imm32/inouts
3882     Single-int-var-in-ecx/imm32/outputs
3883     "b9/copy-to-ecx"/imm32/subx-name
3884     0/imm32/no-rm32
3885     0/imm32/no-r32
3886     1/imm32/imm32-is-first-inout
3887     1/imm32/output-is-write-only
3888     _Primitive-copy-to-edx/imm32/next
3889 _Primitive-copy-to-edx:
3890     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
3891     "copy"/imm32/name
3892     Single-lit-var/imm32/inouts
3893     Single-int-var-in-edx/imm32/outputs
3894     "ba/copy-to-edx"/imm32/subx-name
3895     0/imm32/no-rm32
3896     0/imm32/no-r32
3897     1/imm32/imm32-is-first-inout
3898     1/imm32/output-is-write-only
3899     _Primitive-copy-to-ebx/imm32/next
3900 _Primitive-copy-to-ebx:
3901     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
3902     "copy"/imm32/name
3903     Single-lit-var/imm32/inouts
3904     Single-int-var-in-ebx/imm32/outputs
3905     "bb/copy-to-ebx"/imm32/subx-name
3906     0/imm32/no-rm32
3907     0/imm32/no-r32
3908     1/imm32/imm32-is-first-inout
3909     1/imm32/output-is-write-only
3910     _Primitive-copy-to-esi/imm32/next
3911 _Primitive-copy-to-esi:
3912     # var/esi <- copy lit => be/copy-to-esi lit/imm32
3913     "copy"/imm32/name
3914     Single-lit-var/imm32/inouts
3915     Single-int-var-in-esi/imm32/outputs
3916     "be/copy-to-esi"/imm32/subx-name
3917     0/imm32/no-rm32
3918     0/imm32/no-r32
3919     1/imm32/imm32-is-first-inout
3920     1/imm32/output-is-write-only
3921     _Primitive-copy-to-edi/imm32/next
3922 _Primitive-copy-to-edi:
3923     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
3924     "copy"/imm32/name
3925     Single-lit-var/imm32/inouts
3926     Single-int-var-in-edi/imm32/outputs
3927     "bf/copy-to-edi"/imm32/subx-name
3928     0/imm32/no-rm32
3929     0/imm32/no-r32
3930     1/imm32/imm32-is-first-inout
3931     1/imm32/output-is-write-only
3932     _Primitive-copy-reg-to-reg/imm32/next
3933 _Primitive-copy-reg-to-reg:
3934     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
3935     "copy"/imm32/name
3936     Single-int-var-in-some-register/imm32/inouts
3937     Single-int-var-in-some-register/imm32/outputs
3938     "89/copy-to"/imm32/subx-name
3939     3/imm32/rm32-is-first-output
3940     1/imm32/r32-is-first-inout
3941     0/imm32/no-imm32
3942     1/imm32/output-is-write-only
3943     _Primitive-copy-reg-to-mem/imm32/next
3944 _Primitive-copy-reg-to-mem:
3945     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
3946     "copy-to"/imm32/name
3947     Int-var-and-second-int-var-in-some-register/imm32/inouts
3948     0/imm32/outputs
3949     "89/copy-to"/imm32/subx-name
3950     1/imm32/rm32-is-first-inout
3951     2/imm32/r32-is-second-inout
3952     0/imm32/no-imm32
3953     1/imm32/output-is-write-only
3954     _Primitive-copy-mem-to-reg/imm32/next
3955 _Primitive-copy-mem-to-reg:
3956     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
3957     "copy"/imm32/name
3958     Single-int-var-on-stack/imm32/inouts
3959     Single-int-var-in-some-register/imm32/outputs
3960     "8b/copy-from"/imm32/subx-name
3961     1/imm32/rm32-is-first-inout
3962     3/imm32/r32-is-first-output
3963     0/imm32/no-imm32
3964     1/imm32/output-is-write-only
3965     _Primitive-copy-lit-to-reg/imm32/next
3966 _Primitive-copy-lit-to-reg:
3967     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
3968     "copy"/imm32/name
3969     Single-lit-var/imm32/inouts
3970     Single-int-var-in-some-register/imm32/outputs
3971     "c7 0/subop/copy"/imm32/subx-name
3972     3/imm32/rm32-is-first-output
3973     0/imm32/no-r32
3974     1/imm32/imm32-is-first-inout
3975     1/imm32/output-is-write-only
3976     _Primitive-copy-lit-to-mem/imm32/next
3977 _Primitive-copy-lit-to-mem:
3978     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
3979     "copy-to"/imm32/name
3980     Int-var-and-literal/imm32/inouts
3981     0/imm32/outputs
3982     "c7 0/subop/copy"/imm32/subx-name
3983     1/imm32/rm32-is-first-inout
3984     0/imm32/no-r32
3985     2/imm32/imm32-is-first-inout
3986     1/imm32/output-is-write-only
3987     0/imm32/next
3988 
3989 Single-int-var-on-stack:
3990     Int-var-on-stack/imm32
3991     0/imm32/next
3992 
3993 Int-var-on-stack:
3994     "arg1"/imm32/name
3995     Type-int/imm32
3996     1/imm32/some-block-depth
3997     1/imm32/some-stack-offset
3998     0/imm32/no-register
3999 
4000 Int-var-and-second-int-var-in-some-register:
4001     Int-var-on-stack/imm32
4002     Single-int-var-in-some-register/imm32/next
4003 
4004 Int-var-and-literal:
4005     Int-var-on-stack/imm32
4006     Single-lit-var/imm32/next
4007 
4008 Single-int-var-in-some-register:
4009     Int-var-in-some-register/imm32
4010     0/imm32/next
4011 
4012 Int-var-in-some-register:
4013     "arg1"/imm32/name
4014     Type-int/imm32
4015     1/imm32/some-block-depth
4016     0/imm32/no-stack-offset
4017     "*"/imm32/register
4018 
4019 Single-int-var-in-eax:
4020     Int-var-in-eax/imm32
4021     0/imm32/next
4022 
4023 Int-var-in-eax:
4024     "arg1"/imm32/name
4025     Type-int/imm32
4026     1/imm32/some-block-depth
4027     0/imm32/no-stack-offset
4028     "eax"/imm32/register
4029 
4030 Single-int-var-in-ecx:
4031     Int-var-in-ecx/imm32
4032     0/imm32/next
4033 
4034 Int-var-in-ecx:
4035     "arg1"/imm32/name
4036     Type-int/imm32
4037     1/imm32/some-block-depth
4038     0/imm32/no-stack-offset
4039     "ecx"/imm32/register
4040 
4041 Single-int-var-in-edx:
4042     Int-var-in-edx/imm32
4043     0/imm32/next
4044 
4045 Int-var-in-edx:
4046     "arg1"/imm32/name
4047     Type-int/imm32
4048     1/imm32/some-block-depth
4049     0/imm32/no-stack-offset
4050     "edx"/imm32/register
4051 
4052 Single-int-var-in-ebx:
4053     Int-var-in-ebx/imm32
4054     0/imm32/next
4055 
4056 Int-var-in-ebx:
4057     "arg1"/imm32/name
4058     Type-int/imm32
4059     1/imm32/some-block-depth
4060     0/imm32/no-stack-offset
4061     "ebx"/imm32/register
4062 
4063 Single-int-var-in-esi:
4064     Int-var-in-esi/imm32
4065     0/imm32/next
4066 
4067 Int-var-in-esi:
4068     "arg1"/imm32/name
4069     Type-int/imm32
4070     1/imm32/some-block-depth
4071     0/imm32/no-stack-offset
4072     "esi"/imm32/register
4073 
4074 Single-int-var-in-edi:
4075     Int-var-in-edi/imm32
4076     0/imm32/next
4077 
4078 Int-var-in-edi:
4079     "arg1"/imm32/name
4080     Type-int/imm32
4081     1/imm32/some-block-depth
4082     0/imm32/no-stack-offset
4083     "edi"/imm32/register
4084 
4085 Single-lit-var:
4086     Lit-var/imm32
4087     0/imm32/next
4088 
4089 Lit-var:
4090     "literal"/imm32/name
4091     Type-literal/imm32
4092     1/imm32/some-block-depth
4093     0/imm32/no-stack-offset
4094     0/imm32/no-register
4095 
4096 Type-int:
4097     1/imm32/left/int
4098     0/imm32/right/null
4099 
4100 Type-literal:
4101     0/imm32/left/literal
4102     0/imm32/right/null
4103 
4104 == code
4105 emit-subx-primitive:  # out : (addr buffered-file), stmt : (handle statement), primitive : (handle function)
4106     # . prologue
4107     55/push-ebp
4108     89/<- %ebp 4/r32/esp
4109     # . save registers
4110     50/push-eax
4111     51/push-ecx
4112     # ecx = primitive
4113     8b/-> *(ebp+0x10) 1/r32/ecx
4114     # emit primitive name
4115     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
4116     # emit rm32 if necessary
4117     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
4118     # emit r32 if necessary
4119     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
4120     # emit imm32 if necessary
4121     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
4122 $emit-subx-primitive:end:
4123     # . restore registers
4124     59/pop-to-ecx
4125     58/pop-to-eax
4126     # . epilogue
4127     89/<- %esp 5/r32/ebp
4128     5d/pop-to-ebp
4129     c3/return
4130 
4131 emit-subx-rm32:  # out : (addr buffered-file), l : arg-location, stmt : (handle statement)
4132     # . prologue
4133     55/push-ebp
4134     89/<- %ebp 4/r32/esp
4135     # . save registers
4136     50/push-eax
4137     # if (l == 0) return
4138     81 7/subop/compare *(ebp+0xc) 0/imm32
4139     74/jump-if-equal $emit-subx-rm32:end/disp8
4140     #
4141     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
4142     (emit-subx-var-as-rm32 *(ebp+8) %eax)  # out, var
4143 $emit-subx-rm32:end:
4144     # . restore registers
4145     58/pop-to-eax
4146     # . epilogue
4147     89/<- %esp 5/r32/ebp
4148     5d/pop-to-ebp
4149     c3/return
4150 
4151 get-stmt-operand-from-arg-location:  # stmt : (handle statement), l : arg-location -> var/eax : (handle variable)
4152     # . prologue
4153     55/push-ebp
4154     89/<- %ebp 4/r32/esp
4155     # . save registers
4156     51/push-ecx
4157     # eax = l
4158     8b/-> *(ebp+0xc) 0/r32/eax
4159     # ecx = stmt
4160     8b/-> *(ebp+8) 1/r32/ecx
4161     # if (l == 1) return stmt->inouts->var
4162     {
4163       3d/compare-eax-and 1/imm32
4164       75/jump-if-not-equal break/disp8
4165 $get-stmt-operand-from-arg-location:1:
4166       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
4167       8b/-> *eax 0/r32/eax  # Operand-var
4168       eb/jump $get-stmt-operand-from-arg-location:end/disp8
4169     }
4170     # if (l == 2) return stmt->inouts->next->var
4171     {
4172       3d/compare-eax-and 2/imm32
4173       75/jump-if-not-equal break/disp8
4174 $get-stmt-operand-from-arg-location:2:
4175       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
4176       8b/-> *(eax+4) 0/r32/eax  # Operand-next
4177       8b/-> *eax 0/r32/eax  # Operand-var
4178       eb/jump $get-stmt-operand-from-arg-location:end/disp8
4179     }
4180     # if (l == 3) return stmt->outputs
4181     {
4182       3d/compare-eax-and 3/imm32
4183       75/jump-if-not-equal break/disp8
4184 $get-stmt-operand-from-arg-location:3:
4185       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
4186       8b/-> *eax 0/r32/eax  # Operand-var
4187       eb/jump $get-stmt-operand-from-arg-location:end/disp8
4188     }
4189     # abort
4190     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
4191 $get-stmt-operand-from-arg-location:end:
4192     # . restore registers
4193     59/pop-to-ecx
4194     # . epilogue
4195     89/<- %esp 5/r32/ebp
4196     5d/pop-to-ebp
4197     c3/return
4198 
4199 $get-stmt-operand-from-arg-location:abort:
4200     # error("invalid arg-location " eax)
4201     (write-buffered Stderr "invalid arg-location ")
4202     (print-int32-buffered Stderr %eax)
4203     (write-buffered Stderr "\n")
4204     (flush Stderr)
4205     # . syscall(exit, 1)
4206     bb/copy-to-ebx  1/imm32
4207     b8/copy-to-eax  1/imm32/exit
4208     cd/syscall  0x80/imm8
4209     # never gets here
4210 
4211 emit-subx-r32:  # out : (addr buffered-file), l : arg-location, stmt : (handle statement)
4212     # . prologue
4213     55/push-ebp
4214     89/<- %ebp 4/r32/esp
4215     # . save registers
4216     50/push-eax
4217     51/push-ecx
4218     # if (location == 0) return
4219     81 7/subop/compare *(ebp+0xc) 0/imm32
4220     0f 84/jump-if-equal $emit-subx-r32:end/disp32
4221     #
4222     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
4223     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax : (addr register-index)
4224     (write-buffered *(ebp+8) Space)
4225     (print-int32-buffered *(ebp+8) *eax)
4226     (write-buffered *(ebp+8) "/r32")
4227 $emit-subx-r32:end:
4228     # . restore registers
4229     59/pop-to-ecx
4230     58/pop-to-eax
4231     # . epilogue
4232     89/<- %esp 5/r32/ebp
4233     5d/pop-to-ebp
4234     c3/return
4235 
4236 emit-subx-imm32:  # out : (addr buffered-file), l : arg-location, stmt : (handle statement)
4237     # . prologue
4238     55/push-ebp
4239     89/<- %ebp 4/r32/esp
4240     # . save registers
4241     50/push-eax
4242     51/push-ecx
4243     # if (location == 0) return
4244     81 7/subop/compare *(ebp+0xc) 0/imm32
4245     74/jump-if-equal $emit-subx-imm32:end/disp8
4246     #
4247     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
4248     (write-buffered *(ebp+8) Space)
4249     (write-buffered *(ebp+8) *eax)  # Var-name
4250     (write-buffered *(ebp+8) "/imm32")
4251 $emit-subx-imm32:end:
4252     # . restore registers
4253     59/pop-to-ecx
4254     58/pop-to-eax
4255     # . epilogue
4256     89/<- %esp 5/r32/ebp
4257     5d/pop-to-ebp
4258     c3/return
4259 
4260 emit-subx-call:  # out : (addr buffered-file), stmt : (handle statement), callee : (handle function)
4261     # . prologue
4262     55/push-ebp
4263     89/<- %ebp 4/r32/esp
4264     # . save registers
4265     50/push-eax
4266     51/push-ecx
4267     #
4268     (write-buffered *(ebp+8) "(")
4269     # - emit function name
4270     8b/-> *(ebp+0x10) 1/r32/ecx
4271     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
4272     # - emit arguments
4273     # var curr/ecx : (handle list var) = stmt->inouts
4274     8b/-> *(ebp+0xc) 1/r32/ecx
4275     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
4276     {
4277       # if (curr == null) break
4278       81 7/subop/compare %ecx 0/imm32
4279       74/jump-if-equal break/disp8
4280       #
4281       (emit-subx-call-operand *(ebp+8) *ecx)
4282       # curr = curr->next
4283       8b/-> *(ecx+4) 1/r32/ecx
4284       eb/jump loop/disp8
4285     }
4286     #
4287     (write-buffered *(ebp+8) ")")
4288 $emit-subx-call:end:
4289     # . restore registers
4290     59/pop-to-ecx
4291     58/pop-to-eax
4292     # . epilogue
4293     89/<- %esp 5/r32/ebp
4294     5d/pop-to-ebp
4295     c3/return
4296 
4297 emit-subx-call-operand:  # out : (addr buffered-file), operand : (handle variable)
4298     # . prologue
4299     55/push-ebp
4300     89/<- %ebp 4/r32/esp
4301     # . save registers
4302     50/push-eax
4303     # eax = operand
4304     8b/-> *(ebp+0xc) 0/r32/eax
4305     # if (operand->register) emit "%__"
4306     {
4307       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
4308       74/jump-if-equal break/disp8
4309 $emit-subx-call-operand:register:
4310       (write-buffered *(ebp+8) " %")
4311       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
4312       e9/jump $emit-subx-call-operand:end/disp32
4313     }
4314     # else if (operand->stack-offset) emit "*(ebp+__)"
4315     {
4316       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
4317       74/jump-if-equal break/disp8
4318 $emit-subx-call-operand:stack:
4319       (write-buffered *(ebp+8) Space)
4320       (write-buffered *(ebp+8) "*(ebp+")
4321       8b/-> *(ebp+0xc) 0/r32/eax
4322       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
4323       (write-buffered *(ebp+8) ")")
4324       e9/jump $emit-subx-call-operand:end/disp32
4325     }
4326     # else if (operand->type == literal) emit "__"
4327     {
4328       50/push-eax
4329       8b/-> *(eax+4) 0/r32/eax  # Var-type
4330       81 7/subop/compare *eax 0/imm32  # Tree-left
4331       58/pop-to-eax
4332       75/jump-if-not-equal break/disp8
4333 $emit-subx-call-operand:literal:
4334       (write-buffered *(ebp+8) Space)
4335       (write-buffered *(ebp+8) *eax)
4336     }
4337 $emit-subx-call-operand:end:
4338     # . restore registers
4339     58/pop-to-eax
4340     # . epilogue
4341     89/<- %esp 5/r32/ebp
4342     5d/pop-to-ebp
4343     c3/return
4344 
4345 emit-subx-var-as-rm32:  # out : (addr buffered-file), operand : (handle variable)
4346     # . prologue
4347     55/push-ebp
4348     89/<- %ebp 4/r32/esp
4349     # . save registers
4350     50/push-eax
4351     # eax = operand
4352     8b/-> *(ebp+0xc) 0/r32/eax
4353     # if (operand->register) emit "%__"
4354     {
4355       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
4356       74/jump-if-equal break/disp8
4357 $emit-subx-var-as-rm32:register:
4358       (write-buffered *(ebp+8) " %")
4359       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
4360     }
4361     # else if (operand->stack-offset) emit "*(ebp+__)"
4362     {
4363       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
4364       74/jump-if-equal break/disp8
4365 $emit-subx-var-as-rm32:stack:
4366       (write-buffered *(ebp+8) Space)
4367       (write-buffered *(ebp+8) "*(ebp+")
4368       8b/-> *(ebp+0xc) 0/r32/eax
4369       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
4370       (write-buffered *(ebp+8) ")")
4371     }
4372 $emit-subx-var-as-rm32:end:
4373     # . restore registers
4374     58/pop-to-eax
4375     # . epilogue
4376     89/<- %esp 5/r32/ebp
4377     5d/pop-to-ebp
4378     c3/return
4379 
4380 find-matching-function:  # functions : (addr function), stmt : (handle statement) -> result/eax : (handle function)
4381     # . prologue
4382     55/push-ebp
4383     89/<- %ebp 4/r32/esp
4384     # . save registers
4385     51/push-ecx
4386     # var curr/ecx : (handle function) = functions
4387     8b/-> *(ebp+8) 1/r32/ecx
4388     {
4389       # if (curr == null) break
4390       81 7/subop/compare %ecx 0/imm32
4391       74/jump-if-equal break/disp8
4392       # if match(stmt, curr) return curr
4393       {
4394         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
4395         3d/compare-eax-and 0/imm32
4396         74/jump-if-equal break/disp8
4397         89/<- %eax 1/r32/ecx
4398         eb/jump $find-matching-function:end/disp8
4399       }
4400       # curr = curr->next
4401       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
4402       eb/jump loop/disp8
4403     }
4404     # return null
4405     b8/copy-to-eax 0/imm32
4406 $find-matching-function:end:
4407     # . restore registers
4408     59/pop-to-ecx
4409     # . epilogue
4410     89/<- %esp 5/r32/ebp
4411     5d/pop-to-ebp
4412     c3/return
4413 
4414 find-matching-primitive:  # primitives : (handle primitive), stmt : (handle statement) -> result/eax : (handle primitive)
4415     # . prologue
4416     55/push-ebp
4417     89/<- %ebp 4/r32/esp
4418     # . save registers
4419     51/push-ecx
4420     # var curr/ecx : (handle primitive) = primitives
4421     8b/-> *(ebp+8) 1/r32/ecx
4422     {
4423 $find-matching-primitive:loop:
4424       # if (curr == null) break
4425       81 7/subop/compare %ecx 0/imm32
4426       0f 84/jump-if-equal break/disp32
4427 #?       (write-buffered Stderr "prim: ")
4428 #?       (write-buffered Stderr *ecx)  # Primitive-name
4429 #?       (write-buffered Stderr " => ")
4430 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
4431 #?       (write-buffered Stderr "\n")
4432 #?       (flush Stderr)
4433       # if match(curr, stmt) return curr
4434       {
4435         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
4436         3d/compare-eax-and 0/imm32
4437         74/jump-if-equal break/disp8
4438         89/<- %eax 1/r32/ecx
4439         eb/jump $find-matching-primitive:end/disp8
4440       }
4441 $find-matching-primitive:next-primitive:
4442       # curr = curr->next
4443       8b/-> *(ecx+0x20) 1/r32/ecx  # Primitive-next
4444       e9/jump loop/disp32
4445     }
4446     # return null
4447     b8/copy-to-eax 0/imm32
4448 $find-matching-primitive:end:
4449     # . restore registers
4450     59/pop-to-ecx
4451     # . epilogue
4452     89/<- %esp 5/r32/ebp
4453     5d/pop-to-ebp
4454     c3/return
4455 
4456 mu-stmt-matches-function?:  # stmt : (handle statement), function : (handle function) => result/eax : boolean
4457     # . prologue
4458     55/push-ebp
4459     89/<- %ebp 4/r32/esp
4460     # . save registers
4461     51/push-ecx
4462     # return function->name == stmt->operation
4463     8b/-> *(ebp+8) 1/r32/ecx
4464     8b/-> *(ebp+0xc) 0/r32/eax
4465     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
4466 $mu-stmt-matches-function?:end:
4467     # . restore registers
4468     59/pop-to-ecx
4469     # . epilogue
4470     89/<- %esp 5/r32/ebp
4471     5d/pop-to-ebp
4472     c3/return
4473 
4474 mu-stmt-matches-primitive?:  # stmt : (handle statement), primitive : (handle primitive) => result/eax : boolean
4475     # A mu stmt matches a primitive if the name matches, all the inout vars
4476     # match, and all the output vars match.
4477     # Vars match if types match and registers match.
4478     # In addition, a stmt output matches a primitive's output if types match
4479     # and the primitive has a wildcard register.
4480     # . prologue
4481     55/push-ebp
4482     89/<- %ebp 4/r32/esp
4483     # . save registers
4484     51/push-ecx
4485     52/push-edx
4486     53/push-ebx
4487     56/push-esi
4488     57/push-edi
4489     # ecx = stmt
4490     8b/-> *(ebp+8) 1/r32/ecx
4491     # edx = primitive
4492     8b/-> *(ebp+0xc) 2/r32/edx
4493     {
4494 $mu-stmt-matches-primitive?:check-name:
4495       # if (primitive->name != stmt->operation) return false
4496       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
4497       3d/compare-eax-and 0/imm32
4498       75/jump-if-not-equal break/disp8
4499       b8/copy-to-eax 0/imm32
4500       e9/jump $mu-stmt-matches-primitive?:end/disp32
4501     }
4502 $mu-stmt-matches-primitive?:check-inouts:
4503     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
4504     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts
4505     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
4506     {
4507       # if (curr == 0 && curr2 == 0) move on to check outputs
4508       {
4509         81 7/subop/compare %esi 0/imm32
4510         75/jump-if-not-equal break/disp8
4511 $mu-stmt-matches-primitive?:stmt-inout-is-null:
4512         {
4513           81 7/subop/compare %edi 0/imm32
4514           75/jump-if-not-equal break/disp8
4515           #
4516           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
4517         }
4518         # return false
4519         b8/copy-to-eax 0/imm32/false
4520         e9/jump $mu-stmt-matches-primitive?:end/disp32
4521       }
4522       # if (curr2 == 0) return false
4523       {
4524         81 7/subop/compare %edi 0/imm32
4525         75/jump-if-not-equal break/disp8
4526 $mu-stmt-matches-primitive?:prim-inout-is-null:
4527         b8/copy-to-eax 0/imm32/false
4528         e9/jump $mu-stmt-matches-primitive?:end/disp32
4529       }
4530       # if (curr != curr2) return false
4531       {
4532         (operand-matches-primitive? *esi *edi)  # => eax
4533         3d/compare-eax-and 0/imm32
4534         75/jump-if-not-equal break/disp8
4535         b8/copy-to-eax 0/imm32/false
4536         e9/jump $mu-stmt-matches-primitive?:end/disp32
4537       }
4538       # curr=curr->next
4539       8b/-> *(esi+4) 6/r32/esi  # Operand-next
4540       # curr2=curr2->next
4541       8b/-> *(edi+4) 7/r32/edi  # Operand-next
4542       eb/jump loop/disp8
4543     }
4544 $mu-stmt-matches-primitive?:check-outputs:
4545     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
4546     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
4547     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
4548     {
4549       # if (curr == 0) return (curr2 == 0)
4550       {
4551 $mu-stmt-matches-primitive?:check-output:
4552         81 7/subop/compare %esi 0/imm32
4553         75/jump-if-not-equal break/disp8
4554         {
4555           81 7/subop/compare %edi 0/imm32
4556           75/jump-if-not-equal break/disp8
4557           # return true
4558           b8/copy-to-eax 1/imm32
4559           e9/jump $mu-stmt-matches-primitive?:end/disp32
4560         }
4561         # return false
4562         b8/copy-to-eax 0/imm32
4563         e9/jump $mu-stmt-matches-primitive?:end/disp32
4564       }
4565       # if (curr2 == 0) return false
4566       {
4567         81 7/subop/compare %edi 0/imm32
4568         75/jump-if-not-equal break/disp8
4569         b8/copy-to-eax 0/imm32
4570         e9/jump $mu-stmt-matches-primitive?:end/disp32
4571       }
4572       # if (curr != curr2) return false
4573       {
4574         (operand-matches-primitive? *esi *edi)  # List-value List-value => eax
4575         3d/compare-eax-and 0/imm32
4576         75/jump-if-not-equal break/disp8
4577         b8/copy-to-eax 0/imm32
4578         e9/jump $mu-stmt-matches-primitive?:end/disp32
4579       }
4580       # curr=curr->next
4581       8b/-> *(esi+4) 6/r32/esi  # Operand-next
4582       # curr2=curr2->next
4583       8b/-> *(edi+4) 7/r32/edi  # Operand-next
4584       eb/jump loop/disp8
4585     }
4586 $mu-stmt-matches-primitive?:return-true:
4587     b8/copy-to-eax 1/imm32
4588 $mu-stmt-matches-primitive?:end:
4589     # . restore registers
4590     5f/pop-to-edi
4591     5e/pop-to-esi
4592     5b/pop-to-ebx
4593     5a/pop-to-edx
4594     59/pop-to-ecx
4595     # . epilogue
4596     89/<- %esp 5/r32/ebp
4597     5d/pop-to-ebp
4598     c3/return
4599 
4600 operand-matches-primitive?:  # var : (handle var), prim-var : (handle var) => result/eax : boolean
4601     # . prologue
4602     55/push-ebp
4603     89/<- %ebp 4/r32/esp
4604     # . save registers
4605     56/push-esi
4606     57/push-edi
4607     # esi = var
4608     8b/-> *(ebp+8) 6/r32/esi
4609     # edi = prim-var
4610     8b/-> *(ebp+0xc) 7/r32/edi
4611     # if (var->type != prim-var->type) return false
4612     (type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
4613     3d/compare-eax-and 0/imm32
4614     b8/copy-to-eax 0/imm32/false
4615     74/jump-if-equal $operand-matches-primitive?:end/disp8
4616     # return false if var->register doesn't match prim-var->register
4617     {
4618       # if addresses are equal, don't return here
4619       8b/-> *(esi+0x10) 0/r32/eax
4620       39/compare *(edi+0x10) 0/r32/eax
4621       74/jump-if-equal break/disp8
4622       # if either address is 0, return false
4623       3d/compare-eax-and 0/imm32
4624       74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
4625       81 7/subop/compare *(edi+0x10) 0/imm32
4626       74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
4627       # if prim-var->register is "*", return true
4628       (string-equal? *(edi+0x10) "*")  # Var-register
4629       3d/compare-eax-and 0/imm32
4630       b8/copy-to-eax 1/imm32/true
4631       75/jump-if-not-equal $operand-matches-primitive?:end/disp8
4632       # if string contents don't match, return false
4633       (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register
4634       3d/compare-eax-and 0/imm32
4635       b8/copy-to-eax 0/imm32/false
4636       74/jump-if-equal $operand-matches-primitive?:end/disp8
4637     }
4638     # return true
4639     b8/copy-to-eax 1/imm32/true
4640 $operand-matches-primitive?:end:
4641     # . restore registers
4642     5f/pop-to-edi
4643     5e/pop-to-esi
4644     # . epilogue
4645     89/<- %esp 5/r32/ebp
4646     5d/pop-to-ebp
4647     c3/return
4648 
4649 type-equal?:  # a : (handle tree type-id), b : (handle tree type-id) => result/eax : boolean
4650     # . prologue
4651     55/push-ebp
4652     89/<- %ebp 4/r32/esp
4653     # . save registers
4654     51/push-ecx
4655     52/push-edx
4656     # ecx = a
4657     8b/-> *(ebp+8) 1/r32/ecx
4658     # edx = b
4659     8b/-> *(ebp+0xc) 2/r32/edx
4660     # if (a == b) return true
4661     8b/-> %ecx 0/r32/eax  # Var-type
4662     39/compare %edx 0/r32/eax  # Var-type
4663     b8/copy-to-eax 1/imm32/true
4664     0f 84/jump-if-equal $type-equal?:end/disp32
4665     # if (a == 0) return false
4666     81 7/subop/compare %ecx 0/imm32
4667     b8/copy-to-eax 0/imm32/false
4668     0f 84/jump-if-equal $type-equal?:end/disp32
4669     # if (b == 0) return false
4670     81 7/subop/compare %edx 0/imm32
4671     b8/copy-to-eax 0/imm32/false
4672     0f 84/jump-if-equal $type-equal?:end/disp32
4673     # if (!type-equal?(a->left, b->left)) return false
4674     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
4675     3d/compare-eax-and 0/imm32
4676     0f 84/jump-if-equal $type-equal?:end/disp32
4677     # return type-equal?(a->right, b->right
4678     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
4679 $type-equal?:end:
4680     # . restore registers
4681     5a/pop-to-edx
4682     59/pop-to-ecx
4683     # . epilogue
4684     89/<- %esp 5/r32/ebp
4685     5d/pop-to-ebp
4686     c3/return
4687 
4688 test-emit-subx-statement-primitive:
4689     # Primitive operation on a variable on the stack.
4690     #   increment foo
4691     # =>
4692     #   ff 0/subop/increment *(ebp-8)
4693     #
4694     # There's a variable on the var stack as follows:
4695     #   name: 'foo'
4696     #   type: int
4697     #   stack-offset: -8
4698     #
4699     # There's a primitive with this info:
4700     #   name: 'increment'
4701     #   inouts: int/mem
4702     #   value: 'ff 0/subop/increment'
4703     #
4704     # There's nothing in functions.
4705     #
4706     # . prologue
4707     55/push-ebp
4708     89/<- %ebp 4/r32/esp
4709     # setup
4710     (clear-stream _test-output-stream)
4711     (clear-stream $_test-output-buffered-file->buffer)
4712     # var type/ecx : (handle tree type-id) = int
4713     68/push 0/imm32/right/null
4714     68/push 1/imm32/left/int
4715     89/<- %ecx 4/r32/esp
4716     # var var-foo/ecx : var
4717     68/push 0/imm32/no-register
4718     68/push -8/imm32/stack-offset
4719     68/push 1/imm32/block-depth
4720     51/push-ecx
4721     68/push "foo"/imm32
4722     89/<- %ecx 4/r32/esp
4723     # var operand/ebx : (list var)
4724     68/push 0/imm32/next
4725     51/push-ecx/var-foo
4726     89/<- %ebx 4/r32/esp
4727     # var stmt/esi : statement
4728     68/push 0/imm32/next
4729     68/push 0/imm32/outputs
4730     53/push-ebx/operands
4731     68/push "increment"/imm32/operation
4732     68/push 1/imm32
4733     89/<- %esi 4/r32/esp
4734     # var primitives/ebx : primitive
4735     68/push 0/imm32/next
4736     68/push 0/imm32/output-is-write-only
4737     68/push 0/imm32/no-imm32
4738     68/push 0/imm32/no-r32
4739     68/push 1/imm32/rm32-is-first-inout
4740     68/push "ff 0/subop/increment"/imm32/subx-name
4741     68/push 0/imm32/outputs
4742     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
4743     68/push "increment"/imm32/name
4744     89/<- %ebx 4/r32/esp
4745     # convert
4746     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
4747     (flush _test-output-buffered-file)
4748 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
4754     # check output
4755     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive")
4756     # . epilogue
4757     89/<- %esp 5/r32/ebp
4758     5d/pop-to-ebp
4759     c3/return
4760 
4761 test-emit-subx-statement-primitive-register:
4762     # Primitive operation on a variable in a register.
4763     #   foo <- increment
4764     # =>
4765     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
4766     #
4767     # There's a variable on the var stack as follows:
4768     #   name: 'foo'
4769     #   type: int
4770     #   register: 'eax'
4771     #
4772     # There's a primitive with this info:
4773     #   name: 'increment'
4774     #   out: int/reg
4775     #   value: 'ff 0/subop/increment'
4776     #
4777     # There's nothing in functions.
4778     #
4779     # . prologue
4780     55/push-ebp
4781     89/<- %ebp 4/r32/esp
4782     # setup
4783     (clear-stream _test-output-stream)
4784     (clear-stream $_test-output-buffered-file->buffer)
4785     # var type/ecx : (handle tree type-id) = int
4786     68/push 0/imm32/right/null
4787     68/push 1/imm32/left/int
4788     89/<- %ecx 4/r32/esp
4789     # var var-foo/ecx : var in eax
4790     68/push "eax"/imm32/register
4791     68/push 0/imm32/no-stack-offset
4792     68/push 1/imm32/block-depth
4793     51/push-ecx
4794     68/push "foo"/imm32
4795     89/<- %ecx 4/r32/esp
4796     # var operand/ebx : (list var)
4797     68/push 0/imm32/next
4798     51/push-ecx/var-foo
4799     89/<- %ebx 4/r32/esp
4800     # var stmt/esi : statement
4801     68/push 0/imm32/next
4802     53/push-ebx/outputs
4803     68/push 0/imm32/inouts
4804     68/push "increment"/imm32/operation
4805     68/push 1/imm32
4806     89/<- %esi 4/r32/esp
4807     # var formal-var/ebx : var in any register
4808     68/push Any-register/imm32
4809     68/push 0/imm32/no-stack-offset
4810     68/push 1/imm32/block-depth
4811     ff 6/subop/push *(ecx+4)  # Var-type
4812     68/push "dummy"/imm32
4813     89/<- %ebx 4/r32/esp
4814     # var operand/ebx : (list var)
4815     68/push 0/imm32/next
4816     53/push-ebx/formal-var
4817     89/<- %ebx 4/r32/esp
4818     # var primitives/ebx : primitive
4819     68/push 0/imm32/next
4820     68/push 0/imm32/output-is-write-only
4821     68/push 0/imm32/no-imm32
4822     68/push 0/imm32/no-r32
4823     68/push 3/imm32/rm32-in-first-output
4824     68/push "ff 0/subop/increment"/imm32/subx-name
4825     53/push-ebx/outputs
4826     68/push 0/imm32/inouts
4827     68/push "increment"/imm32/name
4828     89/<- %ebx 4/r32/esp
4829     # convert
4830     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
4831     (flush _test-output-buffered-file)
4832 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
4838     # check output
4839     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register")
4840     # . epilogue
4841     89/<- %esp 5/r32/ebp
4842     5d/pop-to-ebp
4843     c3/return
4844 
4845 test-emit-subx-statement-select-primitive:
4846     # Select the right primitive between overloads.
4847     #   foo <- increment
4848     # =>
4849     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
4850     #
4851     # There's a variable on the var stack as follows:
4852     #   name: 'foo'
4853     #   type: int
4854     #   register: 'eax'
4855     #
4856     # There's two primitives, as follows:
4857     #   - name: 'increment'
4858     #     out: int/reg
4859     #     value: 'ff 0/subop/increment'
4860     #   - name: 'increment'
4861     #     inout: int/mem
4862     #     value: 'ff 0/subop/increment'
4863     #
4864     # There's nothing in functions.
4865     #
4866     # . prologue
4867     55/push-ebp
4868     89/<- %ebp 4/r32/esp
4869     # setup
4870     (clear-stream _test-output-stream)
4871     (clear-stream $_test-output-buffered-file->buffer)
4872     # var type/ecx : (handle tree type-id) = int
4873     68/push 0/imm32/right/null
4874     68/push 1/imm32/left/int
4875     89/<- %ecx 4/r32/esp
4876     # var var-foo/ecx : var in eax
4877     68/push "eax"/imm32/register
4878     68/push 0/imm32/no-stack-offset
4879     68/push 1/imm32/block-depth
4880     51/push-ecx
4881     68/push "foo"/imm32
4882     89/<- %ecx 4/r32/esp
4883     # var real-outputs/edi : (list var)
4884     68/push 0/imm32/next
4885     51/push-ecx/var-foo
4886     89/<- %edi 4/r32/esp
4887     # var stmt/esi : statement
4888     68/push 0/imm32/next
4889     57/push-edi/outputs
4890     68/push 0/imm32/inouts
4891     68/push "increment"/imm32/operation
4892     68/push 1/imm32
4893     89/<- %esi 4/r32/esp
4894     # var formal-var/ebx : var in any register
4895     68/push Any-register/imm32
4896     68/push 0/imm32/no-stack-offset
4897     68/push 1/imm32/block-depth
4898     ff 6/subop/push *(ecx+4)  # Var-type
4899     68/push "dummy"/imm32
4900     89/<- %ebx 4/r32/esp
4901     # var formal-outputs/ebx : (list var) = {formal-var, 0}
4902     68/push 0/imm32/next
4903     53/push-ebx/formal-var
4904     89/<- %ebx 4/r32/esp
4905     # var primitive1/ebx : primitive
4906     68/push 0/imm32/next
4907     68/push 0/imm32/output-is-write-only
4908     68/push 0/imm32/no-imm32
4909     68/push 0/imm32/no-r32
4910     68/push 3/imm32/rm32-in-first-output
4911     68/push "ff 0/subop/increment"/imm32/subx-name
4912     53/push-ebx/outputs/formal-outputs
4913     68/push 0/imm32/inouts
4914     68/push "increment"/imm32/name
4915     89/<- %ebx 4/r32/esp
4916     # var primitives/ebx : primitive
4917     53/push-ebx/next
4918     68/push 0/imm32/output-is-write-only
4919     68/push 0/imm32/no-imm32
4920     68/push 0/imm32/no-r32
4921     68/push 1/imm32/rm32-is-first-inout
4922     68/push "ff 0/subop/increment"/imm32/subx-name
4923     68/push 0/imm32/outputs
4924     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
4925     68/push "increment"/imm32/name
4926     89/<- %ebx 4/r32/esp
4927     # convert
4928     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
4929     (flush _test-output-buffered-file)
4930 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
4936     # check output
4937     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive")
4938     # . epilogue
4939     89/<- %esp 5/r32/ebp
4940     5d/pop-to-ebp
4941     c3/return
4942 
4943 test-emit-subx-statement-select-primitive-2:
4944     # Select the right primitive between overloads.
4945     #   foo <- increment
4946     # =>
4947     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
4948     #
4949     # There's a variable on the var stack as follows:
4950     #   name: 'foo'
4951     #   type: int
4952     #   register: 'eax'
4953     #
4954     # There's two primitives, as follows:
4955     #   - name: 'increment'
4956     #     out: int/reg
4957     #     value: 'ff 0/subop/increment'
4958     #   - name: 'increment'
4959     #     inout: int/mem
4960     #     value: 'ff 0/subop/increment'
4961     #
4962     # There's nothing in functions.
4963     #
4964     # . prologue
4965     55/push-ebp
4966     89/<- %ebp 4/r32/esp
4967     # setup
4968     (clear-stream _test-output-stream)
4969     (clear-stream $_test-output-buffered-file->buffer)
4970     # var type/ecx : (handle tree type-id) = int
4971     68/push 0/imm32/right/null
4972     68/push 1/imm32/left/int
4973     89/<- %ecx 4/r32/esp
4974     # var var-foo/ecx : var in eax
4975     68/push "eax"/imm32/register
4976     68/push 0/imm32/no-stack-offset
4977     68/push 1/imm32/block-depth
4978     51/push-ecx
4979     68/push "foo"/imm32
4980     89/<- %ecx 4/r32/esp
4981     # var inouts/edi : (list var)
4982     68/push 0/imm32/next
4983     51/push-ecx/var-foo
4984     89/<- %edi 4/r32/esp
4985     # var stmt/esi : statement
4986     68/push 0/imm32/next
4987     68/push 0/imm32/outputs
4988     57/push-edi/inouts
4989     68/push "increment"/imm32/operation
4990     68/push 1/imm32
4991     89/<- %esi 4/r32/esp
4992     # var formal-var/ebx : var in any register
4993     68/push Any-register/imm32
4994     68/push 0/imm32/no-stack-offset
4995     68/push 1/imm32/block-depth
4996     ff 6/subop/push *(ecx+4)  # Var-type
4997     68/push "dummy"/imm32
4998     89/<- %ebx 4/r32/esp
4999     # var operand/ebx : (list var)
5000     68/push 0/imm32/next
5001     53/push-ebx/formal-var
5002     89/<- %ebx 4/r32/esp
5003     # var primitive1/ebx : primitive
5004     68/push 0/imm32/next
5005     68/push 0/imm32/output-is-write-only
5006     68/push 0/imm32/no-imm32
5007     68/push 0/imm32/no-r32
5008     68/push 3/imm32/rm32-in-first-output
5009     68/push "ff 0/subop/increment"/imm32/subx-name
5010     53/push-ebx/outputs/formal-outputs
5011     68/push 0/imm32/inouts
5012     68/push "increment"/imm32/name
5013     89/<- %ebx 4/r32/esp
5014     # var primitives/ebx : primitive
5015     53/push-ebx/next
5016     68/push 0/imm32/output-is-write-only
5017     68/push 0/imm32/no-imm32
5018     68/push 0/imm32/no-r32
5019     68/push 1/imm32/rm32-is-first-inout
5020     68/push "ff 0/subop/increment"/imm32/subx-name
5021     68/push 0/imm32/outputs
5022     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
5023     68/push "increment"/imm32/name
5024     89/<- %ebx 4/r32/esp
5025     # convert
5026     (emit-subx-statement _test-output-buffered-file %esi %ebx 0)
5027     (flush _test-output-buffered-file)
5028 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5034     # check output
5035     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2")
5036     # . epilogue
5037     89/<- %esp 5/r32/ebp
5038     5d/pop-to-ebp
5039     c3/return
5040 
5041 test-increment-register:
5042     # Select the right primitive between overloads.
5043     #   foo <- increment
5044     # =>
5045     #   50/increment-eax
5046     #
5047     # There's a variable on the var stack as follows:
5048     #   name: 'foo'
5049     #   type: int
5050     #   register: 'eax'
5051     #
5052     # Primitives are the global definitions.
5053     #
5054     # There are no functions defined.
5055     #
5056     # . prologue
5057     55/push-ebp
5058     89/<- %ebp 4/r32/esp
5059     # setup
5060     (clear-stream _test-output-stream)
5061     (clear-stream $_test-output-buffered-file->buffer)
5062     # var type/ecx : (handle tree type-id) = int
5063     68/push 0/imm32/right/null
5064     68/push 1/imm32/left/int
5065     89/<- %ecx 4/r32/esp
5066     # var var-foo/ecx : var in eax
5067     68/push "eax"/imm32/register
5068     68/push 0/imm32/no-stack-offset
5069     68/push 1/imm32/block-depth
5070     51/push-ecx
5071     68/push "foo"/imm32
5072     89/<- %ecx 4/r32/esp
5073     # var real-outputs/edi : (list var)
5074     68/push 0/imm32/next
5075     51/push-ecx/var-foo
5076     89/<- %edi 4/r32/esp
5077     # var stmt/esi : statement
5078     68/push 0/imm32/next
5079     57/push-edi/outputs
5080     68/push 0/imm32/inouts
5081     68/push "increment"/imm32/operation
5082     68/push 1/imm32/regular-statement
5083     89/<- %esi 4/r32/esp
5084     # convert
5085     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5086     (flush _test-output-buffered-file)
5087 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5093     # check output
5094     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
5095     # . epilogue
5096     89/<- %esp 5/r32/ebp
5097     5d/pop-to-ebp
5098     c3/return
5099 
5100 test-increment-var:
5101     # Select the right primitive between overloads.
5102     #   foo <- increment
5103     # =>
5104     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
5105     #
5106     # There's a variable on the var stack as follows:
5107     #   name: 'foo'
5108     #   type: int
5109     #   register: 'eax'
5110     #
5111     # Primitives are the global definitions.
5112     #
5113     # There are no functions defined.
5114     #
5115     # . prologue
5116     55/push-ebp
5117     89/<- %ebp 4/r32/esp
5118     # setup
5119     (clear-stream _test-output-stream)
5120     (clear-stream $_test-output-buffered-file->buffer)
5121     # var type/ecx : (handle tree type-id) = int
5122     68/push 0/imm32/right/null
5123     68/push 1/imm32/left/int
5124     89/<- %ecx 4/r32/esp
5125     # var var-foo/ecx : var in eax
5126     68/push "eax"/imm32/register
5127     68/push 0/imm32/no-stack-offset
5128     68/push 1/imm32/block-depth
5129     51/push-ecx
5130     68/push "foo"/imm32
5131     89/<- %ecx 4/r32/esp
5132     # var inouts/edi : (list var)
5133     68/push 0/imm32/next
5134     51/push-ecx/var-foo
5135     89/<- %edi 4/r32/esp
5136     # var stmt/esi : statement
5137     68/push 0/imm32/next
5138     68/push 0/imm32/outputs
5139     57/push-edi/inouts
5140     68/push "increment"/imm32/operation
5141     68/push 1/imm32
5142     89/<- %esi 4/r32/esp
5143     # convert
5144     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5145     (flush _test-output-buffered-file)
5146 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5152     # check output
5153     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-var")
5154     # . epilogue
5155     89/<- %esp 5/r32/ebp
5156     5d/pop-to-ebp
5157     c3/return
5158 
5159 test-add-reg-to-reg:
5160     #   var1/reg <- add var2/reg
5161     # =>
5162     #   01/add %var1 var2
5163     #
5164     # . prologue
5165     55/push-ebp
5166     89/<- %ebp 4/r32/esp
5167     # setup
5168     (clear-stream _test-output-stream)
5169     (clear-stream $_test-output-buffered-file->buffer)
5170     # var type/ecx : (handle tree type-id) = int
5171     68/push 0/imm32/right/null
5172     68/push 1/imm32/left/int
5173     89/<- %ecx 4/r32/esp
5174     # var var-var1/ecx : var in eax
5175     68/push "eax"/imm32/register
5176     68/push 0/imm32/no-stack-offset
5177     68/push 1/imm32/block-depth
5178     51/push-ecx
5179     68/push "var1"/imm32
5180     89/<- %ecx 4/r32/esp
5181     # var var-var2/edx : var in ecx
5182     68/push "ecx"/imm32/register
5183     68/push 0/imm32/no-stack-offset
5184     68/push 1/imm32/block-depth
5185     ff 6/subop/push *(ecx+4)  # Var-type
5186     68/push "var2"/imm32
5187     89/<- %edx 4/r32/esp
5188     # var inouts/esi : (list var2)
5189     68/push 0/imm32/next
5190     52/push-edx/var-var2
5191     89/<- %esi 4/r32/esp
5192     # var outputs/edi : (list var1)
5193     68/push 0/imm32/next
5194     51/push-ecx/var-var1
5195     89/<- %edi 4/r32/esp
5196     # var stmt/esi : statement
5197     68/push 0/imm32/next
5198     57/push-edi/outputs
5199     56/push-esi/inouts
5200     68/push "add"/imm32/operation
5201     68/push 1/imm32
5202     89/<- %esi 4/r32/esp
5203     # convert
5204     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5205     (flush _test-output-buffered-file)
5206 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5212     # check output
5213     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
5214     # . epilogue
5215     89/<- %esp 5/r32/ebp
5216     5d/pop-to-ebp
5217     c3/return
5218 
5219 test-add-reg-to-mem:
5220     #   add-to var1 var2/reg
5221     # =>
5222     #   01/add *(ebp+__) var2
5223     #
5224     # . prologue
5225     55/push-ebp
5226     89/<- %ebp 4/r32/esp
5227     # setup
5228     (clear-stream _test-output-stream)
5229     (clear-stream $_test-output-buffered-file->buffer)
5230     # var type/ecx : (handle tree type-id) = int
5231     68/push 0/imm32/right/null
5232     68/push 1/imm32/left/int
5233     89/<- %ecx 4/r32/esp
5234     # var var-var1/ecx : var
5235     68/push 0/imm32/no-register
5236     68/push 8/imm32/stack-offset
5237     68/push 1/imm32/block-depth
5238     51/push-ecx
5239     68/push "var1"/imm32
5240     89/<- %ecx 4/r32/esp
5241     # var var-var2/edx : var in ecx
5242     68/push "ecx"/imm32/register
5243     68/push 0/imm32/no-stack-offset
5244     68/push 1/imm32/block-depth
5245     ff 6/subop/push *(ecx+4)  # Var-type
5246     68/push "var2"/imm32
5247     89/<- %edx 4/r32/esp
5248     # var inouts/esi : (list var2)
5249     68/push 0/imm32/next
5250     52/push-edx/var-var2
5251     89/<- %esi 4/r32/esp
5252     # var inouts = (list var1 var2)
5253     56/push-esi/next
5254     51/push-ecx/var-var1
5255     89/<- %esi 4/r32/esp
5256     # var stmt/esi : statement
5257     68/push 0/imm32/next
5258     68/push 0/imm32/outputs
5259     56/push-esi/inouts
5260     68/push "add-to"/imm32/operation
5261     68/push 1/imm32
5262     89/<- %esi 4/r32/esp
5263     # convert
5264     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5265     (flush _test-output-buffered-file)
5266 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5272     # check output
5273     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
5274     # . epilogue
5275     89/<- %esp 5/r32/ebp
5276     5d/pop-to-ebp
5277     c3/return
5278 
5279 test-add-mem-to-reg:
5280     #   var1/reg <- add var2
5281     # =>
5282     #   03/add *(ebp+__) var1
5283     #
5284     # . prologue
5285     55/push-ebp
5286     89/<- %ebp 4/r32/esp
5287     # setup
5288     (clear-stream _test-output-stream)
5289     (clear-stream $_test-output-buffered-file->buffer)
5290     # var type/ecx : (handle tree type-id) = int
5291     68/push 0/imm32/right/null
5292     68/push 1/imm32/left/int
5293     89/<- %ecx 4/r32/esp
5294     # var var-var1/ecx : var in eax
5295     68/push "eax"/imm32/register
5296     68/push 0/imm32/no-stack-offset
5297     68/push 1/imm32/block-depth
5298     51/push-ecx
5299     68/push "var1"/imm32
5300     89/<- %ecx 4/r32/esp
5301     # var var-var2/edx : var
5302     68/push 0/imm32/no-register
5303     68/push 8/imm32/stack-offset
5304     68/push 1/imm32/block-depth
5305     ff 6/subop/push *(ecx+4)  # Var-type
5306     68/push "var2"/imm32
5307     89/<- %edx 4/r32/esp
5308     # var inouts/esi : (list var2)
5309     68/push 0/imm32/next
5310     52/push-edx/var-var2
5311     89/<- %esi 4/r32/esp
5312     # var outputs/edi : (list var1)
5313     68/push 0/imm32/next
5314     51/push-ecx/var-var1
5315     89/<- %edi 4/r32/esp
5316     # var stmt/esi : statement
5317     68/push 0/imm32/next
5318     57/push-edi/outputs
5319     56/push-esi/inouts
5320     68/push "add"/imm32/operation
5321     68/push 1/imm32
5322     89/<- %esi 4/r32/esp
5323     # convert
5324     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5325     (flush _test-output-buffered-file)
5326 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5332     # check output
5333     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
5334     # . epilogue
5335     89/<- %esp 5/r32/ebp
5336     5d/pop-to-ebp
5337     c3/return
5338 
5339 test-add-literal-to-eax:
5340     #   var1/eax <- add 0x34
5341     # =>
5342     #   05/add-to-eax 0x34/imm32
5343     #
5344     # . prologue
5345     55/push-ebp
5346     89/<- %ebp 4/r32/esp
5347     # setup
5348     (clear-stream _test-output-stream)
5349     (clear-stream $_test-output-buffered-file->buffer)
5350     # var type/ecx : (handle tree type-id) = int
5351     68/push 0/imm32/right/null
5352     68/push 1/imm32/left/int
5353     89/<- %ecx 4/r32/esp
5354     # var var-var1/ecx : var in eax
5355     68/push "eax"/imm32/register
5356     68/push 0/imm32/no-stack-offset
5357     68/push 1/imm32/block-depth
5358     51/push-ecx
5359     68/push "var1"/imm32
5360     89/<- %ecx 4/r32/esp
5361     # var type/edx : (handle tree type-id) = literal
5362     68/push 0/imm32/right/null
5363     68/push 0/imm32/left/literal
5364     89/<- %edx 4/r32/esp
5365     # var var-var2/edx : var literal
5366     68/push 0/imm32/no-register
5367     68/push 0/imm32/no-stack-offset
5368     68/push 1/imm32/block-depth
5369     52/push-edx
5370     68/push "0x34"/imm32
5371     89/<- %edx 4/r32/esp
5372     # var inouts/esi : (list var2)
5373     68/push 0/imm32/next
5374     52/push-edx/var-var2
5375     89/<- %esi 4/r32/esp
5376     # var outputs/edi : (list var1)
5377     68/push 0/imm32/next
5378     51/push-ecx/var-var1
5379     89/<- %edi 4/r32/esp
5380     # var stmt/esi : statement
5381     68/push 0/imm32/next
5382     57/push-edi/outputs
5383     56/push-esi/inouts
5384     68/push "add"/imm32/operation
5385     68/push 1/imm32
5386     89/<- %esi 4/r32/esp
5387     # convert
5388     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5389     (flush _test-output-buffered-file)
5390 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5396     # check output
5397     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
5398     # . epilogue
5399     89/<- %esp 5/r32/ebp
5400     5d/pop-to-ebp
5401     c3/return
5402 
5403 test-add-literal-to-reg:
5404     #   var1/ecx <- add 0x34
5405     # =>
5406     #   81 0/subop/add %ecx 0x34/imm32
5407     #
5408     # . prologue
5409     55/push-ebp
5410     89/<- %ebp 4/r32/esp
5411     # setup
5412     (clear-stream _test-output-stream)
5413     (clear-stream $_test-output-buffered-file->buffer)
5414     # var type/ecx : (handle tree type-id) = int
5415     68/push 0/imm32/right/null
5416     68/push 1/imm32/left/int
5417     89/<- %ecx 4/r32/esp
5418     # var var-var1/ecx : var in ecx
5419     68/push "ecx"/imm32/register
5420     68/push 0/imm32/no-stack-offset
5421     68/push 1/imm32/block-depth
5422     51/push-ecx
5423     68/push "var1"/imm32
5424     89/<- %ecx 4/r32/esp
5425     # var type/edx : (handle tree type-id) = literal
5426     68/push 0/imm32/right/null
5427     68/push 0/imm32/left/literal
5428     89/<- %edx 4/r32/esp
5429     # var var-var2/edx : var literal
5430     68/push 0/imm32/no-register
5431     68/push 0/imm32/no-stack-offset
5432     68/push 1/imm32/block-depth
5433     52/push-edx
5434     68/push "0x34"/imm32
5435     89/<- %edx 4/r32/esp
5436     # var inouts/esi : (list var2)
5437     68/push 0/imm32/next
5438     52/push-edx/var-var2
5439     89/<- %esi 4/r32/esp
5440     # var outputs/edi : (list var1)
5441     68/push 0/imm32/next
5442     51/push-ecx/var-var1
5443     89/<- %edi 4/r32/esp
5444     # var stmt/esi : statement
5445     68/push 0/imm32/next
5446     57/push-edi/outputs
5447     56/push-esi/inouts
5448     68/push "add"/imm32/operation
5449     68/push 1/imm32
5450     89/<- %esi 4/r32/esp
5451     # convert
5452     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5453     (flush _test-output-buffered-file)
5454 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5460     # check output
5461     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
5462     # . epilogue
5463     89/<- %esp 5/r32/ebp
5464     5d/pop-to-ebp
5465     c3/return
5466 
5467 test-add-literal-to-mem:
5468     #   add-to var1, 0x34
5469     # =>
5470     #   81 0/subop/add %eax 0x34/imm32
5471     #
5472     # . prologue
5473     55/push-ebp
5474     89/<- %ebp 4/r32/esp
5475     # setup
5476     (clear-stream _test-output-stream)
5477     (clear-stream $_test-output-buffered-file->buffer)
5478     # var type/ecx : (handle tree type-id) = int
5479     68/push 0/imm32/right/null
5480     68/push 1/imm32/left/int
5481     89/<- %ecx 4/r32/esp
5482     # var var-var1/ecx : var
5483     68/push 0/imm32/no-register
5484     68/push 8/imm32/stack-offset
5485     68/push 1/imm32/block-depth
5486     51/push-ecx
5487     68/push "var1"/imm32
5488     89/<- %ecx 4/r32/esp
5489     # var type/edx : (handle tree type-id) = literal
5490     68/push 0/imm32/right/null
5491     68/push 0/imm32/left/literal
5492     89/<- %edx 4/r32/esp
5493     # var var-var2/edx : var literal
5494     68/push 0/imm32/no-register
5495     68/push 0/imm32/no-stack-offset
5496     68/push 1/imm32/block-depth
5497     52/push-edx
5498     68/push "0x34"/imm32
5499     89/<- %edx 4/r32/esp
5500     # var inouts/esi : (list var2)
5501     68/push 0/imm32/next
5502     52/push-edx/var-var2
5503     89/<- %esi 4/r32/esp
5504     # var inouts = (list var1 inouts)
5505     56/push-esi/next
5506     51/push-ecx/var-var1
5507     89/<- %esi 4/r32/esp
5508     # var stmt/esi : statement
5509     68/push 0/imm32/next
5510     68/push 0/imm32/outputs
5511     56/push-esi/inouts
5512     68/push "add-to"/imm32/operation
5513     68/push 1/imm32
5514     89/<- %esi 4/r32/esp
5515     # convert
5516     (emit-subx-statement _test-output-buffered-file %esi Primitives 0)
5517     (flush _test-output-buffered-file)
5518 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5524     # check output
5525     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
5526     # . epilogue
5527     89/<- %esp 5/r32/ebp
5528     5d/pop-to-ebp
5529     c3/return
5530 
5531 test-emit-subx-statement-function-call:
5532     # Call a function on a variable on the stack.
5533     #   f foo
5534     # =>
5535     #   (f2 *(ebp-8))
5536     # (Changing the function name supports overloading in general, but here it
5537     # just serves to help disambiguate things.)
5538     #
5539     # There's a variable on the var stack as follows:
5540     #   name: 'foo'
5541     #   type: int
5542     #   stack-offset: -8
5543     #
5544     # There's nothing in primitives.
5545     #
5546     # There's a function with this info:
5547     #   name: 'f'
5548     #   inout: int/mem
5549     #   value: 'f2'
5550     #
5551     # . prologue
5552     55/push-ebp
5553     89/<- %ebp 4/r32/esp
5554     # setup
5555     (clear-stream _test-output-stream)
5556     (clear-stream $_test-output-buffered-file->buffer)
5557     # var type/ecx : (handle tree type-id) = int
5558     68/push 0/imm32/right/null
5559     68/push 1/imm32/left/int
5560     89/<- %ecx 4/r32/esp
5561     # var var-foo/ecx : var
5562     68/push 0/imm32/no-register
5563     68/push -8/imm32/stack-offset
5564     68/push 0/imm32/block-depth
5565     51/push-ecx
5566     68/push "foo"/imm32
5567     89/<- %ecx 4/r32/esp
5568     # var operands/esi : (list var)
5569     68/push 0/imm32/next
5570     51/push-ecx/var-foo
5571     89/<- %esi 4/r32/esp
5572     # var stmt/esi : statement
5573     68/push 0/imm32/next
5574     68/push 0/imm32/outputs
5575     56/push-esi/inouts
5576     68/push "f"/imm32/operation
5577     68/push 1/imm32
5578     89/<- %esi 4/r32/esp
5579     # var functions/ebx : function
5580     68/push 0/imm32/next
5581     68/push 0/imm32/body
5582     68/push 0/imm32/outputs
5583     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
5584     68/push "f2"/imm32/subx-name
5585     68/push "f"/imm32/name
5586     89/<- %ebx 4/r32/esp
5587     # convert
5588     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
5589     (flush _test-output-buffered-file)
5590 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5596     # check output
5597     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call")
5598     # . epilogue
5599     89/<- %esp 5/r32/ebp
5600     5d/pop-to-ebp
5601     c3/return
5602 
5603 test-emit-subx-statement-function-call-with-literal-arg:
5604     # Call a function on a literal.
5605     #   f 34
5606     # =>
5607     #   (f2 34)
5608     #
5609     # . prologue
5610     55/push-ebp
5611     89/<- %ebp 4/r32/esp
5612     # setup
5613     (clear-stream _test-output-stream)
5614     (clear-stream $_test-output-buffered-file->buffer)
5615     # var type/ecx : (handle tree type-id) = literal
5616     68/push 0/imm32/right/null
5617     68/push 0/imm32/left/literal
5618     89/<- %ecx 4/r32/esp
5619     # var var-foo/ecx : var literal
5620     68/push 0/imm32/no-register
5621     68/push 0/imm32/no-stack-offset
5622     68/push 0/imm32/block-depth
5623     51/push-ecx
5624     68/push "34"/imm32
5625     89/<- %ecx 4/r32/esp
5626     # var operands/esi : (list var)
5627     68/push 0/imm32/next
5628     51/push-ecx/var-foo
5629     89/<- %esi 4/r32/esp
5630     # var stmt/esi : statement
5631     68/push 0/imm32/next
5632     68/push 0/imm32/outputs
5633     56/push-esi/inouts
5634     68/push "f"/imm32/operation
5635     68/push 1/imm32
5636     89/<- %esi 4/r32/esp
5637     # var functions/ebx : function
5638     68/push 0/imm32/next
5639     68/push 0/imm32/body
5640     68/push 0/imm32/outputs
5641     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
5642     68/push "f2"/imm32/subx-name
5643     68/push "f"/imm32/name
5644     89/<- %ebx 4/r32/esp
5645     # convert
5646     (emit-subx-statement _test-output-buffered-file %esi 0 %ebx)
5647     (flush _test-output-buffered-file)
5648 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
5654     # check output
5655     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg")
5656     # . epilogue
5657     89/<- %esp 5/r32/ebp
5658     5d/pop-to-ebp
5659     c3/return
5660 
5661 emit-subx-prologue:  # out : (addr buffered-file)
5662     # . prologue
5663     55/push-ebp
5664     89/<- %ebp 4/r32/esp
5665     #
5666     (write-buffered *(ebp+8) "# . prologue\n")
5667     (write-buffered *(ebp+8) "55/push-ebp\n")
5668     (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n")
5669 $emit-subx-prologue:end:
5670     # . epilogue
5671     89/<- %esp 5/r32/ebp
5672     5d/pop-to-ebp
5673     c3/return
5674 
5675 emit-subx-epilogue:  # out : (addr buffered-file)
5676     # . prologue
5677     55/push-ebp
5678     89/<- %ebp 4/r32/esp
5679     #
5680     (write-buffered *(ebp+8) "# . epilogue\n")
5681     (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n")
5682     (write-buffered *(ebp+8) "5d/pop-to-ebp\n")
5683     (write-buffered *(ebp+8) "c3/return\n")
5684 $emit-subx-epilogue:end:
5685     # . epilogue
5686     89/<- %esp 5/r32/ebp
5687     5d/pop-to-ebp
5688     c3/return