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