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