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