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