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