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