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