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