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 #
  36 # Function inputs are always passed in memory (on the stack), while outputs
  37 # are always returned in registers.
  38 #
  39 # Blocks mostly consist of statements.
  40 #
  41 # Statements mostly consist of a name, optional inputs and optional outputs.
  42 #
  43 # Statement inputs are variables or literals. Variables need to specify type
  44 # (and storage) the first time they're mentioned but not later.
  45 #
  46 # Statement outputs, like function outputs, must be variables in registers.
  47 #
  48 # Statement names must be either primitives or user-defined functions.
  49 #
  50 # Primitives can write to any register.
  51 # User-defined functions only write to hard-coded registers. Outputs of each
  52 # call must have the same registers as in the function definition.
  53 #
  54 # There are some other statement types:
  55 #   - blocks. Multiple statements surrounded by '{...}' and optionally
  56 #     prefixed with a label name and ':'
  57 #       - {
  58 #           ...
  59 #         }
  60 #       - foo: {
  61 #           ...
  62 #         }
  63 #
  64 #   - variable definitions on the stack. E.g.:
  65 #       - var foo: int
  66 #       - var bar: (array int 3)
  67 #     There's no initializer; variables are automatically initialized.
  68 #     The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
  69 #
  70 #   - variables definitions in a register. E.g.:
  71 #       - var foo/eax: int <- add bar 1
  72 #     The initializer is mandatory and must be a valid instruction that writes
  73 #     a single output to the right register. In practice registers will
  74 #     usually be either initialized by primitives or copied from eax.
  75 #       - var eax: int <- foo bar quux
  76 #         var floo/ecx: int <- copy eax
  77 #
  78 # Still todo:
  79 #   global variables
  80 #   heap allocations (planned name: 'handle')
  81 #   user-defined types: 'type' for structs, 'choice' for unions
  82 #   short-lived 'address' type for efficiently writing inside nested structs
  83 #
  84 # We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle'
  85 # and 'address' in comments. Their definitions are in layer 50, but really you
  86 # can ignore the distinctions on a first reading of this program.
  87 #
  88 # Formal types:
  89 #   A program is a linked list of functions
  90 #   A function contains:
  91 #     name: (handle array byte)
  92 #     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
  93 #       data: (handle var)
  94 #       next: (handle list)
  95 #     outputs: linked list of vars
  96 #       data: (handle var)
  97 #       next: (handle list)
  98 #     body: (handle block)
  99 #   A var-type contains:
 100 #     name: (handle array byte)
 101 #     type: (handle tree type-id)
 102 #
 103 #   A statement can be:
 104 #     tag 0: a block
 105 #     tag 1: a simple statement (stmt1)
 106 #     tag 2: a variable defined on the stack
 107 #     tag 3: a variable defined in a register
 108 #
 109 #   A block contains:
 110 #     tag: 0
 111 #     statements: (handle list stmt)
 112 #     name: (handle array byte) -- starting with '$'
 113 #
 114 #   A regular statement contains:
 115 #     tag: 1
 116 #     operation: (handle array byte)
 117 #     inouts: (handle list operand)
 118 #     outputs: (handle list var)
 119 #
 120 #   A variable defined on the stack contains:
 121 #     tag: 2
 122 #     name: (handle array byte)
 123 #     type: (handle tree type-id)
 124 #
 125 #   A variable defined in a register contains:
 126 #     tag: 3
 127 #     name: (handle array byte)
 128 #     type: (handle tree type-id)
 129 #     reg: (handle array byte)
 130 
 131 # == Translation: managing the stack
 132 # Now that we know what the language looks like in the large, let's think
 133 # about how translation happens from the bottom up. One crucial piece of the
 134 # puzzle is how Mu will clean up variables defined on the stack for you.
 135 #
 136 # Assume that we maintain a 'functions' list while parsing source code. And a
 137 # 'primitives' list is a global constant. Both these contain enough information
 138 # to perform type-checking on function calls or primitive statements, respectively.
 139 #
 140 # Defining variables pushes them on a stack with the current block depth and
 141 # enough information about their location (stack offset or register).
 142 # Starting a block increments the current block id.
 143 # Each statement now has enough information to emit code for it.
 144 # Ending a block is where the magic happens:
 145 #   pop all variables at the current block depth
 146 #   emit code to restore all register variables introduced at the current depth
 147 #   emit code to clean up all stack variables at the current depth (just increment esp)
 148 #   decrement the current block depth
 149 #
 150 # Formal types:
 151 #   live-vars: stack of vars
 152 #   var:
 153 #     name: (handle array byte)
 154 #     type: (handle tree type-id)
 155 #     block: int
 156 #     stack-offset: int  (added to ebp)
 157 #     register: (handle array byte)
 158 #       either usual register names
 159 #       or '*' to indicate any register
 160 #   At most one of stack-offset or register-index must be non-zero.
 161 #   A register of '*' designates a variable _template_. Only legal in formal
 162 #   parameters for primitives.
 163 
 164 # == Translating a single function call
 165 # This one's easy. Assuming we've already checked things, we just drop the
 166 # outputs (which use hard-coded registers) and emit inputs in a standard format.
 167 #
 168 # out1, out2, out3, ... <- name inout1, inout2, inout3, ...
 169 # =>
 170 # (subx-name inout1 inout2 inout3)
 171 #
 172 # Formal types:
 173 #   functions: linked list of info
 174 #     name: (handle array byte)
 175 #     inouts: linked list of vars
 176 #     outputs: linked list of vars
 177 #     body: block (singleton linked list)
 178 #     subx-name: (handle array byte)
 179 
 180 # == Translating a single primitive instruction
 181 # A second crucial piece of the puzzle is how Mu converts fairly regular
 182 # primitives with their uniform syntax to SubX instructions with their gnarly
 183 # x86 details.
 184 #
 185 # Mu instructions have inputs and outputs. Primitives can have up to 2 of
 186 # them.
 187 # SubX instructions have rm32 and r32 operands.
 188 # The translation between them covers almost all the possibilities.
 189 #   Instructions with 1 inout may turn into ones with 1 rm32
 190 #     (e.g. incrementing a var on the stack)
 191 #   Instructions with 1 output may turn into ones with 1 rm32
 192 #     (e.g. incrementing a var in a register)
 193 #   1 inout and 1 output may turn into 1 rm32 and 1 r32
 194 #     (e.g. adding a var to a reg)
 195 #   2 inouts may turn into 1 rm32 and 1 r32
 196 #     (e.g. adding a reg to a var)
 197 #   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
 198 #     (e.g. adding a constant to a var)
 199 #   1 output and 1 literal may turn into 1 rm32 and 1 imm32
 200 #     (e.g. adding a constant to a reg)
 201 #   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
 202 #     (special-case: divide edx:eax by a var or reg)
 203 # Observations:
 204 #   We always emit rm32. It may be the first inout or the first output.
 205 #   We may emit r32 or imm32 or neither.
 206 #   When we emit r32 it may come from first inout or second inout or first output.
 207 #
 208 # Accordingly, the formal data structure for a primitive looks like this:
 209 #   primitives: linked list of info
 210 #     name: (handle array byte)
 211 #     mu-inouts: linked list of vars to check
 212 #     mu-outputs: linked list of vars to check; at most a singleton
 213 #     subx-name: (handle array byte)
 214 #     subx-rm32: enum arg-location
 215 #     subx-r32: enum arg-location
 216 #     subx-imm32: enum arg-location
 217 #     subx-disp32: enum arg-location
 218 #     output-is-write-only: boolean
 219 #   arg-location: enum
 220 #     0 means none
 221 #     1 means first inout
 222 #     2 means second inout
 223 #     3 means first output
 224 
 225 # == Translating a block
 226 # Emit block name if necessary
 227 # Emit '{'
 228 # When you encounter a statement, emit it as above
 229 # When you encounter a variable declaration
 230 #   emit any code needed for it (bzeros)
 231 #   push it on the var stack
 232 #   update register dict if necessary
 233 # When you encounter '}'
 234 #   While popping variables off the var stack until block id changes
 235 #     Emit code needed to clean up the stack
 236 #       either increment esp
 237 #       or pop into appropriate register
 238 
 239 # The rest is straightforward.
 240 
 241 == data
 242 
 243 Program:
 244 _Program-functions:  # (handle function)
 245   0/imm32
 246 _Program-types:  # (handle typeinfo)
 247   0/imm32
 248 
 249 # Some constants for simulating the data structures described above.
 250 # Many constants here come with a type in a comment.
 251 #
 252 # Sometimes the type is of the value at that offset for the given type. For
 253 # example, if you start at a function record and move forward Function-inouts
 254 # bytes, you'll find a (handle list var).
 255 #
 256 # At other times, the type is of the constant itself. For example, the type of
 257 # the constant Function-size is (addr int). To get the size of a function,
 258 # look in *Function-size.
 259 
 260 Function-name:  # (handle array byte)
 261   0/imm32
 262 Function-subx-name:  # (handle array byte)
 263   4/imm32
 264 Function-inouts:  # (handle list var)
 265   8/imm32
 266 Function-outputs:  # (handle list var)
 267   0xc/imm32
 268 Function-body:  # (handle block)
 269   0x10/imm32
 270 Function-next:  # (handle function)
 271   0x14/imm32
 272 Function-size:  # (addr int)
 273   0x18/imm32/24
 274 
 275 Primitive-name:  # (handle array byte)
 276   0/imm32
 277 Primitive-inouts:  # (handle list var)
 278   4/imm32
 279 Primitive-outputs:  # (handle list var)
 280   8/imm32
 281 Primitive-subx-name:  # (handle array byte)
 282   0xc/imm32
 283 Primitive-subx-rm32:  # enum arg-location
 284   0x10/imm32
 285 Primitive-subx-r32:  # enum arg-location
 286   0x14/imm32
 287 Primitive-subx-imm32:  # enum arg-location
 288   0x18/imm32
 289 Primitive-subx-disp32:  # enum arg-location  -- only for branches
 290   0x1c/imm32
 291 Primitive-output-is-write-only:  # boolean
 292   0x20/imm32
 293 Primitive-next:  # (handle function)
 294   0x24/imm32
 295 Primitive-size:  # (addr int)
 296   0x28/imm32/36
 297 
 298 Stmt-tag:  # int
 299   0/imm32
 300 
 301 Block-stmts:  # (handle list stmt)
 302   4/imm32
 303 Block-var:  # (handle var)
 304   8/imm32
 305 
 306 Stmt1-operation:  # (handle array byte)
 307   4/imm32
 308 Stmt1-inouts:  # (handle stmt-var)
 309   8/imm32
 310 Stmt1-outputs:  # (handle stmt-var)
 311   0xc/imm32
 312 
 313 Vardef-var:  # (handle var)
 314   4/imm32
 315 
 316 Regvardef-operation:  # (handle array byte)
 317   4/imm32
 318 Regvardef-inouts:  # (handle stmt-var)
 319   8/imm32
 320 Regvardef-outputs:  # (handle stmt-var)  # will have exactly one element
 321   0xc/imm32
 322 
 323 Stmt-size:  # (addr int)
 324   0x10/imm32
 325 
 326 Var-name:  # (handle array byte)
 327   0/imm32
 328 Var-type:  # (handle tree type-id)
 329   4/imm32
 330 Var-block-depth:  # int
 331   8/imm32
 332 Var-offset:  # int
 333   0xc/imm32
 334 Var-register:  # (handle array byte) -- name of a register
 335   0x10/imm32
 336 Var-size:  # (addr int)
 337   0x14/imm32
 338 
 339 Any-register:  # wildcard
 340   # size
 341   1/imm32
 342   # data
 343   2a/asterisk
 344 
 345 List-value:
 346   0/imm32
 347 List-next:  # (handle list _)
 348   4/imm32
 349 List-size:  # (addr int)
 350   8/imm32
 351 
 352 # A stmt-var is like a list of vars with call-site specific metadata
 353 Stmt-var-value:  # (handle var)
 354   0/imm32
 355 Stmt-var-next:  # (handle stmt-var)
 356   4/imm32
 357 Stmt-var-is-deref:  # boolean
 358   8/imm32
 359 Stmt-var-size:  # (addr int)
 360   0xc/imm32
 361 
 362 # Types are expressed as trees (s-expressions) of type-ids (ints).
 363 # However, there's no need for singletons, so we can assume (int) == int
 364 #   - if x->right == nil, x is an atom
 365 #   - x->left contains either a pointer to a pair, or an atomic type-id directly.
 366 #     type ids will be less than 0x10000 (MAX_TYPE_ID).
 367 
 368 Tree-left:  # either type-id or (addr tree type-id)
 369   0/imm32
 370 Tree-right:  # (addr tree type-id)
 371   4/imm32
 372 Tree-size:  # (addr int)
 373   8/imm32
 374 
 375 # Types
 376 
 377 Max-type-id:
 378   0x10000/imm32
 379 
 380 Type-id:  # (stream (address array byte))
 381   0x1c/imm32/write
 382   0/imm32/read
 383   0x100/imm32/length
 384   # data
 385   "literal"/imm32  # 0
 386   "int"/imm32  # 1
 387   "addr"/imm32  # 2
 388   "array"/imm32  # 3
 389   "handle"/imm32  # 4
 390   "boolean"/imm32  # 5
 391   "constant"/imm32  # 6: like a literal, but replaced with its value in Var-offset
 392   0/imm32
 393   # 0x20
 394   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 395   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 396   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 397   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 398   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 399   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 400   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
 401 
 402 # Program->types contains some typeinfo for each type definition.
 403 # Types contain vars with types, but can't specify registers.
 404 Typeinfo-id:  # type-id
 405   0/imm32
 406 # each field of a type is represented using two var's:
 407 #   1. the input var: expected type of the field; convenient for creating using parse-var-with-type
 408 #   2. the output var: a constant containing the byte offset; convenient for code-generation
 409 Typeinfo-fields:  # (handle table string {(handle var), (handle var)})
 410   4/imm32
 411 Typeinfo-total-size-in-bytes:  # int
 412   8/imm32
 413 Typeinfo-next:  # (handle typeinfo)
 414   0xc/imm32
 415 Typeinfo-size:  # (addr int)
 416   0x10/imm32
 417 
 418 == code
 419 
 420 Entry:
 421     # . prologue
 422     89/<- %ebp 4/r32/esp
 423     (new-segment *Heap-size Heap)
 424     # if (argv[1] == "test') run-tests()
 425     {
 426       # if (argc <= 1) break
 427       81 7/subop/compare *ebp 1/imm32
 428       7e/jump-if-<= break/disp8
 429       # if (argv[1] != "test") break
 430       (kernel-string-equal? *(ebp+8) "test")  # => eax
 431       3d/compare-eax-and 0/imm32/false
 432       74/jump-if-= break/disp8
 433       #
 434       (run-tests)
 435       # syscall(exit, *Num-test-failures)
 436       8b/-> *Num-test-failures 3/r32/ebx
 437       eb/jump $mu-main:end/disp8
 438     }
 439     # otherwise convert Stdin
 440     (convert-mu Stdin Stdout)
 441     (flush Stdout)
 442     # syscall(exit, 0)
 443     bb/copy-to-ebx 0/imm32
 444 $mu-main:end:
 445     b8/copy-to-eax 1/imm32/exit
 446     cd/syscall 0x80/imm8
 447 
 448 convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
 449     # . prologue
 450     55/push-ebp
 451     89/<- %ebp 4/r32/esp
 452     #
 453     (parse-mu *(ebp+8))
 454     (check-mu-types)
 455     (emit-subx *(ebp+0xc))
 456 $convert-mu:end:
 457     # . epilogue
 458     89/<- %esp 5/r32/ebp
 459     5d/pop-to-ebp
 460     c3/return
 461 
 462 test-convert-empty-input:
 463     # empty input => empty output
 464     # . prologue
 465     55/push-ebp
 466     89/<- %ebp 4/r32/esp
 467     # setup
 468     (clear-stream _test-input-stream)
 469     (clear-stream $_test-input-buffered-file->buffer)
 470     (clear-stream _test-output-stream)
 471     (clear-stream $_test-output-buffered-file->buffer)
 472     #
 473     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 474     (flush _test-output-buffered-file)
 475     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 476     # . epilogue
 477     89/<- %esp 5/r32/ebp
 478     5d/pop-to-ebp
 479     c3/return
 480 
 481 test-convert-function-skeleton:
 482     # . prologue
 483     55/push-ebp
 484     89/<- %ebp 4/r32/esp
 485     # setup
 486     (clear-stream _test-input-stream)
 487     (clear-stream $_test-input-buffered-file->buffer)
 488     (clear-stream _test-output-stream)
 489     (clear-stream $_test-output-buffered-file->buffer)
 490     #
 491     (write _test-input-stream "fn foo {\n")
 492     (write _test-input-stream "}\n")
 493     # convert
 494     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 495     (flush _test-output-buffered-file)
 496 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 502     # check output
 503     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
 504     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
 505     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
 506     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 507     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
 508     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 509     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 510     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
 511     # . epilogue
 512     89/<- %esp 5/r32/ebp
 513     5d/pop-to-ebp
 514     c3/return
 515 
 516 test-convert-multiple-function-skeletons:
 517     # . prologue
 518     55/push-ebp
 519     89/<- %ebp 4/r32/esp
 520     # setup
 521     (clear-stream _test-input-stream)
 522     (clear-stream $_test-input-buffered-file->buffer)
 523     (clear-stream _test-output-stream)
 524     (clear-stream $_test-output-buffered-file->buffer)
 525     #
 526     (write _test-input-stream "fn foo {\n")
 527     (write _test-input-stream "}\n")
 528     (write _test-input-stream "fn bar {\n")
 529     (write _test-input-stream "}\n")
 530     # convert
 531     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 532     (flush _test-output-buffered-file)
 533 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 539     # check first function
 540     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
 541     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
 542     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 543     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 544     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 545     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 546     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 547     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
 548     # check second function
 549     (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
 550     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
 551     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 552     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 553     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 554     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 555     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 556     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
 557     # . epilogue
 558     89/<- %esp 5/r32/ebp
 559     5d/pop-to-ebp
 560     c3/return
 561 
 562 test-convert-function-with-arg:
 563     # . prologue
 564     55/push-ebp
 565     89/<- %ebp 4/r32/esp
 566     # setup
 567     (clear-stream _test-input-stream)
 568     (clear-stream $_test-input-buffered-file->buffer)
 569     (clear-stream _test-output-stream)
 570     (clear-stream $_test-output-buffered-file->buffer)
 571     #
 572     (write _test-input-stream "fn foo n: int {\n")
 573     (write _test-input-stream "}\n")
 574     # convert
 575     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 576     (flush _test-output-buffered-file)
 577 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 583     # check output
 584     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
 585     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
 586     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
 587     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
 588     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
 589     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
 590     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
 591     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
 592     # . epilogue
 593     89/<- %esp 5/r32/ebp
 594     5d/pop-to-ebp
 595     c3/return
 596 
 597 test-convert-function-with-arg-and-body:
 598     # . prologue
 599     55/push-ebp
 600     89/<- %ebp 4/r32/esp
 601     # setup
 602     (clear-stream _test-input-stream)
 603     (clear-stream $_test-input-buffered-file->buffer)
 604     (clear-stream _test-output-stream)
 605     (clear-stream $_test-output-buffered-file->buffer)
 606     c7 0/subop/copy *Next-block-index 1/imm32
 607     #
 608     (write _test-input-stream "fn foo n: int {\n")
 609     (write _test-input-stream "  increment n\n")
 610     (write _test-input-stream "}\n")
 611     # convert
 612     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 613     (flush _test-output-buffered-file)
 614 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 620     # check output
 621     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
 622     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
 623     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
 624     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
 625     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
 626     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
 627     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
 628     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
 629     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
 630     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
 631     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
 632     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
 633     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
 634     # . epilogue
 635     89/<- %esp 5/r32/ebp
 636     5d/pop-to-ebp
 637     c3/return
 638 
 639 test-convert-function-distinguishes-args:
 640     # . prologue
 641     55/push-ebp
 642     89/<- %ebp 4/r32/esp
 643     # setup
 644     (clear-stream _test-input-stream)
 645     (clear-stream $_test-input-buffered-file->buffer)
 646     (clear-stream _test-output-stream)
 647     (clear-stream $_test-output-buffered-file->buffer)
 648     c7 0/subop/copy *Next-block-index 1/imm32
 649     #
 650     (write _test-input-stream "fn foo a: int, b: int {\n")
 651     (write _test-input-stream "  increment b\n")
 652     (write _test-input-stream "}\n")
 653     # convert
 654     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 655     (flush _test-output-buffered-file)
 656 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 662     # check output
 663     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
 664     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
 665     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
 666     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
 667     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
 668     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
 669     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
 670     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
 671     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
 672     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
 673     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
 674     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
 675     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
 676     # . epilogue
 677     89/<- %esp 5/r32/ebp
 678     5d/pop-to-ebp
 679     c3/return
 680 
 681 test-convert-function-returns-result:
 682     # . prologue
 683     55/push-ebp
 684     89/<- %ebp 4/r32/esp
 685     # setup
 686     (clear-stream _test-input-stream)
 687     (clear-stream $_test-input-buffered-file->buffer)
 688     (clear-stream _test-output-stream)
 689     (clear-stream $_test-output-buffered-file->buffer)
 690     c7 0/subop/copy *Next-block-index 1/imm32
 691     #
 692     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 693     (write _test-input-stream "  result <- copy a\n")
 694     (write _test-input-stream "  result <- increment\n")
 695     (write _test-input-stream "}\n")
 696     # convert
 697     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 698     (flush _test-output-buffered-file)
 699 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 705     # check output
 706     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-returns-result/0")
 707     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-returns-result/1")
 708     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-returns-result/2")
 709     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
 710     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-returns-result/4")
 711     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-returns-result/5")
 712     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/6")
 713     (check-next-stream-line-equal _test-output-stream "    40/increment-eax"    "F - test-convert-function-returns-result/7")
 714     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-returns-result/8")
 715     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-returns-result/9")
 716     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-returns-result/10")
 717     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/11")
 718     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-returns-result/12")
 719     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-returns-result/13")
 720     # . epilogue
 721     89/<- %esp 5/r32/ebp
 722     5d/pop-to-ebp
 723     c3/return
 724 
 725 test-convert-function-literal-arg:
 726     # . prologue
 727     55/push-ebp
 728     89/<- %ebp 4/r32/esp
 729     # setup
 730     (clear-stream _test-input-stream)
 731     (clear-stream $_test-input-buffered-file->buffer)
 732     (clear-stream _test-output-stream)
 733     (clear-stream $_test-output-buffered-file->buffer)
 734     c7 0/subop/copy *Next-block-index 1/imm32
 735     #
 736     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
 737     (write _test-input-stream "  result <- copy a\n")
 738     (write _test-input-stream "  result <- add 1\n")
 739     (write _test-input-stream "}\n")
 740     # convert
 741     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 742     (flush _test-output-buffered-file)
 743 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 749     # check output
 750     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg/0")
 751     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg/1")
 752     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg/2")
 753     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg/3")
 754     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg/4")
 755     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg/5")
 756     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-literal-arg/6")
 757     (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-literal-arg/7")
 758     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg/8")
 759     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg/9")
 760     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg/10")
 761     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg/11")
 762     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg/12")
 763     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg/13")
 764     # . epilogue
 765     89/<- %esp 5/r32/ebp
 766     5d/pop-to-ebp
 767     c3/return
 768 
 769 test-convert-function-literal-arg-2:
 770     # . prologue
 771     55/push-ebp
 772     89/<- %ebp 4/r32/esp
 773     # setup
 774     (clear-stream _test-input-stream)
 775     (clear-stream $_test-input-buffered-file->buffer)
 776     (clear-stream _test-output-stream)
 777     (clear-stream $_test-output-buffered-file->buffer)
 778     c7 0/subop/copy *Next-block-index 1/imm32
 779     #
 780     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
 781     (write _test-input-stream "  result <- copy a\n")
 782     (write _test-input-stream "  result <- add 1\n")
 783     (write _test-input-stream "}\n")
 784     # convert
 785     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 786     (flush _test-output-buffered-file)
 787 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 793     # check output
 794     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-literal-arg-2/0")
 795     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-literal-arg-2/1")
 796     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-literal-arg-2/2")
 797     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-literal-arg-2/3")
 798     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-literal-arg-2/4")
 799     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-literal-arg-2/5")
 800     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-literal-arg-2/6")
 801     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-literal-arg-2/7")
 802     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-literal-arg-2/8")
 803     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-literal-arg-2/9")
 804     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-literal-arg-2/10")
 805     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-literal-arg-2/11")
 806     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-literal-arg-2/12")
 807     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-literal-arg-2/13")
 808     # . epilogue
 809     89/<- %esp 5/r32/ebp
 810     5d/pop-to-ebp
 811     c3/return
 812 
 813 test-convert-function-call-with-literal-arg:
 814     # . prologue
 815     55/push-ebp
 816     89/<- %ebp 4/r32/esp
 817     # setup
 818     (clear-stream _test-input-stream)
 819     (clear-stream $_test-input-buffered-file->buffer)
 820     (clear-stream _test-output-stream)
 821     (clear-stream $_test-output-buffered-file->buffer)
 822     c7 0/subop/copy *Next-block-index 1/imm32
 823     #
 824     (write _test-input-stream "fn main -> result/ebx: int {\n")
 825     (write _test-input-stream "  result <- do-add 3 4\n")
 826     (write _test-input-stream "}\n")
 827     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
 828     (write _test-input-stream "  result <- copy a\n")
 829     (write _test-input-stream "  result <- add b\n")
 830     (write _test-input-stream "}\n")
 831     # convert
 832     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 833     (flush _test-output-buffered-file)
 834 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 840     # check output
 841     (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
 842     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
 843     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
 844     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
 845     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
 846     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
 847     (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/6")
 848     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/7")
 849     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
 850     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/9")
 851     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/10")
 852     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/11")
 853     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/12")
 854     (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/13")
 855     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/14")
 856     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/15")
 857     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/16")
 858     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/17")
 859     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/18")
 860     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/19")
 861     (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/20")
 862     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/21")
 863     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/22")
 864     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/23")
 865     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/24")
 866     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/25")
 867     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/26")
 868     # . epilogue
 869     89/<- %esp 5/r32/ebp
 870     5d/pop-to-ebp
 871     c3/return
 872 
 873 test-convert-function-with-local-var-in-mem:
 874     # . prologue
 875     55/push-ebp
 876     89/<- %ebp 4/r32/esp
 877     # setup
 878     (clear-stream _test-input-stream)
 879     (clear-stream $_test-input-buffered-file->buffer)
 880     (clear-stream _test-output-stream)
 881     (clear-stream $_test-output-buffered-file->buffer)
 882     c7 0/subop/copy *Next-block-index 1/imm32
 883     #
 884     (write _test-input-stream "fn foo {\n")
 885     (write _test-input-stream "  var x: int\n")
 886     (write _test-input-stream "  increment x\n")
 887     (write _test-input-stream "}\n")
 888     # convert
 889     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 890     (flush _test-output-buffered-file)
 891 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 897     # check output
 898     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
 899     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
 900     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
 901     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
 902     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
 903     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
 904     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
 905     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
 906     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-mem/8")
 907     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
 908     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
 909     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
 910     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
 911     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
 912     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
 913     # . epilogue
 914     89/<- %esp 5/r32/ebp
 915     5d/pop-to-ebp
 916     c3/return
 917 
 918 test-convert-function-with-local-var-in-reg:
 919     # . prologue
 920     55/push-ebp
 921     89/<- %ebp 4/r32/esp
 922     # setup
 923     (clear-stream _test-input-stream)
 924     (clear-stream $_test-input-buffered-file->buffer)
 925     (clear-stream _test-output-stream)
 926     (clear-stream $_test-output-buffered-file->buffer)
 927     c7 0/subop/copy *Next-block-index 1/imm32
 928     #
 929     (write _test-input-stream "fn foo {\n")
 930     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 931     (write _test-input-stream "  x <- increment\n")
 932     (write _test-input-stream "}\n")
 933     # convert
 934     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 935     (flush _test-output-buffered-file)
 936 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 942     # check output
 943     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
 944     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
 945     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
 946     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
 947     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
 948     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
 949     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
 950     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
 951     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
 952     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
 953     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
 954     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
 955     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
 956     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
 957     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
 958     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
 959     # . epilogue
 960     89/<- %esp 5/r32/ebp
 961     5d/pop-to-ebp
 962     c3/return
 963 
 964 test-convert-function-with-local-var-dereferenced:
 965     # . prologue
 966     55/push-ebp
 967     89/<- %ebp 4/r32/esp
 968     # setup
 969     (clear-stream _test-input-stream)
 970     (clear-stream $_test-input-buffered-file->buffer)
 971     (clear-stream _test-output-stream)
 972     (clear-stream $_test-output-buffered-file->buffer)
 973     c7 0/subop/copy *Next-block-index 1/imm32
 974     #
 975     (write _test-input-stream "fn foo {\n")
 976     (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
 977     (write _test-input-stream "  increment *x\n")
 978     (write _test-input-stream "}\n")
 979     # convert
 980     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 981     (flush _test-output-buffered-file)
 982 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
 988     # check output
 989     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
 990     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
 991     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
 992     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
 993     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
 994     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
 995     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
 996     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
 997     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
 998     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
 999     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
1000     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
1001     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
1002     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
1003     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
1004     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
1005     # . epilogue
1006     89/<- %esp 5/r32/ebp
1007     5d/pop-to-ebp
1008     c3/return
1009 
1010 test-convert-compare-register-with-literal:
1011     # . prologue
1012     55/push-ebp
1013     89/<- %ebp 4/r32/esp
1014     # setup
1015     (clear-stream _test-input-stream)
1016     (clear-stream $_test-input-buffered-file->buffer)
1017     (clear-stream _test-output-stream)
1018     (clear-stream $_test-output-buffered-file->buffer)
1019     c7 0/subop/copy *Next-block-index 1/imm32
1020     #
1021     (write _test-input-stream "fn foo {\n")
1022     (write _test-input-stream "  var x/ecx: int <- copy 0\n")
1023     (write _test-input-stream "  compare x, 0\n")
1024     (write _test-input-stream "}\n")
1025     # convert
1026     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1027     (flush _test-output-buffered-file)
1028 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1034     # check output
1035     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
1036     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
1037     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
1038     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
1039     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
1040     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
1041     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
1042     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
1043     (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
1044     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
1045     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
1046     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
1047     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
1048     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
1049     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
1050     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
1051     # . epilogue
1052     89/<- %esp 5/r32/ebp
1053     5d/pop-to-ebp
1054     c3/return
1055 
1056 test-convert-function-with-local-var-in-block:
1057     # . prologue
1058     55/push-ebp
1059     89/<- %ebp 4/r32/esp
1060     # setup
1061     (clear-stream _test-input-stream)
1062     (clear-stream $_test-input-buffered-file->buffer)
1063     (clear-stream _test-output-stream)
1064     (clear-stream $_test-output-buffered-file->buffer)
1065     c7 0/subop/copy *Next-block-index 1/imm32
1066     #
1067     (write _test-input-stream "fn foo {\n")
1068     (write _test-input-stream "  {\n")
1069     (write _test-input-stream "    var x: int\n")
1070     (write _test-input-stream "    increment x\n")
1071     (write _test-input-stream "  }\n")
1072     (write _test-input-stream "}\n")
1073     # convert
1074     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1075     (flush _test-output-buffered-file)
1076 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1082     # check output
1083     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
1084     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
1085     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
1086     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
1087     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
1088     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
1089     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
1090     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
1091     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
1092     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
1093     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-block/10")
1094     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
1095     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
1096     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
1097     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
1098     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
1099     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
1100     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
1101     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
1102     # . epilogue
1103     89/<- %esp 5/r32/ebp
1104     5d/pop-to-ebp
1105     c3/return
1106 
1107 test-convert-function-with-local-var-in-named-block:
1108     # . prologue
1109     55/push-ebp
1110     89/<- %ebp 4/r32/esp
1111     # setup
1112     (clear-stream _test-input-stream)
1113     (clear-stream $_test-input-buffered-file->buffer)
1114     (clear-stream _test-output-stream)
1115     (clear-stream $_test-output-buffered-file->buffer)
1116     c7 0/subop/copy *Next-block-index 1/imm32
1117     #
1118     (write _test-input-stream "fn foo {\n")
1119     (write _test-input-stream "  $bar: {\n")
1120     (write _test-input-stream "    var x: int\n")
1121     (write _test-input-stream "    increment x\n")
1122     (write _test-input-stream "  }\n")
1123     (write _test-input-stream "}\n")
1124     # convert
1125     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1126     (flush _test-output-buffered-file)
1127 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1133     # check output
1134     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
1135     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
1136     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
1137     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
1138     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
1139     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
1140     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
1141     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
1142     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
1143     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-named-block/9")
1144     (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/10")
1145     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
1146     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
1147     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
1148     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
1149     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
1150     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
1151     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
1152     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
1153     # . epilogue
1154     89/<- %esp 5/r32/ebp
1155     5d/pop-to-ebp
1156     c3/return
1157 
1158 test-convert-function-with-branches-in-block:
1159     # . prologue
1160     55/push-ebp
1161     89/<- %ebp 4/r32/esp
1162     # setup
1163     (clear-stream _test-input-stream)
1164     (clear-stream $_test-input-buffered-file->buffer)
1165     (clear-stream _test-output-stream)
1166     (clear-stream $_test-output-buffered-file->buffer)
1167     c7 0/subop/copy *Next-block-index 1/imm32
1168     #
1169     (write _test-input-stream "fn foo x: int {\n")
1170     (write _test-input-stream "  {\n")
1171     (write _test-input-stream "    break-if->=\n")
1172     (write _test-input-stream "    loop-if-addr<\n")
1173     (write _test-input-stream "    increment x\n")
1174     (write _test-input-stream "    loop\n")
1175     (write _test-input-stream "  }\n")
1176     (write _test-input-stream "}\n")
1177     # convert
1178     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1179     (flush _test-output-buffered-file)
1180 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1186     # check output
1187     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
1188     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
1189     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
1190     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
1191     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
1192     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
1193     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
1194     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
1195     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
1196     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
1197     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
1198     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
1199     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
1200     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
1201     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
1202     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
1203     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
1204     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
1205     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
1206     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
1207     # . epilogue
1208     89/<- %esp 5/r32/ebp
1209     5d/pop-to-ebp
1210     c3/return
1211 
1212 test-convert-function-with-branches-in-named-block:
1213     # . prologue
1214     55/push-ebp
1215     89/<- %ebp 4/r32/esp
1216     # setup
1217     (clear-stream _test-input-stream)
1218     (clear-stream $_test-input-buffered-file->buffer)
1219     (clear-stream _test-output-stream)
1220     (clear-stream $_test-output-buffered-file->buffer)
1221     c7 0/subop/copy *Next-block-index 1/imm32
1222     #
1223     (write _test-input-stream "fn foo x: int {\n")
1224     (write _test-input-stream "  $bar: {\n")
1225     (write _test-input-stream "    break-if->= $bar\n")
1226     (write _test-input-stream "    loop-if-addr< $bar\n")
1227     (write _test-input-stream "    increment x\n")
1228     (write _test-input-stream "    loop\n")
1229     (write _test-input-stream "  }\n")
1230     (write _test-input-stream "}\n")
1231     # convert
1232     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1233     (flush _test-output-buffered-file)
1234 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1240     # check output
1241     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
1242     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
1243     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
1244     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
1245     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
1246     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
1247     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
1248     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
1249     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= $bar:break/disp32"  "F - test-convert-function-with-branches-in-named-block/8")
1250     (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/9")
1251     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
1252     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
1253     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
1254     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
1255     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
1256     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
1257     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
1258     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
1259     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
1260     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
1261     # . epilogue
1262     89/<- %esp 5/r32/ebp
1263     5d/pop-to-ebp
1264     c3/return
1265 
1266 test-convert-function-with-var-in-nested-block:
1267     # . prologue
1268     55/push-ebp
1269     89/<- %ebp 4/r32/esp
1270     # setup
1271     (clear-stream _test-input-stream)
1272     (clear-stream $_test-input-buffered-file->buffer)
1273     (clear-stream _test-output-stream)
1274     (clear-stream $_test-output-buffered-file->buffer)
1275     c7 0/subop/copy *Next-block-index 1/imm32
1276     #
1277     (write _test-input-stream "fn foo x: int {\n")
1278     (write _test-input-stream "  {\n")
1279     (write _test-input-stream "    {\n")
1280     (write _test-input-stream "      var x: int\n")
1281     (write _test-input-stream "      increment x\n")
1282     (write _test-input-stream "    }\n")
1283     (write _test-input-stream "  }\n")
1284     (write _test-input-stream "}\n")
1285     # convert
1286     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1287     (flush _test-output-buffered-file)
1288 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1294     # check output
1295     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
1296     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
1297     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
1298     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
1299     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
1300     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
1301     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
1302     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
1303     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
1304     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
1305     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
1306     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
1307     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-var-in-nested-block/12")
1308     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
1309     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
1310     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
1311     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
1312     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
1313     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
1314     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
1315     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
1316     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
1317     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
1318     # . epilogue
1319     89/<- %esp 5/r32/ebp
1320     5d/pop-to-ebp
1321     c3/return
1322 
1323 test-convert-function-with-multiple-vars-in-nested-blocks:
1324     # . prologue
1325     55/push-ebp
1326     89/<- %ebp 4/r32/esp
1327     # setup
1328     (clear-stream _test-input-stream)
1329     (clear-stream $_test-input-buffered-file->buffer)
1330     (clear-stream _test-output-stream)
1331     (clear-stream $_test-output-buffered-file->buffer)
1332     c7 0/subop/copy *Next-block-index 1/imm32
1333     #
1334     (write _test-input-stream "fn foo x: int {\n")
1335     (write _test-input-stream "  {\n")
1336     (write _test-input-stream "    var x/eax: int <- copy 0\n")
1337     (write _test-input-stream "    {\n")
1338     (write _test-input-stream "      var y: int\n")
1339     (write _test-input-stream "      x <- add y\n")
1340     (write _test-input-stream "    }\n")
1341     (write _test-input-stream "  }\n")
1342     (write _test-input-stream "}\n")
1343     # convert
1344     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1345     (flush _test-output-buffered-file)
1346 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1352     # check output
1353     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
1354     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
1355     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
1356     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
1357     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
1358     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
1359     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
1360     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
1361     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
1362     (check-next-stream-line-equal _test-output-stream "      b8/copy-to-eax 0/imm32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/9")
1363     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
1364     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
1365     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
1366     (check-next-stream-line-equal _test-output-stream "        03/add *(ebp+0xfffffff8) 0x00000000/r32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/13")
1367     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/14")
1368     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
1369     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
1370     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
1371     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
1372     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
1373     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
1374     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
1375     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
1376     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
1377     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
1378     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
1379     # . epilogue
1380     89/<- %esp 5/r32/ebp
1381     5d/pop-to-ebp
1382     c3/return
1383 
1384 test-convert-function-with-branches-and-local-vars:
1385     # A conditional 'break' after a 'var' in a block is converted into a
1386     # nested block that performs all necessary cleanup before jumping. This
1387     # results in some ugly code duplication.
1388     # . prologue
1389     55/push-ebp
1390     89/<- %ebp 4/r32/esp
1391     # setup
1392     (clear-stream _test-input-stream)
1393     (clear-stream $_test-input-buffered-file->buffer)
1394     (clear-stream _test-output-stream)
1395     (clear-stream $_test-output-buffered-file->buffer)
1396     c7 0/subop/copy *Next-block-index 1/imm32
1397     #
1398     (write _test-input-stream "fn foo {\n")
1399     (write _test-input-stream "  {\n")
1400     (write _test-input-stream "    var x: int\n")
1401     (write _test-input-stream "    break-if->=\n")
1402     (write _test-input-stream "    increment x\n")
1403     (write _test-input-stream "  }\n")
1404     (write _test-input-stream "}\n")
1405     # convert
1406     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1407     (flush _test-output-buffered-file)
1408 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1414     # check output
1415     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
1416     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
1417     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
1418     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
1419     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
1420     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
1421     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
1422     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
1423     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
1424     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
1425     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
1426     (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")
1427     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
1428     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
1429     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
1430     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/15")
1431     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
1432     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
1433     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
1434     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
1435     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
1436     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
1437     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
1438     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
1439     # . epilogue
1440     89/<- %esp 5/r32/ebp
1441     5d/pop-to-ebp
1442     c3/return
1443 
1444 test-convert-function-with-conditional-loops-and-local-vars:
1445     # A conditional 'loop' after a 'var' in a block is converted into a nested
1446     # block that performs all necessary cleanup before jumping. This results
1447     # in some ugly code duplication.
1448     # . prologue
1449     55/push-ebp
1450     89/<- %ebp 4/r32/esp
1451     # setup
1452     (clear-stream _test-input-stream)
1453     (clear-stream $_test-input-buffered-file->buffer)
1454     (clear-stream _test-output-stream)
1455     (clear-stream $_test-output-buffered-file->buffer)
1456     c7 0/subop/copy *Next-block-index 1/imm32
1457     #
1458     (write _test-input-stream "fn foo {\n")
1459     (write _test-input-stream "  {\n")
1460     (write _test-input-stream "    var x: int\n")
1461     (write _test-input-stream "    loop-if->=\n")
1462     (write _test-input-stream "    increment x\n")
1463     (write _test-input-stream "  }\n")
1464     (write _test-input-stream "}\n")
1465     # convert
1466     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1467     (flush _test-output-buffered-file)
1468 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1474     # check output
1475     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
1476     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
1477     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
1478     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
1479     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
1480     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
1481     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
1482     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
1483     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
1484     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
1485     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-conditional-loops-and-local-vars/10")
1486     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/11")
1487     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:loop/disp32"  "F - test-convert-function-with-conditional-loops-and-local-vars/12")
1488     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
1489     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-conditional-loops-and-local-vars/14")
1490     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/15")
1491     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
1492     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
1493     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
1494     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
1495     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
1496     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
1497     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
1498     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
1499     # . epilogue
1500     89/<- %esp 5/r32/ebp
1501     5d/pop-to-ebp
1502     c3/return
1503 
1504 test-convert-function-with-unconditional-loops-and-local-vars:
1505     # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
1506     # regular block cleanup. Any instructions after 'loop' are dead and
1507     # therefore skipped.
1508     # . prologue
1509     55/push-ebp
1510     89/<- %ebp 4/r32/esp
1511     # setup
1512     (clear-stream _test-input-stream)
1513     (clear-stream $_test-input-buffered-file->buffer)
1514     (clear-stream _test-output-stream)
1515     (clear-stream $_test-output-buffered-file->buffer)
1516     c7 0/subop/copy *Next-block-index 1/imm32
1517     #
1518     (write _test-input-stream "fn foo {\n")
1519     (write _test-input-stream "  {\n")
1520     (write _test-input-stream "    var x: int\n")
1521     (write _test-input-stream "    loop\n")
1522     (write _test-input-stream "    increment x\n")
1523     (write _test-input-stream "  }\n")
1524     (write _test-input-stream "}\n")
1525     # convert
1526     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1527     (flush _test-output-buffered-file)
1528 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1534     # check output
1535     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
1536     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
1537     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
1538     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
1539     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
1540     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
1541     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
1542     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
1543     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
1544     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/9")
1545     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
1546     # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
1547     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
1548     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
1549     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
1550     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
1551     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
1552     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
1553     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
1554     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
1555     # . epilogue
1556     89/<- %esp 5/r32/ebp
1557     5d/pop-to-ebp
1558     c3/return
1559 
1560 test-convert-function-with-branches-and-loops-and-local-vars:
1561     # . prologue
1562     55/push-ebp
1563     89/<- %ebp 4/r32/esp
1564     # setup
1565     (clear-stream _test-input-stream)
1566     (clear-stream $_test-input-buffered-file->buffer)
1567     (clear-stream _test-output-stream)
1568     (clear-stream $_test-output-buffered-file->buffer)
1569     c7 0/subop/copy *Next-block-index 1/imm32
1570     #
1571     (write _test-input-stream "fn foo {\n")
1572     (write _test-input-stream "  {\n")
1573     (write _test-input-stream "    var x: int\n")
1574     (write _test-input-stream "    break-if->=\n")
1575     (write _test-input-stream "    increment x\n")
1576     (write _test-input-stream "    loop\n")
1577     (write _test-input-stream "  }\n")
1578     (write _test-input-stream "}\n")
1579     # convert
1580     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1581     (flush _test-output-buffered-file)
1582 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1588     # check output
1589     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
1590     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
1591     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
1592     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-loops-and-local-vars/3")
1593     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
1594     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
1595     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
1596     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
1597     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
1598     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
1599     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/10")
1600     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/11")
1601     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/12")
1602     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
1603     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-loops-and-local-vars/14")
1604     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/15")
1605     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
1606     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
1607     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
1608     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
1609     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
1610     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
1611     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-loops-and-local-vars/22")
1612     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
1613     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
1614     # . epilogue
1615     89/<- %esp 5/r32/ebp
1616     5d/pop-to-ebp
1617     c3/return
1618 
1619 test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
1620     # . prologue
1621     55/push-ebp
1622     89/<- %ebp 4/r32/esp
1623     # setup
1624     (clear-stream _test-input-stream)
1625     (clear-stream $_test-input-buffered-file->buffer)
1626     (clear-stream _test-output-stream)
1627     (clear-stream $_test-output-buffered-file->buffer)
1628     c7 0/subop/copy *Next-block-index 1/imm32
1629     #
1630     (write _test-input-stream "fn foo {\n")
1631     (write _test-input-stream "  a: {\n")
1632     (write _test-input-stream "    var x: int\n")
1633     (write _test-input-stream "    {\n")
1634     (write _test-input-stream "      var y: int\n")
1635     (write _test-input-stream "      break-if->= a\n")
1636     (write _test-input-stream "      increment x\n")
1637     (write _test-input-stream "      loop\n")
1638     (write _test-input-stream "    }\n")
1639     (write _test-input-stream "  }\n")
1640     (write _test-input-stream "}\n")
1641     # convert
1642     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1643     (flush _test-output-buffered-file)
1644 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1650     # check output
1651     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
1652     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
1653     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
1654     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/3")
1655     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
1656     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
1657     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
1658     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
1659     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/8")
1660     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
1661     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
1662     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/11")
1663     (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
1664     (check-next-stream-line-equal _test-output-stream "          0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/13")
1665     (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/14")
1666     (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/15")
1667     (check-next-stream-line-equal _test-output-stream "          e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/16")
1668     (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
1669     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/18")
1670     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/19")
1671     (check-next-stream-line-equal _test-output-stream "        e9/jump loop/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/20")
1672     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
1673     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
1674     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/23")
1675     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
1676     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
1677     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
1678     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
1679     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
1680     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/29")
1681     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/30")
1682     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
1683     # . epilogue
1684     89/<- %esp 5/r32/ebp
1685     5d/pop-to-ebp
1686     c3/return
1687 
1688 test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
1689     # . prologue
1690     55/push-ebp
1691     89/<- %ebp 4/r32/esp
1692     # setup
1693     (clear-stream _test-input-stream)
1694     (clear-stream $_test-input-buffered-file->buffer)
1695     (clear-stream _test-output-stream)
1696     (clear-stream $_test-output-buffered-file->buffer)
1697     c7 0/subop/copy *Next-block-index 1/imm32
1698     #
1699     (write _test-input-stream "fn foo {\n")
1700     (write _test-input-stream "  a: {\n")
1701     (write _test-input-stream "    var x: int\n")
1702     (write _test-input-stream "    {\n")
1703     (write _test-input-stream "      var y: int\n")
1704     (write _test-input-stream "      break a\n")
1705     (write _test-input-stream "      increment x\n")
1706     (write _test-input-stream "    }\n")
1707     (write _test-input-stream "  }\n")
1708     (write _test-input-stream "}\n")
1709     # convert
1710     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1711     (flush _test-output-buffered-file)
1712 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1718     # check output
1719     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
1720     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
1721     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
1722     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/3")
1723     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
1724     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
1725     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
1726     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
1727     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
1728     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
1729     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
1730     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
1731     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/12")
1732     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/13")
1733     (check-next-stream-line-equal _test-output-stream "        e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/14")
1734     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
1735     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
1736     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17")
1737     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
1738     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
1739     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
1740     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
1741     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
1742     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/23")
1743     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
1744     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
1745     # . epilogue
1746     89/<- %esp 5/r32/ebp
1747     5d/pop-to-ebp
1748     c3/return
1749 
1750 test-convert-function-with-unconditional-break-and-local-vars:
1751     # . prologue
1752     55/push-ebp
1753     89/<- %ebp 4/r32/esp
1754     # setup
1755     (clear-stream _test-input-stream)
1756     (clear-stream $_test-input-buffered-file->buffer)
1757     (clear-stream _test-output-stream)
1758     (clear-stream $_test-output-buffered-file->buffer)
1759     c7 0/subop/copy *Next-block-index 1/imm32
1760     #
1761     (write _test-input-stream "fn foo {\n")
1762     (write _test-input-stream "  {\n")
1763     (write _test-input-stream "    var x: int\n")
1764     (write _test-input-stream "    {\n")
1765     (write _test-input-stream "      var y: int\n")
1766     (write _test-input-stream "      break\n")
1767     (write _test-input-stream "      increment x\n")
1768     (write _test-input-stream "    }\n")
1769     (write _test-input-stream "  }\n")
1770     (write _test-input-stream "}\n")
1771     # convert
1772     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1773     (flush _test-output-buffered-file)
1774 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1780     # check output
1781     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
1782     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
1783     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
1784     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
1785     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
1786     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
1787     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
1788     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
1789     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
1790     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
1791     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
1792     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
1793     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-break-and-local-vars/12")
1794     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
1795     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
1796     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-break-and-local-vars/15")
1797     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
1798     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
1799     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
1800     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
1801     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
1802     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
1803     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
1804     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
1805     # . epilogue
1806     89/<- %esp 5/r32/ebp
1807     5d/pop-to-ebp
1808     c3/return
1809 
1810 test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
1811     # . prologue
1812     55/push-ebp
1813     89/<- %ebp 4/r32/esp
1814     # setup
1815     (clear-stream _test-input-stream)
1816     (clear-stream $_test-input-buffered-file->buffer)
1817     (clear-stream _test-output-stream)
1818     (clear-stream $_test-output-buffered-file->buffer)
1819     c7 0/subop/copy *Next-block-index 1/imm32
1820     #
1821     (write _test-input-stream "fn foo {\n")
1822     (write _test-input-stream "  a: {\n")
1823     (write _test-input-stream "    var x: int\n")
1824     (write _test-input-stream "    {\n")
1825     (write _test-input-stream "      var y: int\n")
1826     (write _test-input-stream "      loop a\n")
1827     (write _test-input-stream "      increment x\n")
1828     (write _test-input-stream "    }\n")
1829     (write _test-input-stream "  }\n")
1830     (write _test-input-stream "}\n")
1831     # convert
1832     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1833     (flush _test-output-buffered-file)
1834 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1840     # check output
1841     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
1842     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
1843     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
1844     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/3")
1845     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
1846     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
1847     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
1848     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
1849     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
1850     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
1851     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
1852     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
1853     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/12")
1854     (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/13")
1855     (check-next-stream-line-equal _test-output-stream "        e9/jump a:loop/disp32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/14")
1856     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
1857     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
1858     (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17")
1859     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
1860     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
1861     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
1862     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
1863     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
1864     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/23")
1865     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
1866     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
1867     # . epilogue
1868     89/<- %esp 5/r32/ebp
1869     5d/pop-to-ebp
1870     c3/return
1871 
1872 test-convert-length-of-array:
1873     # . prologue
1874     55/push-ebp
1875     89/<- %ebp 4/r32/esp
1876     # setup
1877     (clear-stream _test-input-stream)
1878     (clear-stream $_test-input-buffered-file->buffer)
1879     (clear-stream _test-output-stream)
1880     (clear-stream $_test-output-buffered-file->buffer)
1881     c7 0/subop/copy *Next-block-index 1/imm32
1882     #
1883     (write _test-input-stream "fn foo a: (addr array int) {\n")
1884     (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
1885     (write _test-input-stream "  var c/eax: int <- length b\n")
1886     (write _test-input-stream "}\n")
1887     # convert
1888     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1889     (flush _test-output-buffered-file)
1890 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1896     # check output
1897     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
1898     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
1899     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
1900     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
1901     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
1902     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
1903     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
1904     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
1905     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/8")
1906     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
1907     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/10")
1908     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
1909     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
1910     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
1911     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
1912     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
1913     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
1914     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
1915     # . epilogue
1916     89/<- %esp 5/r32/ebp
1917     5d/pop-to-ebp
1918     c3/return
1919 
1920 test-convert-index-into-array:
1921     # . prologue
1922     55/push-ebp
1923     89/<- %ebp 4/r32/esp
1924     # setup
1925     (clear-stream _test-input-stream)
1926     (clear-stream $_test-input-buffered-file->buffer)
1927     (clear-stream _test-output-stream)
1928     (clear-stream $_test-output-buffered-file->buffer)
1929     c7 0/subop/copy *Next-block-index 1/imm32
1930     #
1931     (write _test-input-stream "fn foo {\n")
1932     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
1933     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
1934     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
1935     (write _test-input-stream "}\n")
1936     # convert
1937     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1938     (flush _test-output-buffered-file)
1939 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
1945     # check output
1946     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-length-of-array/0")
1947     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-length-of-array/1")
1948     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-length-of-array/2")
1949     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-length-of-array/3")
1950     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-length-of-array/4")
1951     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-length-of-array/5")
1952     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-length-of-array/6")
1953     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-length-of-array/7")
1954     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-length-of-array/8")
1955     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-length-of-array/9")
1956     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-length-of-array/10")
1957     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<2 + 4) 0x00000000/r32"  "F - test-convert-length-of-array/11")
1958     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-length-of-array/12")
1959     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-length-of-array/13")
1960     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-length-of-array/14")
1961     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-length-of-array/15")
1962     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-length-of-array/16")
1963     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-length-of-array/17")
1964     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-length-of-array/18")
1965     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-length-of-array/19")
1966     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-length-of-array/20")
1967     # . epilogue
1968     89/<- %esp 5/r32/ebp
1969     5d/pop-to-ebp
1970     c3/return
1971 
1972 test-convert-function-and-type-definition:
1973     # . prologue
1974     55/push-ebp
1975     89/<- %ebp 4/r32/esp
1976     # setup
1977     (clear-stream _test-input-stream)
1978     (clear-stream $_test-input-buffered-file->buffer)
1979     (clear-stream _test-output-stream)
1980     (clear-stream $_test-output-buffered-file->buffer)
1981     c7 0/subop/copy *Next-block-index 1/imm32
1982     #
1983     (write _test-input-stream "fn foo a: (addr t) {\n")
1984     (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
1985     (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
1986     (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
1987     (write _test-input-stream "}\n")
1988     (write _test-input-stream "type t {\n")
1989     (write _test-input-stream "  x: int\n")
1990     (write _test-input-stream "  y: int\n")
1991     (write _test-input-stream "}\n")
1992     # convert
1993     (convert-mu _test-input-buffered-file _test-output-buffered-file)
1994     (flush _test-output-buffered-file)
1995 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
2001     # check output
2002     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
2003     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
2004     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
2005     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
2006     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
2007     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
2008     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
2009     (check-next-stream-line-equal _test-output-stream "    8b/copy-from *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
2010     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
2011     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
2012     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/10")
2013     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
2014     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/12")
2015     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
2016     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
2017     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
2018     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
2019     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
2020     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
2021     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
2022     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
2023     # . epilogue
2024     89/<- %esp 5/r32/ebp
2025     5d/pop-to-ebp
2026     c3/return
2027 
2028 test-convert-function-with-local-var-with-user-defined-type:
2029     # . prologue
2030     55/push-ebp
2031     89/<- %ebp 4/r32/esp
2032     # setup
2033     (clear-stream _test-input-stream)
2034     (clear-stream $_test-input-buffered-file->buffer)
2035     (clear-stream _test-output-stream)
2036     (clear-stream $_test-output-buffered-file->buffer)
2037     c7 0/subop/copy *Next-block-index 1/imm32
2038     #
2039     (write _test-input-stream "fn foo {\n")
2040     (write _test-input-stream "  var a: t\n")
2041     (write _test-input-stream "}\n")
2042     (write _test-input-stream "type t {\n")
2043     (write _test-input-stream "  x: int\n")
2044     (write _test-input-stream "  y: int\n")
2045     (write _test-input-stream "}\n")
2046     # convert
2047     (convert-mu _test-input-buffered-file _test-output-buffered-file)
2048     (flush _test-output-buffered-file)
2049 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
2055     # check output
2056     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
2057     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
2058     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
2059     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-user-defined-type/3")
2060     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
2061     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
2062     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
2063     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
2064     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-with-local-var-with-user-defined-type/8")
2065     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
2066     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
2067     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
2068     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-user-defined-type/12")
2069     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
2070     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
2071     # . epilogue
2072     89/<- %esp 5/r32/ebp
2073     5d/pop-to-ebp
2074     c3/return
2075 
2076 #######################################################
2077 # Parsing
2078 #######################################################
2079 
2080 parse-mu:  # in: (addr buffered-file)
2081     # pseudocode
2082     #   var curr-function: (addr (handle function)) = Program->functions
2083     #   var curr-type: (addr (handle typeinfo)) = Program->types
2084     #   var line: (stream byte 512)
2085     #   var word-slice: slice
2086     #   while true                                  # line loop
2087     #     clear-stream(line)
2088     #     read-line-buffered(in, line)
2089     #     if (line->write == 0) break               # end of file
2090     #     word-slice = next-mu-token(line)
2091     #     if slice-empty?(word-slice)               # end of line
2092     #       continue
2093     #     else if slice-starts-with?(word-slice, "#")  # comment
2094     #       continue                                # end of line
2095     #     else if slice-equal?(word-slice, "fn")
2096     #       var new-function: (handle function) = allocate(function)
2097     #       var vars: (stack (addr var) 256)
2098     #       populate-mu-function-header(in, new-function, vars)
2099     #       populate-mu-function-body(in, new-function, vars)
2100     #       assert(vars->top == 0)
2101     #       *curr-function = new-function
2102     #       curr-function = &new-function->next
2103     #     else if slice-equal?(word-slice, "type")
2104     #       word-slice = next-mu-token(line)
2105     #       type-id = pos-or-insert-slice(Type-id, word-slice)
2106     #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
2107     #       assert(next-word(line) == "{")
2108     #       populate-mu-type(in, new-type)
2109     #     else
2110     #       abort()
2111     #
2112     # . prologue
2113     55/push-ebp
2114     89/<- %ebp 4/r32/esp
2115     # . save registers
2116     50/push-eax
2117     51/push-ecx
2118     52/push-edx
2119     53/push-ebx
2120     56/push-esi
2121     57/push-edi
2122     # var line/ecx: (stream byte 512)
2123     81 5/subop/subtract %esp 0x200/imm32
2124     68/push 0x200/imm32/length
2125     68/push 0/imm32/read
2126     68/push 0/imm32/write
2127     89/<- %ecx 4/r32/esp
2128     # var word-slice/edx: slice
2129     68/push 0/imm32/end
2130     68/push 0/imm32/start
2131     89/<- %edx 4/r32/esp
2132     # var curr-function/edi: (addr (handle function))
2133     bf/copy-to-edi _Program-functions/imm32
2134     # var curr-type/esi: (addr (handle typeinfo))
2135     be/copy-to-esi _Program-types/imm32
2136     # var vars/ebx: (stack (addr var) 256)
2137     81 5/subop/subtract %esp 0x400/imm32
2138     68/push 0x400/imm32/length
2139     68/push 0/imm32/top
2140     89/<- %ebx 4/r32/esp
2141     {
2142 $parse-mu:line-loop:
2143       (clear-stream %ecx)
2144       (read-line-buffered *(ebp+8) %ecx)
2145       # if (line->write == 0) break
2146       81 7/subop/compare *ecx 0/imm32
2147       0f 84/jump-if-= break/disp32
2148 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
2154       (next-mu-token %ecx %edx)
2155       # if slice-empty?(word-slice) continue
2156       (slice-empty? %edx)
2157       3d/compare-eax-and 0/imm32/false
2158       0f 85/jump-if-!= loop/disp32
2159       # if (*word-slice->start == "#") continue
2160       # . eax = *word-slice->start
2161       8b/-> *edx 0/r32/eax
2162       8a/copy-byte *eax 0/r32/AL
2163       81 4/subop/and %eax 0xff/imm32
2164       # . if (eax == '#') continue
2165       3d/compare-eax-and 0x23/imm32/hash
2166       0f 84/jump-if-= loop/disp32
2167       # if (slice-equal?(word-slice, "fn")) parse a function
2168       {
2169 $parse-mu:fn:
2170         (slice-equal? %edx "fn")
2171         3d/compare-eax-and 0/imm32/false
2172         0f 84/jump-if-= break/disp32
2173         # var new-function/eax: (handle function) = populate-mu-function(line, in, vars)
2174         (allocate Heap *Function-size)  # => eax
2175         (zero-out %eax *Function-size)
2176         (clear-stack %ebx)
2177         (populate-mu-function-header %ecx %eax %ebx)
2178         (populate-mu-function-body *(ebp+8) %eax %ebx)
2179         # *curr-function = new-function
2180         89/<- *edi 0/r32/eax
2181         # curr-function = &new-function->next
2182         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
2183         e9/jump $parse-mu:line-loop/disp32
2184       }
2185       # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
2186       {
2187 $parse-mu:type:
2188         (slice-equal? %edx "type")
2189         3d/compare-eax-and 0/imm32
2190         0f 84/jump-if-= break/disp32
2191         (next-mu-token %ecx %edx)
2192         # var type-id/eax: int
2193         (pos-or-insert-slice Type-id %edx)  # => eax
2194         # var new-type/eax: (handle typeinfo)
2195         (find-or-create-typeinfo %eax)  # => eax
2196         # TODO: ensure that 'line' has nothing else but '{'
2197         (populate-mu-type *(ebp+8) %eax)  # => eax
2198         e9/jump $parse-mu:line-loop/disp32
2199       }
2200       # otherwise abort
2201       e9/jump $parse-mu:error1/disp32
2202     } # end line loop
2203 $parse-mu:end:
2204     # . reclaim locals
2205     81 0/subop/add %esp 0x630/imm32
2206     # . restore registers
2207     5f/pop-to-edi
2208     5e/pop-to-esi
2209     5b/pop-to-ebx
2210     5a/pop-to-edx
2211     59/pop-to-ecx
2212     58/pop-to-eax
2213     # . epilogue
2214     89/<- %esp 5/r32/ebp
2215     5d/pop-to-ebp
2216     c3/return
2217 
2218 $parse-mu:error1:
2219     # error("unexpected top-level command: " word-slice "\n")
2220     (write-buffered Stderr "unexpected top-level command: ")
2221     (write-slice-buffered Stderr %edx)
2222     (write-buffered Stderr "\n")
2223     (flush Stderr)
2224     # . syscall(exit, 1)
2225     bb/copy-to-ebx  1/imm32
2226     b8/copy-to-eax  1/imm32/exit
2227     cd/syscall  0x80/imm8
2228     # never gets here
2229 
2230 $parse-mu:error2:
2231     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
2232     (print-int32-buffered Stderr *ebx)
2233     (write-buffered Stderr " vars not reclaimed after fn '")
2234     (write-slice-buffered Stderr *eax)  # Function-name
2235     (write-buffered Stderr "'\n")
2236     (flush Stderr)
2237     # . syscall(exit, 1)
2238     bb/copy-to-ebx  1/imm32
2239     b8/copy-to-eax  1/imm32/exit
2240     cd/syscall  0x80/imm8
2241     # never gets here
2242 
2243 # scenarios considered:
2244 # ✗ fn foo  # no block
2245 # ✓ fn foo {
2246 # ✗ fn foo { {
2247 # ✗ fn foo { }
2248 # ✗ fn foo { } {
2249 # ✗ fn foo x {
2250 # ✗ fn foo x: {
2251 # ✓ fn foo x: int {
2252 # ✓ fn foo x: int {
2253 # ✓ fn foo x: int -> y/eax: int {
2254 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
2255     # pseudocode:
2256     #   var name: slice
2257     #   next-mu-token(first-line, name)
2258     #   assert(name not in '{' '}' '->')
2259     #   out->name = slice-to-string(name)
2260     #   var next-offset: int = 8
2261     #   ## inouts
2262     #   while true
2263     #     ## name
2264     #     name = next-mu-token(first-line)
2265     #     if (name == '{') goto done
2266     #     if (name == '->') break
2267     #     assert(name != '}')
2268     #     var v: (handle var) = parse-var-with-type(name, first-line)
2269     #     assert(v->register == null)
2270     #     v->stack-offset = next-offset
2271     #     next-offset += size-of(v)
2272     #     # v->block-depth is implicitly 0
2273     #     out->inouts = append(out->inouts, v)
2274     #     push(vars, v)
2275     #   ## outputs
2276     #   while true
2277     #     ## name
2278     #     name = next-mu-token(first-line)
2279     #     assert(name not in '{' '}' '->')
2280     #     var v: (handle var) = parse-var-with-type(name, first-line)
2281     #     assert(v->register != null)
2282     #     out->outputs = append(out->outputs, v)
2283     #   done:
2284     #
2285     # . prologue
2286     55/push-ebp
2287     89/<- %ebp 4/r32/esp
2288     # . save registers
2289     50/push-eax
2290     51/push-ecx
2291     52/push-edx
2292     53/push-ebx
2293     57/push-edi
2294     # edi = out
2295     8b/-> *(ebp+0xc) 7/r32/edi
2296     # var word-slice/ecx: slice
2297     68/push 0/imm32/end
2298     68/push 0/imm32/start
2299     89/<- %ecx 4/r32/esp
2300     # var next-offset/edx = 8
2301     ba/copy-to-edx 8/imm32
2302     # read function name
2303     (next-mu-token *(ebp+8) %ecx)
2304     # error checking
2305     # TODO: error if name starts with 'break' or 'loop'
2306     # if (word-slice == '{') abort
2307     (slice-equal? %ecx "{")   # => eax
2308     3d/compare-eax-and 0/imm32/false
2309     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2310     # if (word-slice == '->') abort
2311     (slice-equal? %ecx "->")   # => eax
2312     3d/compare-eax-and 0/imm32/false
2313     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2314     # if (word-slice == '}') abort
2315     (slice-equal? %ecx "}")   # => eax
2316     3d/compare-eax-and 0/imm32/false
2317     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2318     # save function name
2319     (slice-to-string Heap %ecx)  # => eax
2320     89/<- *edi 0/r32/eax  # Function-name
2321     # initialize default subx-name as well
2322     89/<- *(edi+4) 0/r32/eax  # Function-subx-name
2323     # save function inouts
2324     {
2325 $populate-mu-function-header:check-for-inout:
2326       (next-mu-token *(ebp+8) %ecx)
2327       # if (word-slice == '{') goto done
2328       (slice-equal? %ecx "{")   # => eax
2329       3d/compare-eax-and 0/imm32/false
2330       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
2331       # if (word-slice == '->') break
2332       (slice-equal? %ecx "->")   # => eax
2333       3d/compare-eax-and 0/imm32/false
2334       0f 85/jump-if-!= break/disp32
2335       # if (word-slice == '}') abort
2336       (slice-equal? %ecx "}")   # => eax
2337       3d/compare-eax-and 0/imm32/false
2338       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2339       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
2340       (parse-var-with-type %ecx *(ebp+8))  # => eax
2341       89/<- %ebx 0/r32/eax
2342       # assert(v->register == null)
2343       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
2344       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
2345       # v->stack-offset = next-offset
2346       89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
2347       # next-offset += size-of(v)
2348       (size-of %ebx)  # => eax
2349       01/add %edx 0/r32/eax
2350       # v->block-depth is implicitly 0
2351       #
2352       # out->inouts = append(out->inouts, v)
2353       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
2354       89/<- *(edi+8) 0/r32/eax  # Function-inouts
2355       # push(vars, v)
2356       (push *(ebp+0x10) %ebx)
2357       #
2358       e9/jump loop/disp32
2359     }
2360     # save function outputs
2361     {
2362 $populate-mu-function-header:check-for-out:
2363       (next-mu-token *(ebp+8) %ecx)
2364       # if (word-slice == '{') break
2365       (slice-equal? %ecx "{")   # => eax
2366       3d/compare-eax-and 0/imm32/false
2367       0f 85/jump-if-!= break/disp32
2368       # if (word-slice == '->') abort
2369       (slice-equal? %ecx "->")   # => eax
2370       3d/compare-eax-and 0/imm32/false
2371       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2372       # if (word-slice == '}') abort
2373       (slice-equal? %ecx "}")   # => eax
2374       3d/compare-eax-and 0/imm32/false
2375       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
2376       #
2377       (parse-var-with-type %ecx *(ebp+8))  # => eax
2378       89/<- %ebx 0/r32/eax
2379       # assert(var->register != null)
2380       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
2381       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
2382       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
2383       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
2384       e9/jump loop/disp32
2385     }
2386 $populate-mu-function-header:done:
2387     (check-no-tokens-left *(ebp+8))
2388 $populate-mu-function-header:end:
2389     # . reclaim locals
2390     81 0/subop/add %esp 8/imm32
2391     # . restore registers
2392     5f/pop-to-edi
2393     5b/pop-to-ebx
2394     5a/pop-to-edx
2395     59/pop-to-ecx
2396     58/pop-to-eax
2397     # . epilogue
2398     89/<- %esp 5/r32/ebp
2399     5d/pop-to-ebp
2400     c3/return
2401 
2402 $populate-mu-function-header:error1:
2403     # error("function header not in form 'fn <name> {'")
2404     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
2405     (flush Stderr)
2406     (rewind-stream *(ebp+8))
2407     (write-stream 2 *(ebp+8))
2408     (write-buffered Stderr "'\n")
2409     (flush Stderr)
2410     # . syscall(exit, 1)
2411     bb/copy-to-ebx  1/imm32
2412     b8/copy-to-eax  1/imm32/exit
2413     cd/syscall  0x80/imm8
2414     # never gets here
2415 
2416 $populate-mu-function-header:error2:
2417     # error("function input '" var "' cannot be in a register")
2418     (write-buffered Stderr "function input '")
2419     (write-buffered Stderr *ebx)  # Var-name
2420     (write-buffered Stderr "' cannot be in a register")
2421     (flush Stderr)
2422     # . syscall(exit, 1)
2423     bb/copy-to-ebx  1/imm32
2424     b8/copy-to-eax  1/imm32/exit
2425     cd/syscall  0x80/imm8
2426     # never gets here
2427 
2428 $populate-mu-function-header:error3:
2429     # error("function input '" var "' must be in a register")
2430     (write-buffered Stderr "function input '")
2431     (write-buffered Stderr *eax)  # Var-name
2432     (write-buffered Stderr " must be in a register'")
2433     (flush Stderr)
2434     (rewind-stream *(ebp+8))
2435     (write-stream 2 *(ebp+8))
2436     (write-buffered Stderr "'\n")
2437     (flush Stderr)
2438     # . syscall(exit, 1)
2439     bb/copy-to-ebx  1/imm32
2440     b8/copy-to-eax  1/imm32/exit
2441     cd/syscall  0x80/imm8
2442     # never gets here
2443 
2444 test-function-header-with-arg:
2445     # . prologue
2446     55/push-ebp
2447     89/<- %ebp 4/r32/esp
2448     # setup
2449     (clear-stream _test-input-stream)
2450     (write _test-input-stream "foo n: int {\n")
2451     # var result/ecx: function
2452     2b/subtract-> *Function-size 4/r32/esp
2453     89/<- %ecx 4/r32/esp
2454     (zero-out %ecx *Function-size)
2455     # var vars/ebx: (stack (addr var) 16)
2456     81 5/subop/subtract %esp 0x10/imm32
2457     68/push 0x10/imm32/length
2458     68/push 0/imm32/top
2459     89/<- %ebx 4/r32/esp
2460     # convert
2461     (populate-mu-function-header _test-input-stream %ecx %ebx)
2462     # check result
2463     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
2464     # edx: (handle list var) = result->inouts
2465     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
2466     # ebx: (handle var) = result->inouts->value
2467     8b/-> *edx 3/r32/ebx  # List-value
2468     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
2469     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2470     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-left
2471     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-right
2472     # . epilogue
2473     89/<- %esp 5/r32/ebp
2474     5d/pop-to-ebp
2475     c3/return
2476 
2477 test-function-header-with-multiple-args:
2478     # . prologue
2479     55/push-ebp
2480     89/<- %ebp 4/r32/esp
2481     # setup
2482     (clear-stream _test-input-stream)
2483     (write _test-input-stream "foo a: int, b: int c: int {\n")
2484     # result/ecx: (handle function)
2485     2b/subtract-> *Function-size 4/r32/esp
2486     89/<- %ecx 4/r32/esp
2487     (zero-out %ecx *Function-size)
2488     # var vars/ebx: (stack (addr var) 16)
2489     81 5/subop/subtract %esp 0x10/imm32
2490     68/push 0x10/imm32/length
2491     68/push 0/imm32/top
2492     89/<- %ebx 4/r32/esp
2493     # convert
2494     (populate-mu-function-header _test-input-stream %ecx %ebx)
2495     # check result
2496     (check-strings-equal *ecx "foo")  # Function-name
2497     # edx: (handle list var) = result->inouts
2498     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
2499 $test-function-header-with-multiple-args:inout0:
2500     # ebx: (handle var) = result->inouts->value
2501     8b/-> *edx 3/r32/ebx  # List-value
2502     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
2503     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2504     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-left
2505     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-right
2506     # edx = result->inouts->next
2507     8b/-> *(edx+4) 2/r32/edx  # List-next
2508 $test-function-header-with-multiple-args:inout1:
2509     # ebx = result->inouts->next->value
2510     8b/-> *edx 3/r32/ebx  # List-value
2511     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
2512     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2513     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-left
2514     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-right
2515     # edx = result->inouts->next->next
2516     8b/-> *(edx+4) 2/r32/edx  # List-next
2517 $test-function-header-with-multiple-args:inout2:
2518     # ebx = result->inouts->next->next->value
2519     8b/-> *edx 3/r32/ebx  # List-value
2520     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
2521     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2522     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-left
2523     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-right
2524     # . epilogue
2525     89/<- %esp 5/r32/ebp
2526     5d/pop-to-ebp
2527     c3/return
2528 
2529 test-function-with-multiple-args-and-outputs:
2530     # . prologue
2531     55/push-ebp
2532     89/<- %ebp 4/r32/esp
2533     # setup
2534     (clear-stream _test-input-stream)
2535     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
2536     # result/ecx: (handle function)
2537     2b/subtract-> *Function-size 4/r32/esp
2538     89/<- %ecx 4/r32/esp
2539     (zero-out %ecx *Function-size)
2540     # var vars/ebx: (stack (addr var) 16)
2541     81 5/subop/subtract %esp 0x10/imm32
2542     68/push 0x10/imm32/length
2543     68/push 0/imm32/top
2544     89/<- %ebx 4/r32/esp
2545     # convert
2546     (populate-mu-function-header _test-input-stream %ecx %ebx)
2547     # check result
2548     (check-strings-equal *ecx "foo")  # Function-name
2549     # edx: (handle list var) = result->inouts
2550     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
2551     # ebx: (handle var) = result->inouts->value
2552     8b/-> *edx 3/r32/ebx  # List-value
2553     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
2554     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2555     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-left
2556     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-right
2557     # edx = result->inouts->next
2558     8b/-> *(edx+4) 2/r32/edx  # List-next
2559     # ebx = result->inouts->next->value
2560     8b/-> *edx 3/r32/ebx  # List-value
2561     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
2562     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2563     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-left
2564     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-right
2565     # edx = result->inouts->next->next
2566     8b/-> *(edx+4) 2/r32/edx  # List-next
2567     # ebx = result->inouts->next->next->value
2568     8b/-> *edx 3/r32/ebx  # List-value
2569     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
2570     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2571     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-left
2572     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-right
2573     # edx: (handle list var) = result->outputs
2574     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
2575     # ebx: (handle var) = result->outputs->value
2576     8b/-> *edx 3/r32/ebx  # List-value
2577     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
2578     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
2579     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2580     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-left
2581     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-right
2582     # edx = result->outputs->next
2583     8b/-> *(edx+4) 2/r32/edx  # List-next
2584     # ebx = result->outputs->next->value
2585     8b/-> *edx 3/r32/ebx  # List-value
2586     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
2587     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
2588     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
2589     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-left
2590     (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-right
2591     # . epilogue
2592     89/<- %esp 5/r32/ebp
2593     5d/pop-to-ebp
2594     c3/return
2595 
2596 # format for variables with types
2597 #   x: int
2598 #   x: int,
2599 #   x/eax: int
2600 #   x/eax: int,
2601 # ignores at most one trailing comma
2602 # WARNING: modifies name
2603 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
2604     # pseudocode:
2605     #   var v: (handle var) = allocate(Heap, Var-size)
2606     #   var s: slice
2607     #   if (!slice-ends-with(name, ":"))
2608     #     abort
2609     #   --name->end to skip ':'
2610     #   next-token-from-slice(name->start, name->end, '/', s)
2611     #   v->name = slice-to-string(s)
2612     #   ## register
2613     #   next-token-from-slice(s->end, name->end, '/', s)
2614     #   if (!slice-empty?(s))
2615     #     v->register = slice-to-string(s)
2616     #   ## type
2617     #   var type: (handle tree type-id) = parse-type(first-line)
2618     #   v->type = type
2619     #   return v
2620     #
2621     # . prologue
2622     55/push-ebp
2623     89/<- %ebp 4/r32/esp
2624     # . save registers
2625     51/push-ecx
2626     52/push-edx
2627     53/push-ebx
2628     56/push-esi
2629     57/push-edi
2630     # esi = name
2631     8b/-> *(ebp+8) 6/r32/esi
2632     # if (!slice-ends-with?(name, ":")) abort
2633     8b/-> *(esi+4) 1/r32/ecx  # Slice-end
2634     49/decrement-ecx
2635     8a/copy-byte *ecx 1/r32/CL
2636     81 4/subop/and %ecx 0xff/imm32
2637     81 7/subop/compare %ecx 0x3a/imm32/colon
2638     0f 85/jump-if-!= $parse-var-with-type:abort/disp32
2639     # --name->end to skip ':'
2640     ff 1/subop/decrement *(esi+4)
2641     # var result/edi: (handle var) = allocate(Heap, Var-size)
2642     (allocate Heap *Var-size)  # => eax
2643     (zero-out %eax *Var-size)
2644     89/<- %edi 0/r32/eax
2645     # var s/ecx: slice
2646     68/push 0/imm32/end
2647     68/push 0/imm32/start
2648     89/<- %ecx 4/r32/esp
2649 $parse-var-with-type:save-name:
2650     # save v->name
2651     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
2652     # . end/edx = s->end
2653     8b/-> *(ecx+4) 2/r32/edx
2654 $parse-var-with-type:write-name:
2655     (slice-to-string Heap %ecx)  # => eax
2656     89/<- *edi 0/r32/eax  # Var-name
2657     # save v->register
2658 $parse-var-with-type:save-register:
2659     (next-token-from-slice %edx *(esi+4) 0x2f %ecx)  # end, name->end, '/'
2660     # if (!slice-empty?(s)) v->register = slice-to-string(s)
2661     {
2662 $parse-var-with-type:write-register:
2663       (slice-empty? %ecx)  # => eax
2664       3d/compare-eax-and 0/imm32/false
2665       75/jump-if-!= break/disp8
2666       (slice-to-string Heap %ecx)
2667       89/<- *(edi+0x10) 0/r32/eax  # Var-register
2668     }
2669 $parse-var-with-type:save-type:
2670     (parse-type Heap *(ebp+0xc))  # => eax
2671 #?     (write-buffered Stderr "saving to var ")
2672 #?     (print-int32-buffered Stderr %edi)
2673 #?     (write-buffered Stderr Newline)
2674 #?     (flush Stderr)
2675     89/<- *(edi+4) 0/r32/eax  # Var-type
2676 $parse-var-with-type:end:
2677     # return result
2678     89/<- %eax 7/r32/edi
2679     # . reclaim locals
2680     81 0/subop/add %esp 8/imm32
2681     # . restore registers
2682     5f/pop-to-edi
2683     5e/pop-to-esi
2684     5b/pop-to-ebx
2685     5a/pop-to-edx
2686     59/pop-to-ecx
2687     # . epilogue
2688     89/<- %esp 5/r32/ebp
2689     5d/pop-to-ebp
2690     c3/return
2691 
2692 $parse-var-with-type:abort:
2693     # error("var should have form 'name: type' in '" line "'\n")
2694     (write-buffered Stderr "var should have form 'name: type' in '")
2695     (flush Stderr)
2696     (rewind-stream *(ebp+0xc))
2697     (write-stream 2 *(ebp+0xc))
2698     (write-buffered Stderr "'\n")
2699     (flush Stderr)
2700     # . syscall(exit, 1)
2701     bb/copy-to-ebx  1/imm32
2702     b8/copy-to-eax  1/imm32/exit
2703     cd/syscall  0x80/imm8
2704     # never gets here
2705 
2706 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2707     # pseudocode:
2708     #   var s: slice = next-mu-token(in)
2709     #   assert s != ""
2710     #   assert s != "->"
2711     #   assert s != "{"
2712     #   assert s != "}"
2713     #   if s == ")"
2714     #     return 0
2715     #   result = allocate(Tree)
2716     #   zero-out(result, *Tree-size)
2717     #   if s != "("
2718     #     result->left = pos-or-insert-slice(Type-id, s)
2719     #     return
2720     #   result->left = parse-type(ad, in)
2721     #   result->right = parse-type-tree(ad, in)
2722     #
2723     # . prologue
2724     55/push-ebp
2725     89/<- %ebp 4/r32/esp
2726     # . save registers
2727     51/push-ecx
2728     52/push-edx
2729     # var s/ecx: slice
2730     68/push 0/imm32
2731     68/push 0/imm32
2732     89/<- %ecx 4/r32/esp
2733     # s = next-mu-token(in)
2734     (next-mu-token *(ebp+0xc) %ecx)
2735 #?     (write-buffered Stderr "tok: ")
2736 #?     (write-slice-buffered Stderr %ecx)
2737 #?     (write-buffered Stderr "$\n")
2738 #?     (flush Stderr)
2739     # assert s != ""
2740     (slice-equal? %ecx "")
2741     3d/compare-eax-and 0/imm32/false
2742     0f 85/jump-if-!= $parse-type:abort/disp32
2743     # assert s != "{"
2744     (slice-equal? %ecx "{")
2745     3d/compare-eax-and 0/imm32/false
2746     0f 85/jump-if-!= $parse-type:abort/disp32
2747     # assert s != "}"
2748     (slice-equal? %ecx "}")
2749     3d/compare-eax-and 0/imm32/false
2750     0f 85/jump-if-!= $parse-type:abort/disp32
2751     # assert s != "->"
2752     (slice-equal? %ecx "->")
2753     3d/compare-eax-and 0/imm32/false
2754     0f 85/jump-if-!= $parse-type:abort/disp32
2755     # if (s == ")") return 0
2756     (slice-equal? %ecx ")")
2757     3d/compare-eax-and 0/imm32/false
2758     b8/copy-to-eax 0/imm32
2759     0f 85/jump-if-!= $parse-type:end/disp32
2760     # var result/edx: (handle tree type-id)
2761     (allocate *(ebp+8) *Tree-size)  # => eax
2762     (zero-out %eax *Tree-size)
2763     89/<- %edx 0/r32/eax
2764     {
2765       # if (s != "(") break
2766       (slice-equal? %ecx "(")
2767       3d/compare-eax-and 0/imm32/false
2768       75/jump-if-!= break/disp8
2769       # result->left = pos-or-insert-slice(Type-id, s)
2770       (pos-or-insert-slice Type-id %ecx)  # => eax
2771 #?       (write-buffered Stderr "=> {")
2772 #?       (print-int32-buffered Stderr %eax)
2773 #?       (write-buffered Stderr ", 0}\n")
2774 #?       (flush Stderr)
2775       89/<- *edx 0/r32/eax  # Tree-left
2776       e9/jump $parse-type:return-edx/disp32
2777     }
2778     # otherwise s == "("
2779     # result->left = parse-type(ad, in)
2780     (parse-type *(ebp+8) *(ebp+0xc))
2781 #?     (write-buffered Stderr "=> {")
2782 #?     (print-int32-buffered Stderr %eax)
2783     89/<- *edx 0/r32/eax  # Tree-left
2784     # result->right = parse-type-tree(ad, in)
2785     (parse-type-tree *(ebp+8) *(ebp+0xc))
2786 #?     (write-buffered Stderr Space)
2787 #?     (print-int32-buffered Stderr %eax)
2788 #?     (write-buffered Stderr "}\n")
2789 #?     (flush Stderr)
2790     89/<- *(edx+4) 0/r32/eax  # Tree-right
2791 $parse-type:return-edx:
2792     89/<- %eax 2/r32/edx
2793 $parse-type:end:
2794     # . reclaim locals
2795     81 0/subop/add %esp 8/imm32
2796     # . restore registers
2797     5a/pop-to-edx
2798     59/pop-to-ecx
2799     # . epilogue
2800     89/<- %esp 5/r32/ebp
2801     5d/pop-to-ebp
2802     c3/return
2803 
2804 $parse-type:abort:
2805     # error("unexpected token when parsing type: '" s "'\n")
2806     (write-buffered Stderr "unexpected token when parsing type: '")
2807     (write-slice-buffered Stderr %ecx)
2808     (write-buffered Stderr "'\n")
2809     (flush Stderr)
2810     # . syscall(exit, 1)
2811     bb/copy-to-ebx  1/imm32
2812     b8/copy-to-eax  1/imm32/exit
2813     cd/syscall  0x80/imm8
2814     # never gets here
2815 
2816 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
2817     # pseudocode:
2818     #   var tmp: (handle tree type-id) = parse-type(ad, in)
2819     #   if tmp == 0
2820     #     return 0
2821     #   result = allocate(Tree)
2822     #   zero-out(result, *Tree-size)
2823     #   result->left = tmp
2824     #   result->right = parse-type-tree(ad, in)
2825     #
2826     # . prologue
2827     55/push-ebp
2828     89/<- %ebp 4/r32/esp
2829     # . save registers
2830     51/push-ecx
2831     52/push-edx
2832     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
2833     (parse-type *(ebp+8) *(ebp+0xc))
2834     # if (tmp == 0) return tmp
2835     3d/compare-eax-and 0/imm32
2836     74/jump-if-= $parse-type-tree:end/disp8
2837     # var tmp2/ecx = tmp
2838     89/<- %ecx 0/r32/eax
2839     # var result/edx: (handle tree type-id)
2840     (allocate *(ebp+8) *Tree-size)  # => eax
2841     (zero-out %eax *Tree-size)
2842     89/<- %edx 0/r32/eax
2843     # result->left = tmp2
2844     89/<- *edx 1/r32/ecx  # Tree-left
2845     # result->right = parse-type-tree(ad, in)
2846     (parse-type-tree *(ebp+8) *(ebp+0xc))
2847     89/<- *(edx+4) 0/r32/eax  # Tree-right
2848 $parse-type-tree:return-edx:
2849     89/<- %eax 2/r32/edx
2850 $parse-type-tree:end:
2851     # . restore registers
2852     5a/pop-to-edx
2853     59/pop-to-ecx
2854     # . epilogue
2855     89/<- %esp 5/r32/ebp
2856     5d/pop-to-ebp
2857     c3/return
2858 
2859 next-mu-token:  # in: (addr stream byte), out: (addr slice)
2860     # pseudocode:
2861     # start:
2862     #   skip-chars-matching-whitespace(in)
2863     #   if in->read >= in->write              # end of in
2864     #     out = {0, 0}
2865     #     return
2866     #   out->start = &in->data[in->read]
2867     #   var curr-byte/eax: byte = in->data[in->read]
2868     #   if curr->byte == ','                  # comment token
2869     #     ++in->read
2870     #     goto start
2871     #   if curr-byte == '#'                   # comment
2872     #     goto done                             # treat as eof
2873     #   if curr-byte == '"'                   # string literal
2874     #     skip-string(in)
2875     #     goto done                           # no metadata
2876     #   if curr-byte == '('
2877     #     ++in->read
2878     #     goto done
2879     #   if curr-byte == ')'
2880     #     ++in->read
2881     #     goto done
2882     #   # read a word
2883     #   while true
2884     #     if in->read >= in->write
2885     #       break
2886     #     curr-byte = in->data[in->read]
2887     #     if curr-byte == ' '
2888     #       break
2889     #     if curr-byte == '\r'
2890     #       break
2891     #     if curr-byte == '\n'
2892     #       break
2893     #     if curr-byte == '('
2894     #       break
2895     #     if curr-byte == ')'
2896     #       break
2897     #     if curr-byte == ','
2898     #       break
2899     #     ++in->read
2900     # done:
2901     #   out->end = &in->data[in->read]
2902     #
2903     # . prologue
2904     55/push-ebp
2905     89/<- %ebp 4/r32/esp
2906     # . save registers
2907     50/push-eax
2908     51/push-ecx
2909     56/push-esi
2910     57/push-edi
2911     # esi = in
2912     8b/-> *(ebp+8) 6/r32/esi
2913     # edi = out
2914     8b/-> *(ebp+0xc) 7/r32/edi
2915 $next-mu-token:start:
2916     (skip-chars-matching-whitespace %esi)
2917 $next-mu-token:check0:
2918     # if (in->read >= in->write) return out = {0, 0}
2919     # . ecx = in->read
2920     8b/-> *(esi+4) 1/r32/ecx
2921     # . if (ecx >= in->write) return out = {0, 0}
2922     3b/compare 1/r32/ecx *esi
2923     c7 0/subop/copy *edi 0/imm32
2924     c7 0/subop/copy *(edi+4) 0/imm32
2925     0f 8d/jump-if->= $next-mu-token:end/disp32
2926     # out->start = &in->data[in->read]
2927     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
2928     89/<- *edi 0/r32/eax
2929     # var curr-byte/eax: byte = in->data[in->read]
2930     31/xor %eax 0/r32/eax
2931     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2932     {
2933 $next-mu-token:check-for-comma:
2934       # if (curr-byte != ',') break
2935       3d/compare-eax-and 0x2c/imm32/comma
2936       75/jump-if-!= break/disp8
2937       # ++in->read
2938       ff 0/subop/increment *(esi+4)
2939       # restart
2940       e9/jump $next-mu-token:start/disp32
2941     }
2942     {
2943 $next-mu-token:check-for-comment:
2944       # if (curr-byte != '#') break
2945       3d/compare-eax-and 0x23/imm32/pound
2946       75/jump-if-!= break/disp8
2947       # return eof
2948       e9/jump $next-mu-token:done/disp32
2949     }
2950     {
2951 $next-mu-token:check-for-string-literal:
2952       # if (curr-byte != '"') break
2953       3d/compare-eax-and 0x22/imm32/dquote
2954       75/jump-if-!= break/disp8
2955       (skip-string %esi)
2956       # return
2957       e9/jump $next-mu-token:done/disp32
2958     }
2959     {
2960 $next-mu-token:check-for-open-paren:
2961       # if (curr-byte != '(') break
2962       3d/compare-eax-and 0x28/imm32/open-paren
2963       75/jump-if-!= break/disp8
2964       # ++in->read
2965       ff 0/subop/increment *(esi+4)
2966       # return
2967       e9/jump $next-mu-token:done/disp32
2968     }
2969     {
2970 $next-mu-token:check-for-close-paren:
2971       # if (curr-byte != ')') break
2972       3d/compare-eax-and 0x29/imm32/close-paren
2973       75/jump-if-!= break/disp8
2974       # ++in->read
2975       ff 0/subop/increment *(esi+4)
2976       # return
2977       e9/jump $next-mu-token:done/disp32
2978     }
2979     {
2980 $next-mu-token:regular-word-without-metadata:
2981       # if (in->read >= in->write) break
2982       # . ecx = in->read
2983       8b/-> *(esi+4) 1/r32/ecx
2984       # . if (ecx >= in->write) break
2985       3b/compare *esi 1/r32/ecx
2986       7d/jump-if->= break/disp8
2987       # var c/eax: byte = in->data[in->read]
2988       31/xor %eax 0/r32/eax
2989       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
2990       # if (c == ' ') break
2991       3d/compare-eax-and 0x20/imm32/space
2992       74/jump-if-= break/disp8
2993       # if (c == '\r') break
2994       3d/compare-eax-and 0xd/imm32/carriage-return
2995       74/jump-if-= break/disp8
2996       # if (c == '\n') break
2997       3d/compare-eax-and 0xa/imm32/newline
2998       74/jump-if-= break/disp8
2999       # if (c == '(') break
3000       3d/compare-eax-and 0x28/imm32/open-paren
3001       0f 84/jump-if-= break/disp32
3002       # if (c == ')') break
3003       3d/compare-eax-and 0x29/imm32/close-paren
3004       0f 84/jump-if-= break/disp32
3005       # if (c == ',') break
3006       3d/compare-eax-and 0x2c/imm32/comma
3007       0f 84/jump-if-= break/disp32
3008       # ++in->read
3009       ff 0/subop/increment *(esi+4)
3010       #
3011       e9/jump loop/disp32
3012     }
3013 $next-mu-token:done:
3014     # out->end = &in->data[in->read]
3015     8b/-> *(esi+4) 1/r32/ecx
3016     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
3017     89/<- *(edi+4) 0/r32/eax
3018 $next-mu-token:end:
3019     # . restore registers
3020     5f/pop-to-edi
3021     5e/pop-to-esi
3022     59/pop-to-ecx
3023     58/pop-to-eax
3024     # . epilogue
3025     89/<- %esp 5/r32/ebp
3026     5d/pop-to-ebp
3027     c3/return
3028 
3029 pos-or-insert-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
3030     # . prologue
3031     55/push-ebp
3032     89/<- %ebp 4/r32/esp
3033     # if (pos-slice(arr, s) != -1) return it
3034     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
3035     3d/compare-eax-and -1/imm32
3036     75/jump-if-not-equal $pos-or-insert-slice:end/disp8
3037     #
3038     (slice-to-string Heap *(ebp+0xc))  # => eax
3039     (write-int *(ebp+8) %eax)
3040     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
3041 $pos-or-insert-slice:end:
3042     # . epilogue
3043     89/<- %esp 5/r32/ebp
3044     5d/pop-to-ebp
3045     c3/return
3046 
3047 # return the index in an array of strings matching 's', -1 if not found
3048 # index is denominated in elements, not bytes
3049 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
3050     # . prologue
3051     55/push-ebp
3052     89/<- %ebp 4/r32/esp
3053     # . save registers
3054     51/push-ecx
3055     52/push-edx
3056     53/push-ebx
3057     56/push-esi
3058 #?     (write-buffered Stderr "pos-slice: ")
3059 #?     (write-slice-buffered Stderr *(ebp+0xc))
3060 #?     (write-buffered Stderr "\n")
3061 #?     (flush Stderr)
3062     # esi = arr
3063     8b/-> *(ebp+8) 6/r32/esi
3064     # var index/ecx: int = 0
3065     b9/copy-to-ecx 0/imm32
3066     # var curr/edx: (addr (addr array byte)) = arr->data
3067     8d/copy-address *(esi+0xc) 2/r32/edx
3068     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
3069     8b/-> *esi 3/r32/ebx
3070     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
3071     {
3072 #?       (write-buffered Stderr "  ")
3073 #?       (print-int32-buffered Stderr %ecx)
3074 #?       (write-buffered Stderr "\n")
3075 #?       (flush Stderr)
3076       # if (curr >= max) return -1
3077       39/compare %edx 3/r32/ebx
3078       b8/copy-to-eax -1/imm32
3079       73/jump-if-addr>= $pos-slice:end/disp8
3080       # if (slice-equal?(s, *curr)) break
3081       (slice-equal? *(ebp+0xc) *edx)  # => eax
3082       3d/compare-eax-and 0/imm32/false
3083       75/jump-if-!= break/disp8
3084       # ++index
3085       41/increment-ecx
3086       # curr += 4
3087       81 0/subop/add %edx 4/imm32
3088       #
3089       eb/jump loop/disp8
3090     }
3091     # return index
3092     89/<- %eax 1/r32/ecx
3093 $pos-slice:end:
3094 #?     (write-buffered Stderr "=> ")
3095 #?     (print-int32-buffered Stderr %eax)
3096 #?     (write-buffered Stderr "\n")
3097     # . restore registers
3098     5e/pop-to-esi
3099     5b/pop-to-ebx
3100     5a/pop-to-edx
3101     59/pop-to-ecx
3102     # . epilogue
3103     89/<- %esp 5/r32/ebp
3104     5d/pop-to-ebp
3105     c3/return
3106 
3107 test-parse-var-with-type:
3108     # . prologue
3109     55/push-ebp
3110     89/<- %ebp 4/r32/esp
3111     # (eax..ecx) = "x:"
3112     b8/copy-to-eax "x:"/imm32
3113     8b/-> *eax 1/r32/ecx
3114     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3115     05/add-to-eax 4/imm32
3116     # var slice/ecx: slice = {eax, ecx}
3117     51/push-ecx
3118     50/push-eax
3119     89/<- %ecx 4/r32/esp
3120     # _test-input-stream contains "int"
3121     (clear-stream _test-input-stream)
3122     (write _test-input-stream "int")
3123     #
3124     (parse-var-with-type %ecx _test-input-stream)
3125     8b/-> *eax 2/r32/edx  # Var-name
3126     (check-strings-equal %edx "x" "F - test-var-with-type/name")
3127     8b/-> *(eax+4) 2/r32/edx  # Var-type
3128     (check-ints-equal *edx 1 "F - test-var-with-type/type")
3129     (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type")
3130     # . epilogue
3131     89/<- %esp 5/r32/ebp
3132     5d/pop-to-ebp
3133     c3/return
3134 
3135 test-parse-var-with-type-and-register:
3136     # . prologue
3137     55/push-ebp
3138     89/<- %ebp 4/r32/esp
3139     # (eax..ecx) = "x/eax:"
3140     b8/copy-to-eax "x/eax:"/imm32
3141     8b/-> *eax 1/r32/ecx
3142     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3143     05/add-to-eax 4/imm32
3144     # var slice/ecx: slice = {eax, ecx}
3145     51/push-ecx
3146     50/push-eax
3147     89/<- %ecx 4/r32/esp
3148     # _test-input-stream contains "int"
3149     (clear-stream _test-input-stream)
3150     (write _test-input-stream "int")
3151     #
3152     (parse-var-with-type %ecx _test-input-stream)
3153     8b/-> *eax 2/r32/edx  # Var-name
3154     (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name")
3155     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
3156     (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register")
3157     8b/-> *(eax+4) 2/r32/edx  # Var-type
3158     (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type")
3159     (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type")
3160     # . epilogue
3161     89/<- %esp 5/r32/ebp
3162     5d/pop-to-ebp
3163     c3/return
3164 
3165 test-parse-var-with-trailing-characters:
3166     # . prologue
3167     55/push-ebp
3168     89/<- %ebp 4/r32/esp
3169     # (eax..ecx) = "x:"
3170     b8/copy-to-eax "x:"/imm32
3171     8b/-> *eax 1/r32/ecx
3172     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3173     05/add-to-eax 4/imm32
3174     # var slice/ecx: slice = {eax, ecx}
3175     51/push-ecx
3176     50/push-eax
3177     89/<- %ecx 4/r32/esp
3178     # _test-input-stream contains "int,"
3179     (clear-stream _test-input-stream)
3180     (write _test-input-stream "int,")
3181     #
3182     (parse-var-with-type %ecx _test-input-stream)
3183     8b/-> *eax 2/r32/edx  # Var-name
3184     (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name")
3185     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
3186     (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register")
3187     8b/-> *(eax+4) 2/r32/edx  # Var-type
3188     (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type")
3189     (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type")
3190     # . epilogue
3191     89/<- %esp 5/r32/ebp
3192     5d/pop-to-ebp
3193     c3/return
3194 
3195 test-parse-var-with-register-and-trailing-characters:
3196     # . prologue
3197     55/push-ebp
3198     89/<- %ebp 4/r32/esp
3199     # (eax..ecx) = "x/eax:"
3200     b8/copy-to-eax "x/eax:"/imm32
3201     8b/-> *eax 1/r32/ecx
3202     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3203     05/add-to-eax 4/imm32
3204     # var slice/ecx: slice = {eax, ecx}
3205     51/push-ecx
3206     50/push-eax
3207     89/<- %ecx 4/r32/esp
3208     # _test-input-stream contains "int,"
3209     (clear-stream _test-input-stream)
3210     (write _test-input-stream "int,")
3211     #
3212     (parse-var-with-type %ecx _test-input-stream)
3213     8b/-> *eax 2/r32/edx  # Var-name
3214     (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name")
3215     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
3216     (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register")
3217     8b/-> *(eax+4) 2/r32/edx  # Var-type
3218     (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type")
3219     (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type")
3220     # . epilogue
3221     89/<- %esp 5/r32/ebp
3222     5d/pop-to-ebp
3223     c3/return
3224 
3225 test-parse-var-with-compound-type:
3226     # . prologue
3227     55/push-ebp
3228     89/<- %ebp 4/r32/esp
3229     # (eax..ecx) = "x:"
3230     b8/copy-to-eax "x:"/imm32
3231     8b/-> *eax 1/r32/ecx
3232     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3233     05/add-to-eax 4/imm32
3234     # var slice/ecx: slice = {eax, ecx}
3235     51/push-ecx
3236     50/push-eax
3237     89/<- %ecx 4/r32/esp
3238     # _test-input-stream contains "(addr int)"
3239     (clear-stream _test-input-stream)
3240     (write _test-input-stream "(addr int)")
3241     #
3242     (parse-var-with-type %ecx _test-input-stream)
3243     8b/-> *eax 2/r32/edx  # Var-name
3244     (check-strings-equal %edx "x" "F - test-var-with-compound-type/name")
3245     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
3246     (check-ints-equal %edx 0 "F - test-var-with-compound-type/register")
3247     # var type/edx: (handle tree type-id) = var->type
3248     8b/-> *(eax+4) 2/r32/edx  # Var-type
3249     # type->left == atom(addr)
3250     8b/-> *edx 0/r32/eax  # Atom-value
3251     (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0")  # Tree-left
3252     # type->right->left == atom(int)
3253     8b/-> *(edx+4) 2/r32/edx  # Tree-right
3254     8b/-> *edx 0/r32/eax  # Tree-left
3255     (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1")  # Atom-value
3256     # type->right->right == null
3257     (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2")  # Tree-right
3258     # . epilogue
3259     89/<- %esp 5/r32/ebp
3260     5d/pop-to-ebp
3261     c3/return
3262 
3263 # identifier starts with a letter or '$' or '_'
3264 # no constraints at the moment on later letters
3265 # all we really want to do so far is exclude '{', '}' and '->'
3266 is-identifier?:  # in: (addr slice) -> result/eax: boolean
3267     # . prologue
3268     55/push-ebp
3269     89/<- %ebp 4/r32/esp
3270     # if (slice-empty?(in)) return false
3271     (slice-empty? *(ebp+8))  # => eax
3272     3d/compare-eax-and 0/imm32/false
3273     75/jump-if-!= $is-identifier?:false/disp8
3274     # var c/eax: byte = *in->start
3275     8b/-> *(ebp+8) 0/r32/eax
3276     8b/-> *eax 0/r32/eax
3277     8a/copy-byte *eax 0/r32/AL
3278     81 4/subop/and %eax 0xff/imm32
3279     # if (c == '$') return true
3280     3d/compare-eax-and 0x24/imm32/$
3281     74/jump-if-= $is-identifier?:true/disp8
3282     # if (c == '_') return true
3283     3d/compare-eax-and 0x5f/imm32/_
3284     74/jump-if-= $is-identifier?:true/disp8
3285     # drop case
3286     25/and-eax-with 0x5f/imm32
3287     # if (c < 'A') return false
3288     3d/compare-eax-and 0x41/imm32/A
3289     7c/jump-if-< $is-identifier?:false/disp8
3290     # if (c > 'Z') return false
3291     3d/compare-eax-and 0x5a/imm32/Z
3292     7f/jump-if-> $is-identifier?:false/disp8
3293     # otherwise return true
3294 $is-identifier?:true:
3295     b8/copy-to-eax 1/imm32/true
3296     eb/jump $is-identifier?:end/disp8
3297 $is-identifier?:false:
3298     b8/copy-to-eax 0/imm32/false
3299 $is-identifier?:end:
3300     # . epilogue
3301     89/<- %esp 5/r32/ebp
3302     5d/pop-to-ebp
3303     c3/return
3304 
3305 test-is-identifier-dollar:
3306     # . prologue
3307     55/push-ebp
3308     89/<- %ebp 4/r32/esp
3309     # (eax..ecx) = "$a"
3310     b8/copy-to-eax "$a"/imm32
3311     8b/-> *eax 1/r32/ecx
3312     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3313     05/add-to-eax 4/imm32
3314     # var slice/ecx: slice = {eax, ecx}
3315     51/push-ecx
3316     50/push-eax
3317     89/<- %ecx 4/r32/esp
3318     #
3319     (is-identifier? %ecx)
3320     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
3321     # . epilogue
3322     89/<- %esp 5/r32/ebp
3323     5d/pop-to-ebp
3324     c3/return
3325 
3326 test-is-identifier-underscore:
3327     # . prologue
3328     55/push-ebp
3329     89/<- %ebp 4/r32/esp
3330     # (eax..ecx) = "_a"
3331     b8/copy-to-eax "_a"/imm32
3332     8b/-> *eax 1/r32/ecx
3333     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3334     05/add-to-eax 4/imm32
3335     # var slice/ecx: slice = {eax, ecx}
3336     51/push-ecx
3337     50/push-eax
3338     89/<- %ecx 4/r32/esp
3339     #
3340     (is-identifier? %ecx)
3341     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
3342     # . epilogue
3343     89/<- %esp 5/r32/ebp
3344     5d/pop-to-ebp
3345     c3/return
3346 
3347 test-is-identifier-a:
3348     # . prologue
3349     55/push-ebp
3350     89/<- %ebp 4/r32/esp
3351     # (eax..ecx) = "a$"
3352     b8/copy-to-eax "a$"/imm32
3353     8b/-> *eax 1/r32/ecx
3354     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3355     05/add-to-eax 4/imm32
3356     # var slice/ecx: slice = {eax, ecx}
3357     51/push-ecx
3358     50/push-eax
3359     89/<- %ecx 4/r32/esp
3360     #
3361     (is-identifier? %ecx)
3362     (check-ints-equal %eax 1 "F - test-is-identifier-a")
3363     # . epilogue
3364     89/<- %esp 5/r32/ebp
3365     5d/pop-to-ebp
3366     c3/return
3367 
3368 test-is-identifier-z:
3369     # . prologue
3370     55/push-ebp
3371     89/<- %ebp 4/r32/esp
3372     # (eax..ecx) = "z$"
3373     b8/copy-to-eax "z$"/imm32
3374     8b/-> *eax 1/r32/ecx
3375     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3376     05/add-to-eax 4/imm32
3377     # var slice/ecx: slice = {eax, ecx}
3378     51/push-ecx
3379     50/push-eax
3380     89/<- %ecx 4/r32/esp
3381     #
3382     (is-identifier? %ecx)
3383     (check-ints-equal %eax 1 "F - test-is-identifier-z")
3384     # . epilogue
3385     89/<- %esp 5/r32/ebp
3386     5d/pop-to-ebp
3387     c3/return
3388 
3389 test-is-identifier-A:
3390     # . prologue
3391     55/push-ebp
3392     89/<- %ebp 4/r32/esp
3393     # (eax..ecx) = "A$"
3394     b8/copy-to-eax "A$"/imm32
3395     8b/-> *eax 1/r32/ecx
3396     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3397     05/add-to-eax 4/imm32
3398     # var slice/ecx: slice = {eax, ecx}
3399     51/push-ecx
3400     50/push-eax
3401     89/<- %ecx 4/r32/esp
3402     #
3403     (is-identifier? %ecx)
3404     (check-ints-equal %eax 1 "F - test-is-identifier-A")
3405     # . epilogue
3406     89/<- %esp 5/r32/ebp
3407     5d/pop-to-ebp
3408     c3/return
3409 
3410 test-is-identifier-Z:
3411     # . prologue
3412     55/push-ebp
3413     89/<- %ebp 4/r32/esp
3414     # (eax..ecx) = "Z$"
3415     b8/copy-to-eax "Z$"/imm32
3416     8b/-> *eax 1/r32/ecx
3417     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3418     05/add-to-eax 4/imm32
3419     # var slice/ecx: slice = {eax, ecx}
3420     51/push-ecx
3421     50/push-eax
3422     89/<- %ecx 4/r32/esp
3423     #
3424     (is-identifier? %ecx)
3425     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
3426     # . epilogue
3427     89/<- %esp 5/r32/ebp
3428     5d/pop-to-ebp
3429     c3/return
3430 
3431 test-is-identifier-@:
3432     # character before 'A' is invalid
3433     # . prologue
3434     55/push-ebp
3435     89/<- %ebp 4/r32/esp
3436     # (eax..ecx) = "@a"
3437     b8/copy-to-eax "@a"/imm32
3438     8b/-> *eax 1/r32/ecx
3439     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3440     05/add-to-eax 4/imm32
3441     # var slice/ecx: slice = {eax, ecx}
3442     51/push-ecx
3443     50/push-eax
3444     89/<- %ecx 4/r32/esp
3445     #
3446     (is-identifier? %ecx)
3447     (check-ints-equal %eax 0 "F - test-is-identifier-@")
3448     # . epilogue
3449     89/<- %esp 5/r32/ebp
3450     5d/pop-to-ebp
3451     c3/return
3452 
3453 test-is-identifier-square-bracket:
3454     # character after 'Z' is invalid
3455     # . prologue
3456     55/push-ebp
3457     89/<- %ebp 4/r32/esp
3458     # (eax..ecx) = "[a"
3459     b8/copy-to-eax "[a"/imm32
3460     8b/-> *eax 1/r32/ecx
3461     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3462     05/add-to-eax 4/imm32
3463     # var slice/ecx: slice = {eax, ecx}
3464     51/push-ecx
3465     50/push-eax
3466     89/<- %ecx 4/r32/esp
3467     #
3468     (is-identifier? %ecx)
3469     (check-ints-equal %eax 0 "F - test-is-identifier-@")
3470     # . epilogue
3471     89/<- %esp 5/r32/ebp
3472     5d/pop-to-ebp
3473     c3/return
3474 
3475 test-is-identifier-backtick:
3476     # character before 'a' is invalid
3477     # . prologue
3478     55/push-ebp
3479     89/<- %ebp 4/r32/esp
3480     # (eax..ecx) = "`a"
3481     b8/copy-to-eax "`a"/imm32
3482     8b/-> *eax 1/r32/ecx
3483     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3484     05/add-to-eax 4/imm32
3485     # var slice/ecx: slice = {eax, ecx}
3486     51/push-ecx
3487     50/push-eax
3488     89/<- %ecx 4/r32/esp
3489     #
3490     (is-identifier? %ecx)
3491     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
3492     # . epilogue
3493     89/<- %esp 5/r32/ebp
3494     5d/pop-to-ebp
3495     c3/return
3496 
3497 test-is-identifier-curly-brace-open:
3498     # character after 'z' is invalid; also used for blocks
3499     # . prologue
3500     55/push-ebp
3501     89/<- %ebp 4/r32/esp
3502     # (eax..ecx) = "{a"
3503     b8/copy-to-eax "{a"/imm32
3504     8b/-> *eax 1/r32/ecx
3505     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3506     05/add-to-eax 4/imm32
3507     # var slice/ecx: slice = {eax, ecx}
3508     51/push-ecx
3509     50/push-eax
3510     89/<- %ecx 4/r32/esp
3511     #
3512     (is-identifier? %ecx)
3513     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
3514     # . epilogue
3515     89/<- %esp 5/r32/ebp
3516     5d/pop-to-ebp
3517     c3/return
3518 
3519 test-is-identifier-curly-brace-close:
3520     # . prologue
3521     55/push-ebp
3522     89/<- %ebp 4/r32/esp
3523     # (eax..ecx) = "}a"
3524     b8/copy-to-eax "}a"/imm32
3525     8b/-> *eax 1/r32/ecx
3526     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3527     05/add-to-eax 4/imm32
3528     # var slice/ecx: slice = {eax, ecx}
3529     51/push-ecx
3530     50/push-eax
3531     89/<- %ecx 4/r32/esp
3532     #
3533     (is-identifier? %ecx)
3534     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
3535     # . epilogue
3536     89/<- %esp 5/r32/ebp
3537     5d/pop-to-ebp
3538     c3/return
3539 
3540 test-is-identifier-hyphen:
3541     # disallow leading '-' since '->' has special meaning
3542     # . prologue
3543     55/push-ebp
3544     89/<- %ebp 4/r32/esp
3545     # (eax..ecx) = "-a"
3546     b8/copy-to-eax "-a"/imm32
3547     8b/-> *eax 1/r32/ecx
3548     8d/copy-address *(eax+ecx+4) 1/r32/ecx
3549     05/add-to-eax 4/imm32
3550     # var slice/ecx: slice = {eax, ecx}
3551     51/push-ecx
3552     50/push-eax
3553     89/<- %ecx 4/r32/esp
3554     #
3555     (is-identifier? %ecx)
3556     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
3557     # . epilogue
3558     89/<- %esp 5/r32/ebp
3559     5d/pop-to-ebp
3560     c3/return
3561 
3562 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
3563     # . prologue
3564     55/push-ebp
3565     89/<- %ebp 4/r32/esp
3566     # . save registers
3567     50/push-eax
3568     56/push-esi
3569     57/push-edi
3570     # esi = in
3571     8b/-> *(ebp+8) 6/r32/esi
3572     # edi = out
3573     8b/-> *(ebp+0xc) 7/r32/edi
3574     # initialize some global state
3575     c7 0/subop/copy *Curr-block-depth 1/imm32
3576     c7 0/subop/copy *Next-local-stack-offset -4/imm32
3577     # var eax: (handle block) = parse-mu-block(in, vars, fn)
3578     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
3579     # out->body = eax
3580     89/<- *(edi+0x10) 0/r32/eax  # Function-body
3581 $populate-mu-function-body:end:
3582     # . restore registers
3583     5f/pop-to-edi
3584     5e/pop-to-esi
3585     58/pop-to-eax
3586     # . epilogue
3587     89/<- %esp 5/r32/ebp
3588     5d/pop-to-ebp
3589     c3/return
3590 
3591 == data
3592 
3593 # Global state added to each var record when parsing a function
3594 
3595 Curr-block-depth:  # (addr int)
3596     0/imm32
3597 Next-local-stack-offset:  # (addr int)
3598     -4/imm32
3599 
3600 Next-block-index:  # (addr int)
3601     1/imm32
3602 
3603 == code
3604 
3605 # parses a block, assuming that the leading '{' has already been read by the caller
3606 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
3607     # pseudocode:
3608     #   var line: (stream byte 512)
3609     #   var word-slice: slice
3610     #   increment *Curr-block-depth
3611     #   result/eax = allocate(Heap, Stmt-size)
3612     #   result->tag = 0/block
3613     #   result->name = some unique name
3614     #   while true                                  # line loop
3615     #     clear-stream(line)
3616     #     read-line-buffered(in, line)
3617     #     if (line->write == 0) break               # end of file
3618     #     word-slice = next-mu-token(line)
3619     #     if slice-empty?(word-slice)               # end of line
3620     #       continue
3621     #     else if slice-starts-with?(word-slice, "#")
3622     #       continue
3623     #     else if slice-equal?(word-slice, "{")
3624     #       assert(no-tokens-in(line))
3625     #       block = parse-mu-block(in, vars, fn)
3626     #       append-to-block(result, block)
3627     #     else if slice-equal?(word-slice, "}")
3628     #       break
3629     #     else if slice-ends-with?(word-slice, ":")
3630     #       # TODO: error-check the rest of 'line'
3631     #       --word-slice->end to skip ':'
3632     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
3633     #       append-to-block(result, named-block)
3634     #     else if slice-equal?(word-slice, "var")
3635     #       var-def = parse-mu-var-def(line, vars)
3636     #       append-to-block(result, var-def)
3637     #     else
3638     #       stmt = parse-mu-stmt(line, vars, fn)
3639     #       append-to-block(result, stmt)
3640     #   decrement *Curr-block-depth
3641     #   return result
3642     #
3643     # . prologue
3644     55/push-ebp
3645     89/<- %ebp 4/r32/esp
3646     # . save registers
3647     51/push-ecx
3648     52/push-edx
3649     53/push-ebx
3650     57/push-edi
3651     # var line/ecx: (stream byte 512)
3652     81 5/subop/subtract %esp 0x200/imm32
3653     68/push 0x200/imm32/length
3654     68/push 0/imm32/read
3655     68/push 0/imm32/write
3656     89/<- %ecx 4/r32/esp
3657     # var word-slice/edx: slice
3658     68/push 0/imm32/end
3659     68/push 0/imm32/start
3660     89/<- %edx 4/r32/esp
3661     # edi = result
3662     (allocate Heap *Stmt-size)  # => eax
3663     (zero-out %eax *Stmt-size)
3664     89/<- %edi 0/r32/eax
3665     # set result->tag
3666     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
3667     # set result->var
3668     (new-block-name *(ebp+0x10))  # => eax
3669     89/<- *(edi+8) 0/r32/eax  # Block-var
3670     # push result->var to vars
3671     (push *(ebp+0xc) %eax)
3672     # increment *Curr-block-depth
3673     ff 0/subop/increment *Curr-block-depth
3674     {
3675 $parse-mu-block:line-loop:
3676       # line = read-line-buffered(in)
3677       (clear-stream %ecx)
3678       (read-line-buffered *(ebp+8) %ecx)
3679 #?       (write-buffered Stderr "line: ")
3680 #?       (write-stream-data Stderr %ecx)
3681 #?       (write-buffered Stderr Newline)
3682 #?       (flush Stderr)
3683       # if (line->write == 0) break
3684       81 7/subop/compare *ecx 0/imm32
3685       0f 84/jump-if-= break/disp32
3686       # word-slice = next-mu-token(line)
3687       (next-mu-token %ecx %edx)
3688 #?       (write-buffered Stderr "word: ")
3689 #?       (write-slice-buffered Stderr %edx)
3690 #?       (write-buffered Stderr Newline)
3691 #?       (flush Stderr)
3692       # if slice-empty?(word-slice) continue
3693       (slice-empty? %edx)
3694       3d/compare-eax-and 0/imm32/false
3695       0f 85/jump-if-!= loop/disp32
3696       # if (slice-starts-with?(word-slice, '#') continue
3697       # . eax = *word-slice->start
3698       8b/-> *edx 0/r32/eax
3699       8a/copy-byte *eax 0/r32/AL
3700       81 4/subop/and %eax 0xff/imm32
3701       # . if (eax == '#') continue
3702       3d/compare-eax-and 0x23/imm32/hash
3703       0f 84/jump-if-= loop/disp32
3704       # if slice-equal?(word-slice, "{")
3705       {
3706 $parse-mu-block:check-for-block:
3707         (slice-equal? %edx "{")
3708         3d/compare-eax-and 0/imm32/false
3709         74/jump-if-= break/disp8
3710         (check-no-tokens-left %ecx)
3711         # parse new block and append
3712         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3713         (append-to-block Heap %edi %eax)
3714         e9/jump $parse-mu-block:line-loop/disp32
3715       }
3716       # if slice-equal?(word-slice, "}") break
3717 $parse-mu-block:check-for-end:
3718       (slice-equal? %edx "}")
3719       3d/compare-eax-and 0/imm32/false
3720       0f 85/jump-if-!= break/disp32
3721       # if slice-ends-with?(word-slice, ":") parse named block and append
3722       {
3723 $parse-mu-block:check-for-named-block:
3724         # . eax = *(word-slice->end-1)
3725         8b/-> *(edx+4) 0/r32/eax
3726         48/decrement-eax
3727         8a/copy-byte *eax 0/r32/AL
3728         81 4/subop/and %eax 0xff/imm32
3729         # . if (eax != ':') break
3730         3d/compare-eax-and 0x3a/imm32/colon
3731         0f 85/jump-if-!= break/disp32
3732         # TODO: error-check the rest of 'line'
3733         #
3734         # skip ':'
3735         ff 1/subop/decrement *(edx+4)  # Slice-end
3736         #
3737         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
3738         (append-to-block Heap %edi %eax)
3739         e9/jump $parse-mu-block:line-loop/disp32
3740       }
3741       # if slice-equal?(word-slice, "var")
3742       {
3743 $parse-mu-block:check-for-var:
3744         (slice-equal? %edx "var")
3745         3d/compare-eax-and 0/imm32/false
3746         74/jump-if-= break/disp8
3747         #
3748         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
3749         (append-to-block Heap %edi %eax)
3750         e9/jump $parse-mu-block:line-loop/disp32
3751       }
3752 $parse-mu-block:regular-stmt:
3753       # otherwise
3754       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
3755       (append-to-block Heap %edi %eax)
3756       e9/jump loop/disp32
3757     } # end line loop
3758     # decrement *Curr-block-depth
3759     ff 1/subop/decrement *Curr-block-depth
3760     #
3761     (pop *(ebp+0xc))  # => eax
3762     # return result
3763     89/<- %eax 7/r32/edi
3764 $parse-mu-block:end:
3765     # . reclaim locals
3766     81 0/subop/add %esp 0x214/imm32
3767     # . restore registers
3768     5f/pop-to-edi
3769     5b/pop-to-ebx
3770     5a/pop-to-edx
3771     59/pop-to-ecx
3772     # . epilogue
3773     89/<- %esp 5/r32/ebp
3774     5d/pop-to-ebp
3775     c3/return
3776 
3777 $parse-mu-block:abort:
3778     # error("'{' or '}' should be on its own line, but got '")
3779     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3780     (rewind-stream %ecx)
3781     (write-stream 2 %ecx)
3782     (write-buffered Stderr "'\n")
3783     (flush Stderr)
3784     # . syscall(exit, 1)
3785     bb/copy-to-ebx  1/imm32
3786     b8/copy-to-eax  1/imm32/exit
3787     cd/syscall  0x80/imm8
3788     # never gets here
3789 
3790 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
3791     # . prologue
3792     55/push-ebp
3793     89/<- %ebp 4/r32/esp
3794     # . save registers
3795     51/push-ecx
3796     52/push-edx
3797     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
3798     8b/-> *(ebp+8) 0/r32/eax
3799     8b/-> *eax 0/r32/eax  # Function-name
3800     8b/-> *eax 0/r32/eax  # String-length
3801     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
3802     89/<- %ecx 0/r32/eax
3803     # var name/edx: (stream byte n)
3804     29/subtract %esp 1/r32/ecx
3805     ff 6/subop/push %ecx
3806     68/push 0/imm32/read
3807     68/push 0/imm32/write
3808     89/<- %edx 4/r32/esp
3809     (clear-stream %edx)
3810     # eax = fn->name
3811     8b/-> *(ebp+8) 0/r32/eax
3812     8b/-> *eax 0/r32/eax  # Function-name
3813     # construct result using Next-block-index (and increment it)
3814     (write %edx "$")
3815     (write %edx %eax)
3816     (write %edx ":")
3817     (print-int32 %edx *Next-block-index)
3818     ff 0/subop/increment *Next-block-index
3819     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
3820     # . eax = name->write
3821     8b/-> *edx 0/r32/eax
3822     # . edx = name->data
3823     8d/copy-address *(edx+0xc) 2/r32/edx
3824     # . eax = name->write + name->data
3825     01/add %eax 2/r32/edx
3826     # . push {edx, eax}
3827     ff 6/subop/push %eax
3828     ff 6/subop/push %edx
3829     89/<- %eax 4/r32/esp
3830     # result->var = new literal(s)
3831     (new-literal Heap %eax)  # => eax
3832 $new-block-name:end:
3833     # . reclaim locals
3834     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
3835     81 0/subop/add %ecx 8/imm32  # slice
3836     01/add %esp 1/r32/ecx
3837     # . restore registers
3838     5a/pop-to-edx
3839     59/pop-to-ecx
3840     # . epilogue
3841     89/<- %esp 5/r32/ebp
3842     5d/pop-to-ebp
3843     c3/return
3844 
3845 check-no-tokens-left:  # line: (addr stream byte)
3846     # . prologue
3847     55/push-ebp
3848     89/<- %ebp 4/r32/esp
3849     # . save registers
3850     50/push-eax
3851     51/push-ecx
3852     # var s/ecx: slice
3853     68/push 0/imm32/end
3854     68/push 0/imm32/start
3855     89/<- %ecx 4/r32/esp
3856     #
3857     (next-mu-token *(ebp+8) %ecx)
3858     # if slice-empty?(s) return
3859     (slice-empty? %ecx)
3860     3d/compare-eax-and 0/imm32/false
3861     75/jump-if-!= $check-no-tokens-left:end/disp8
3862     # if (slice-starts-with?(s, '#') return
3863     # . eax = *s->start
3864     8b/-> *edx 0/r32/eax
3865     8a/copy-byte *eax 0/r32/AL
3866     81 4/subop/and %eax 0xff/imm32
3867     # . if (eax == '#') continue
3868     3d/compare-eax-and 0x23/imm32/hash
3869     74/jump-if-= $check-no-tokens-left:end/disp8
3870     # abort
3871     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
3872     (rewind-stream %ecx)
3873     (write-stream 2 %ecx)
3874     (write-buffered Stderr "'\n")
3875     (flush Stderr)
3876     # . syscall(exit, 1)
3877     bb/copy-to-ebx  1/imm32
3878     b8/copy-to-eax  1/imm32/exit
3879     cd/syscall  0x80/imm8
3880     # never gets here
3881 $check-no-tokens-left:end:
3882     # . reclaim locals
3883     81 0/subop/add %esp 8/imm32
3884     # . restore registers
3885     59/pop-to-ecx
3886     58/pop-to-eax
3887     # . epilogue
3888     89/<- %esp 5/r32/ebp
3889     5d/pop-to-ebp
3890     c3/return
3891 
3892 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
3893     # pseudocode:
3894     #   var s: (addr array byte) = slice-to-string(name)
3895     #   var v: (handle var) = new-var(s, 0)
3896     #   v->block-depth = *Curr-block-depth  # containing block depth
3897     #   push(vars, v)
3898     #   result = parse-mu-block(in, vars, fn)
3899     #   pop(vars)
3900     #   result->name = s
3901     #   return result
3902     #
3903     # . prologue
3904     55/push-ebp
3905     89/<- %ebp 4/r32/esp
3906     # . save registers
3907     51/push-ecx
3908     # var v/ecx: (handle var)
3909     (new-literal Heap *(ebp+8))  # => eax
3910     89/<- %ecx 0/r32/eax
3911     # push(vars, v)
3912     (push *(ebp+0x10) %ecx)
3913     # eax = result
3914     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
3915     # pop the var
3916     50/push-eax
3917     (pop *(ebp+0x10))  # => eax
3918     58/pop-to-eax
3919     # result->tag = named-block
3920     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
3921     # result->var = v
3922     89/<- *(eax+8) 1/r32/ecx  # Block-var
3923 $parse-mu-named-block:end:
3924     # . restore registers
3925     59/pop-to-ecx
3926     # . epilogue
3927     89/<- %esp 5/r32/ebp
3928     5d/pop-to-ebp
3929     c3/return
3930 
3931 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
3932     # . prologue
3933     55/push-ebp
3934     89/<- %ebp 4/r32/esp
3935     # . save registers
3936     51/push-ecx
3937     52/push-edx
3938     # var word-slice/ecx: slice
3939     68/push 0/imm32/end
3940     68/push 0/imm32/start
3941     89/<- %ecx 4/r32/esp
3942     # var v/edx: (handle var) = parse-var-with-type(line)
3943     (next-mu-token *(ebp+8) %ecx)
3944     (parse-var-with-type %ecx *(ebp+8))  # => eax
3945     89/<- %edx 0/r32/eax
3946     # v->block-depth = *Curr-block-depth
3947     8b/-> *Curr-block-depth 0/r32/eax
3948     89/<- *(edx+8) 0/r32/eax
3949     #
3950     (push *(ebp+0xc) %edx)
3951     # either v has no register and there's no more to this line
3952     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
3953     3d/compare-eax-and 0/imm32
3954     {
3955       75/jump-if-!= break/disp8
3956       # v->stack-offset = *Next-local-stack-offset
3957       8b/-> *Next-local-stack-offset 0/r32/eax
3958       89/<- *(edx+0xc) 0/r32/eax  # Var-offset
3959       # TODO: ensure that there's nothing else on this line
3960       (new-var-def Heap %edx)  # => eax
3961       eb/jump $parse-mu-var-def:end/disp8
3962     }
3963     # or v has a register and there's more to this line
3964     {
3965       74/jump-if-= break/disp8
3966       # ensure that the next word is '<-'
3967       (next-mu-token *(ebp+8) %ecx)
3968       (slice-equal? %ecx "<-")  # => eax
3969       3d/compare-eax-and 0/imm32/false
3970       74/jump-if-= $parse-mu-var-def:abort/disp8
3971       #
3972       (new-reg-var-def Heap %edx)  # => eax
3973       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
3974     }
3975 $parse-mu-var-def:end:
3976     # *Next-local-stack-offset -= size-of(v)
3977     50/push-eax
3978     (size-of %edx)  # => eax
3979     29/subtract-from *Next-local-stack-offset 0/r32/eax
3980     58/pop-to-eax
3981     # . reclaim locals
3982     81 0/subop/add %esp 8/imm32
3983     # . restore registers
3984     5a/pop-to-edx
3985     59/pop-to-ecx
3986     # . epilogue
3987     89/<- %esp 5/r32/ebp
3988     5d/pop-to-ebp
3989     c3/return
3990 
3991 $parse-mu-var-def:abort:
3992     (rewind-stream *(ebp+8))
3993     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
3994     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
3995     (flush Stderr)
3996     (write-stream 2 *(ebp+8))
3997     (write-buffered Stderr "'\n")
3998     (flush Stderr)
3999     # . syscall(exit, 1)
4000     bb/copy-to-ebx  1/imm32
4001     b8/copy-to-eax  1/imm32/exit
4002     cd/syscall  0x80/imm8
4003     # never gets here
4004 
4005 test-parse-mu-var-def:
4006     # 'var n: int'
4007     # . prologue
4008     55/push-ebp
4009     89/<- %ebp 4/r32/esp
4010     # setup
4011     (clear-stream _test-input-stream)
4012     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
4013     c7 0/subop/copy *Curr-block-depth 1/imm32
4014     c7 0/subop/copy *Next-local-stack-offset -4/imm32
4015     # var vars/ecx: (stack (addr var) 4)
4016     81 5/subop/subtract %esp 0x10/imm32
4017     68/push 0x10/imm32/length
4018     68/push 0/imm32/top
4019     89/<- %ecx 4/r32/esp
4020     (clear-stack %ecx)
4021     # convert
4022     (parse-mu-var-def _test-input-stream %ecx)  # => eax
4023     # check result
4024     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
4025     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
4026     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
4027     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
4028     (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
4029     (check-ints-equal *(eax+0xc) -4 "F - test-parse-mu-reg-var-def/output-stack-offset")  # Var-offset
4030     # ensure type is int
4031     8b/-> *(eax+4) 0/r32/eax  # Var-type
4032     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
4033     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
4034     # globals
4035     (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
4036     # . epilogue
4037     89/<- %esp 5/r32/ebp
4038     5d/pop-to-ebp
4039     c3/return
4040 
4041 test-parse-mu-reg-var-def:
4042     # 'var n/eax: int <- copy 0'
4043     # . prologue
4044     55/push-ebp
4045     89/<- %ebp 4/r32/esp
4046     # setup
4047     (clear-stream _test-input-stream)
4048     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
4049     c7 0/subop/copy *Curr-block-depth 1/imm32
4050     c7 0/subop/copy *Next-local-stack-offset -4/imm32
4051     # var vars/ecx: (stack (addr var) 4)
4052     81 5/subop/subtract %esp 0x10/imm32
4053     68/push 0x10/imm32/length
4054     68/push 0/imm32/top
4055     89/<- %ecx 4/r32/esp
4056     (clear-stack %ecx)
4057     # convert
4058     (parse-mu-var-def _test-input-stream %ecx)  # => eax
4059     # check result
4060     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
4061     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
4062     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
4063     8b/-> *eax 0/r32/eax  # Stmt-var-value
4064     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
4065     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
4066     (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
4067     (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-stack-offset")  # Var-offset
4068     # ensure type is int
4069     8b/-> *(eax+4) 0/r32/eax  # Var-type
4070     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
4071     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
4072     # globals
4073     (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
4074     # . epilogue
4075     89/<- %esp 5/r32/ebp
4076     5d/pop-to-ebp
4077     c3/return
4078 
4079 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
4080     # pseudocode:
4081     #   var name: slice
4082     #   result = allocate(Heap, Stmt-size)
4083     #   if stmt-has-outputs?(line)
4084     #     while true
4085     #       name = next-mu-token(line)
4086     #       if (name == '<-') break
4087     #       assert(is-identifier?(name))
4088     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
4089     #       result->outputs = append(result->outputs, v)
4090     #   add-operation-and-inputs-to-stmt(result, line, vars)
4091     #
4092     # . prologue
4093     55/push-ebp
4094     89/<- %ebp 4/r32/esp
4095     # . save registers
4096     51/push-ecx
4097     52/push-edx
4098     57/push-edi
4099     # var name/ecx: slice
4100     68/push 0/imm32/end
4101     68/push 0/imm32/start
4102     89/<- %ecx 4/r32/esp
4103     # var is-deref?/edx: boolean = false
4104     ba/copy-to-edx 0/imm32/false
4105     # result/edi: (handle stmt)
4106     (allocate Heap *Stmt-size)  # => eax
4107     (zero-out %eax *Stmt-size)
4108     89/<- %edi 0/r32/eax
4109     # result->tag = 1/stmt
4110     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
4111     {
4112       (stmt-has-outputs? *(ebp+8))
4113       3d/compare-eax-and 0/imm32/false
4114       0f 84/jump-if-= break/disp32
4115       {
4116 $parse-mu-stmt:read-outputs:
4117         # name = next-mu-token(line)
4118         (next-mu-token *(ebp+8) %ecx)
4119         # if slice-empty?(word-slice) break
4120         (slice-empty? %ecx)  # => eax
4121         3d/compare-eax-and 0/imm32/false
4122         0f 85/jump-if-!= break/disp32
4123         # if (name == "<-") break
4124         (slice-equal? %ecx "<-")  # => eax
4125         3d/compare-eax-and 0/imm32/false
4126         0f 85/jump-if-!= break/disp32
4127         # is-deref? = false
4128         ba/copy-to-edx 0/imm32/false
4129         # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
4130         8b/-> *ecx 0/r32/eax  # Slice-start
4131         8a/copy-byte *eax 0/r32/AL
4132         81 4/subop/and %eax 0xff/imm32
4133         3d/compare-eax-and 0x2a/imm32/asterisk
4134         {
4135           75/jump-if-!= break/disp8
4136           ff 0/subop/increment *ecx
4137           ba/copy-to-edx 1/imm32/true
4138         }
4139         # assert(is-identifier?(name))
4140         (is-identifier? %ecx)  # => eax
4141         3d/compare-eax-and 0/imm32/false
4142         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
4143         # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
4144         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
4145         (append-stmt-var Heap %eax *(edi+0xc) %edx)  # Stmt1-outputs => eax
4146         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
4147         e9/jump loop/disp32
4148       }
4149     }
4150     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
4151 $parse-mu-stmt:end:
4152     # return result
4153     89/<- %eax 7/r32/edi
4154     # . reclaim locals
4155     81 0/subop/add %esp 8/imm32
4156     # . restore registers
4157     5f/pop-to-edi
4158     5a/pop-to-edx
4159     59/pop-to-ecx
4160     # . epilogue
4161     89/<- %esp 5/r32/ebp
4162     5d/pop-to-ebp
4163     c3/return
4164 
4165 $parse-mu-stmt:abort:
4166     # error("invalid identifier '" name "'\n")
4167     (write-buffered Stderr "invalid identifier '")
4168     (write-slice-buffered Stderr %ecx)
4169     (write-buffered Stderr "'\n")
4170     (flush Stderr)
4171     # . syscall(exit, 1)
4172     bb/copy-to-ebx  1/imm32
4173     b8/copy-to-eax  1/imm32/exit
4174     cd/syscall  0x80/imm8
4175     # never gets here
4176 
4177 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
4178     # pseudocode:
4179     #   stmt->name = slice-to-string(next-mu-token(line))
4180     #   while true
4181     #     name = next-mu-token(line)
4182     #     v = lookup-var-or-literal(name)
4183     #     stmt->inouts = append(stmt->inouts, v)
4184     #
4185     # . prologue
4186     55/push-ebp
4187     89/<- %ebp 4/r32/esp
4188     # . save registers
4189     50/push-eax
4190     51/push-ecx
4191     52/push-edx
4192     53/push-ebx
4193     57/push-edi
4194     # edi = stmt
4195     8b/-> *(ebp+8) 7/r32/edi
4196     # var name/ecx: slice
4197     68/push 0/imm32/end
4198     68/push 0/imm32/start
4199     89/<- %ecx 4/r32/esp
4200     # var is-deref?/edx: boolean = false
4201     ba/copy-to-edx 0/imm32/false
4202 $add-operation-and-inputs-to-stmt:read-operation:
4203     (next-mu-token *(ebp+0xc) %ecx)
4204     (slice-to-string Heap %ecx)  # => eax
4205     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
4206     # var is-get?/ebx: boolean = (name == "get")
4207     (slice-equal? %ecx "get")  # => eax
4208     89/<- %ebx 0/r32/eax
4209     {
4210 $add-operation-and-inputs-to-stmt:read-inouts:
4211       # name = next-mu-token(line)
4212       (next-mu-token *(ebp+0xc) %ecx)
4213       # if slice-empty?(word-slice) break
4214       (slice-empty? %ecx)  # => eax
4215       3d/compare-eax-and 0/imm32/false
4216       0f 85/jump-if-!= break/disp32
4217       # if (name == "<-") abort
4218       (slice-equal? %ecx "<-")
4219       3d/compare-eax-and 0/imm32/false
4220       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
4221       # if (is-get? && second operand) lookup or create offset
4222       {
4223         81 7/subop/compare %ebx 0/imm32/false
4224         74/jump-if-= break/disp8
4225         81 7/subop/compare *(edi+8) 0/imm32  # Stmt1-inouts or Regvardef-inouts
4226         74/jump-if-= break/disp8
4227         (lookup-or-create-constant *(edi+8) %ecx)  # Stmt1-inouts => eax
4228 #?         (write-buffered Stderr "creating new output var ")
4229 #?         (print-int32-buffered Stderr %eax)
4230 #?         (write-buffered Stderr " for field called ")
4231 #?         (write-slice-buffered Stderr %ecx)
4232 #?         (write-buffered Stderr Newline)
4233 #?         (flush Stderr)
4234         e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
4235       }
4236       # is-deref? = false
4237       ba/copy-to-edx 0/imm32/false
4238       # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
4239       8b/-> *ecx 0/r32/eax  # Slice-start
4240       8a/copy-byte *eax 0/r32/AL
4241       81 4/subop/and %eax 0xff/imm32
4242       3d/compare-eax-and 0x2a/imm32/asterisk
4243       {
4244         75/jump-if-!= break/disp8
4245 $add-operation-and-inputs-to-stmt:inout-is-deref:
4246         ff 0/subop/increment *ecx
4247         ba/copy-to-edx 1/imm32/true
4248       }
4249       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
4250 $add-operation-and-inputs-to-stmt:save-var:
4251       (append-stmt-var Heap %eax *(edi+8) %edx)  # Stmt1-inouts or Regvardef-inouts => eax
4252       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
4253       e9/jump loop/disp32
4254     }
4255 $add-operation-and-inputs-to-stmt:end:
4256     # . reclaim locals
4257     81 0/subop/add %esp 8/imm32
4258     # . restore registers
4259     5f/pop-to-edi
4260     5b/pop-to-ebx
4261     5a/pop-to-edx
4262     59/pop-to-ecx
4263     58/pop-to-eax
4264     # . epilogue
4265     89/<- %esp 5/r32/ebp
4266     5d/pop-to-ebp
4267     c3/return
4268 
4269 $add-operation-and-inputs-to-stmt:abort:
4270     # error("invalid statement '" line "'\n")
4271     (rewind-stream *(ebp+8))
4272     (write-buffered Stderr "invalid identifier '")
4273     (flush Stderr)
4274     (write-stream 2 *(ebp+8))
4275     (write-buffered Stderr "'\n")
4276     (flush Stderr)
4277     # . syscall(exit, 1)
4278     bb/copy-to-ebx  1/imm32
4279     b8/copy-to-eax  1/imm32/exit
4280     cd/syscall  0x80/imm8
4281     # never gets here
4282 
4283 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
4284     # . prologue
4285     55/push-ebp
4286     89/<- %ebp 4/r32/esp
4287     # . save registers
4288     51/push-ecx
4289     # var word-slice/ecx: slice
4290     68/push 0/imm32/end
4291     68/push 0/imm32/start
4292     89/<- %ecx 4/r32/esp
4293     # result = false
4294     b8/copy-to-eax 0/imm32/false
4295     (rewind-stream *(ebp+8))
4296     {
4297       (next-mu-token *(ebp+8) %ecx)
4298       # if slice-empty?(word-slice) break
4299       (slice-empty? %ecx)
4300       3d/compare-eax-and 0/imm32/false
4301       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
4302       0f 85/jump-if-!= break/disp32
4303       # if slice-starts-with?(word-slice, '#') break
4304       # . eax = *word-slice->start
4305       8b/-> *ecx 0/r32/eax
4306       8a/copy-byte *eax 0/r32/AL
4307       81 4/subop/and %eax 0xff/imm32
4308       # . if (eax == '#') break
4309       3d/compare-eax-and 0x23/imm32/hash
4310       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
4311       0f 84/jump-if-= break/disp32
4312       # if slice-equal?(word-slice, '<-') return true
4313       (slice-equal? %ecx "<-")
4314       3d/compare-eax-and 0/imm32/false
4315       74/jump-if-= loop/disp8
4316       b8/copy-to-eax 1/imm32/true
4317     }
4318 $stmt-has-outputs:end:
4319     (rewind-stream *(ebp+8))
4320     # . reclaim locals
4321     81 0/subop/add %esp 8/imm32
4322     # . restore registers
4323     59/pop-to-ecx
4324     # . epilogue
4325     89/<- %esp 5/r32/ebp
4326     5d/pop-to-ebp
4327     c3/return
4328 
4329 # if 'name' starts with a digit, create a new literal var for it
4330 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
4331 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
4332     # . prologue
4333     55/push-ebp
4334     89/<- %ebp 4/r32/esp
4335     # . save registers
4336     51/push-ecx
4337     56/push-esi
4338     # esi = name
4339     8b/-> *(ebp+8) 6/r32/esi
4340     # if slice-empty?(name) abort
4341     (slice-empty? %esi)  # => eax
4342     3d/compare-eax-and 0/imm32/false
4343     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
4344     # var c/ecx: byte = *name->start
4345     8b/-> *esi 1/r32/ecx
4346     8a/copy-byte *ecx 1/r32/CL
4347     81 4/subop/and %ecx 0xff/imm32
4348     # if is-decimal-digit?(c) return new var(name)
4349     {
4350       (is-decimal-digit? %ecx)  # => eax
4351       3d/compare-eax-and 0/imm32/false
4352       74/jump-if-= break/disp8
4353       (new-literal-integer Heap %esi)  # => eax
4354       eb/jump $lookup-var-or-literal:end/disp8
4355     }
4356     # else if (c == '"') return new var(name)
4357     {
4358       81 7/subop/compare %ecx 0x22/imm32/dquote
4359       75/jump-if-!= break/disp8
4360       (new-literal Heap %esi)  # => eax
4361       eb/jump $lookup-var-or-literal:end/disp8
4362     }
4363     # otherwise return lookup-var(name, vars)
4364     {
4365       (lookup-var %esi *(ebp+0xc))  # => eax
4366     }
4367 $lookup-var-or-literal:end:
4368     # . restore registers
4369     5e/pop-to-esi
4370     59/pop-to-ecx
4371     # . epilogue
4372     89/<- %esp 5/r32/ebp
4373     5d/pop-to-ebp
4374     c3/return
4375 
4376 $lookup-var-or-literal:abort:
4377     (write-buffered Stderr "empty variable!")
4378     (flush Stderr)
4379     # . syscall(exit, 1)
4380     bb/copy-to-ebx  1/imm32
4381     b8/copy-to-eax  1/imm32/exit
4382     cd/syscall  0x80/imm8
4383     # never gets here
4384 
4385 # return first 'name' from the top (back) of 'vars' and abort if not found
4386 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
4387     # . prologue
4388     55/push-ebp
4389     89/<- %ebp 4/r32/esp
4390     # var target/eax: (handle array byte) = slice-to-string(name)
4391     (slice-to-string Heap *(ebp+8))  # => eax
4392     #
4393     (lookup-var-helper %eax *(ebp+0xc))  # => eax
4394     # if (result == 0) abort
4395     3d/compare-eax-and 0/imm32
4396     74/jump-if-= $lookup-var:abort/disp8
4397 $lookup-var:end:
4398     # . epilogue
4399     89/<- %esp 5/r32/ebp
4400     5d/pop-to-ebp
4401     c3/return
4402 
4403 $lookup-var:abort:
4404     (write-buffered Stderr "unknown variable '")
4405     (write-slice-buffered Stderr *(ebp+8))
4406     (write-buffered Stderr "'\n")
4407     (flush Stderr)
4408     # . syscall(exit, 1)
4409     bb/copy-to-ebx  1/imm32
4410     b8/copy-to-eax  1/imm32/exit
4411     cd/syscall  0x80/imm8
4412     # never gets here
4413 
4414 # return first 'name' from the top (back) of 'vars', and 0/null if not found
4415 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
4416     # pseudocode:
4417     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
4418     #   var min = vars->data
4419     #   while curr >= min
4420     #     var v: (handle var) = *curr
4421     #     if v->name == name
4422     #       return v
4423     #   return 0
4424     #
4425     # . prologue
4426     55/push-ebp
4427     89/<- %ebp 4/r32/esp
4428     # . save registers
4429     52/push-edx
4430     53/push-ebx
4431     56/push-esi
4432     # esi = vars
4433     8b/-> *(ebp+0xc) 6/r32/esi
4434     # ebx = vars->top
4435     8b/-> *esi 3/r32/ebx
4436     # if (vars->top > vars->length) abort
4437     3b/compare 0/r32/eax *(esi+4)
4438     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
4439     # var min/edx: (addr handle var) = vars->data
4440     8d/copy-address *(esi+8) 2/r32/edx
4441     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
4442     81 5/subop/subtract %ebx 4/imm32
4443     8d/copy-address *(esi+ebx+8) 3/r32/ebx
4444     {
4445       # if (curr < min) return 0
4446       39/compare %ebx 2/r32/edx
4447       b8/copy-to-eax 0/imm32
4448       0f 82/jump-if-addr< break/disp32
4449       # var v/eax: (handle var) = *curr
4450       8b/-> *ebx 0/r32/eax
4451       # if (v->name == name) return v
4452       (string-equal? *eax *(ebp+8))  # Var-name
4453       3d/compare-eax-and 0/imm32/false
4454       8b/-> *ebx 0/r32/eax
4455       75/jump-if-!= break/disp8
4456       # curr -= 4
4457       81 5/subop/subtract %ebx 4/imm32
4458       e9/jump loop/disp32
4459     }
4460 $lookup-var-helper:end:
4461     # . restore registers
4462     5e/pop-to-esi
4463     5b/pop-to-ebx
4464     5a/pop-to-edx
4465     # . epilogue
4466     89/<- %esp 5/r32/ebp
4467     5d/pop-to-ebp
4468     c3/return
4469 
4470 $lookup-var-helper:error1:
4471     (write-buffered Stderr "malformed stack when looking up '")
4472     (write-slice-buffered Stderr *(ebp+8))
4473     (write-buffered Stderr "'\n")
4474     (flush Stderr)
4475     # . syscall(exit, 1)
4476     bb/copy-to-ebx  1/imm32
4477     b8/copy-to-eax  1/imm32/exit
4478     cd/syscall  0x80/imm8
4479     # never gets here
4480 
4481 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
4482 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
4483     # . prologue
4484     55/push-ebp
4485     89/<- %ebp 4/r32/esp
4486     # . save registers
4487     51/push-ecx
4488     # var target/ecx: (handle array byte) = slice-to-string(name)
4489     (slice-to-string Heap *(ebp+8))  # => eax
4490     89/<- %ecx 0/r32/eax
4491     #
4492     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
4493     {
4494       # if (result != 0) return
4495       3d/compare-eax-and 0/imm32
4496       75/jump-if-!= break/disp8
4497       # if name is one of fn's outputs, return it
4498       {
4499         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
4500         3d/compare-eax-and 0/imm32
4501         # otherwise abort
4502         0f 84/jump-if-!= $lookup-var:abort/disp32
4503       }
4504     }
4505 $lookup-or-define-var:end:
4506     # . restore registers
4507     59/pop-to-ecx
4508     # . epilogue
4509     89/<- %esp 5/r32/ebp
4510     5d/pop-to-ebp
4511     c3/return
4512 
4513 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
4514     # . prologue
4515     55/push-ebp
4516     89/<- %ebp 4/r32/esp
4517     # . save registers
4518     51/push-ecx
4519     # var curr/ecx: (handle list var) = fn->outputs
4520     8b/-> *(ebp+8) 1/r32/ecx
4521     8b/-> *(ecx+0xc) 1/r32/ecx
4522     # while curr != null
4523     {
4524       81 7/subop/compare %ecx 0/imm32
4525       74/jump-if-= break/disp8
4526       # var v: (handle var) = *curr
4527       8b/-> *ecx 0/r32/eax  # List-value
4528       # if (curr->name == name) return curr
4529       50/push-eax
4530       (string-equal? *eax *(ebp+0xc))
4531       3d/compare-eax-and 0/imm32/false
4532       58/pop-to-eax
4533       75/jump-if-!= $find-in-function-outputs:end/disp8
4534       # curr = curr->next
4535       8b/-> *(ecx+4) 1/r32/ecx  # List-next
4536       eb/jump loop/disp8
4537     }
4538     b8/copy-to-eax 0/imm32
4539 $find-in-function-outputs:end:
4540     # . restore registers
4541     59/pop-to-ecx
4542     # . epilogue
4543     89/<- %esp 5/r32/ebp
4544     5d/pop-to-ebp
4545     c3/return
4546 
4547 test-parse-mu-stmt:
4548     # . prologue
4549     55/push-ebp
4550     89/<- %ebp 4/r32/esp
4551     # setup
4552     (clear-stream _test-input-stream)
4553     (write _test-input-stream "increment n\n")
4554     # var vars/ecx: (stack (addr var) 4)
4555     81 5/subop/subtract %esp 0x10/imm32
4556     68/push 0x10/imm32/length
4557     68/push 0/imm32/top
4558     89/<- %ecx 4/r32/esp
4559     (clear-stack %ecx)
4560     # var v/edx: var
4561     81 5/subop/subtract %esp 0x14/imm32  # Var-size
4562     89/<- %edx 4/r32/esp
4563     (zero-out %edx 0x14)  # Var-size
4564     # v->name = "n"
4565     c7 0/subop/copy *edx "n"/imm32  # Var-name
4566     #
4567     (push %ecx %edx)
4568     # convert
4569     (parse-mu-stmt _test-input-stream %ecx)  # => eax
4570     # check result
4571     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
4572     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
4573     # edx: (handle list var) = result->inouts
4574     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
4575     # ebx: (handle var) = result->inouts->value
4576     8b/-> *edx 3/r32/ebx  # Stmt-var-value
4577     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
4578     # . epilogue
4579     89/<- %esp 5/r32/ebp
4580     5d/pop-to-ebp
4581     c3/return
4582 
4583 test-parse-mu-stmt-with-comma:
4584     # . prologue
4585     55/push-ebp
4586     89/<- %ebp 4/r32/esp
4587     # setup
4588     (clear-stream _test-input-stream)
4589     (write _test-input-stream "copy-to n, 3\n")
4590     # var vars/ecx: (stack (addr var) 4)
4591     81 5/subop/subtract %esp 0x10/imm32
4592     68/push 0x10/imm32/length
4593     68/push 0/imm32/top
4594     89/<- %ecx 4/r32/esp
4595     (clear-stack %ecx)
4596     # var v/edx: var
4597     81 5/subop/subtract %esp 0x14/imm32  # Var-size
4598     89/<- %edx 4/r32/esp
4599     (zero-out %edx 0x14)  # Var-size
4600     # v->name = "n"
4601     c7 0/subop/copy *edx "n"/imm32  # Var-name
4602     #
4603     (push %ecx %edx)
4604     # convert
4605     (parse-mu-stmt _test-input-stream %ecx)  # => eax
4606     # check result
4607     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
4608     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
4609     # edx: (handle list var) = result->inouts
4610     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
4611     # ebx: (handle var) = result->inouts->value
4612     8b/-> *edx 3/r32/ebx  # Stmt-var-value
4613     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
4614     # . epilogue
4615     89/<- %esp 5/r32/ebp
4616     5d/pop-to-ebp
4617     c3/return
4618 
4619 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)
4620     # . prologue
4621     55/push-ebp
4622     89/<- %ebp 4/r32/esp
4623     # . save registers
4624     51/push-ecx
4625     #
4626     (allocate *(ebp+8) *Function-size)  # => eax
4627     8b/-> *(ebp+0xc) 1/r32/ecx
4628     89/<- *eax 1/r32/ecx  # Function-name
4629     8b/-> *(ebp+0x10) 1/r32/ecx
4630     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
4631     8b/-> *(ebp+0x14) 1/r32/ecx
4632     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
4633     8b/-> *(ebp+0x18) 1/r32/ecx
4634     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
4635     8b/-> *(ebp+0x1c) 1/r32/ecx
4636     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
4637     8b/-> *(ebp+0x20) 1/r32/ecx
4638     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
4639 $new-function:end:
4640     # . restore registers
4641     59/pop-to-ecx
4642     # . epilogue
4643     89/<- %esp 5/r32/ebp
4644     5d/pop-to-ebp
4645     c3/return
4646 
4647 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte), type: (addr tree type-id), block: int, offset: int, register: (addr array byte) -> result/eax: (handle var)
4648     # . prologue
4649     55/push-ebp
4650     89/<- %ebp 4/r32/esp
4651     # . save registers
4652     51/push-ecx
4653     #
4654     (allocate *(ebp+8) *Var-size)  # => eax
4655     8b/-> *(ebp+0xc) 1/r32/ecx
4656     89/<- *eax 1/r32/ecx  # Var-name
4657     8b/-> *(ebp+0x10) 1/r32/ecx
4658     89/<- *(eax+4) 1/r32/ecx  # Var-type
4659     8b/-> *(ebp+0x14) 1/r32/ecx
4660     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
4661     8b/-> *(ebp+0x18) 1/r32/ecx
4662     89/<- *(eax+0xc) 1/r32/ecx  # Var-offset
4663     8b/-> *(ebp+0x1c) 1/r32/ecx
4664     89/<- *(eax+0x10) 1/r32/ecx  # Var-register
4665 $new-var:end:
4666     # . restore registers
4667     59/pop-to-ecx
4668     # . epilogue
4669     89/<- %esp 5/r32/ebp
4670     5d/pop-to-ebp
4671     c3/return
4672 
4673 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4674     # . prologue
4675     55/push-ebp
4676     89/<- %ebp 4/r32/esp
4677     # . save registers
4678     51/push-ecx
4679     # if (!is-hex-int?(name)) abort
4680     (is-hex-int? *(ebp+0xc))  # => eax
4681     3d/compare-eax-and 0/imm32/false
4682     0f 84/jump-if-= $new-literal-integer:abort/disp32
4683     # var s/ecx: (addr array byte)
4684     (slice-to-string Heap *(ebp+0xc))  # => eax
4685     89/<- %ecx 0/r32/eax
4686     # result/ecx = new var(s)
4687     (allocate *(ebp+8) *Var-size)  # => eax
4688     (zero-out %eax *Var-size)
4689     89/<- *eax 1/r32/ecx  # Var-name
4690     89/<- %ecx 0/r32/eax
4691     # result->type = new type()
4692     (allocate *(ebp+8) *Tree-size)  # => eax
4693     (zero-out %eax *Tree-size)  # default type is 'literal'
4694     89/<- *(ecx+4) 0/r32/eax  # Var-type
4695     # move result to eax
4696     89/<- %eax 1/r32/ecx
4697 $new-literal-integer:end:
4698     # . restore registers
4699     59/pop-to-ecx
4700     # . epilogue
4701     89/<- %esp 5/r32/ebp
4702     5d/pop-to-ebp
4703     c3/return
4704 
4705 $new-literal-integer:abort:
4706     (write-buffered Stderr "variable cannot begin with a digit '")
4707     (write-slice-buffered Stderr *(ebp+0xc))
4708     (write-buffered Stderr "'\n")
4709     (flush Stderr)
4710     # . syscall(exit, 1)
4711     bb/copy-to-ebx  1/imm32
4712     b8/copy-to-eax  1/imm32/exit
4713     cd/syscall  0x80/imm8
4714     # never gets here
4715 
4716 new-literal:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4717     # . prologue
4718     55/push-ebp
4719     89/<- %ebp 4/r32/esp
4720     # . save registers
4721     51/push-ecx
4722     # var s/ecx: (addr array byte)
4723     (slice-to-string Heap *(ebp+0xc))  # => eax
4724     89/<- %ecx 0/r32/eax
4725     # result->type = new type()
4726     (allocate *(ebp+8) *Tree-size)  # => eax
4727     (zero-out %eax *Tree-size)  # default type is 'literal'
4728     #
4729     (new-var *(ebp+8) %ecx %eax *Curr-block-depth 0 0)  # => eax
4730 $new-literal:end:
4731     # . restore registers
4732     59/pop-to-ecx
4733     # . epilogue
4734     89/<- %esp 5/r32/ebp
4735     5d/pop-to-ebp
4736     c3/return
4737 
4738 new-label:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
4739     # . prologue
4740     55/push-ebp
4741     89/<- %ebp 4/r32/esp
4742     # . save registers
4743     51/push-ecx
4744     # var s/ecx: (addr array byte)
4745     (slice-to-string Heap *(ebp+0xc))  # => eax
4746     89/<- %ecx 0/r32/eax
4747     #
4748     (allocate *(ebp+8) *Var-size)  # => eax
4749     89/<- *eax 1/r32/ecx  # Var-name
4750     89/<- %ecx 0/r32/eax
4751     (allocate *(ebp+8) *Tree-size)  # => eax
4752     (zero-out %eax *Tree-size)  # labels are literals
4753     89/<- *(ecx+4) 0/r32/eax  # Var-type
4754     89/<- %eax 1/r32/ecx
4755     c7 0/subop/copy *(eax+8) 0/imm32  # Var-block-depth
4756     c7 0/subop/copy *(eax+0xc) 0/imm32  # Var-offset
4757     c7 0/subop/copy *(eax+0x10) 0/imm32  # Var-register
4758 $new-label:end:
4759     # . restore registers
4760     59/pop-to-ecx
4761     # . epilogue
4762     89/<- %esp 5/r32/ebp
4763     5d/pop-to-ebp
4764     c3/return
4765 
4766 new-block:  # ad: (addr allocation-descriptor), data: (handle list stmt) -> result/eax: (handle stmt)
4767     # . prologue
4768     55/push-ebp
4769     89/<- %ebp 4/r32/esp
4770     # . save registers
4771     51/push-ecx
4772     #
4773     (allocate *(ebp+8) *Stmt-size)  # => eax
4774     (zero-out %eax *Stmt-size)
4775     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
4776     8b/-> *(ebp+0xc) 1/r32/ecx
4777     89/<- *(eax+4) 1/r32/ecx  # Block-stmts
4778 $new-block:end:
4779     # . restore registers
4780     59/pop-to-ecx
4781     # . epilogue
4782     89/<- %esp 5/r32/ebp
4783     5d/pop-to-ebp
4784     c3/return
4785 
4786 new-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
4787     # . prologue
4788     55/push-ebp
4789     89/<- %ebp 4/r32/esp
4790     # . save registers
4791     51/push-ecx
4792     #
4793     (allocate *(ebp+8) *Stmt-size)  # => eax
4794     (zero-out %eax *Stmt-size)
4795     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
4796     # result->var = var
4797     8b/-> *(ebp+0xc) 1/r32/ecx
4798     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
4799 $new-var-def:end:
4800     # . restore registers
4801     59/pop-to-ecx
4802     # . epilogue
4803     89/<- %esp 5/r32/ebp
4804     5d/pop-to-ebp
4805     c3/return
4806 
4807 new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
4808     # . prologue
4809     55/push-ebp
4810     89/<- %ebp 4/r32/esp
4811     # . save registers
4812     51/push-ecx
4813     57/push-edi
4814     # ecx = var
4815     8b/-> *(ebp+0xc) 1/r32/ecx
4816     # edi = result
4817     (allocate *(ebp+8) *Stmt-size)  # => eax
4818     89/<- %edi 0/r32/eax
4819     (zero-out %edi *Stmt-size)
4820     # set tag
4821     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
4822     # set output
4823     (append-stmt-var Heap %ecx *(edi+0xc) 0)  # Regvardef-outputs => eax
4824     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
4825 $new-reg-var-def:end:
4826     89/<- %eax 7/r32/edi
4827     # . restore registers
4828     5f/pop-to-edi
4829     59/pop-to-ecx
4830     # . epilogue
4831     89/<- %esp 5/r32/ebp
4832     5d/pop-to-ebp
4833     c3/return
4834 
4835 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
4836     # . prologue
4837     55/push-ebp
4838     89/<- %ebp 4/r32/esp
4839     # . save registers
4840     51/push-ecx
4841     #
4842     (allocate *(ebp+8) *List-size)  # => eax
4843     (zero-out %eax *List-size)
4844     8b/-> *(ebp+0xc) 1/r32/ecx
4845     89/<- *eax 1/r32/ecx  # List-value
4846     # if (list == null) return result
4847     81 7/subop/compare *(ebp+0x10) 0/imm32
4848     74/jump-if-= $append-list:end/disp8
4849     # otherwise append
4850     # var curr/ecx = list
4851     8b/-> *(ebp+0x10) 1/r32/ecx
4852     # while (curr->next != null) curr = curr->next
4853     {
4854       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
4855       74/jump-if-= break/disp8
4856       # curr = curr->next
4857       8b/-> *(ecx+4) 1/r32/ecx
4858       eb/jump loop/disp8
4859     }
4860     # curr->next = result
4861     89/<- *(ecx+4) 0/r32/eax
4862     # return list
4863     8b/-> *(ebp+0x10) 0/r32/eax
4864 $append-list:end:
4865     # . restore registers
4866     59/pop-to-ecx
4867     # . epilogue
4868     89/<- %esp 5/r32/ebp
4869     5d/pop-to-ebp
4870     c3/return
4871 
4872 append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
4873     # . prologue
4874     55/push-ebp
4875     89/<- %ebp 4/r32/esp
4876     # . save registers
4877     51/push-ecx
4878     #
4879     (allocate *(ebp+8) *Stmt-var-size)  # => eax
4880     (zero-out %eax *Stmt-var-size)
4881     8b/-> *(ebp+0xc) 1/r32/ecx
4882     89/<- *eax 1/r32/ecx  # Stmt-var-value
4883     8b/-> *(ebp+0x14) 1/r32/ecx
4884     89/<- *(eax+8) 1/r32/ecx  # Stmt-var-is-deref
4885     # if (list == null) return result
4886     81 7/subop/compare *(ebp+0x10) 0/imm32
4887     74/jump-if-= $append-stmt-var:end/disp8
4888     # otherwise append
4889     # var curr/ecx: (handle stmt-var) = vars
4890     8b/-> *(ebp+0x10) 1/r32/ecx
4891     # while (curr->next != null) curr = curr->next
4892     {
4893       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
4894       74/jump-if-= break/disp8
4895       # curr = curr->next
4896       8b/-> *(ecx+4) 1/r32/ecx
4897       eb/jump loop/disp8
4898     }
4899     # curr->next = result
4900     89/<- *(ecx+4) 0/r32/eax
4901     # return vars
4902     8b/-> *(ebp+0x10) 0/r32/eax
4903 $append-stmt-var:end:
4904     # . restore registers
4905     59/pop-to-ecx
4906     # . epilogue
4907     89/<- %esp 5/r32/ebp
4908     5d/pop-to-ebp
4909     c3/return
4910 
4911 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
4912     # . prologue
4913     55/push-ebp
4914     89/<- %ebp 4/r32/esp
4915     # . save registers
4916     56/push-esi
4917     # esi = block
4918     8b/-> *(ebp+0xc) 6/r32/esi
4919     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-stmts
4920     89/<- *(esi+4) 0/r32/eax  # Block-stmts
4921 $append-to-block:end:
4922     # . restore registers
4923     5e/pop-to-esi
4924     # . epilogue
4925     89/<- %esp 5/r32/ebp
4926     5d/pop-to-ebp
4927     c3/return
4928 
4929 ## Parsing types
4930 # We need to create metadata on user-defined types, and we need to use this
4931 # metadata as we parse instructions.
4932 # However, we also want to allow types to be used before their definitions.
4933 # This means we can't ever assume any type data structures exist.
4934 
4935 lookup-or-create-constant:  # container: (handle stmt-var), field-name: (addr slice) -> result/eax: (handle var)
4936     # . prologue
4937     55/push-ebp
4938     89/<- %ebp 4/r32/esp
4939     # . save registers
4940     56/push-esi
4941     # var container-type/esi: type-id
4942     (container-type *(ebp+8))  # => eax
4943 #?     (write-buffered Stderr "lookup-or-create-constant: container type-id: ")
4944 #?     (print-int32-buffered Stderr %eax)
4945 #?     (write-buffered Stderr Newline)
4946 #?     (flush Stderr)
4947     89/<- %esi 0/r32/eax
4948     # var typeinfo/eax: (addr typeinfo)
4949     (find-or-create-typeinfo %esi)  # => eax
4950 #?     (write-buffered Stderr "lookup-or-create-constant: typeinfo: ")
4951 #?     (print-int32-buffered Stderr %eax)
4952 #?     (write-buffered Stderr Newline)
4953 #?     (flush Stderr)
4954     # result = find-or-create-typeinfo-constant(typeinfo, field-name)
4955     (find-or-create-typeinfo-constant %eax *(ebp+0xc))  # => eax
4956 $lookup-or-create-constant:end:
4957     # . restore registers
4958     5e/pop-to-esi
4959     # . epilogue
4960     89/<- %esp 5/r32/ebp
4961     5d/pop-to-ebp
4962     c3/return
4963 
4964 # container->var->type->right->left->value
4965 container-type:  # container: (handle stmt-var) -> result/eax: type-id
4966     # . prologue
4967     55/push-ebp
4968     89/<- %ebp 4/r32/esp
4969     #
4970     8b/-> *(ebp+8) 0/r32/eax
4971     8b/-> *eax 0/r32/eax  # Stmt-var-value
4972     8b/-> *(eax+4) 0/r32/eax  # Var-type
4973     8b/-> *(eax+4) 0/r32/eax  # Tree-right
4974     8b/-> *eax 0/r32/eax  # Tree-left
4975     8b/-> *eax 0/r32/eax  # Atom-value
4976 $container-type:end:
4977     # . epilogue
4978     89/<- %esp 5/r32/ebp
4979     5d/pop-to-ebp
4980     c3/return
4981 
4982 find-or-create-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
4983     # . prologue
4984     55/push-ebp
4985     89/<- %ebp 4/r32/esp
4986     # . save registers
4987     51/push-ecx
4988     # eax = find-typeinfo(t)
4989     (find-typeinfo *(ebp+8))  # => eax
4990     {
4991       # if (curr != 0) break
4992       3d/compare-eax-and 0/imm32
4993       75/jump-if-!= break/disp8
4994 $find-or-create-typeinfo:create:
4995       (allocate Heap *Typeinfo-size)  # => eax
4996       (zero-out %eax *Typeinfo-size)
4997       # result->id = t
4998       8b/-> *(ebp+8) 1/r32/ecx
4999       89/<- *eax 1/r32/ecx  # Typeinfo-id
5000       # result->fields = new table
5001       50/push-eax
5002       (new-stream Heap 0x40 0xc)  # => eax
5003       89/<- %ecx 0/r32/eax
5004       58/pop-to-eax
5005       89/<- *(eax+4) 1/r32/ecx
5006       # result->next = Program->types
5007       8b/-> *_Program-types 1/r32/ecx
5008       89/<- *(eax+0xc) 1/r32/ecx  # Typeinfo-next
5009       # Program->types = result
5010       89/<- *_Program-types 0/r32/eax
5011     }
5012 $find-or-create-typeinfo:end:
5013     # . restore registers
5014     59/pop-to-ecx
5015     # . epilogue
5016     89/<- %esp 5/r32/ebp
5017     5d/pop-to-ebp
5018     c3/return
5019 
5020 find-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
5021     # . prologue
5022     55/push-ebp
5023     89/<- %ebp 4/r32/esp
5024     # . save registers
5025     51/push-ecx
5026     # ecx = t
5027     8b/-> *(ebp+8) 1/r32/ecx
5028     # var curr/eax: (handle typeinfo) = Program->types
5029     8b/-> *_Program-types 0/r32/eax
5030     {
5031       # if (curr == 0) break
5032       3d/compare-eax-and 0/imm32
5033       74/jump-if-= break/disp8
5034       # if (curr->id == t) return curr
5035       39/compare *eax 1/r32/ecx  # Typeinfo-id
5036       0f 84/jump-if-= $find-or-create-typeinfo:end/disp32
5037       # curr = curr->next
5038       8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-next
5039       #
5040       eb/jump loop/disp8
5041     }
5042 $find-typeinfo:end:
5043     # . restore registers
5044     59/pop-to-ecx
5045     # . epilogue
5046     89/<- %esp 5/r32/ebp
5047     5d/pop-to-ebp
5048     c3/return
5049 
5050 find-or-create-typeinfo-constant:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle var)
5051     # . prologue
5052     55/push-ebp
5053     89/<- %ebp 4/r32/esp
5054     # . save registers
5055     51/push-ecx
5056     56/push-esi
5057     # esi = T->fields
5058     8b/-> *(ebp+8) 6/r32/esi
5059     8b/-> *(esi+4) 6/r32/esi  # Typeinfo-fields
5060     # esi = get-or-insert(T->fields, f)
5061     (leaky-get-or-insert-slice %esi *(ebp+0xc) 0xc)  # => eax
5062     89/<- %esi 0/r32/eax
5063     # if output var exists, return it
5064     81 7/subop/compare *(esi+4) 0/imm32  # output var
5065     8b/-> *(esi+4) 0/r32/eax  # output var
5066     75/jump-if-!= $find-or-create-typeinfo-constant:end/disp8
5067     # var type/ecx: (handle tree type-id) = new var("dummy name", constant type, -1 offset)
5068     (allocate Heap *Tree-size)  # => eax
5069     c7 0/subop/copy *eax 6/imm32/constant  # Atom-value
5070     c7 0/subop/copy *(eax+4) 0/imm32  # Tree-right
5071     (new-var Heap "field" %eax 0 -1 0)  # => eax
5072     # offset (constant value) isn't filled out yet
5073     # save output var in row
5074     89/<- *(esi+4) 0/r32/eax
5075 $find-or-create-typeinfo-constant:end:
5076     # . restore registers
5077     5e/pop-to-esi
5078     59/pop-to-ecx
5079     # . epilogue
5080     89/<- %esp 5/r32/ebp
5081     5d/pop-to-ebp
5082     c3/return
5083 
5084 populate-mu-type:  # in: (addr stream byte), t: (handle typeinfo)
5085     # pseudocode:
5086     #   var line: (stream byte 512)
5087     #   curr-offset = 0
5088     #   while true
5089     #     clear-stream(line)
5090     #     read-line-buffered(in, line)
5091     #     if line->write == 0
5092     #       abort
5093     #     word-slice = next-mu-token(line)
5094     #     if slice-empty?(word-slice)               # end of line
5095     #       continue
5096     #     if slice-equal?(word-slice, "}")
5097     #       break
5098     #     var v: (handle var) = parse-var-with-type(word-slice, line)
5099     #     var r: (addr {(handle var) (handle var)}) = get-or-insert(t, v->name, row-size=12)
5100     #     TODO: ensure that r->first is null
5101     #     r->first = v
5102     #     if r->second == 0
5103     #       r->second = new var("dummy name", constant type, -1 offset)
5104     #     r->second->offset = curr-offset
5105     #     curr-offset += size-of(existing)
5106     #     TODO: ensure nothing else in line
5107     #
5108     # . prologue
5109     55/push-ebp
5110     89/<- %ebp 4/r32/esp
5111     # . save registers
5112     50/push-eax
5113     51/push-ecx
5114     52/push-edx
5115     53/push-ebx
5116     56/push-esi
5117     57/push-edi
5118     # edi = t
5119     8b/-> *(ebp+0xc) 7/r32/edi
5120     # var line/ecx: (stream byte 512)
5121     81 5/subop/subtract %esp 0x200/imm32
5122     68/push 0x200/imm32/length
5123     68/push 0/imm32/read
5124     68/push 0/imm32/write
5125     89/<- %ecx 4/r32/esp
5126     # var word-slice/edx: slice
5127     68/push 0/imm32/end
5128     68/push 0/imm32/start
5129     89/<- %edx 4/r32/esp
5130     # var curr-offset/ebx: int = 0
5131     bb/copy-to-ebx 0/imm32
5132     {
5133 $populate-mu-type:line-loop:
5134       (clear-stream %ecx)
5135       (read-line-buffered *(ebp+8) %ecx)
5136       # if (line->write == 0) abort
5137       81 7/subop/compare *ecx 0/imm32
5138       0f 84/jump-if-= $populate-mu-type:abort/disp32
5139 +--  6 lines: #?       # dump line ------------------------------------------------------------------------------------------------------------------------------------------------------
5145       (next-mu-token %ecx %edx)
5146       # if slice-empty?(word-slice) continue
5147       (slice-empty? %edx)
5148       3d/compare-eax-and 0/imm32
5149       0f 85/jump-if-!= loop/disp32
5150       # if slice-equal?(word-slice, "}") break
5151       (slice-equal? %edx "}")
5152       3d/compare-eax-and 0/imm32
5153       0f 85/jump-if-!= break/disp32
5154       # var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
5155       (parse-var-with-type %edx %ecx)  # => eax
5156 #?       (write-buffered Stderr "populate-mu-type: ")
5157 #?       (write-buffered Stderr *eax)  # Var-name
5158 #?       (write-buffered Stderr " at ")
5159 #?       (print-int32-buffered Stderr %eax)
5160 #?       (write-buffered Stderr Newline)
5161 #?       (flush Stderr)
5162       89/<- %esi 0/r32/eax
5163       # var r/eax: (addr {(handle var) (handle var)})
5164 #?       (write-buffered Stderr "populate-mu-type: typeinfo: ")
5165 #?       (print-int32-buffered Stderr %edi)
5166 #?       (write-buffered Stderr Newline)
5167 #?       (flush Stderr)
5168       (get-or-insert *(edi+4) *esi 0xc)  # Typeinfo-fields Var-name => eax
5169       # r->first = v
5170       89/<- *eax 6/r32/esi
5171       # if (r->second == 0) create a new var with some placeholder data
5172       {
5173         81 7/subop/compare *(eax+4) 0/imm32
5174         75/jump-if-!= break/disp8
5175         # temporarily spill r to esi
5176         89/<- %esi 0/r32/eax
5177         (new-literal Heap %edx)  # => eax
5178         89/<- *(esi+4) 0/r32/eax
5179         89/<- %eax 6/r32/esi
5180 #?         (write-buffered Stderr "creating new output var ")
5181 #?         (print-int32-buffered Stderr %eax)
5182 #?         (write-buffered Stderr Newline)
5183 #?         (flush Stderr)
5184       }
5185       # r->second->offset = curr-offset
5186       8b/-> *(eax+4) 0/r32/eax
5187 #?       (write-buffered Stderr "writing offset ")
5188 #?       (print-int32-buffered Stderr %ebx)
5189 #?       (write-buffered Stderr " to output var ")
5190 #?       (print-int32-buffered Stderr %eax)
5191 #?       (write-buffered Stderr Newline)
5192 #?       (flush Stderr)
5193       89/<- *(eax+0xc) 3/r32/ebx
5194       # curr-offset += size-of(v)
5195 #?       (write-buffered Stderr "elem ")
5196 #?       (write-buffered Stderr *eax)  # Var-name
5197       50/push-eax
5198       (size-of %esi)  # => eax
5199 #?       (write-buffered Stderr ": ")
5200 #?       (print-int32-buffered Stderr %eax)  # Var-name
5201 #?       (write-buffered Stderr Newline)
5202 #?       (flush Stderr)
5203       01/add-to %ebx 0/r32/eax
5204       58/pop-to-eax
5205       #
5206       e9/jump loop/disp32
5207     }
5208     # persist the total size of the type
5209     89/<- *(edi+8) 3/r32/ebx  # Typeinfo-total-size-in-bytes
5210 $populate-mu-type:end:
5211     # . reclaim locals
5212     81 0/subop/add %esp 0x214/imm32
5213     # . restore registers
5214     5f/pop-to-edi
5215     5e/pop-to-esi
5216     5b/pop-to-ebx
5217     5a/pop-to-edx
5218     59/pop-to-ecx
5219     58/pop-to-eax
5220     # . epilogue
5221     89/<- %esp 5/r32/ebp
5222     5d/pop-to-ebp
5223     c3/return
5224 
5225 $populate-mu-type:abort:
5226     # error("unexpected top-level command: " word-slice "\n")
5227     (write-buffered Stderr "incomplete type definition '")
5228     (type-name *edi)  # Typeinfo-id => eax
5229     (write-buffered Stderr %eax)
5230     (write-buffered Stderr "\n")
5231     (flush Stderr)
5232     # . syscall(exit, 1)
5233     bb/copy-to-ebx  1/imm32
5234     b8/copy-to-eax  1/imm32/exit
5235     cd/syscall  0x80/imm8
5236     # never gets here
5237 
5238 type-name:  # index: int -> result/eax: (addr array byte)
5239     # . prologue
5240     55/push-ebp
5241     89/<- %ebp 4/r32/esp
5242     #
5243     (index Type-id *(ebp+8))
5244 $type-name:end:
5245     # . epilogue
5246     89/<- %esp 5/r32/ebp
5247     5d/pop-to-ebp
5248     c3/return
5249 
5250 index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
5251     # . prologue
5252     55/push-ebp
5253     89/<- %ebp 4/r32/esp
5254     # . save registers
5255     56/push-esi
5256     # TODO: bounds-check index
5257     # esi = arr
5258     8b/-> *(ebp+8) 6/r32/esi
5259     # eax = index
5260     8b/-> *(ebp+0xc) 0/r32/eax
5261     # eax = *(arr + 12 + index)
5262     8b/-> *(esi+eax+0xc) 0/r32/eax
5263 $index:end:
5264     # . restore registers
5265     5e/pop-to-esi
5266     # . epilogue
5267     89/<- %esp 5/r32/ebp
5268     5d/pop-to-ebp
5269     c3/return
5270 
5271 #######################################################
5272 # Type-checking
5273 #######################################################
5274 
5275 check-mu-types:
5276     # . prologue
5277     55/push-ebp
5278     89/<- %ebp 4/r32/esp
5279     #
5280 $check-mu-types:end:
5281     # . epilogue
5282     89/<- %esp 5/r32/ebp
5283     5d/pop-to-ebp
5284     c3/return
5285 
5286 size-of:  # v: (handle var) -> result/eax: int
5287     # . prologue
5288     55/push-ebp
5289     89/<- %ebp 4/r32/esp
5290     # . save registers
5291     51/push-ecx
5292     # ecx = v->type
5293     8b/-> *(ebp+8) 1/r32/ecx
5294     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
5295     # if v is a literal, return 0
5296     81 7/subop/compare *ecx 0/imm32  # Tree-left
5297     b8/copy-to-eax 0/imm32
5298     74/jump-if-= $size-of:end/disp8
5299     # if v has a user-defined type, return its size
5300     # TODO: support non-atom type
5301     (find-typeinfo *ecx)  # => eax
5302     {
5303       3d/compare-eax-and 0/imm32
5304       74/jump-if-= break/disp8
5305       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
5306       eb/jump $size-of:end/disp8
5307     }
5308     # otherwise return the word size
5309     b8/copy-to-eax 4/imm32
5310 $size-of:end:
5311     # . restore registers
5312     59/pop-to-ecx
5313     # . epilogue
5314     89/<- %esp 5/r32/ebp
5315     5d/pop-to-ebp
5316     c3/return
5317 
5318 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
5319     # . prologue
5320     55/push-ebp
5321     89/<- %ebp 4/r32/esp
5322     # . save registers
5323     51/push-ecx
5324     52/push-edx
5325     # ecx = a
5326     8b/-> *(ebp+8) 1/r32/ecx
5327     # edx = b
5328     8b/-> *(ebp+0xc) 2/r32/edx
5329     # if (a == b) return true
5330     8b/-> %ecx 0/r32/eax  # Var-type
5331     39/compare %edx 0/r32/eax  # Var-type
5332     b8/copy-to-eax 1/imm32/true
5333     74/jump-if-= $type-equal?:end/disp8
5334     # if (a < MAX_TYPE_ID) return false
5335     81 7/subop/compare %ecx 0x10000/imm32
5336     b8/copy-to-eax 0/imm32/false
5337     72/jump-if-addr< $type-equal?:end/disp8
5338     # if (b < MAX_TYPE_ID) return false
5339     81 7/subop/compare %edx 0x10000/imm32
5340     b8/copy-to-eax 0/imm32/false
5341     72/jump-if-addr< $type-equal?:end/disp8
5342     # if (!type-equal?(a->left, b->left)) return false
5343     (type-equal? *ecx *edx)  # Tree-left, Tree-left => eax
5344     3d/compare-eax-and 0/imm32/false
5345     74/jump-if-= $type-equal?:end/disp8
5346     # return type-equal?(a->right, b->right)
5347     (type-equal? *(ecx+4) *(edx+4))  # Tree-right, Tree-right => eax
5348 $type-equal?:end:
5349     # . restore registers
5350     5a/pop-to-edx
5351     59/pop-to-ecx
5352     # . epilogue
5353     89/<- %esp 5/r32/ebp
5354     5d/pop-to-ebp
5355     c3/return
5356 
5357 == data
5358 
5359 # not yet used, but it will be
5360 Type-size:  # (stream int)
5361   0x18/imm32/write
5362   0/imm32/read
5363   0x100/imm32/length
5364   # data
5365   4/imm32  # literal
5366   4/imm32  # int
5367   4/imm32  # addr
5368   0/imm32  # array (logic elsewhere)
5369   8/imm32  # handle (fat pointer)
5370   4/imm32  # boolean
5371   0/imm32
5372   0/imm32
5373   # 0x20
5374   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5375   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5376   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5377   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5378   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5379   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5380   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
5381 
5382 == code
5383 
5384 #######################################################
5385 # Code-generation
5386 #######################################################
5387 
5388 emit-subx:  # out: (addr buffered-file)
5389     # . prologue
5390     55/push-ebp
5391     89/<- %ebp 4/r32/esp
5392     # . save registers
5393     50/push-eax
5394     51/push-ecx
5395     57/push-edi
5396     # edi = out
5397     8b/-> *(ebp+8) 7/r32/edi
5398     # var curr/ecx: (handle function) = *Program->functions
5399     8b/-> *_Program-functions 1/r32/ecx
5400     {
5401       # if (curr == null) break
5402       81 7/subop/compare %ecx 0/imm32
5403       0f 84/jump-if-= break/disp32
5404       (emit-subx-function %edi %ecx)
5405       # curr = curr->next
5406       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
5407       e9/jump loop/disp32
5408     }
5409 $emit-subx:end:
5410     # . restore registers
5411     5f/pop-to-edi
5412     59/pop-to-ecx
5413     58/pop-to-eax
5414     # . epilogue
5415     89/<- %esp 5/r32/ebp
5416     5d/pop-to-ebp
5417     c3/return
5418 
5419 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
5420     # . prologue
5421     55/push-ebp
5422     89/<- %ebp 4/r32/esp
5423     # . save registers
5424     50/push-eax
5425     51/push-ecx
5426     52/push-edx
5427     57/push-edi
5428     # edi = out
5429     8b/-> *(ebp+8) 7/r32/edi
5430     # ecx = f
5431     8b/-> *(ebp+0xc) 1/r32/ecx
5432     # var vars/edx: (stack (addr var) 256)
5433     81 5/subop/subtract %esp 0x400/imm32
5434     68/push 0x400/imm32/length
5435     68/push 0/imm32/top
5436     89/<- %edx 4/r32/esp
5437     #
5438     (write-buffered %edi *ecx)
5439     (write-buffered %edi ":\n")
5440     # Important: each block's depth during code-generation should be identical
5441     # to what it was during parsing.
5442     c7 0/subop/copy *Curr-block-depth 1/imm32
5443     (emit-subx-prologue %edi)
5444     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
5445     (emit-subx-epilogue %edi)
5446 $emit-subx-function:end:
5447     # . reclaim locals
5448     81 0/subop/add %esp 408/imm32
5449     # . restore registers
5450     5f/pop-to-edi
5451     5a/pop-to-edx
5452     59/pop-to-ecx
5453     58/pop-to-eax
5454     # . epilogue
5455     89/<- %esp 5/r32/ebp
5456     5d/pop-to-ebp
5457     c3/return
5458 
5459 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
5460     # . prologue
5461     55/push-ebp
5462     89/<- %ebp 4/r32/esp
5463     # . save registers
5464     50/push-eax
5465     51/push-ecx
5466     52/push-edx
5467     53/push-ebx
5468     56/push-esi
5469     # esi = stmts
5470     8b/-> *(ebp+0xc) 6/r32/esi
5471     # var var-seen?/edx: boolean <- copy false
5472     ba/copy-to-edx 0/imm32/false
5473     #
5474     {
5475 $emit-subx-stmt-list:loop:
5476       81 7/subop/compare %esi 0/imm32
5477       0f 84/jump-if-= break/disp32
5478       # var curr-stmt/ecx = stmts->value
5479       8b/-> *esi 1/r32/ecx  # List-value
5480       {
5481 $emit-subx-stmt-list:check-for-block:
5482         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
5483         75/jump-if-!= break/disp8
5484 $emit-subx-stmt-list:block:
5485         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
5486       }
5487       {
5488 $emit-subx-stmt-list:check-for-stmt:
5489         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
5490         0f 85/jump-if-!= break/disp32
5491 $emit-subx-stmt-list:stmt1:
5492         {
5493           (is-mu-branch? %ecx)  # => eax
5494           3d/compare-eax-and 0/imm32/false
5495           0f 84/jump-if-= break/disp32
5496 $emit-subx-stmt-list:branch-stmt:
5497           # if !var-seen? break
5498           81 7/subop/compare %edx 0/imm32/false
5499           0f 84/jump-if-= break/disp32
5500 $emit-subx-stmt-list:branch-stmt-and-var-seen:
5501 +-- 26 lines: # unconditional loops -----------------------------------------------------------------------------------------------------------------------------------------------------
5527 +-- 15 lines: # unconditional breaks ----------------------------------------------------------------------------------------------------------------------------------------------------
5542 +-- 37 lines: # simple conditional branches without a target ----------------------------------------------------------------------------------------------------------------------------
5579 +-- 19 lines: # conditional branches with an explicit target ----------------------------------------------------------------------------------------------------------------------------
5598         }
5599 $emit-subx-stmt-list:1-to-1:
5600         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
5601       }
5602       {
5603 $emit-subx-stmt-list:check-for-var-def:
5604         81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
5605         75/jump-if-!= break/disp8
5606 $emit-subx-stmt-list:var-def:
5607         (emit-subx-var-def *(ebp+8) %ecx)
5608         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
5609         # var-seen? = true
5610         ba/copy-to-edx 1/imm32/true
5611       }
5612       {
5613 $emit-subx-stmt-list:check-for-reg-var-def:
5614         81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
5615         0f 85/jump-if-!= break/disp32
5616 $emit-subx-stmt-list:reg-var-def:
5617         # TODO: ensure that there's exactly one output
5618         # var output/eax: (handle var) = curr-stmt->outputs->value
5619         8b/-> *(ecx+0xc) 0/r32/eax
5620         8b/-> *eax 0/r32/eax
5621         # ensure that output is in a register
5622         81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
5623         0f 84/jump-if-= $emit-subx-stmt-list:abort-reg-var-def-without-register/disp32
5624         # emit spill
5625         (emit-indent *(ebp+8) *Curr-block-depth)
5626         (write-buffered *(ebp+8) "ff 6/subop/push %")
5627         (write-buffered *(ebp+8) *(eax+0x10))
5628         (write-buffered *(ebp+8) Newline)
5629         # register variable definition
5630         (push *(ebp+0x10) %eax)
5631         # emit the instruction as usual
5632         (emit-subx-stmt *(ebp+8) %ecx Primitives *_Program-functions)
5633         # var-seen? = true
5634         ba/copy-to-edx 1/imm32/true
5635       }
5636 $emit-subx-stmt-list:continue:
5637       # TODO: raise an error on unrecognized Stmt-tag
5638       8b/-> *(esi+4) 6/r32/esi  # List-next
5639       e9/jump loop/disp32
5640     }
5641 $emit-subx-stmt-list:emit-cleanup:
5642     (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
5643 $emit-subx-stmt-list:cleanup:
5644     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
5645 $emit-subx-stmt-list:end:
5646     # . restore registers
5647     5e/pop-to-esi
5648     5b/pop-to-ebx
5649     5a/pop-to-edx
5650     59/pop-to-ecx
5651     58/pop-to-eax
5652     # . epilogue
5653     89/<- %esp 5/r32/ebp
5654     5d/pop-to-ebp
5655     c3/return
5656 
5657 $emit-subx-stmt-list:abort-reg-var-def-without-register:
5658     # error("var '" var->name "' initialized from an instruction must live in a register\n")
5659     (write-buffered Stderr "var '")
5660     (write-buffered Stderr *eax)  # Var-name
5661     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
5662     (flush Stderr)
5663     # . syscall(exit, 1)
5664     bb/copy-to-ebx  1/imm32
5665     b8/copy-to-eax  1/imm32/exit
5666     cd/syscall  0x80/imm8
5667     # never gets here
5668 
5669 emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
5670     # . prologue
5671     55/push-ebp
5672     89/<- %ebp 4/r32/esp
5673     # . save registers
5674     50/push-eax
5675     51/push-ecx
5676     52/push-edx
5677     # ecx = stmt
5678     8b/-> *(ebp+0xc) 1/r32/ecx
5679     # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
5680     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
5681     8b/-> *edx 2/r32/edx  # Stmt-var-value
5682     8b/-> *edx 2/r32/edx  # Var-name
5683     # clean up until target block
5684     (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
5685     # emit jump to target block
5686     (emit-indent *(ebp+8) *Curr-block-depth)
5687     (write-buffered *(ebp+8) "e9/jump ")
5688     (write-buffered *(ebp+8) %edx)
5689     (string-starts-with? *(ecx+4) "break")
5690     3d/compare-eax-and 0/imm32/false
5691     {
5692       74/jump-if-= break/disp8
5693       (write-buffered *(ebp+8) ":break/disp32\n")
5694     }
5695     3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
5696     {
5697       75/jump-if-!= break/disp8
5698       (write-buffered *(ebp+8) ":loop/disp32\n")
5699     }
5700 $emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
5701     # . restore registers
5702     5a/pop-to-edx
5703     59/pop-to-ecx
5704     58/pop-to-eax
5705     # . epilogue
5706     89/<- %esp 5/r32/ebp
5707     5d/pop-to-ebp
5708     c3/return
5709 
5710 is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
5711     # . prologue
5712     55/push-ebp
5713     89/<- %ebp 4/r32/esp
5714     # . save registers
5715     51/push-ecx
5716     # ecx = stmt
5717     8b/-> *(ebp+8) 1/r32/ecx
5718     # if (stmt->operation starts with "loop") return true
5719     (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
5720     3d/compare-eax-and 0/imm32/false
5721     75/jump-if-not-equal $is-mu-branch?:end/disp8
5722     # otherwise return (stmt->operation starts with "break")
5723     (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
5724 $is-mu-branch?:end:
5725     # . restore registers
5726     59/pop-to-ecx
5727     # . epilogue
5728     89/<- %esp 5/r32/ebp
5729     5d/pop-to-ebp
5730     c3/return
5731 
5732 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
5733     # . prologue
5734     55/push-ebp
5735     89/<- %ebp 4/r32/esp
5736     # . save registers
5737     50/push-eax
5738     # eax = stmt
5739     8b/-> *(ebp+0xc) 0/r32/eax
5740     #
5741     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
5742     (emit-indent *(ebp+8) *Curr-block-depth)
5743     (write-buffered *(ebp+8) *eax)
5744     (write-buffered *(ebp+8) " break/disp32\n")
5745 $emit-reverse-break:end:
5746     # . restore registers
5747     58/pop-to-eax
5748     # . epilogue
5749     89/<- %esp 5/r32/ebp
5750     5d/pop-to-ebp
5751     c3/return
5752 
5753 == data
5754 
5755 Reverse-branch:  # (table string string)
5756   # a table is a stream
5757   0xa0/imm32/write
5758   0/imm32/read
5759   0xa0/imm32/length
5760   # data
5761   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
5762   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
5763   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
5764   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
5765   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
5766   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
5767   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
5768   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
5769   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
5770   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
5771   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
5772   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
5773   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
5774   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
5775   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
5776   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
5777   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
5778   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
5779   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
5780   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
5781 
5782 == code
5783 
5784 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
5785     # . prologue
5786     55/push-ebp
5787     89/<- %ebp 4/r32/esp
5788     # . save registers
5789     50/push-eax
5790     51/push-ecx
5791     52/push-edx
5792     53/push-ebx
5793     # ecx = vars
5794     8b/-> *(ebp+0xc) 1/r32/ecx
5795     # var eax: int = vars->top
5796     8b/-> *ecx 0/r32/eax
5797     # var min/ecx: (address (handle var)) = vars->data
5798     81 0/subop/add %ecx 8/imm32
5799     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
5800     81 5/subop/subtract %eax 4/imm32
5801     8d/copy-address *(ecx+eax) 0/r32/eax
5802     # edx = depth
5803     8b/-> *(ebp+0x10) 2/r32/edx
5804     {
5805 $emit-unconditional-jump-to-depth:loop:
5806       # if (curr < min) break
5807       39/compare %eax 1/r32/ecx
5808       0f 82/jump-if-addr< break/disp32
5809       # var v/ebx: (handle var) = *curr
5810       8b/-> *eax 3/r32/ebx
5811       # if (v->block-depth < until-block-depth) break
5812       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
5813       0f 8c/jump-if-< break/disp32
5814       {
5815 $emit-unconditional-jump-to-depth:check:
5816         # if v->block-depth != until-block-depth, continue
5817         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
5818         0f 85/jump-if-!= break/disp32
5819 $emit-unconditional-jump-to-depth:depth-found:
5820         # if v is not a literal, continue
5821         # . var eax: int = size-of(v)
5822         50/push-eax
5823         (size-of %ebx)  # => eax
5824         # . if (eax != 0) continue
5825         3d/compare-eax-and 0/imm32
5826         58/pop-to-eax
5827         #
5828         0f 85/jump-if-!= break/disp32
5829 $emit-unconditional-jump-to-depth:label-found:
5830         # emit unconditional jump, then return
5831         (emit-indent *(ebp+8) *Curr-block-depth)
5832         (write-buffered *(ebp+8) "e9/jump ")
5833         (write-buffered *(ebp+8) *ebx)  # Var-name
5834         (write-buffered *(ebp+8) ":")
5835         (write-buffered *(ebp+8) *(ebp+0x14))
5836         (write-buffered *(ebp+8) "/disp32\n")
5837         eb/jump $emit-unconditional-jump-to-depth:end/disp8
5838       }
5839       # curr -= 4
5840       2d/subtract-from-eax 4/imm32
5841       e9/jump loop/disp32
5842     }
5843     # TODO: error if no label at 'depth' was found
5844 $emit-unconditional-jump-to-depth:end:
5845     # . restore registers
5846     5b/pop-to-ebx
5847     5a/pop-to-edx
5848     59/pop-to-ecx
5849     58/pop-to-eax
5850     # . epilogue
5851     89/<- %esp 5/r32/ebp
5852     5d/pop-to-ebp
5853     c3/return
5854 
5855 # emit clean-up code for 'vars' until some block depth
5856 # doesn't actually modify 'vars' so we need traverse manually inside the stack
5857 emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
5858     # . prologue
5859     55/push-ebp
5860     89/<- %ebp 4/r32/esp
5861     # . save registers
5862     50/push-eax
5863     51/push-ecx
5864     52/push-edx
5865     53/push-ebx
5866     # ecx = vars
5867     8b/-> *(ebp+0xc) 1/r32/ecx
5868     # var eax: int = vars->top
5869     8b/-> *ecx 0/r32/eax
5870     # var min/ecx: (address (handle var)) = vars->data
5871     81 0/subop/add %ecx 8/imm32
5872     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
5873     81 5/subop/subtract %eax 4/imm32
5874     8d/copy-address *(ecx+eax) 0/r32/eax
5875     # edx = until-block-depth
5876     8b/-> *(ebp+0x10) 2/r32/edx
5877     {
5878 $emit-cleanup-code-until-depth:loop:
5879       # if (curr < min) break
5880       39/compare %eax 1/r32/ecx
5881       0f 82/jump-if-addr< break/disp32
5882       # var v/ebx: (handle var) = *curr
5883       8b/-> *eax 3/r32/ebx
5884       # if (v->block-depth < until-block-depth) break
5885       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
5886       0f 8c/jump-if-< break/disp32
5887       # if v is in a register
5888       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
5889       {
5890         74/jump-if-= break/disp8
5891 $emit-cleanup-code-until-depth:reclaim-var-in-register:
5892         (emit-indent *(ebp+8) *Curr-block-depth)
5893         (write-buffered *(ebp+8) "8f 0/subop/pop %")
5894         (write-buffered *(ebp+8) *(ebx+0x10))
5895         (write-buffered *(ebp+8) Newline)
5896       }
5897       # otherwise v is on the stack
5898       {
5899         75/jump-if-!= break/disp8
5900 $emit-cleanup-code-until-depth:reclaim-var-on-stack:
5901         50/push-eax
5902         (size-of %ebx)  # => eax
5903         # don't emit code for labels
5904         3d/compare-eax-and 0/imm32
5905         74/jump-if-= break/disp8
5906         #
5907         (emit-indent *(ebp+8) *Curr-block-depth)
5908         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
5909         (print-int32-buffered *(ebp+8) %eax)
5910         (write-buffered *(ebp+8) "/imm32\n")
5911         58/pop-to-eax
5912       }
5913       # curr -= 4
5914       2d/subtract-from-eax 4/imm32
5915       e9/jump loop/disp32
5916     }
5917 $emit-cleanup-code-until-depth:end:
5918     # . restore registers
5919     5b/pop-to-ebx
5920     5a/pop-to-edx
5921     59/pop-to-ecx
5922     58/pop-to-eax
5923     # . epilogue
5924     89/<- %esp 5/r32/ebp
5925     5d/pop-to-ebp
5926     c3/return
5927 
5928 # emit clean-up code for 'vars' until a given label is encountered
5929 # doesn't actually modify 'vars' so we need traverse manually inside the stack
5930 emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
5931     # . prologue
5932     55/push-ebp
5933     89/<- %ebp 4/r32/esp
5934     # . save registers
5935     50/push-eax
5936     51/push-ecx
5937     52/push-edx
5938     53/push-ebx
5939     # ecx = vars
5940     8b/-> *(ebp+0xc) 1/r32/ecx
5941     # var eax: int = vars->top
5942     8b/-> *ecx 0/r32/eax
5943     # var min/ecx: (address (handle var)) = vars->data
5944     81 0/subop/add %ecx 8/imm32
5945     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
5946     81 5/subop/subtract %eax 4/imm32
5947     8d/copy-address *(ecx+eax) 2/r32/edx
5948     {
5949 $emit-cleanup-code-until-target:loop:
5950       # if (curr < min) break
5951       39/compare %edx 1/r32/ecx
5952       0f 82/jump-if-addr< break/disp32
5953       # var v/ebx: (handle var) = *curr
5954       8b/-> *edx 3/r32/ebx
5955       # if (v->name == until-block-label) break
5956       (string-equal? *ebx *(ebp+0x10))  # => eax
5957       3d/compare-eax-and 0/imm32/false
5958       0f 85/jump-if-!= break/disp32
5959       # if v is in a register
5960       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
5961       {
5962         74/jump-if-= break/disp8
5963 $emit-cleanup-code-until-target:reclaim-var-in-register:
5964         (emit-indent *(ebp+8) *Curr-block-depth)
5965         (write-buffered *(ebp+8) "8f 0/subop/pop %")
5966         (write-buffered *(ebp+8) *(ebx+0x10))
5967         (write-buffered *(ebp+8) Newline)
5968       }
5969       # otherwise v is on the stack
5970       {
5971         75/jump-if-!= break/disp8
5972 $emit-cleanup-code-until-target:reclaim-var-on-stack:
5973         (size-of %ebx)  # => eax
5974         # don't emit code for labels
5975         3d/compare-eax-and 0/imm32
5976         74/jump-if-= break/disp8
5977         #
5978         (emit-indent *(ebp+8) *Curr-block-depth)
5979         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
5980         (print-int32-buffered *(ebp+8) %eax)
5981         (write-buffered *(ebp+8) "/imm32\n")
5982       }
5983       # curr -= 4
5984       81 5/subop/subtract %edx 4/imm32
5985       e9/jump loop/disp32
5986     }
5987 $emit-cleanup-code-until-target:end:
5988     # . restore registers
5989     5b/pop-to-ebx
5990     5a/pop-to-edx
5991     59/pop-to-ecx
5992     58/pop-to-eax
5993     # . epilogue
5994     89/<- %esp 5/r32/ebp
5995     5d/pop-to-ebp
5996     c3/return
5997 
5998 # clean up global state for 'vars' until some block depth
5999 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
6000     # . prologue
6001     55/push-ebp
6002     89/<- %ebp 4/r32/esp
6003     # . save registers
6004     50/push-eax
6005     51/push-ecx
6006     56/push-esi
6007     # esi = vars
6008     8b/-> *(ebp+8) 6/r32/esi
6009     # ecx = until-block-depth
6010     8b/-> *(ebp+0xc) 1/r32/ecx
6011     {
6012 $clean-up-blocks:reclaim-loop:
6013       # if (vars->top <= 0) break
6014       81 7/subop/compare *esi 0/imm32  # Stack-top
6015       7e/jump-if-<= break/disp8
6016       # var v/eax: (handle var) = top(vars)
6017       (top %esi)  # => eax
6018       # if (v->block-depth < until-block-depth) break
6019       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
6020       7c/jump-if-< break/disp8
6021       # if v is on the stack, update Next-local-stack-offset
6022       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
6023       {
6024         75/jump-if-!= break/disp8
6025 $clean-up-blocks:reclaim-var-on-stack:
6026         (size-of %eax)  # => eax
6027         01/add *Next-local-stack-offset 0/r32/eax
6028       }
6029       (pop %esi)
6030       e9/jump loop/disp32
6031     }
6032 $clean-up-blocks:end:
6033     # . restore registers
6034     5e/pop-to-esi
6035     59/pop-to-ecx
6036     58/pop-to-eax
6037     # . epilogue
6038     89/<- %esp 5/r32/ebp
6039     5d/pop-to-ebp
6040     c3/return
6041 
6042 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle stmt)
6043     # . prologue
6044     55/push-ebp
6045     89/<- %ebp 4/r32/esp
6046     # . save registers
6047     50/push-eax
6048     51/push-ecx
6049     # eax = stmt
6050     8b/-> *(ebp+0xc) 0/r32/eax
6051     # var n/eax: int = size-of(stmt->var)
6052     (size-of *(eax+4))  # Vardef-var => eax
6053     # while n > 0
6054     {
6055       3d/compare-eax-with 0/imm32
6056       7e/jump-if-<= break/disp8
6057       (emit-indent *(ebp+8) *Curr-block-depth)
6058       (write-buffered *(ebp+8) "68/push 0/imm32\n")
6059       # n -= 4
6060       2d/subtract-from-eax 4/imm32
6061       #
6062       eb/jump loop/disp8
6063     }
6064 $emit-subx-var-def:end:
6065     # . restore registers
6066     59/pop-to-ecx
6067     58/pop-to-eax
6068     # . epilogue
6069     89/<- %esp 5/r32/ebp
6070     5d/pop-to-ebp
6071     c3/return
6072 
6073 emit-subx-stmt:  # out: (addr buffered-file), stmt: (handle stmt), primitives: (handle primitive), functions: (handle function)
6074     # . prologue
6075     55/push-ebp
6076     89/<- %ebp 4/r32/esp
6077     # . save registers
6078     50/push-eax
6079     51/push-ecx
6080     # handle some special cases
6081     # ecx = stmt
6082     8b/-> *(ebp+0xc) 1/r32/ecx
6083 +-- 24 lines: # array length ------------------------------------------------------------------------------------------------------------------------------------------------------------
6107 +-- 33 lines: # index into array --------------------------------------------------------------------------------------------------------------------------------------------------------
6140 +-- 26 lines: # get field from record ---------------------------------------------------------------------------------------------------------------------------------------------------
6166     # if stmt matches a primitive, emit it
6167     {
6168 $emit-subx-stmt:check-for-primitive:
6169       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
6170       3d/compare-eax-and 0/imm32
6171       74/jump-if-= break/disp8
6172 $emit-subx-stmt:primitive:
6173       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
6174       e9/jump $emit-subx-stmt:end/disp32
6175     }
6176     # else if stmt matches a function, emit a call to it
6177     {
6178 $emit-subx-stmt:check-for-call:
6179       (find-matching-function *(ebp+0x14) *(ebp+0xc))  # functions, stmt => curr/eax
6180       3d/compare-eax-and 0/imm32
6181       74/jump-if-= break/disp8
6182 $emit-subx-stmt:call:
6183       (emit-subx-call *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
6184       e9/jump $emit-subx-stmt:end/disp32
6185     }
6186     # else assume it's a SubX function (TODO: how to type-check?!)
6187     (emit-hailmary-call *(ebp+8) *(ebp+0xc))
6188 $emit-subx-stmt:end:
6189     # . restore registers
6190     59/pop-to-ecx
6191     58/pop-to-eax
6192     # . epilogue
6193     89/<- %esp 5/r32/ebp
6194     5d/pop-to-ebp
6195     c3/return
6196 
6197 $emit-subx-stmt:abort:
6198     # error("couldn't translate '" stmt "'\n")
6199     (write-buffered Stderr "couldn't translate an instruction with operation '")
6200     8b/-> *(ebp+0xc) 0/r32/eax
6201     (write-buffered Stderr *(eax+4))  # Stmt1-operation
6202     (write-buffered Stderr "'\n")
6203     (flush Stderr)
6204     # . syscall(exit, 1)
6205     bb/copy-to-ebx  1/imm32
6206     b8/copy-to-eax  1/imm32/exit
6207     cd/syscall  0x80/imm8
6208     # never gets here
6209 
6210 print-mu-get-offset:  # out: (addr buffered-file), stmt: (handle stmt)
6211     # . prologue
6212     55/push-ebp
6213     89/<- %ebp 4/r32/esp
6214     # . save registers
6215     50/push-eax
6216     # var second-inout/eax: (handle stmt-var) = stmt->inouts->next
6217     8b/-> *(ebp+0xc) 0/r32/eax
6218     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
6219     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
6220     # var output-var/eax: (handle var) = second-inout->value
6221     8b/-> *eax 0/r32/eax  # Stmt-var-value
6222     # print offset
6223 #?     (write-buffered Stderr "emitting offset from output var ")
6224 #?     (print-int32-buffered Stderr %eax)
6225 #?     (write-buffered Stderr Newline)
6226 #?     (flush Stderr)
6227     (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-offset
6228 $emit-get-offset:end:
6229     # . restore registers
6230     58/pop-to-eax
6231     # . epilogue
6232     89/<- %esp 5/r32/ebp
6233     5d/pop-to-ebp
6234     c3/return
6235 
6236 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
6237     # . prologue
6238     55/push-ebp
6239     89/<- %ebp 4/r32/esp
6240     # . save registers
6241     50/push-eax
6242     51/push-ecx
6243     56/push-esi
6244     # esi = block
6245     8b/-> *(ebp+0xc) 6/r32/esi
6246     # var stmts/eax: (handle list stmt) = block->statements
6247     8b/-> *(esi+4) 0/r32/eax  # Block-stmts
6248     #
6249     {
6250 $emit-subx-block:check-empty:
6251       3d/compare-eax-and 0/imm32
6252       0f 84/jump-if-= break/disp32
6253       (emit-indent *(ebp+8) *Curr-block-depth)
6254       (write-buffered *(ebp+8) "{\n")
6255       # var v/ecx: (addr array byte) = block->var->name
6256       8b/-> *(esi+8) 1/r32/ecx  # Block-var
6257       (write-buffered *(ebp+8) *ecx)  # Var-name
6258       (write-buffered *(ebp+8) ":loop:\n")
6259       ff 0/subop/increment *Curr-block-depth
6260       (push *(ebp+0x10) %ecx)
6261       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
6262       (pop *(ebp+0x10))  # => eax
6263       ff 1/subop/decrement *Curr-block-depth
6264       (emit-indent *(ebp+8) *Curr-block-depth)
6265       (write-buffered *(ebp+8) "}\n")
6266       (write-buffered *(ebp+8) *ecx)  # Var-name
6267       (write-buffered *(ebp+8) ":break:\n")
6268     }
6269 $emit-subx-block:end:
6270     # . restore registers
6271     5e/pop-to-esi
6272     59/pop-to-ecx
6273     58/pop-to-eax
6274     # . epilogue
6275     89/<- %esp 5/r32/ebp
6276     5d/pop-to-ebp
6277     c3/return
6278 
6279 # Primitives supported
6280 # For each operation, put variants with hard-coded registers before flexible ones.
6281 == data
6282 Primitives:
6283 # - increment/decrement
6284 _Primitive-inc-eax:
6285     # var/eax <- increment => 40/increment-eax
6286     "increment"/imm32/name
6287     0/imm32/no-inouts
6288     Single-int-var-in-eax/imm32/outputs
6289     "40/increment-eax"/imm32/subx-name
6290     0/imm32/no-rm32
6291     0/imm32/no-r32
6292     0/imm32/no-imm32
6293     0/imm32/no-disp32
6294     0/imm32/output-is-write-only
6295     _Primitive-inc-ecx/imm32/next
6296 _Primitive-inc-ecx:
6297     # var/ecx <- increment => 41/increment-ecx
6298     "increment"/imm32/name
6299     0/imm32/no-inouts
6300     Single-int-var-in-ecx/imm32/outputs
6301     "41/increment-ecx"/imm32/subx-name
6302     0/imm32/no-rm32
6303     0/imm32/no-r32
6304     0/imm32/no-imm32
6305     0/imm32/no-disp32
6306     0/imm32/output-is-write-only
6307     _Primitive-inc-edx/imm32/next
6308 _Primitive-inc-edx:
6309     # var/edx <- increment => 42/increment-edx
6310     "increment"/imm32/name
6311     0/imm32/no-inouts
6312     Single-int-var-in-edx/imm32/outputs
6313     "42/increment-edx"/imm32/subx-name
6314     0/imm32/no-rm32
6315     0/imm32/no-r32
6316     0/imm32/no-imm32
6317     0/imm32/no-disp32
6318     0/imm32/output-is-write-only
6319     _Primitive-inc-ebx/imm32/next
6320 _Primitive-inc-ebx:
6321     # var/ebx <- increment => 43/increment-ebx
6322     "increment"/imm32/name
6323     0/imm32/no-inouts
6324     Single-int-var-in-ebx/imm32/outputs
6325     "43/increment-ebx"/imm32/subx-name
6326     0/imm32/no-rm32
6327     0/imm32/no-r32
6328     0/imm32/no-imm32
6329     0/imm32/no-disp32
6330     0/imm32/output-is-write-only
6331     _Primitive-inc-esi/imm32/next
6332 _Primitive-inc-esi:
6333     # var/esi <- increment => 46/increment-esi
6334     "increment"/imm32/name
6335     0/imm32/no-inouts
6336     Single-int-var-in-esi/imm32/outputs
6337     "46/increment-esi"/imm32/subx-name
6338     0/imm32/no-rm32
6339     0/imm32/no-r32
6340     0/imm32/no-imm32
6341     0/imm32/no-disp32
6342     0/imm32/output-is-write-only
6343     _Primitive-inc-edi/imm32/next
6344 _Primitive-inc-edi:
6345     # var/edi <- increment => 47/increment-edi
6346     "increment"/imm32/name
6347     0/imm32/no-inouts
6348     Single-int-var-in-edi/imm32/outputs
6349     "47/increment-edi"/imm32/subx-name
6350     0/imm32/no-rm32
6351     0/imm32/no-r32
6352     0/imm32/no-imm32
6353     0/imm32/no-disp32
6354     0/imm32/output-is-write-only
6355     _Primitive-dec-eax/imm32/next
6356 _Primitive-dec-eax:
6357     # var/eax <- decrement => 48/decrement-eax
6358     "decrement"/imm32/name
6359     0/imm32/no-inouts
6360     Single-int-var-in-eax/imm32/outputs
6361     "48/decrement-eax"/imm32/subx-name
6362     0/imm32/no-rm32
6363     0/imm32/no-r32
6364     0/imm32/no-imm32
6365     0/imm32/no-disp32
6366     0/imm32/output-is-write-only
6367     _Primitive-dec-ecx/imm32/next
6368 _Primitive-dec-ecx:
6369     # var/ecx <- decrement => 49/decrement-ecx
6370     "decrement"/imm32/name
6371     0/imm32/no-inouts
6372     Single-int-var-in-ecx/imm32/outputs
6373     "49/decrement-ecx"/imm32/subx-name
6374     0/imm32/no-rm32
6375     0/imm32/no-r32
6376     0/imm32/no-imm32
6377     0/imm32/no-disp32
6378     0/imm32/output-is-write-only
6379     _Primitive-dec-edx/imm32/next
6380 _Primitive-dec-edx:
6381     # var/edx <- decrement => 4a/decrement-edx
6382     "decrement"/imm32/name
6383     0/imm32/no-inouts
6384     Single-int-var-in-edx/imm32/outputs
6385     "4a/decrement-edx"/imm32/subx-name
6386     0/imm32/no-rm32
6387     0/imm32/no-r32
6388     0/imm32/no-imm32
6389     0/imm32/no-disp32
6390     0/imm32/output-is-write-only
6391     _Primitive-dec-ebx/imm32/next
6392 _Primitive-dec-ebx:
6393     # var/ebx <- decrement => 4b/decrement-ebx
6394     "decrement"/imm32/name
6395     0/imm32/no-inouts
6396     Single-int-var-in-ebx/imm32/outputs
6397     "4b/decrement-ebx"/imm32/subx-name
6398     0/imm32/no-rm32
6399     0/imm32/no-r32
6400     0/imm32/no-imm32
6401     0/imm32/no-disp32
6402     0/imm32/output-is-write-only
6403     _Primitive-dec-esi/imm32/next
6404 _Primitive-dec-esi:
6405     # var/esi <- decrement => 4e/decrement-esi
6406     "decrement"/imm32/name
6407     0/imm32/no-inouts
6408     Single-int-var-in-esi/imm32/outputs
6409     "4e/decrement-esi"/imm32/subx-name
6410     0/imm32/no-rm32
6411     0/imm32/no-r32
6412     0/imm32/no-imm32
6413     0/imm32/no-disp32
6414     0/imm32/output-is-write-only
6415     _Primitive-dec-edi/imm32/next
6416 _Primitive-dec-edi:
6417     # var/edi <- decrement => 4f/decrement-edi
6418     "decrement"/imm32/name
6419     0/imm32/no-inouts
6420     Single-int-var-in-edi/imm32/outputs
6421     "4f/decrement-edi"/imm32/subx-name
6422     0/imm32/no-rm32
6423     0/imm32/no-r32
6424     0/imm32/no-imm32
6425     0/imm32/no-disp32
6426     0/imm32/output-is-write-only
6427     _Primitive-inc-mem/imm32/next
6428 _Primitive-inc-mem:
6429     # increment var => ff 0/subop/increment *(ebp+__)
6430     "increment"/imm32/name
6431     Single-int-var-in-mem/imm32/inouts
6432     0/imm32/no-outputs
6433     "ff 0/subop/increment"/imm32/subx-name
6434     1/imm32/rm32-is-first-inout
6435     0/imm32/no-r32
6436     0/imm32/no-imm32
6437     0/imm32/no-disp32
6438     0/imm32/output-is-write-only
6439     _Primitive-inc-reg/imm32/next
6440 _Primitive-inc-reg:
6441     # var/reg <- increment => ff 0/subop/increment %__
6442     "increment"/imm32/name
6443     0/imm32/no-inouts
6444     Single-int-var-in-some-register/imm32/outputs
6445     "ff 0/subop/increment"/imm32/subx-name
6446     3/imm32/rm32-is-first-output
6447     0/imm32/no-r32
6448     0/imm32/no-imm32
6449     0/imm32/no-disp32
6450     0/imm32/output-is-write-only
6451     _Primitive-dec-mem/imm32/next
6452 _Primitive-dec-mem:
6453     # decrement var => ff 1/subop/decrement *(ebp+__)
6454     "decrement"/imm32/name
6455     Single-int-var-in-mem/imm32/inouts
6456     0/imm32/no-outputs
6457     "ff 1/subop/decrement"/imm32/subx-name
6458     1/imm32/rm32-is-first-inout
6459     0/imm32/no-r32
6460     0/imm32/no-imm32
6461     0/imm32/no-disp32
6462     0/imm32/output-is-write-only
6463     _Primitive-dec-reg/imm32/next
6464 _Primitive-dec-reg:
6465     # var/reg <- decrement => ff 1/subop/decrement %__
6466     "decrement"/imm32/name
6467     0/imm32/no-inouts
6468     Single-int-var-in-some-register/imm32/outputs
6469     "ff 1/subop/decrement"/imm32/subx-name
6470     3/imm32/rm32-is-first-output
6471     0/imm32/no-r32
6472     0/imm32/no-imm32
6473     0/imm32/no-disp32
6474     0/imm32/output-is-write-only
6475     _Primitive-add-to-eax/imm32/next
6476 # - add
6477 _Primitive-add-to-eax:
6478     # var/eax <- add lit => 05/add-to-eax lit/imm32
6479     "add"/imm32/name
6480     Single-lit-var/imm32/inouts
6481     Single-int-var-in-eax/imm32/outputs
6482     "05/add-to-eax"/imm32/subx-name
6483     0/imm32/no-rm32
6484     0/imm32/no-r32
6485     1/imm32/imm32-is-first-inout
6486     0/imm32/no-disp32
6487     0/imm32/output-is-write-only
6488     _Primitive-add-reg-to-reg/imm32/next
6489 _Primitive-add-reg-to-reg:
6490     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
6491     "add"/imm32/name
6492     Single-int-var-in-some-register/imm32/inouts
6493     Single-int-var-in-some-register/imm32/outputs
6494     "01/add-to"/imm32/subx-name
6495     3/imm32/rm32-is-first-output
6496     1/imm32/r32-is-first-inout
6497     0/imm32/no-imm32
6498     0/imm32/no-disp32
6499     0/imm32/output-is-write-only
6500     _Primitive-add-reg-to-mem/imm32/next
6501 _Primitive-add-reg-to-mem:
6502     # add-to var1 var2/reg => 01/add-to var1 var2/r32
6503     "add-to"/imm32/name
6504     Two-args-int-stack-int-reg/imm32/inouts
6505     0/imm32/outputs
6506     "01/add-to"/imm32/subx-name
6507     1/imm32/rm32-is-first-inout
6508     2/imm32/r32-is-second-inout
6509     0/imm32/no-imm32
6510     0/imm32/no-disp32
6511     0/imm32/output-is-write-only
6512     _Primitive-add-mem-to-reg/imm32/next
6513 _Primitive-add-mem-to-reg:
6514     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
6515     "add"/imm32/name
6516     Single-int-var-in-mem/imm32/inouts
6517     Single-int-var-in-some-register/imm32/outputs
6518     "03/add"/imm32/subx-name
6519     1/imm32/rm32-is-first-inout
6520     3/imm32/r32-is-first-output
6521     0/imm32/no-imm32
6522     0/imm32/no-disp32
6523     0/imm32/output-is-write-only
6524     _Primitive-add-lit-to-reg/imm32/next
6525 _Primitive-add-lit-to-reg:
6526     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
6527     "add"/imm32/name
6528     Single-lit-var/imm32/inouts
6529     Single-int-var-in-some-register/imm32/outputs
6530     "81 0/subop/add"/imm32/subx-name
6531     3/imm32/rm32-is-first-output
6532     0/imm32/no-r32
6533     1/imm32/imm32-is-first-inout
6534     0/imm32/no-disp32
6535     0/imm32/output-is-write-only
6536     _Primitive-add-lit-to-mem/imm32/next
6537 _Primitive-add-lit-to-mem:
6538     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
6539     "add-to"/imm32/name
6540     Int-var-and-literal/imm32/inouts
6541     0/imm32/outputs
6542     "81 0/subop/add"/imm32/subx-name
6543     1/imm32/rm32-is-first-inout
6544     0/imm32/no-r32
6545     2/imm32/imm32-is-second-inout
6546     0/imm32/no-disp32
6547     0/imm32/output-is-write-only
6548     _Primitive-subtract-from-eax/imm32/next
6549 # - subtract
6550 _Primitive-subtract-from-eax:
6551     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
6552     "subtract"/imm32/name
6553     Single-lit-var/imm32/inouts
6554     Single-int-var-in-eax/imm32/outputs
6555     "2d/subtract-from-eax"/imm32/subx-name
6556     0/imm32/no-rm32
6557     0/imm32/no-r32
6558     1/imm32/imm32-is-first-inout
6559     0/imm32/no-disp32
6560     0/imm32/output-is-write-only
6561     _Primitive-subtract-reg-from-reg/imm32/next
6562 _Primitive-subtract-reg-from-reg:
6563     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
6564     "subtract"/imm32/name
6565     Single-int-var-in-some-register/imm32/inouts
6566     Single-int-var-in-some-register/imm32/outputs
6567     "29/subtract-from"/imm32/subx-name
6568     3/imm32/rm32-is-first-output
6569     1/imm32/r32-is-first-inout
6570     0/imm32/no-imm32
6571     0/imm32/no-disp32
6572     0/imm32/output-is-write-only
6573     _Primitive-subtract-reg-from-mem/imm32/next
6574 _Primitive-subtract-reg-from-mem:
6575     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
6576     "subtract-from"/imm32/name
6577     Two-args-int-stack-int-reg/imm32/inouts
6578     0/imm32/outputs
6579     "29/subtract-from"/imm32/subx-name
6580     1/imm32/rm32-is-first-inout
6581     2/imm32/r32-is-second-inout
6582     0/imm32/no-imm32
6583     0/imm32/no-disp32
6584     0/imm32/output-is-write-only
6585     _Primitive-subtract-mem-from-reg/imm32/next
6586 _Primitive-subtract-mem-from-reg:
6587     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
6588     "subtract"/imm32/name
6589     Single-int-var-in-mem/imm32/inouts
6590     Single-int-var-in-some-register/imm32/outputs
6591     "2b/subtract"/imm32/subx-name
6592     1/imm32/rm32-is-first-inout
6593     3/imm32/r32-is-first-output
6594     0/imm32/no-imm32
6595     0/imm32/no-disp32
6596     0/imm32/output-is-write-only
6597     _Primitive-subtract-lit-from-reg/imm32/next
6598 _Primitive-subtract-lit-from-reg:
6599     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
6600     "subtract"/imm32/name
6601     Single-lit-var/imm32/inouts
6602     Single-int-var-in-some-register/imm32/outputs
6603     "81 5/subop/subtract"/imm32/subx-name
6604     3/imm32/rm32-is-first-output
6605     0/imm32/no-r32
6606     1/imm32/imm32-is-first-inout
6607     0/imm32/no-disp32
6608     0/imm32/output-is-write-only
6609     _Primitive-subtract-lit-from-mem/imm32/next
6610 _Primitive-subtract-lit-from-mem:
6611     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
6612     "subtract-from"/imm32/name
6613     Int-var-and-literal/imm32/inouts
6614     0/imm32/outputs
6615     "81 5/subop/subtract"/imm32/subx-name
6616     1/imm32/rm32-is-first-inout
6617     0/imm32/no-r32
6618     2/imm32/imm32-is-first-inout
6619     0/imm32/no-disp32
6620     0/imm32/output-is-write-only
6621     _Primitive-and-with-eax/imm32/next
6622 # - and
6623 _Primitive-and-with-eax:
6624     # var/eax <- and lit => 25/and-with-eax lit/imm32
6625     "and"/imm32/name
6626     Single-lit-var/imm32/inouts
6627     Single-int-var-in-eax/imm32/outputs
6628     "25/and-with-eax"/imm32/subx-name
6629     0/imm32/no-rm32
6630     0/imm32/no-r32
6631     1/imm32/imm32-is-first-inout
6632     0/imm32/no-disp32
6633     0/imm32/output-is-write-only
6634     _Primitive-and-reg-with-reg/imm32/next
6635 _Primitive-and-reg-with-reg:
6636     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
6637     "and"/imm32/name
6638     Single-int-var-in-some-register/imm32/inouts
6639     Single-int-var-in-some-register/imm32/outputs
6640     "21/and-with"/imm32/subx-name
6641     3/imm32/rm32-is-first-output
6642     1/imm32/r32-is-first-inout
6643     0/imm32/no-imm32
6644     0/imm32/no-disp32
6645     0/imm32/output-is-write-only
6646     _Primitive-and-reg-with-mem/imm32/next
6647 _Primitive-and-reg-with-mem:
6648     # and-with var1 var2/reg => 21/and-with var1 var2/r32
6649     "and-with"/imm32/name
6650     Two-args-int-stack-int-reg/imm32/inouts
6651     0/imm32/outputs
6652     "21/and-with"/imm32/subx-name
6653     1/imm32/rm32-is-first-inout
6654     2/imm32/r32-is-second-inout
6655     0/imm32/no-imm32
6656     0/imm32/no-disp32
6657     0/imm32/output-is-write-only
6658     _Primitive-and-mem-with-reg/imm32/next
6659 _Primitive-and-mem-with-reg:
6660     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
6661     "and"/imm32/name
6662     Single-int-var-in-mem/imm32/inouts
6663     Single-int-var-in-some-register/imm32/outputs
6664     "23/and"/imm32/subx-name
6665     1/imm32/rm32-is-first-inout
6666     3/imm32/r32-is-first-output
6667     0/imm32/no-imm32
6668     0/imm32/no-disp32
6669     0/imm32/output-is-write-only
6670     _Primitive-and-lit-with-reg/imm32/next
6671 _Primitive-and-lit-with-reg:
6672     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
6673     "and"/imm32/name
6674     Single-lit-var/imm32/inouts
6675     Single-int-var-in-some-register/imm32/outputs
6676     "81 4/subop/and"/imm32/subx-name
6677     3/imm32/rm32-is-first-output
6678     0/imm32/no-r32
6679     1/imm32/imm32-is-first-inout
6680     0/imm32/no-disp32
6681     0/imm32/output-is-write-only
6682     _Primitive-and-lit-with-mem/imm32/next
6683 _Primitive-and-lit-with-mem:
6684     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
6685     "and-with"/imm32/name
6686     Int-var-and-literal/imm32/inouts
6687     0/imm32/outputs
6688     "81 4/subop/and"/imm32/subx-name
6689     1/imm32/rm32-is-first-inout
6690     0/imm32/no-r32
6691     2/imm32/imm32-is-first-inout
6692     0/imm32/no-disp32
6693     0/imm32/output-is-write-only
6694     _Primitive-or-with-eax/imm32/next
6695 # - or
6696 _Primitive-or-with-eax:
6697     # var/eax <- or lit => 0d/or-with-eax lit/imm32
6698     "or"/imm32/name
6699     Single-lit-var/imm32/inouts
6700     Single-int-var-in-eax/imm32/outputs
6701     "0d/or-with-eax"/imm32/subx-name
6702     0/imm32/no-rm32
6703     0/imm32/no-r32
6704     1/imm32/imm32-is-first-inout
6705     0/imm32/no-disp32
6706     0/imm32/output-is-write-only
6707     _Primitive-or-reg-with-reg/imm32/next
6708 _Primitive-or-reg-with-reg:
6709     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
6710     "or"/imm32/name
6711     Single-int-var-in-some-register/imm32/inouts
6712     Single-int-var-in-some-register/imm32/outputs
6713     "09/or-with"/imm32/subx-name
6714     3/imm32/rm32-is-first-output
6715     1/imm32/r32-is-first-inout
6716     0/imm32/no-imm32
6717     0/imm32/no-disp32
6718     0/imm32/output-is-write-only
6719     _Primitive-or-reg-with-mem/imm32/next
6720 _Primitive-or-reg-with-mem:
6721     # or-with var1 var2/reg => 09/or-with var1 var2/r32
6722     "or-with"/imm32/name
6723     Two-args-int-stack-int-reg/imm32/inouts
6724     0/imm32/outputs
6725     "09/or-with"/imm32/subx-name
6726     1/imm32/rm32-is-first-inout
6727     2/imm32/r32-is-second-inout
6728     0/imm32/no-imm32
6729     0/imm32/no-disp32
6730     0/imm32/output-is-write-only
6731     _Primitive-or-mem-with-reg/imm32/next
6732 _Primitive-or-mem-with-reg:
6733     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
6734     "or"/imm32/name
6735     Single-int-var-in-mem/imm32/inouts
6736     Single-int-var-in-some-register/imm32/outputs
6737     "0b/or"/imm32/subx-name
6738     1/imm32/rm32-is-first-inout
6739     3/imm32/r32-is-first-output
6740     0/imm32/no-imm32
6741     0/imm32/no-disp32
6742     0/imm32/output-is-write-only
6743     _Primitive-or-lit-with-reg/imm32/next
6744 _Primitive-or-lit-with-reg:
6745     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
6746     "or"/imm32/name
6747     Single-lit-var/imm32/inouts
6748     Single-int-var-in-some-register/imm32/outputs
6749     "81 1/subop/or"/imm32/subx-name
6750     3/imm32/rm32-is-first-output
6751     0/imm32/no-r32
6752     1/imm32/imm32-is-first-inout
6753     0/imm32/no-disp32
6754     0/imm32/output-is-write-only
6755     _Primitive-or-lit-with-mem/imm32/next
6756 _Primitive-or-lit-with-mem:
6757     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
6758     "or-with"/imm32/name
6759     Int-var-and-literal/imm32/inouts
6760     0/imm32/outputs
6761     "81 1/subop/or"/imm32/subx-name
6762     1/imm32/rm32-is-first-inout
6763     0/imm32/no-r32
6764     2/imm32/imm32-is-second-inout
6765     0/imm32/no-disp32
6766     0/imm32/output-is-write-only
6767     _Primitive-xor-with-eax/imm32/next
6768 # - xor
6769 _Primitive-xor-with-eax:
6770     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
6771     "xor"/imm32/name
6772     Single-lit-var/imm32/inouts
6773     Single-int-var-in-eax/imm32/outputs
6774     "35/xor-with-eax"/imm32/subx-name
6775     0/imm32/no-rm32
6776     0/imm32/no-r32
6777     1/imm32/imm32-is-first-inout
6778     0/imm32/no-disp32
6779     0/imm32/output-is-write-only
6780     _Primitive-xor-reg-with-reg/imm32/next
6781 _Primitive-xor-reg-with-reg:
6782     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
6783     "xor"/imm32/name
6784     Single-int-var-in-some-register/imm32/inouts
6785     Single-int-var-in-some-register/imm32/outputs
6786     "31/xor-with"/imm32/subx-name
6787     3/imm32/rm32-is-first-output
6788     1/imm32/r32-is-first-inout
6789     0/imm32/no-imm32
6790     0/imm32/no-disp32
6791     0/imm32/output-is-write-only
6792     _Primitive-xor-reg-with-mem/imm32/next
6793 _Primitive-xor-reg-with-mem:
6794     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
6795     "xor-with"/imm32/name
6796     Two-args-int-stack-int-reg/imm32/inouts
6797     0/imm32/outputs
6798     "31/xor-with"/imm32/subx-name
6799     1/imm32/rm32-is-first-inout
6800     2/imm32/r32-is-second-inout
6801     0/imm32/no-imm32
6802     0/imm32/no-disp32
6803     0/imm32/output-is-write-only
6804     _Primitive-xor-mem-with-reg/imm32/next
6805 _Primitive-xor-mem-with-reg:
6806     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
6807     "xor"/imm32/name
6808     Single-int-var-in-mem/imm32/inouts
6809     Single-int-var-in-some-register/imm32/outputs
6810     "33/xor"/imm32/subx-name
6811     1/imm32/rm32-is-first-inout
6812     3/imm32/r32-is-first-output
6813     0/imm32/no-imm32
6814     0/imm32/no-disp32
6815     0/imm32/output-is-write-only
6816     _Primitive-xor-lit-with-reg/imm32/next
6817 _Primitive-xor-lit-with-reg:
6818     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
6819     "xor"/imm32/name
6820     Single-lit-var/imm32/inouts
6821     Single-int-var-in-some-register/imm32/outputs
6822     "81 6/subop/xor"/imm32/subx-name
6823     3/imm32/rm32-is-first-output
6824     0/imm32/no-r32
6825     1/imm32/imm32-is-first-inout
6826     0/imm32/no-disp32
6827     0/imm32/output-is-write-only
6828     _Primitive-xor-lit-with-mem/imm32/next
6829 _Primitive-xor-lit-with-mem:
6830     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
6831     "xor-with"/imm32/name
6832     Int-var-and-literal/imm32/inouts
6833     0/imm32/outputs
6834     "81 6/subop/xor"/imm32/subx-name
6835     1/imm32/rm32-is-first-inout
6836     0/imm32/no-r32
6837     2/imm32/imm32-is-first-inout
6838     0/imm32/no-disp32
6839     0/imm32/output-is-write-only
6840     _Primitive-copy-to-eax/imm32/next
6841 # - copy
6842 _Primitive-copy-to-eax:
6843     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
6844     "copy"/imm32/name
6845     Single-lit-var/imm32/inouts
6846     Single-int-var-in-eax/imm32/outputs
6847     "b8/copy-to-eax"/imm32/subx-name
6848     0/imm32/no-rm32
6849     0/imm32/no-r32
6850     1/imm32/imm32-is-first-inout
6851     0/imm32/no-disp32
6852     1/imm32/output-is-write-only
6853     _Primitive-copy-to-ecx/imm32/next
6854 _Primitive-copy-to-ecx:
6855     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
6856     "copy"/imm32/name
6857     Single-lit-var/imm32/inouts
6858     Single-int-var-in-ecx/imm32/outputs
6859     "b9/copy-to-ecx"/imm32/subx-name
6860     0/imm32/no-rm32
6861     0/imm32/no-r32
6862     1/imm32/imm32-is-first-inout
6863     0/imm32/no-disp32
6864     1/imm32/output-is-write-only
6865     _Primitive-copy-to-edx/imm32/next
6866 _Primitive-copy-to-edx:
6867     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
6868     "copy"/imm32/name
6869     Single-lit-var/imm32/inouts
6870     Single-int-var-in-edx/imm32/outputs
6871     "ba/copy-to-edx"/imm32/subx-name
6872     0/imm32/no-rm32
6873     0/imm32/no-r32
6874     1/imm32/imm32-is-first-inout
6875     0/imm32/no-disp32
6876     1/imm32/output-is-write-only
6877     _Primitive-copy-to-ebx/imm32/next
6878 _Primitive-copy-to-ebx:
6879     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
6880     "copy"/imm32/name
6881     Single-lit-var/imm32/inouts
6882     Single-int-var-in-ebx/imm32/outputs
6883     "bb/copy-to-ebx"/imm32/subx-name
6884     0/imm32/no-rm32
6885     0/imm32/no-r32
6886     1/imm32/imm32-is-first-inout
6887     0/imm32/no-disp32
6888     1/imm32/output-is-write-only
6889     _Primitive-copy-to-esi/imm32/next
6890 _Primitive-copy-to-esi:
6891     # var/esi <- copy lit => be/copy-to-esi lit/imm32
6892     "copy"/imm32/name
6893     Single-lit-var/imm32/inouts
6894     Single-int-var-in-esi/imm32/outputs
6895     "be/copy-to-esi"/imm32/subx-name
6896     0/imm32/no-rm32
6897     0/imm32/no-r32
6898     1/imm32/imm32-is-first-inout
6899     0/imm32/no-disp32
6900     1/imm32/output-is-write-only
6901     _Primitive-copy-to-edi/imm32/next
6902 _Primitive-copy-to-edi:
6903     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
6904     "copy"/imm32/name
6905     Single-lit-var/imm32/inouts
6906     Single-int-var-in-edi/imm32/outputs
6907     "bf/copy-to-edi"/imm32/subx-name
6908     0/imm32/no-rm32
6909     0/imm32/no-r32
6910     1/imm32/imm32-is-first-inout
6911     0/imm32/no-disp32
6912     1/imm32/output-is-write-only
6913     _Primitive-copy-reg-to-reg/imm32/next
6914 _Primitive-copy-reg-to-reg:
6915     # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32
6916     "copy"/imm32/name
6917     Single-int-var-in-some-register/imm32/inouts
6918     Single-int-var-in-some-register/imm32/outputs
6919     "89/copy-to"/imm32/subx-name
6920     3/imm32/rm32-is-first-output
6921     1/imm32/r32-is-first-inout
6922     0/imm32/no-imm32
6923     0/imm32/no-disp32
6924     1/imm32/output-is-write-only
6925     _Primitive-copy-reg-to-mem/imm32/next
6926 _Primitive-copy-reg-to-mem:
6927     # copy-to var1 var2/reg => 89/copy-to var1 var2/r32
6928     "copy-to"/imm32/name
6929     Two-args-int-stack-int-reg/imm32/inouts
6930     0/imm32/outputs
6931     "89/copy-to"/imm32/subx-name
6932     1/imm32/rm32-is-first-inout
6933     2/imm32/r32-is-second-inout
6934     0/imm32/no-imm32
6935     0/imm32/no-disp32
6936     1/imm32/output-is-write-only
6937     _Primitive-copy-mem-to-reg/imm32/next
6938 _Primitive-copy-mem-to-reg:
6939     # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32
6940     "copy"/imm32/name
6941     Single-int-var-in-mem/imm32/inouts
6942     Single-int-var-in-some-register/imm32/outputs
6943     "8b/copy-from"/imm32/subx-name
6944     1/imm32/rm32-is-first-inout
6945     3/imm32/r32-is-first-output
6946     0/imm32/no-imm32
6947     0/imm32/no-disp32
6948     1/imm32/output-is-write-only
6949     _Primitive-copy-lit-to-reg/imm32/next
6950 _Primitive-copy-lit-to-reg:
6951     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
6952     "copy"/imm32/name
6953     Single-lit-var/imm32/inouts
6954     Single-int-var-in-some-register/imm32/outputs
6955     "c7 0/subop/copy"/imm32/subx-name
6956     3/imm32/rm32-is-first-output
6957     0/imm32/no-r32
6958     1/imm32/imm32-is-first-inout
6959     0/imm32/no-disp32
6960     1/imm32/output-is-write-only
6961     _Primitive-copy-lit-to-mem/imm32/next
6962 _Primitive-copy-lit-to-mem:
6963     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
6964     "copy-to"/imm32/name
6965     Int-var-and-literal/imm32/inouts
6966     0/imm32/outputs
6967     "c7 0/subop/copy"/imm32/subx-name
6968     1/imm32/rm32-is-first-inout
6969     0/imm32/no-r32
6970     2/imm32/imm32-is-first-inout
6971     0/imm32/no-disp32
6972     1/imm32/output-is-write-only
6973     _Primitive-compare-mem-with-reg/imm32/next
6974 # - compare
6975 _Primitive-compare-mem-with-reg:
6976     # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32
6977     "compare"/imm32/name
6978     Two-args-int-stack-int-reg/imm32/inouts
6979     0/imm32/outputs
6980     "39/compare->"/imm32/subx-name
6981     1/imm32/rm32-is-first-inout
6982     2/imm32/r32-is-second-inout
6983     0/imm32/no-imm32
6984     0/imm32/no-disp32
6985     0/imm32/output-is-write-only
6986     _Primitive-compare-reg-with-mem/imm32/next
6987 _Primitive-compare-reg-with-mem:
6988     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
6989     "compare"/imm32/name
6990     Two-args-int-reg-int-stack/imm32/inouts
6991     0/imm32/outputs
6992     "3b/compare<-"/imm32/subx-name
6993     2/imm32/rm32-is-second-inout
6994     1/imm32/r32-is-first-inout
6995     0/imm32/no-imm32
6996     0/imm32/no-disp32
6997     0/imm32/output-is-write-only
6998     _Primitive-compare-eax-with-literal/imm32/next
6999 _Primitive-compare-eax-with-literal:
7000     # compare var1/eax n => 3d/compare-eax-with n/imm32
7001     "compare"/imm32/name
7002     Two-args-int-eax-int-literal/imm32/inouts
7003     0/imm32/outputs
7004     "3d/compare-eax-with"/imm32/subx-name
7005     0/imm32/no-rm32
7006     0/imm32/no-r32
7007     2/imm32/imm32-is-second-inout
7008     0/imm32/no-disp32
7009     0/imm32/output-is-write-only
7010     _Primitive-compare-reg-with-literal/imm32/next
7011 _Primitive-compare-reg-with-literal:
7012     # compare var1/reg n => 81 7/subop/compare %reg n/imm32
7013     "compare"/imm32/name
7014     Int-var-in-register-and-literal/imm32/inouts
7015     0/imm32/outputs
7016     "81 7/subop/compare"/imm32/subx-name
7017     1/imm32/rm32-is-first-inout
7018     0/imm32/no-r32
7019     2/imm32/imm32-is-second-inout
7020     0/imm32/no-disp32
7021     0/imm32/output-is-write-only
7022     _Primitive-compare-mem-with-literal/imm32/next
7023 _Primitive-compare-mem-with-literal:
7024     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
7025     "compare"/imm32/name
7026     Int-var-and-literal/imm32/inouts
7027     0/imm32/outputs
7028     "81 7/subop/compare"/imm32/subx-name
7029     1/imm32/rm32-is-first-inout
7030     0/imm32/no-r32
7031     2/imm32/imm32-is-second-inout
7032     0/imm32/no-disp32
7033     0/imm32/output-is-write-only
7034     _Primitive-multiply-reg-by-mem/imm32/next
7035 # - multiply
7036 _Primitive-multiply-reg-by-mem:
7037     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
7038     "multiply"/imm32/name
7039     Single-int-var-in-mem/imm32/inouts
7040     Single-int-var-in-some-register/imm32/outputs
7041     "0f af/multiply"/imm32/subx-name
7042     1/imm32/rm32-is-first-inout
7043     3/imm32/r32-is-first-output
7044     0/imm32/no-imm32
7045     0/imm32/no-disp32
7046     0/imm32/output-is-write-only
7047     _Primitive-break-if-addr</imm32/next
7048 # - branches
7049 _Primitive-break-if-addr<:
7050     "break-if-addr<"/imm32/name
7051     0/imm32/inouts
7052     0/imm32/outputs
7053     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
7054     0/imm32/no-rm32
7055     0/imm32/no-r32
7056     0/imm32/no-imm32
7057     0/imm32/no-disp32
7058     0/imm32/no-output
7059     _Primitive-break-if-addr>=/imm32/next
7060 _Primitive-break-if-addr>=:
7061     "break-if-addr>="/imm32/name
7062     0/imm32/inouts
7063     0/imm32/outputs
7064     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
7065     0/imm32/no-rm32
7066     0/imm32/no-r32
7067     0/imm32/no-imm32
7068     0/imm32/no-disp32
7069     0/imm32/no-output
7070     _Primitive-break-if-=/imm32/next
7071 _Primitive-break-if-=:
7072     "break-if-="/imm32/name
7073     0/imm32/inouts
7074     0/imm32/outputs
7075     "0f 84/jump-if-= break/disp32"/imm32/subx-name
7076     0/imm32/no-rm32
7077     0/imm32/no-r32
7078     0/imm32/no-imm32
7079     0/imm32/no-disp32
7080     0/imm32/no-output
7081     _Primitive-break-if-!=/imm32/next
7082 _Primitive-break-if-!=:
7083     "break-if-!="/imm32/name
7084     0/imm32/inouts
7085     0/imm32/outputs
7086     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
7087     0/imm32/no-rm32
7088     0/imm32/no-r32
7089     0/imm32/no-imm32
7090     0/imm32/no-disp32
7091     0/imm32/no-output
7092     _Primitive-break-if-addr<=/imm32/next
7093 _Primitive-break-if-addr<=:
7094     "break-if-addr<="/imm32/name
7095     0/imm32/inouts
7096     0/imm32/outputs
7097     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
7098     0/imm32/no-rm32
7099     0/imm32/no-r32
7100     0/imm32/no-imm32
7101     0/imm32/no-disp32
7102     0/imm32/no-output
7103     _Primitive-break-if-addr>/imm32/next
7104 _Primitive-break-if-addr>:
7105     "break-if-addr>"/imm32/name
7106     0/imm32/inouts
7107     0/imm32/outputs
7108     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
7109     0/imm32/no-rm32
7110     0/imm32/no-r32
7111     0/imm32/no-imm32
7112     0/imm32/no-disp32
7113     0/imm32/no-output
7114     _Primitive-break-if-</imm32/next
7115 _Primitive-break-if-<:
7116     "break-if-<"/imm32/name
7117     0/imm32/inouts
7118     0/imm32/outputs
7119     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
7120     0/imm32/no-rm32
7121     0/imm32/no-r32
7122     0/imm32/no-imm32
7123     0/imm32/no-disp32
7124     0/imm32/no-output
7125     _Primitive-break-if->=/imm32/next
7126 _Primitive-break-if->=:
7127     "break-if->="/imm32/name
7128     0/imm32/inouts
7129     0/imm32/outputs
7130     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
7131     0/imm32/no-rm32
7132     0/imm32/no-r32
7133     0/imm32/no-imm32
7134     0/imm32/no-disp32
7135     0/imm32/no-output
7136     _Primitive-break-if-<=/imm32/next
7137 _Primitive-break-if-<=:
7138     "break-if-<="/imm32/name
7139     0/imm32/inouts
7140     0/imm32/outputs
7141     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
7142     0/imm32/no-rm32
7143     0/imm32/no-r32
7144     0/imm32/no-imm32
7145     0/imm32/no-disp32
7146     0/imm32/no-output
7147     _Primitive-break-if->/imm32/next
7148 _Primitive-break-if->:
7149     "break-if->"/imm32/name
7150     0/imm32/inouts
7151     0/imm32/outputs
7152     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
7153     0/imm32/no-rm32
7154     0/imm32/no-r32
7155     0/imm32/no-imm32
7156     0/imm32/no-disp32
7157     0/imm32/no-output
7158     _Primitive-break/imm32/next
7159 _Primitive-break:
7160     "break"/imm32/name
7161     0/imm32/inouts
7162     0/imm32/outputs
7163     "e9/jump break/disp32"/imm32/subx-name
7164     0/imm32/no-rm32
7165     0/imm32/no-r32
7166     0/imm32/no-imm32
7167     0/imm32/no-disp32
7168     0/imm32/no-output
7169     _Primitive-loop-if-addr</imm32/next
7170 _Primitive-loop-if-addr<:
7171     "loop-if-addr<"/imm32/name
7172     0/imm32/inouts
7173     0/imm32/outputs
7174     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
7175     0/imm32/no-rm32
7176     0/imm32/no-r32
7177     0/imm32/no-imm32
7178     0/imm32/no-disp32
7179     0/imm32/no-output
7180     _Primitive-loop-if-addr>=/imm32/next
7181 _Primitive-loop-if-addr>=:
7182     "loop-if-addr>="/imm32/name
7183     0/imm32/inouts
7184     0/imm32/outputs
7185     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
7186     0/imm32/no-rm32
7187     0/imm32/no-r32
7188     0/imm32/no-imm32
7189     0/imm32/no-disp32
7190     0/imm32/no-output
7191     _Primitive-loop-if-=/imm32/next
7192 _Primitive-loop-if-=:
7193     "loop-if-="/imm32/name
7194     0/imm32/inouts
7195     0/imm32/outputs
7196     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
7197     0/imm32/no-rm32
7198     0/imm32/no-r32
7199     0/imm32/no-imm32
7200     0/imm32/no-disp32
7201     0/imm32/no-output
7202     _Primitive-loop-if-!=/imm32/next
7203 _Primitive-loop-if-!=:
7204     "loop-if-!="/imm32/name
7205     0/imm32/inouts
7206     0/imm32/outputs
7207     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
7208     0/imm32/no-rm32
7209     0/imm32/no-r32
7210     0/imm32/no-imm32
7211     0/imm32/no-disp32
7212     0/imm32/no-output
7213     _Primitive-loop-if-addr<=/imm32/next
7214 _Primitive-loop-if-addr<=:
7215     "loop-if-addr<="/imm32/name
7216     0/imm32/inouts
7217     0/imm32/outputs
7218     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
7219     0/imm32/no-rm32
7220     0/imm32/no-r32
7221     0/imm32/no-imm32
7222     0/imm32/no-disp32
7223     0/imm32/no-output
7224     _Primitive-loop-if-addr>/imm32/next
7225 _Primitive-loop-if-addr>:
7226     "loop-if-addr>"/imm32/name
7227     0/imm32/inouts
7228     0/imm32/outputs
7229     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
7230     0/imm32/no-rm32
7231     0/imm32/no-r32
7232     0/imm32/no-imm32
7233     0/imm32/no-disp32
7234     0/imm32/no-output
7235     _Primitive-loop-if-</imm32/next
7236 _Primitive-loop-if-<:
7237     "loop-if-<"/imm32/name
7238     0/imm32/inouts
7239     0/imm32/outputs
7240     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
7241     0/imm32/no-rm32
7242     0/imm32/no-r32
7243     0/imm32/no-imm32
7244     0/imm32/no-disp32
7245     0/imm32/no-output
7246     _Primitive-loop-if->=/imm32/next
7247 _Primitive-loop-if->=:
7248     "loop-if->="/imm32/name
7249     0/imm32/inouts
7250     0/imm32/outputs
7251     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
7252     0/imm32/no-rm32
7253     0/imm32/no-r32
7254     0/imm32/no-imm32
7255     0/imm32/no-disp32
7256     0/imm32/no-output
7257     _Primitive-loop-if-<=/imm32/next
7258 _Primitive-loop-if-<=:
7259     "loop-if-<="/imm32/name
7260     0/imm32/inouts
7261     0/imm32/outputs
7262     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
7263     0/imm32/no-rm32
7264     0/imm32/no-r32
7265     0/imm32/no-imm32
7266     0/imm32/no-disp32
7267     0/imm32/no-output
7268     _Primitive-loop-if->/imm32/next
7269 _Primitive-loop-if->:
7270     "loop-if->"/imm32/name
7271     0/imm32/inouts
7272     0/imm32/outputs
7273     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
7274     0/imm32/no-rm32
7275     0/imm32/no-r32
7276     0/imm32/no-imm32
7277     0/imm32/no-disp32
7278     0/imm32/no-output
7279     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
7280 _Primitive-loop:
7281     "loop"/imm32/name
7282     0/imm32/inouts
7283     0/imm32/outputs
7284     "e9/jump loop/disp32"/imm32/subx-name
7285     0/imm32/no-rm32
7286     0/imm32/no-r32
7287     0/imm32/no-imm32
7288     0/imm32/no-disp32
7289     0/imm32/no-output
7290     _Primitive-break-if-addr<-named/imm32/next
7291 # - branches to named blocks
7292 _Primitive-break-if-addr<-named:
7293     "break-if-addr<"/imm32/name
7294     Single-lit-var/imm32/inouts
7295     0/imm32/outputs
7296     "0f 82/jump-if-addr<"/imm32/subx-name
7297     0/imm32/no-rm32
7298     0/imm32/no-r32
7299     0/imm32/no-imm32
7300     1/imm32/disp32-is-first-inout
7301     0/imm32/no-output
7302     _Primitive-break-if-addr>=-named/imm32/next
7303 _Primitive-break-if-addr>=-named:
7304     "break-if-addr>="/imm32/name
7305     Single-lit-var/imm32/inouts
7306     0/imm32/outputs
7307     "0f 83/jump-if-addr>="/imm32/subx-name
7308     0/imm32/no-rm32
7309     0/imm32/no-r32
7310     0/imm32/no-imm32
7311     1/imm32/disp32-is-first-inout
7312     0/imm32/no-output
7313     _Primitive-break-if-=-named/imm32/next
7314 _Primitive-break-if-=-named:
7315     "break-if-="/imm32/name
7316     Single-lit-var/imm32/inouts
7317     0/imm32/outputs
7318     "0f 84/jump-if-="/imm32/subx-name
7319     0/imm32/no-rm32
7320     0/imm32/no-r32
7321     0/imm32/no-imm32
7322     1/imm32/disp32-is-first-inout
7323     0/imm32/no-output
7324     _Primitive-break-if-!=-named/imm32/next
7325 _Primitive-break-if-!=-named:
7326     "break-if-!="/imm32/name
7327     Single-lit-var/imm32/inouts
7328     0/imm32/outputs
7329     "0f 85/jump-if-!="/imm32/subx-name
7330     0/imm32/no-rm32
7331     0/imm32/no-r32
7332     0/imm32/no-imm32
7333     1/imm32/disp32-is-first-inout
7334     0/imm32/no-output
7335     _Primitive-break-if-addr<=-named/imm32/next
7336 _Primitive-break-if-addr<=-named:
7337     "break-if-addr<="/imm32/name
7338     Single-lit-var/imm32/inouts
7339     0/imm32/outputs
7340     "0f 86/jump-if-addr<="/imm32/subx-name
7341     0/imm32/no-rm32
7342     0/imm32/no-r32
7343     0/imm32/no-imm32
7344     1/imm32/disp32-is-first-inout
7345     0/imm32/no-output
7346     _Primitive-break-if-addr>-named/imm32/next
7347 _Primitive-break-if-addr>-named:
7348     "break-if-addr>"/imm32/name
7349     Single-lit-var/imm32/inouts
7350     0/imm32/outputs
7351     "0f 87/jump-if-addr>"/imm32/subx-name
7352     0/imm32/no-rm32
7353     0/imm32/no-r32
7354     0/imm32/no-imm32
7355     1/imm32/disp32-is-first-inout
7356     0/imm32/no-output
7357     _Primitive-break-if-<-named/imm32/next
7358 _Primitive-break-if-<-named:
7359     "break-if-<"/imm32/name
7360     Single-lit-var/imm32/inouts
7361     0/imm32/outputs
7362     "0f 8c/jump-if-<"/imm32/subx-name
7363     0/imm32/no-rm32
7364     0/imm32/no-r32
7365     0/imm32/no-imm32
7366     1/imm32/disp32-is-first-inout
7367     0/imm32/no-output
7368     _Primitive-break-if->=-named/imm32/next
7369 _Primitive-break-if->=-named:
7370     "break-if->="/imm32/name
7371     Single-lit-var/imm32/inouts
7372     0/imm32/outputs
7373     "0f 8d/jump-if->="/imm32/subx-name
7374     0/imm32/no-rm32
7375     0/imm32/no-r32
7376     0/imm32/no-imm32
7377     1/imm32/disp32-is-first-inout
7378     0/imm32/no-output
7379     _Primitive-break-if-<=-named/imm32/next
7380 _Primitive-break-if-<=-named:
7381     "break-if-<="/imm32/name
7382     Single-lit-var/imm32/inouts
7383     0/imm32/outputs
7384     "0f 8e/jump-if-<="/imm32/subx-name
7385     0/imm32/no-rm32
7386     0/imm32/no-r32
7387     0/imm32/no-imm32
7388     1/imm32/disp32-is-first-inout
7389     0/imm32/no-output
7390     _Primitive-break-if->-named/imm32/next
7391 _Primitive-break-if->-named:
7392     "break-if->"/imm32/name
7393     Single-lit-var/imm32/inouts
7394     0/imm32/outputs
7395     "0f 8f/jump-if->"/imm32/subx-name
7396     0/imm32/no-rm32
7397     0/imm32/no-r32
7398     0/imm32/no-imm32
7399     1/imm32/disp32-is-first-inout
7400     0/imm32/no-output
7401     _Primitive-break-named/imm32/next
7402 _Primitive-break-named:
7403     "break"/imm32/name
7404     Single-lit-var/imm32/inouts
7405     0/imm32/outputs
7406     "e9/jump"/imm32/subx-name
7407     0/imm32/no-rm32
7408     0/imm32/no-r32
7409     0/imm32/no-imm32
7410     1/imm32/disp32-is-first-inout
7411     0/imm32/no-output
7412     _Primitive-loop-if-addr<-named/imm32/next
7413 _Primitive-loop-if-addr<-named:
7414     "loop-if-addr<"/imm32/name
7415     Single-lit-var/imm32/inouts
7416     0/imm32/outputs
7417     "0f 82/jump-if-addr<"/imm32/subx-name
7418     0/imm32/no-rm32
7419     0/imm32/no-r32
7420     0/imm32/no-imm32
7421     1/imm32/disp32-is-first-inout
7422     0/imm32/no-output
7423     _Primitive-loop-if-addr>=-named/imm32/next
7424 _Primitive-loop-if-addr>=-named:
7425     "loop-if-addr>="/imm32/name
7426     Single-lit-var/imm32/inouts
7427     0/imm32/outputs
7428     "0f 83/jump-if-addr>="/imm32/subx-name
7429     0/imm32/no-rm32
7430     0/imm32/no-r32
7431     0/imm32/no-imm32
7432     1/imm32/disp32-is-first-inout
7433     0/imm32/no-output
7434     _Primitive-loop-if-=-named/imm32/next
7435 _Primitive-loop-if-=-named:
7436     "loop-if-="/imm32/name
7437     Single-lit-var/imm32/inouts
7438     0/imm32/outputs
7439     "0f 84/jump-if-="/imm32/subx-name
7440     0/imm32/no-rm32
7441     0/imm32/no-r32
7442     0/imm32/no-imm32
7443     1/imm32/disp32-is-first-inout
7444     0/imm32/no-output
7445     _Primitive-loop-if-!=-named/imm32/next
7446 _Primitive-loop-if-!=-named:
7447     "loop-if-!="/imm32/name
7448     Single-lit-var/imm32/inouts
7449     0/imm32/outputs
7450     "0f 85/jump-if-!="/imm32/subx-name
7451     0/imm32/no-rm32
7452     0/imm32/no-r32
7453     0/imm32/no-imm32
7454     1/imm32/disp32-is-first-inout
7455     0/imm32/no-output
7456     _Primitive-loop-if-addr<=-named/imm32/next
7457 _Primitive-loop-if-addr<=-named:
7458     "loop-if-addr<="/imm32/name
7459     Single-lit-var/imm32/inouts
7460     0/imm32/outputs
7461     "0f 86/jump-if-addr<="/imm32/subx-name
7462     0/imm32/no-rm32
7463     0/imm32/no-r32
7464     0/imm32/no-imm32
7465     1/imm32/disp32-is-first-inout
7466     0/imm32/no-output
7467     _Primitive-loop-if-addr>-named/imm32/next
7468 _Primitive-loop-if-addr>-named:
7469     "loop-if-addr>"/imm32/name
7470     Single-lit-var/imm32/inouts
7471     0/imm32/outputs
7472     "0f 87/jump-if-addr>"/imm32/subx-name
7473     0/imm32/no-rm32
7474     0/imm32/no-r32
7475     0/imm32/no-imm32
7476     1/imm32/disp32-is-first-inout
7477     0/imm32/no-output
7478     _Primitive-loop-if-<-named/imm32/next
7479 _Primitive-loop-if-<-named:
7480     "loop-if-<"/imm32/name
7481     Single-lit-var/imm32/inouts
7482     0/imm32/outputs
7483     "0f 8c/jump-if-<"/imm32/subx-name
7484     0/imm32/no-rm32
7485     0/imm32/no-r32
7486     0/imm32/no-imm32
7487     1/imm32/disp32-is-first-inout
7488     0/imm32/no-output
7489     _Primitive-loop-if->=-named/imm32/next
7490 _Primitive-loop-if->=-named:
7491     "loop-if->="/imm32/name
7492     Single-lit-var/imm32/inouts
7493     0/imm32/outputs
7494     "0f 8d/jump-if->="/imm32/subx-name
7495     0/imm32/no-rm32
7496     0/imm32/no-r32
7497     0/imm32/no-imm32
7498     1/imm32/disp32-is-first-inout
7499     0/imm32/no-output
7500     _Primitive-loop-if-<=-named/imm32/next
7501 _Primitive-loop-if-<=-named:
7502     "loop-if-<="/imm32/name
7503     Single-lit-var/imm32/inouts
7504     0/imm32/outputs
7505     "0f 8e/jump-if-<="/imm32/subx-name
7506     0/imm32/no-rm32
7507     0/imm32/no-r32
7508     0/imm32/no-imm32
7509     1/imm32/disp32-is-first-inout
7510     0/imm32/no-output
7511     _Primitive-loop-if->-named/imm32/next
7512 _Primitive-loop-if->-named:
7513     "loop-if->"/imm32/name
7514     Single-lit-var/imm32/inouts
7515     0/imm32/outputs
7516     "0f 8f/jump-if->"/imm32/subx-name
7517     0/imm32/no-rm32
7518     0/imm32/no-r32
7519     0/imm32/no-imm32
7520     1/imm32/disp32-is-first-inout
7521     0/imm32/no-output
7522     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
7523 _Primitive-loop-named:
7524     "loop"/imm32/name
7525     Single-lit-var/imm32/inouts
7526     0/imm32/outputs
7527     "e9/jump"/imm32/subx-name
7528     0/imm32/no-rm32
7529     0/imm32/no-r32
7530     0/imm32/no-imm32
7531     1/imm32/disp32-is-first-inout
7532     0/imm32/no-output
7533     0/imm32/next
7534 
7535 Single-int-var-in-mem:
7536     Int-var-in-mem/imm32
7537     0/imm32/next
7538 
7539 Int-var-in-mem:
7540     "arg1"/imm32/name
7541     Type-int/imm32
7542     1/imm32/some-block-depth
7543     1/imm32/some-stack-offset
7544     0/imm32/no-register
7545 
7546 Two-args-int-stack-int-reg:
7547     Int-var-in-mem/imm32
7548     Single-int-var-in-some-register/imm32/next
7549 
7550 Two-args-int-reg-int-stack:
7551     Int-var-in-some-register/imm32
7552     Single-int-var-in-mem/imm32/next
7553 
7554 Two-args-int-eax-int-literal:
7555     Int-var-in-eax/imm32
7556     Single-lit-var/imm32/next
7557 
7558 Int-var-and-literal:
7559     Int-var-in-mem/imm32
7560     Single-lit-var/imm32/next
7561 
7562 Int-var-in-register-and-literal:
7563     Int-var-in-some-register/imm32
7564     Single-lit-var/imm32/next
7565 
7566 Single-int-var-in-some-register:
7567     Int-var-in-some-register/imm32
7568     0/imm32/next
7569 
7570 Int-var-in-some-register:
7571     "arg1"/imm32/name
7572     Type-int/imm32
7573     1/imm32/some-block-depth
7574     0/imm32/no-stack-offset
7575     Any-register/imm32
7576 
7577 Single-int-var-in-eax:
7578     Int-var-in-eax/imm32
7579     0/imm32/next
7580 
7581 Int-var-in-eax:
7582     "arg1"/imm32/name
7583     Type-int/imm32
7584     1/imm32/some-block-depth
7585     0/imm32/no-stack-offset
7586     "eax"/imm32/register
7587 
7588 Single-int-var-in-ecx:
7589     Int-var-in-ecx/imm32
7590     0/imm32/next
7591 
7592 Int-var-in-ecx:
7593     "arg1"/imm32/name
7594     Type-int/imm32
7595     1/imm32/some-block-depth
7596     0/imm32/no-stack-offset
7597     "ecx"/imm32/register
7598 
7599 Single-int-var-in-edx:
7600     Int-var-in-edx/imm32
7601     0/imm32/next
7602 
7603 Int-var-in-edx:
7604     "arg1"/imm32/name
7605     Type-int/imm32
7606     1/imm32/some-block-depth
7607     0/imm32/no-stack-offset
7608     "edx"/imm32/register
7609 
7610 Single-int-var-in-ebx:
7611     Int-var-in-ebx/imm32
7612     0/imm32/next
7613 
7614 Int-var-in-ebx:
7615     "arg1"/imm32/name
7616     Type-int/imm32
7617     1/imm32/some-block-depth
7618     0/imm32/no-stack-offset
7619     "ebx"/imm32/register
7620 
7621 Single-int-var-in-esi:
7622     Int-var-in-esi/imm32
7623     0/imm32/next
7624 
7625 Int-var-in-esi:
7626     "arg1"/imm32/name
7627     Type-int/imm32
7628     1/imm32/some-block-depth
7629     0/imm32/no-stack-offset
7630     "esi"/imm32/register
7631 
7632 Single-int-var-in-edi:
7633     Int-var-in-edi/imm32
7634     0/imm32/next
7635 
7636 Int-var-in-edi:
7637     "arg1"/imm32/name
7638     Type-int/imm32
7639     1/imm32/some-block-depth
7640     0/imm32/no-stack-offset
7641     "edi"/imm32/register
7642 
7643 Single-lit-var:
7644     Lit-var/imm32
7645     0/imm32/next
7646 
7647 Lit-var:
7648     "literal"/imm32/name
7649     Type-literal/imm32
7650     1/imm32/some-block-depth
7651     0/imm32/no-stack-offset
7652     0/imm32/no-register
7653 
7654 Type-int:
7655     1/imm32/left/int
7656     0/imm32/right/null
7657 
7658 Type-literal:
7659     0/imm32/left/literal
7660     0/imm32/right/null
7661 
7662 == code
7663 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle stmt), primitive: (handle function)
7664     # . prologue
7665     55/push-ebp
7666     89/<- %ebp 4/r32/esp
7667     # . save registers
7668     50/push-eax
7669     51/push-ecx
7670     # ecx = primitive
7671     8b/-> *(ebp+0x10) 1/r32/ecx
7672     # emit primitive name
7673     (emit-indent *(ebp+8) *Curr-block-depth)
7674     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
7675     # emit rm32 if necessary
7676     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
7677     # emit r32 if necessary
7678     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
7679     # emit imm32 if necessary
7680     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
7681     # emit disp32 if necessary
7682     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
7683     (write-buffered *(ebp+8) Newline)
7684 $emit-subx-primitive:end:
7685     # . restore registers
7686     59/pop-to-ecx
7687     58/pop-to-eax
7688     # . epilogue
7689     89/<- %esp 5/r32/ebp
7690     5d/pop-to-ebp
7691     c3/return
7692 
7693 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
7694     # . prologue
7695     55/push-ebp
7696     89/<- %ebp 4/r32/esp
7697     # . save registers
7698     50/push-eax
7699     # if (l == 0) return
7700     81 7/subop/compare *(ebp+0xc) 0/imm32
7701     74/jump-if-= $emit-subx-rm32:end/disp8
7702     # var v/eax: (handle var)
7703     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7704     (emit-subx-var-as-rm32 *(ebp+8) %eax)
7705 $emit-subx-rm32:end:
7706     # . restore registers
7707     58/pop-to-eax
7708     # . epilogue
7709     89/<- %esp 5/r32/ebp
7710     5d/pop-to-ebp
7711     c3/return
7712 
7713 get-stmt-operand-from-arg-location:  # stmt: (handle stmt), l: arg-location -> var/eax: (handle stmt-var)
7714     # . prologue
7715     55/push-ebp
7716     89/<- %ebp 4/r32/esp
7717     # . save registers
7718     51/push-ecx
7719     # eax = l
7720     8b/-> *(ebp+0xc) 0/r32/eax
7721     # ecx = stmt
7722     8b/-> *(ebp+8) 1/r32/ecx
7723     # if (l == 1) return stmt->inouts
7724     {
7725       3d/compare-eax-and 1/imm32
7726       75/jump-if-!= break/disp8
7727 $get-stmt-operand-from-arg-location:1:
7728       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
7729       eb/jump $get-stmt-operand-from-arg-location:end/disp8
7730     }
7731     # if (l == 2) return stmt->inouts->next
7732     {
7733       3d/compare-eax-and 2/imm32
7734       75/jump-if-!= break/disp8
7735 $get-stmt-operand-from-arg-location:2:
7736       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
7737       8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
7738       eb/jump $get-stmt-operand-from-arg-location:end/disp8
7739     }
7740     # if (l == 3) return stmt->outputs
7741     {
7742       3d/compare-eax-and 3/imm32
7743       75/jump-if-!= break/disp8
7744 $get-stmt-operand-from-arg-location:3:
7745       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
7746       eb/jump $get-stmt-operand-from-arg-location:end/disp8
7747     }
7748     # abort
7749     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
7750 $get-stmt-operand-from-arg-location:end:
7751     # . restore registers
7752     59/pop-to-ecx
7753     # . epilogue
7754     89/<- %esp 5/r32/ebp
7755     5d/pop-to-ebp
7756     c3/return
7757 
7758 $get-stmt-operand-from-arg-location:abort:
7759     # error("invalid arg-location " eax)
7760     (write-buffered Stderr "invalid arg-location ")
7761     (print-int32-buffered Stderr %eax)
7762     (write-buffered Stderr Newline)
7763     (flush Stderr)
7764     # . syscall(exit, 1)
7765     bb/copy-to-ebx  1/imm32
7766     b8/copy-to-eax  1/imm32/exit
7767     cd/syscall  0x80/imm8
7768     # never gets here
7769 
7770 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
7771     # . prologue
7772     55/push-ebp
7773     89/<- %ebp 4/r32/esp
7774     # . save registers
7775     50/push-eax
7776     51/push-ecx
7777     # if (location == 0) return
7778     81 7/subop/compare *(ebp+0xc) 0/imm32
7779     0f 84/jump-if-= $emit-subx-r32:end/disp32
7780     # var v/eax: (handle var)
7781     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7782     8b/-> *eax 0/r32/eax  # Stmt-var-value
7783     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
7784     (write-buffered *(ebp+8) Space)
7785     (print-int32-buffered *(ebp+8) *eax)
7786     (write-buffered *(ebp+8) "/r32")
7787 $emit-subx-r32:end:
7788     # . restore registers
7789     59/pop-to-ecx
7790     58/pop-to-eax
7791     # . epilogue
7792     89/<- %esp 5/r32/ebp
7793     5d/pop-to-ebp
7794     c3/return
7795 
7796 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
7797     # . prologue
7798     55/push-ebp
7799     89/<- %ebp 4/r32/esp
7800     # . save registers
7801     50/push-eax
7802     51/push-ecx
7803     # if (location == 0) return
7804     81 7/subop/compare *(ebp+0xc) 0/imm32
7805     74/jump-if-= $emit-subx-imm32:end/disp8
7806     # var v/eax: (handle var)
7807     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7808     8b/-> *eax 0/r32/eax  # Stmt-var-value
7809     (write-buffered *(ebp+8) Space)
7810     (write-buffered *(ebp+8) *eax)  # Var-name
7811     (write-buffered *(ebp+8) "/imm32")
7812 $emit-subx-imm32:end:
7813     # . restore registers
7814     59/pop-to-ecx
7815     58/pop-to-eax
7816     # . epilogue
7817     89/<- %esp 5/r32/ebp
7818     5d/pop-to-ebp
7819     c3/return
7820 
7821 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
7822     # . prologue
7823     55/push-ebp
7824     89/<- %ebp 4/r32/esp
7825     # . save registers
7826     50/push-eax
7827     51/push-ecx
7828     # if (location == 0) return
7829     81 7/subop/compare *(ebp+0xc) 0/imm32
7830     0f 84/jump-if-= $emit-subx-disp32:end/disp32
7831     # var v/eax: (handle var)
7832     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
7833     8b/-> *eax 0/r32/eax  # Stmt-var-value
7834     (write-buffered *(ebp+8) Space)
7835     (write-buffered *(ebp+8) *eax)  # Var-name
7836     # hack: if instruction operation starts with "break", emit ":break"
7837     # var name/ecx: (addr array byte) = stmt->operation
7838     8b/-> *(ebp+0x10) 0/r32/eax
7839     8b/-> *(eax+4) 1/r32/ecx
7840     {
7841       (string-starts-with? %ecx "break")  # => eax
7842       3d/compare-eax-and 0/imm32/false
7843       74/jump-if-= break/disp8
7844       (write-buffered *(ebp+8) ":break")
7845     }
7846     # hack: if instruction operation starts with "loop", emit ":loop"
7847     {
7848       (string-starts-with? %ecx "loop")  # => eax
7849       3d/compare-eax-and 0/imm32/false
7850       74/jump-if-= break/disp8
7851       (write-buffered *(ebp+8) ":loop")
7852     }
7853     (write-buffered *(ebp+8) "/disp32")
7854 $emit-subx-disp32:end:
7855     # . restore registers
7856     59/pop-to-ecx
7857     58/pop-to-eax
7858     # . epilogue
7859     89/<- %esp 5/r32/ebp
7860     5d/pop-to-ebp
7861     c3/return
7862 
7863 emit-subx-call:  # out: (addr buffered-file), stmt: (handle stmt), callee: (handle function)
7864     # . prologue
7865     55/push-ebp
7866     89/<- %ebp 4/r32/esp
7867     # . save registers
7868     51/push-ecx
7869     #
7870     (emit-indent *(ebp+8) *Curr-block-depth)
7871     (write-buffered *(ebp+8) "(")
7872     # - emit function name
7873     8b/-> *(ebp+0x10) 1/r32/ecx
7874     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
7875     # - emit arguments
7876     # var curr/ecx: (handle stmt-var) = stmt->inouts
7877     8b/-> *(ebp+0xc) 1/r32/ecx
7878     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
7879     {
7880       # if (curr == null) break
7881       81 7/subop/compare %ecx 0/imm32
7882       74/jump-if-= break/disp8
7883       #
7884       (emit-subx-call-operand *(ebp+8) %ecx)
7885       # curr = curr->next
7886       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
7887       eb/jump loop/disp8
7888     }
7889     #
7890     (write-buffered *(ebp+8) ")\n")
7891 $emit-subx-call:end:
7892     # . restore registers
7893     59/pop-to-ecx
7894     # . epilogue
7895     89/<- %esp 5/r32/ebp
7896     5d/pop-to-ebp
7897     c3/return
7898 
7899 # like a function call, except we have no idea what function it is
7900 # we hope it's defined in SubX and that the types are ok
7901 emit-hailmary-call:  # out: (addr buffered-file), stmt: (handle stmt)
7902     # . prologue
7903     55/push-ebp
7904     89/<- %ebp 4/r32/esp
7905     # . save registers
7906     51/push-ecx
7907     #
7908     (emit-indent *(ebp+8) *Curr-block-depth)
7909     (write-buffered *(ebp+8) "(")
7910     # ecx = stmt
7911     8b/-> *(ebp+0xc) 1/r32/ecx
7912     # - emit function name
7913     (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
7914     # - emit arguments
7915     # var curr/ecx: (handle stmt-var) = stmt->inouts
7916     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
7917     {
7918       # if (curr == null) break
7919       81 7/subop/compare %ecx 0/imm32
7920       74/jump-if-= break/disp8
7921       #
7922       (emit-subx-call-operand *(ebp+8) %ecx)
7923       # curr = curr->next
7924       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
7925       eb/jump loop/disp8
7926     }
7927     #
7928     (write-buffered *(ebp+8) ")\n")
7929 $emit-hailmary-call:end:
7930     # . restore registers
7931     59/pop-to-ecx
7932     # . epilogue
7933     89/<- %esp 5/r32/ebp
7934     5d/pop-to-ebp
7935     c3/return
7936 
7937 emit-subx-call-operand:  # out: (addr buffered-file), s: (handle stmt-var)
7938     # shares code with emit-subx-var-as-rm32
7939     # . prologue
7940     55/push-ebp
7941     89/<- %ebp 4/r32/esp
7942     # . save registers
7943     50/push-eax
7944     51/push-ecx
7945     56/push-esi
7946     # ecx = s
7947     8b/-> *(ebp+0xc) 1/r32/ecx
7948     # var operand/esi: (handle var) = s->value
7949     8b/-> *ecx 6/r32/esi  # Stmt-var-value
7950     # if (operand->register && s->is-deref?) emit "*__"
7951     {
7952 $emit-subx-call-operand:check-for-register-indirect:
7953       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7954       74/jump-if-= break/disp8
7955       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7956       74/jump-if-= break/disp8
7957 $emit-subx-call-operand:register-indirect:
7958       (write-buffered *(ebp+8) " *")
7959       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
7960       e9/jump $emit-subx-call-operand:end/disp32
7961     }
7962     # if (operand->register && !s->is-deref?) emit "%__"
7963     {
7964 $emit-subx-call-operand:check-for-register-direct:
7965       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
7966       74/jump-if-= break/disp8
7967       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
7968       75/jump-if-!= break/disp8
7969 $emit-subx-call-operand:register-direct:
7970       (write-buffered *(ebp+8) " %")
7971       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
7972       e9/jump $emit-subx-call-operand:end/disp32
7973     }
7974     # else if (operand->stack-offset) emit "*(ebp+__)"
7975     {
7976       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
7977       74/jump-if-= break/disp8
7978 $emit-subx-call-operand:stack:
7979       (write-buffered *(ebp+8) Space)
7980       (write-buffered *(ebp+8) "*(ebp+")
7981       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
7982       (write-buffered *(ebp+8) ")")
7983       e9/jump $emit-subx-call-operand:end/disp32
7984     }
7985     # else if (operand->type == literal) emit "__"
7986     {
7987       8b/-> *(esi+4) 0/r32/eax  # Var-type
7988       81 7/subop/compare *eax 0/imm32  # Tree-left
7989       75/jump-if-!= break/disp8
7990 $emit-subx-call-operand:literal:
7991       (write-buffered *(ebp+8) Space)
7992       (write-buffered *(ebp+8) *esi)
7993     }
7994 $emit-subx-call-operand:end:
7995     # . restore registers
7996     5e/pop-to-esi
7997     59/pop-to-ecx
7998     58/pop-to-eax
7999     # . epilogue
8000     89/<- %esp 5/r32/ebp
8001     5d/pop-to-ebp
8002     c3/return
8003 
8004 emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (handle stmt-var)
8005     # . prologue
8006     55/push-ebp
8007     89/<- %ebp 4/r32/esp
8008     # . save registers
8009     50/push-eax
8010     51/push-ecx
8011     56/push-esi
8012     # ecx = s
8013     8b/-> *(ebp+0xc) 1/r32/ecx
8014     # var operand/esi: (handle var) = s->value
8015     8b/-> *ecx 6/r32/esi  # Stmt-var-value
8016     # if (operand->register && s->is-deref?) emit "*__"
8017     {
8018 $emit-subx-var-as-rm32:check-for-register-indirect:
8019       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
8020       74/jump-if-= break/disp8
8021       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
8022       74/jump-if-= break/disp8
8023 $emit-subx-var-as-rm32:register-indirect:
8024       (write-buffered *(ebp+8) " *")
8025       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
8026     }
8027     # if (operand->register && !s->is-deref?) emit "%__"
8028     {
8029 $emit-subx-var-as-rm32:check-for-register-direct:
8030       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
8031       74/jump-if-= break/disp8
8032       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
8033       75/jump-if-!= break/disp8
8034 $emit-subx-var-as-rm32:register-direct:
8035       (write-buffered *(ebp+8) " %")
8036       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
8037     }
8038     # else if (operand->stack-offset) emit "*(ebp+__)"
8039     {
8040       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
8041       74/jump-if-= break/disp8
8042 $emit-subx-var-as-rm32:stack:
8043       (write-buffered *(ebp+8) Space)
8044       (write-buffered *(ebp+8) "*(ebp+")
8045       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
8046       (write-buffered *(ebp+8) ")")
8047     }
8048 $emit-subx-var-as-rm32:end:
8049     # . restore registers
8050     5e/pop-to-esi
8051     59/pop-to-ecx
8052     58/pop-to-eax
8053     # . epilogue
8054     89/<- %esp 5/r32/ebp
8055     5d/pop-to-ebp
8056     c3/return
8057 
8058 find-matching-function:  # functions: (addr function), stmt: (handle stmt) -> result/eax: (handle function)
8059     # . prologue
8060     55/push-ebp
8061     89/<- %ebp 4/r32/esp
8062     # . save registers
8063     51/push-ecx
8064     # var curr/ecx: (handle function) = functions
8065     8b/-> *(ebp+8) 1/r32/ecx
8066     {
8067       # if (curr == null) break
8068       81 7/subop/compare %ecx 0/imm32
8069       74/jump-if-= break/disp8
8070       # if match(stmt, curr) return curr
8071       {
8072         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
8073         3d/compare-eax-and 0/imm32/false
8074         74/jump-if-= break/disp8
8075         89/<- %eax 1/r32/ecx
8076         eb/jump $find-matching-function:end/disp8
8077       }
8078       # curr = curr->next
8079       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
8080       eb/jump loop/disp8
8081     }
8082     # return null
8083     b8/copy-to-eax 0/imm32
8084 $find-matching-function:end:
8085     # . restore registers
8086     59/pop-to-ecx
8087     # . epilogue
8088     89/<- %esp 5/r32/ebp
8089     5d/pop-to-ebp
8090     c3/return
8091 
8092 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle stmt) -> result/eax: (handle primitive)
8093     # . prologue
8094     55/push-ebp
8095     89/<- %ebp 4/r32/esp
8096     # . save registers
8097     51/push-ecx
8098     # var curr/ecx: (handle primitive) = primitives
8099     8b/-> *(ebp+8) 1/r32/ecx
8100     {
8101 $find-matching-primitive:loop:
8102       # if (curr == null) break
8103       81 7/subop/compare %ecx 0/imm32
8104       0f 84/jump-if-= break/disp32
8105 #?       (write-buffered Stderr "prim: ")
8106 #?       (write-buffered Stderr *ecx)  # Primitive-name
8107 #?       (write-buffered Stderr " => ")
8108 #?       (write-buffered Stderr *(ecx+0xc))  # Primitive-subx-name
8109 #?       (write-buffered Stderr Newline)
8110 #?       (flush Stderr)
8111       # if match(curr, stmt) return curr
8112       {
8113         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
8114         3d/compare-eax-and 0/imm32/false
8115         74/jump-if-= break/disp8
8116         89/<- %eax 1/r32/ecx
8117         eb/jump $find-matching-primitive:end/disp8
8118       }
8119 $find-matching-primitive:next-primitive:
8120       # curr = curr->next
8121       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
8122       e9/jump loop/disp32
8123     }
8124     # return null
8125     b8/copy-to-eax 0/imm32
8126 $find-matching-primitive:end:
8127     # . restore registers
8128     59/pop-to-ecx
8129     # . epilogue
8130     89/<- %esp 5/r32/ebp
8131     5d/pop-to-ebp
8132     c3/return
8133 
8134 mu-stmt-matches-function?:  # stmt: (handle stmt), function: (handle function) -> result/eax: boolean
8135     # . prologue
8136     55/push-ebp
8137     89/<- %ebp 4/r32/esp
8138     # . save registers
8139     51/push-ecx
8140     # return function->name == stmt->operation
8141     8b/-> *(ebp+8) 1/r32/ecx
8142     8b/-> *(ebp+0xc) 0/r32/eax
8143     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Function-name => eax
8144 $mu-stmt-matches-function?:end:
8145     # . restore registers
8146     59/pop-to-ecx
8147     # . epilogue
8148     89/<- %esp 5/r32/ebp
8149     5d/pop-to-ebp
8150     c3/return
8151 
8152 mu-stmt-matches-primitive?:  # stmt: (handle stmt), primitive: (handle primitive) -> result/eax: boolean
8153     # A mu stmt matches a primitive if the name matches, all the inout vars
8154     # match, and all the output vars match.
8155     # Vars match if types match and registers match.
8156     # In addition, a stmt output matches a primitive's output if types match
8157     # and the primitive has a wildcard register.
8158     # . prologue
8159     55/push-ebp
8160     89/<- %ebp 4/r32/esp
8161     # . save registers
8162     51/push-ecx
8163     52/push-edx
8164     53/push-ebx
8165     56/push-esi
8166     57/push-edi
8167     # ecx = stmt
8168     8b/-> *(ebp+8) 1/r32/ecx
8169     # edx = primitive
8170     8b/-> *(ebp+0xc) 2/r32/edx
8171     {
8172 $mu-stmt-matches-primitive?:check-name:
8173       # if (primitive->name != stmt->operation) return false
8174       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
8175       3d/compare-eax-and 0/imm32/false
8176       75/jump-if-!= break/disp8
8177       b8/copy-to-eax 0/imm32
8178       e9/jump $mu-stmt-matches-primitive?:end/disp32
8179     }
8180 $mu-stmt-matches-primitive?:check-inouts:
8181     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
8182     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
8183     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
8184     {
8185       # if (curr == 0 && curr2 == 0) move on to check outputs
8186       {
8187         81 7/subop/compare %esi 0/imm32
8188         75/jump-if-!= break/disp8
8189 $mu-stmt-matches-primitive?:stmt-inout-is-null:
8190         {
8191           81 7/subop/compare %edi 0/imm32
8192           75/jump-if-!= break/disp8
8193           #
8194           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
8195         }
8196         # return false
8197         b8/copy-to-eax 0/imm32/false
8198         e9/jump $mu-stmt-matches-primitive?:end/disp32
8199       }
8200       # if (curr2 == 0) return false
8201       {
8202         81 7/subop/compare %edi 0/imm32
8203         75/jump-if-!= break/disp8
8204 $mu-stmt-matches-primitive?:prim-inout-is-null:
8205         b8/copy-to-eax 0/imm32/false
8206         e9/jump $mu-stmt-matches-primitive?:end/disp32
8207       }
8208       # if (curr != curr2) return false
8209       {
8210         (operand-matches-primitive? %esi *edi)  # List-value => eax
8211         3d/compare-eax-and 0/imm32/false
8212         75/jump-if-!= break/disp8
8213         b8/copy-to-eax 0/imm32/false
8214         e9/jump $mu-stmt-matches-primitive?:end/disp32
8215       }
8216       # curr=curr->next
8217       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
8218       # curr2=curr2->next
8219       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
8220       eb/jump loop/disp8
8221     }
8222 $mu-stmt-matches-primitive?:check-outputs:
8223     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
8224     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
8225     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
8226     {
8227       # if (curr == 0) return (curr2 == 0)
8228       {
8229 $mu-stmt-matches-primitive?:check-output:
8230         81 7/subop/compare %esi 0/imm32
8231         75/jump-if-!= break/disp8
8232         {
8233           81 7/subop/compare %edi 0/imm32
8234           75/jump-if-!= break/disp8
8235           # return true
8236           b8/copy-to-eax 1/imm32
8237           e9/jump $mu-stmt-matches-primitive?:end/disp32
8238         }
8239         # return false
8240         b8/copy-to-eax 0/imm32
8241         e9/jump $mu-stmt-matches-primitive?:end/disp32
8242       }
8243       # if (curr2 == 0) return false
8244       {
8245         81 7/subop/compare %edi 0/imm32
8246         75/jump-if-!= break/disp8
8247         b8/copy-to-eax 0/imm32
8248         e9/jump $mu-stmt-matches-primitive?:end/disp32
8249       }
8250       # if (curr != curr2) return false
8251       {
8252         (operand-matches-primitive? %esi *edi)  # List-value => eax
8253         3d/compare-eax-and 0/imm32/false
8254         75/jump-if-!= break/disp8
8255         b8/copy-to-eax 0/imm32
8256         e9/jump $mu-stmt-matches-primitive?:end/disp32
8257       }
8258       # curr=curr->next
8259       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
8260       # curr2=curr2->next
8261       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
8262       eb/jump loop/disp8
8263     }
8264 $mu-stmt-matches-primitive?:return-true:
8265     b8/copy-to-eax 1/imm32
8266 $mu-stmt-matches-primitive?:end:
8267     # . restore registers
8268     5f/pop-to-edi
8269     5e/pop-to-esi
8270     5b/pop-to-ebx
8271     5a/pop-to-edx
8272     59/pop-to-ecx
8273     # . epilogue
8274     89/<- %esp 5/r32/ebp
8275     5d/pop-to-ebp
8276     c3/return
8277 
8278 operand-matches-primitive?:  # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
8279     # . prologue
8280     55/push-ebp
8281     89/<- %ebp 4/r32/esp
8282     # . save registers
8283     51/push-ecx
8284     56/push-esi
8285     57/push-edi
8286     # ecx = s
8287     8b/-> *(ebp+8) 1/r32/ecx
8288     # var var/esi : (handle var) = s->value
8289     8b/-> *ecx 6/r32/esi  # Stmt-var-value
8290     # edi = prim-var
8291     8b/-> *(ebp+0xc) 7/r32/edi
8292 $operand-matches-primitive?:check-type:
8293     # if (var->type != prim-var->type) return false
8294     (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
8295     3d/compare-eax-and 0/imm32/false
8296     b8/copy-to-eax 0/imm32/false
8297     0f 84/jump-if-= $operand-matches-primitive?:end/disp32
8298     {
8299 $operand-matches-primitive?:check-register:
8300       # if prim-var is in memory and var is in register but dereference, match
8301       {
8302         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
8303         0f 85/jump-if-!= break/disp32
8304         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
8305         74/jump-if-= break/disp8
8306         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
8307         74/jump-if-= break/disp8
8308         e9/jump $operand-matches-primitive?:return-true/disp32
8309       }
8310       # if prim-var is in register and var is in register but dereference, no match
8311       {
8312         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
8313         0f 84/jump-if-= break/disp32
8314         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
8315         0f 84/jump-if-= break/disp32
8316         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
8317         74/jump-if-= break/disp8
8318         e9/jump $operand-matches-primitive?:return-false/disp32
8319       }
8320       # return false if var->register doesn't match prim-var->register
8321       {
8322         # if register addresses are equal, it's a match
8323         8b/-> *(esi+0x10) 0/r32/eax  # Var-register
8324         39/compare *(edi+0x10) 0/r32/eax  # Var-register
8325         74/jump-if-= break/disp8
8326         # if either address is 0, return false
8327         3d/compare-eax-and 0/imm32
8328         74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
8329         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
8330         74/jump-if-=  $operand-matches-primitive?:return-false/disp8
8331         # if prim-var->register is wildcard, it's a match
8332         (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
8333         3d/compare-eax-and 0/imm32/false
8334         75/jump-if-!= break/disp8
8335         # if string contents aren't equal, return false
8336         (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
8337         3d/compare-eax-and 0/imm32/false
8338         74/jump-if-= $operand-matches-primitive?:return-false/disp8
8339       }
8340     }
8341 $operand-matches-primitive?:return-true:
8342     b8/copy-to-eax 1/imm32/true
8343     eb/jump $operand-matches-primitive?:end/disp8
8344 $operand-matches-primitive?:return-false:
8345     b8/copy-to-eax 0/imm32/false
8346 $operand-matches-primitive?:end:
8347     # . restore registers
8348     5f/pop-to-edi
8349     5e/pop-to-esi
8350     59/pop-to-ecx
8351     # . epilogue
8352     89/<- %esp 5/r32/ebp
8353     5d/pop-to-ebp
8354     c3/return
8355 
8356 subx-type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
8357     # . prologue
8358     55/push-ebp
8359     89/<- %ebp 4/r32/esp
8360     # . save registers
8361     51/push-ecx
8362     # var alit/ecx: boolean = is-literal-type?(a)
8363     (is-literal-type? *(ebp+8))  # => eax
8364     89/<- %ecx 0/r32/eax
8365     # var blit/eax: boolean = is-literal-type?(b)
8366     (is-literal-type? *(ebp+0xc))  # => eax
8367     # return alit == blit
8368     39/compare %eax 1/r32/ecx
8369     74/jump-if-= $subx-type-equal?:true/disp8
8370 $subx-type-equal?:false:
8371     b8/copy-to-eax 0/imm32/false
8372     eb/jump $subx-type-equal?:end/disp8
8373 $subx-type-equal?:true:
8374     b8/copy-to-eax 1/imm32/true
8375 $subx-type-equal?:end:
8376     # . restore registers
8377     59/pop-to-ecx
8378     # . epilogue
8379     89/<- %esp 5/r32/ebp
8380     5d/pop-to-ebp
8381     c3/return
8382 
8383 is-literal-type?:  # a: (handle tree type-id) -> result/eax: boolean
8384     # . prologue
8385     55/push-ebp
8386     89/<- %ebp 4/r32/esp
8387     #
8388     8b/-> *(ebp+8) 0/r32/eax
8389     8b/-> *eax 0/r32/eax  # Atom-value
8390     3d/compare-eax-and 0/imm32/false
8391     74/jump-if-equal $is-literal-type?:end/disp8
8392     b8/copy-to-eax 1/imm32/true
8393 $is-literal-type?:end:
8394     # . epilogue
8395     89/<- %esp 5/r32/ebp
8396     5d/pop-to-ebp
8397     c3/return
8398 
8399 test-emit-subx-stmt-primitive:
8400     # Primitive operation on a variable on the stack.
8401     #   increment foo
8402     # =>
8403     #   ff 0/subop/increment *(ebp-8)
8404     #
8405     # There's a variable on the var stack as follows:
8406     #   name: 'foo'
8407     #   type: int
8408     #   stack-offset: -8
8409     #
8410     # There's a primitive with this info:
8411     #   name: 'increment'
8412     #   inouts: int/mem
8413     #   value: 'ff 0/subop/increment'
8414     #
8415     # There's nothing in functions.
8416     #
8417     # . prologue
8418     55/push-ebp
8419     89/<- %ebp 4/r32/esp
8420     # setup
8421     (clear-stream _test-output-stream)
8422     (clear-stream $_test-output-buffered-file->buffer)
8423     # var type/ecx: (handle tree type-id) = int
8424     68/push 0/imm32/right/null
8425     68/push 1/imm32/left/int
8426     89/<- %ecx 4/r32/esp
8427     # var var-foo/ecx: var
8428     68/push 0/imm32/no-register
8429     68/push -8/imm32/stack-offset
8430     68/push 1/imm32/block-depth
8431     51/push-ecx
8432     68/push "foo"/imm32
8433     89/<- %ecx 4/r32/esp
8434     # var operand/ebx: (handle stmt-var)
8435     68/push 0/imm32/is-deref:false
8436     68/push 0/imm32/next
8437     51/push-ecx/var-foo
8438     89/<- %ebx 4/r32/esp
8439     # var stmt/esi: statement
8440     68/push 0/imm32/next
8441     68/push 0/imm32/outputs
8442     53/push-ebx/operands
8443     68/push "increment"/imm32/operation
8444     68/push 1/imm32
8445     89/<- %esi 4/r32/esp
8446     # var primitives/ebx: primitive
8447     68/push 0/imm32/next
8448     68/push 0/imm32/output-is-write-only
8449     68/push 0/imm32/no-disp32
8450     68/push 0/imm32/no-imm32
8451     68/push 0/imm32/no-r32
8452     68/push 1/imm32/rm32-is-first-inout
8453     68/push "ff 0/subop/increment"/imm32/subx-name
8454     68/push 0/imm32/outputs
8455     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
8456     68/push "increment"/imm32/name
8457     89/<- %ebx 4/r32/esp
8458     # convert
8459     c7 0/subop/copy *Curr-block-depth 0/imm32
8460     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
8461     (flush _test-output-buffered-file)
8462 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8468     # check output
8469     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
8470     # . epilogue
8471     89/<- %esp 5/r32/ebp
8472     5d/pop-to-ebp
8473     c3/return
8474 
8475 test-emit-subx-stmt-primitive-register:
8476     # Primitive operation on a variable in a register.
8477     #   foo <- increment
8478     # =>
8479     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
8480     #
8481     # There's a variable on the var stack as follows:
8482     #   name: 'foo'
8483     #   type: int
8484     #   register: 'eax'
8485     #
8486     # There's a primitive with this info:
8487     #   name: 'increment'
8488     #   out: int/reg
8489     #   value: 'ff 0/subop/increment'
8490     #
8491     # There's nothing in functions.
8492     #
8493     # . prologue
8494     55/push-ebp
8495     89/<- %ebp 4/r32/esp
8496     # setup
8497     (clear-stream _test-output-stream)
8498     (clear-stream $_test-output-buffered-file->buffer)
8499     # var type/ecx: (handle tree type-id) = int
8500     68/push 0/imm32/right/null
8501     68/push 1/imm32/left/int
8502     89/<- %ecx 4/r32/esp
8503     # var var-foo/ecx: var in eax
8504     68/push "eax"/imm32/register
8505     68/push 0/imm32/no-stack-offset
8506     68/push 1/imm32/block-depth
8507     51/push-ecx
8508     68/push "foo"/imm32
8509     89/<- %ecx 4/r32/esp
8510     # var operand/ebx: (handle stmt-var)
8511     68/push 0/imm32/is-deref:false
8512     68/push 0/imm32/next
8513     51/push-ecx/var-foo
8514     89/<- %ebx 4/r32/esp
8515     # var stmt/esi: statement
8516     68/push 0/imm32/next
8517     53/push-ebx/outputs
8518     68/push 0/imm32/inouts
8519     68/push "increment"/imm32/operation
8520     68/push 1/imm32
8521     89/<- %esi 4/r32/esp
8522     # var formal-var/ebx: var in any register
8523     68/push Any-register/imm32
8524     68/push 0/imm32/no-stack-offset
8525     68/push 1/imm32/block-depth
8526     ff 6/subop/push *(ecx+4)  # Var-type
8527     68/push "dummy"/imm32
8528     89/<- %ebx 4/r32/esp
8529     # var operand/ebx: (handle stmt-var)
8530     68/push 0/imm32/is-deref:false
8531     68/push 0/imm32/next
8532     53/push-ebx/formal-var
8533     89/<- %ebx 4/r32/esp
8534     # var primitives/ebx: primitive
8535     68/push 0/imm32/next
8536     68/push 0/imm32/output-is-write-only
8537     68/push 0/imm32/no-disp32
8538     68/push 0/imm32/no-imm32
8539     68/push 0/imm32/no-r32
8540     68/push 3/imm32/rm32-in-first-output
8541     68/push "ff 0/subop/increment"/imm32/subx-name
8542     53/push-ebx/outputs
8543     68/push 0/imm32/inouts
8544     68/push "increment"/imm32/name
8545     89/<- %ebx 4/r32/esp
8546     # convert
8547     c7 0/subop/copy *Curr-block-depth 0/imm32
8548     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
8549     (flush _test-output-buffered-file)
8550 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8556     # check output
8557     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
8558     # . epilogue
8559     89/<- %esp 5/r32/ebp
8560     5d/pop-to-ebp
8561     c3/return
8562 
8563 test-emit-subx-stmt-select-primitive:
8564     # Select the right primitive between overloads.
8565     #   foo <- increment
8566     # =>
8567     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
8568     #
8569     # There's a variable on the var stack as follows:
8570     #   name: 'foo'
8571     #   type: int
8572     #   register: 'eax'
8573     #
8574     # There's two primitives, as follows:
8575     #   - name: 'increment'
8576     #     out: int/reg
8577     #     value: 'ff 0/subop/increment'
8578     #   - name: 'increment'
8579     #     inout: int/mem
8580     #     value: 'ff 0/subop/increment'
8581     #
8582     # There's nothing in functions.
8583     #
8584     # . prologue
8585     55/push-ebp
8586     89/<- %ebp 4/r32/esp
8587     # setup
8588     (clear-stream _test-output-stream)
8589     (clear-stream $_test-output-buffered-file->buffer)
8590     # var type/ecx: (handle tree type-id) = int
8591     68/push 0/imm32/right/null
8592     68/push 1/imm32/left/int
8593     89/<- %ecx 4/r32/esp
8594     # var var-foo/ecx: var in eax
8595     68/push "eax"/imm32/register
8596     68/push 0/imm32/no-stack-offset
8597     68/push 1/imm32/block-depth
8598     51/push-ecx
8599     68/push "foo"/imm32
8600     89/<- %ecx 4/r32/esp
8601     # var real-outputs/edi: (handle stmt-var)
8602     68/push 0/imm32/is-deref:false
8603     68/push 0/imm32/next
8604     51/push-ecx/var-foo
8605     89/<- %edi 4/r32/esp
8606     # var stmt/esi: statement
8607     68/push 0/imm32/next
8608     57/push-edi/outputs
8609     68/push 0/imm32/inouts
8610     68/push "increment"/imm32/operation
8611     68/push 1/imm32
8612     89/<- %esi 4/r32/esp
8613     # var formal-var/ebx: var in any register
8614     68/push Any-register/imm32
8615     68/push 0/imm32/no-stack-offset
8616     68/push 1/imm32/block-depth
8617     ff 6/subop/push *(ecx+4)  # Var-type
8618     68/push "dummy"/imm32
8619     89/<- %ebx 4/r32/esp
8620     # var formal-outputs/ebx: (handle stmt-var)
8621     68/push 0/imm32/is-deref:false
8622     68/push 0/imm32/next
8623     53/push-ebx/formal-var
8624     89/<- %ebx 4/r32/esp
8625     # var primitive1/ebx: primitive
8626     68/push 0/imm32/next
8627     68/push 0/imm32/output-is-write-only
8628     68/push 0/imm32/no-disp32
8629     68/push 0/imm32/no-imm32
8630     68/push 0/imm32/no-r32
8631     68/push 3/imm32/rm32-in-first-output
8632     68/push "ff 0/subop/increment"/imm32/subx-name
8633     53/push-ebx/outputs/formal-outputs
8634     68/push 0/imm32/inouts
8635     68/push "increment"/imm32/name
8636     89/<- %ebx 4/r32/esp
8637     # var primitives/ebx: primitive
8638     53/push-ebx/next
8639     68/push 0/imm32/output-is-write-only
8640     68/push 0/imm32/no-disp32
8641     68/push 0/imm32/no-imm32
8642     68/push 0/imm32/no-r32
8643     68/push 1/imm32/rm32-is-first-inout
8644     68/push "ff 0/subop/increment"/imm32/subx-name
8645     68/push 0/imm32/outputs
8646     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
8647     68/push "increment"/imm32/name
8648     89/<- %ebx 4/r32/esp
8649     # convert
8650     c7 0/subop/copy *Curr-block-depth 0/imm32
8651     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
8652     (flush _test-output-buffered-file)
8653 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8659     # check output
8660     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
8661     # . epilogue
8662     89/<- %esp 5/r32/ebp
8663     5d/pop-to-ebp
8664     c3/return
8665 
8666 test-emit-subx-stmt-select-primitive-2:
8667     # Select the right primitive between overloads.
8668     #   foo <- increment
8669     # =>
8670     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
8671     #
8672     # There's a variable on the var stack as follows:
8673     #   name: 'foo'
8674     #   type: int
8675     #   register: 'eax'
8676     #
8677     # There's two primitives, as follows:
8678     #   - name: 'increment'
8679     #     out: int/reg
8680     #     value: 'ff 0/subop/increment'
8681     #   - name: 'increment'
8682     #     inout: int/mem
8683     #     value: 'ff 0/subop/increment'
8684     #
8685     # There's nothing in functions.
8686     #
8687     # . prologue
8688     55/push-ebp
8689     89/<- %ebp 4/r32/esp
8690     # setup
8691     (clear-stream _test-output-stream)
8692     (clear-stream $_test-output-buffered-file->buffer)
8693     # var type/ecx: (handle tree type-id) = int
8694     68/push 0/imm32/right/null
8695     68/push 1/imm32/left/int
8696     89/<- %ecx 4/r32/esp
8697     # var var-foo/ecx: var in eax
8698     68/push "eax"/imm32/register
8699     68/push 0/imm32/no-stack-offset
8700     68/push 1/imm32/block-depth
8701     51/push-ecx
8702     68/push "foo"/imm32
8703     89/<- %ecx 4/r32/esp
8704     # var inouts/edi: (handle stmt-var)
8705     68/push 0/imm32/is-deref:false
8706     68/push 0/imm32/next
8707     51/push-ecx/var-foo
8708     89/<- %edi 4/r32/esp
8709     # var stmt/esi: statement
8710     68/push 0/imm32/next
8711     68/push 0/imm32/outputs
8712     57/push-edi/inouts
8713     68/push "increment"/imm32/operation
8714     68/push 1/imm32
8715     89/<- %esi 4/r32/esp
8716     # var formal-var/ebx: var in any register
8717     68/push Any-register/imm32
8718     68/push 0/imm32/no-stack-offset
8719     68/push 1/imm32/block-depth
8720     ff 6/subop/push *(ecx+4)  # Var-type
8721     68/push "dummy"/imm32
8722     89/<- %ebx 4/r32/esp
8723     # var operand/ebx: (handle stmt-var)
8724     68/push 0/imm32/is-deref:false
8725     68/push 0/imm32/next
8726     53/push-ebx/formal-var
8727     89/<- %ebx 4/r32/esp
8728     # var primitive1/ebx: primitive
8729     68/push 0/imm32/next
8730     68/push 0/imm32/output-is-write-only
8731     68/push 0/imm32/no-disp32
8732     68/push 0/imm32/no-imm32
8733     68/push 0/imm32/no-r32
8734     68/push 3/imm32/rm32-in-first-output
8735     68/push "ff 0/subop/increment"/imm32/subx-name
8736     53/push-ebx/outputs/formal-outputs
8737     68/push 0/imm32/inouts
8738     68/push "increment"/imm32/name
8739     89/<- %ebx 4/r32/esp
8740     # var primitives/ebx: primitive
8741     53/push-ebx/next
8742     68/push 0/imm32/output-is-write-only
8743     68/push 0/imm32/no-disp32
8744     68/push 0/imm32/no-imm32
8745     68/push 0/imm32/no-r32
8746     68/push 1/imm32/rm32-is-first-inout
8747     68/push "ff 0/subop/increment"/imm32/subx-name
8748     68/push 0/imm32/outputs
8749     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
8750     68/push "increment"/imm32/name
8751     89/<- %ebx 4/r32/esp
8752     # convert
8753     c7 0/subop/copy *Curr-block-depth 0/imm32
8754     (emit-subx-stmt _test-output-buffered-file %esi %ebx 0)
8755     (flush _test-output-buffered-file)
8756 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8762     # check output
8763     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
8764     # . epilogue
8765     89/<- %esp 5/r32/ebp
8766     5d/pop-to-ebp
8767     c3/return
8768 
8769 test-increment-register:
8770     # Select the right register between overloads.
8771     #   foo <- increment
8772     # =>
8773     #   50/increment-eax
8774     #
8775     # There's a variable on the var stack as follows:
8776     #   name: 'foo'
8777     #   type: int
8778     #   register: 'eax'
8779     #
8780     # Primitives are the global definitions.
8781     #
8782     # There are no functions defined.
8783     #
8784     # . prologue
8785     55/push-ebp
8786     89/<- %ebp 4/r32/esp
8787     # setup
8788     (clear-stream _test-output-stream)
8789     (clear-stream $_test-output-buffered-file->buffer)
8790     # var type/ecx: (handle tree type-id) = int
8791     68/push 0/imm32/right/null
8792     68/push 1/imm32/left/int
8793     89/<- %ecx 4/r32/esp
8794     # var var-foo/ecx: var in eax
8795     68/push "eax"/imm32/register
8796     68/push 0/imm32/no-stack-offset
8797     68/push 1/imm32/block-depth
8798     51/push-ecx
8799     68/push "foo"/imm32
8800     89/<- %ecx 4/r32/esp
8801     # var real-outputs/edi: (handle stmt-var)
8802     68/push 0/imm32/is-deref:false
8803     68/push 0/imm32/next
8804     51/push-ecx/var-foo
8805     89/<- %edi 4/r32/esp
8806     # var stmt/esi: statement
8807     68/push 0/imm32/next
8808     57/push-edi/outputs
8809     68/push 0/imm32/inouts
8810     68/push "increment"/imm32/operation
8811     68/push 1/imm32/regular-stmt
8812     89/<- %esi 4/r32/esp
8813     # convert
8814     c7 0/subop/copy *Curr-block-depth 0/imm32
8815     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
8816     (flush _test-output-buffered-file)
8817 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8823     # check output
8824     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
8825     # . epilogue
8826     89/<- %esp 5/r32/ebp
8827     5d/pop-to-ebp
8828     c3/return
8829 
8830 test-increment-var:
8831     # Select the right primitive between overloads.
8832     #   foo <- increment
8833     # =>
8834     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
8835     #
8836     # There's a variable on the var stack as follows:
8837     #   name: 'foo'
8838     #   type: int
8839     #   register: 'eax'
8840     #
8841     # Primitives are the global definitions.
8842     #
8843     # There are no functions defined.
8844     #
8845     # . prologue
8846     55/push-ebp
8847     89/<- %ebp 4/r32/esp
8848     # setup
8849     (clear-stream _test-output-stream)
8850     (clear-stream $_test-output-buffered-file->buffer)
8851     # var type/ecx: (handle tree type-id) = int
8852     68/push 0/imm32/right/null
8853     68/push 1/imm32/left/int
8854     89/<- %ecx 4/r32/esp
8855     # var var-foo/ecx: var in eax
8856     68/push "eax"/imm32/register
8857     68/push 0/imm32/no-stack-offset
8858     68/push 1/imm32/block-depth
8859     51/push-ecx
8860     68/push "foo"/imm32
8861     89/<- %ecx 4/r32/esp
8862     # var inouts/edi: (handle stmt-var)
8863     68/push 0/imm32/is-deref:false
8864     68/push 0/imm32/next
8865     51/push-ecx/var-foo
8866     89/<- %edi 4/r32/esp
8867     # var stmt/esi: statement
8868     68/push 0/imm32/next
8869     57/push-edi/outputs
8870     68/push 0/imm32/inouts
8871     68/push "increment"/imm32/operation
8872     68/push 1/imm32
8873     89/<- %esi 4/r32/esp
8874     # convert
8875     c7 0/subop/copy *Curr-block-depth 0/imm32
8876     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
8877     (flush _test-output-buffered-file)
8878 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8884     # check output
8885     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
8886     # . epilogue
8887     89/<- %esp 5/r32/ebp
8888     5d/pop-to-ebp
8889     c3/return
8890 
8891 test-add-reg-to-reg:
8892     #   var1/reg <- add var2/reg
8893     # =>
8894     #   01/add %var1 var2
8895     #
8896     # . prologue
8897     55/push-ebp
8898     89/<- %ebp 4/r32/esp
8899     # setup
8900     (clear-stream _test-output-stream)
8901     (clear-stream $_test-output-buffered-file->buffer)
8902     # var type/ecx: (handle tree type-id) = int
8903     68/push 0/imm32/right/null
8904     68/push 1/imm32/left/int
8905     89/<- %ecx 4/r32/esp
8906     # var var-var1/ecx: var in eax
8907     68/push "eax"/imm32/register
8908     68/push 0/imm32/no-stack-offset
8909     68/push 1/imm32/block-depth
8910     51/push-ecx
8911     68/push "var1"/imm32
8912     89/<- %ecx 4/r32/esp
8913     # var var-var2/edx: var in ecx
8914     68/push "ecx"/imm32/register
8915     68/push 0/imm32/no-stack-offset
8916     68/push 1/imm32/block-depth
8917     ff 6/subop/push *(ecx+4)  # Var-type
8918     68/push "var2"/imm32
8919     89/<- %edx 4/r32/esp
8920     # var inouts/esi: (handle stmt-var) = [var2]
8921     68/push 0/imm32/is-deref:false
8922     68/push 0/imm32/next
8923     52/push-edx/var-var2
8924     89/<- %esi 4/r32/esp
8925     # var outputs/edi: (handle stmt-var) = [var1, var2]
8926     68/push 0/imm32/is-deref:false
8927     68/push 0/imm32/next
8928     51/push-ecx/var-var1
8929     89/<- %edi 4/r32/esp
8930     # var stmt/esi: statement
8931     68/push 0/imm32/next
8932     57/push-edi/outputs
8933     56/push-esi/inouts
8934     68/push "add"/imm32/operation
8935     68/push 1/imm32
8936     89/<- %esi 4/r32/esp
8937     # convert
8938     c7 0/subop/copy *Curr-block-depth 0/imm32
8939     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
8940     (flush _test-output-buffered-file)
8941 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
8947     # check output
8948     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
8949     # . epilogue
8950     89/<- %esp 5/r32/ebp
8951     5d/pop-to-ebp
8952     c3/return
8953 
8954 test-add-reg-to-mem:
8955     #   add-to var1 var2/reg
8956     # =>
8957     #   01/add *(ebp+__) var2
8958     #
8959     # . prologue
8960     55/push-ebp
8961     89/<- %ebp 4/r32/esp
8962     # setup
8963     (clear-stream _test-output-stream)
8964     (clear-stream $_test-output-buffered-file->buffer)
8965     # var type/ecx: (handle tree type-id) = int
8966     68/push 0/imm32/right/null
8967     68/push 1/imm32/left/int
8968     89/<- %ecx 4/r32/esp
8969     # var var-var1/ecx: var
8970     68/push 0/imm32/no-register
8971     68/push 8/imm32/stack-offset
8972     68/push 1/imm32/block-depth
8973     51/push-ecx
8974     68/push "var1"/imm32
8975     89/<- %ecx 4/r32/esp
8976     # var var-var2/edx: var in ecx
8977     68/push "ecx"/imm32/register
8978     68/push 0/imm32/no-stack-offset
8979     68/push 1/imm32/block-depth
8980     ff 6/subop/push *(ecx+4)  # Var-type
8981     68/push "var2"/imm32
8982     89/<- %edx 4/r32/esp
8983     # var inouts/esi: (handle stmt-var) = [var2]
8984     68/push 0/imm32/is-deref:false
8985     68/push 0/imm32/next
8986     52/push-edx/var-var2
8987     89/<- %esi 4/r32/esp
8988     # var inouts = (handle stmt-var) = [var1, var2]
8989     56/push-esi/next
8990     51/push-ecx/var-var1
8991     89/<- %esi 4/r32/esp
8992     # var stmt/esi: statement
8993     68/push 0/imm32/next
8994     68/push 0/imm32/outputs
8995     56/push-esi/inouts
8996     68/push "add-to"/imm32/operation
8997     68/push 1/imm32
8998     89/<- %esi 4/r32/esp
8999     # convert
9000     c7 0/subop/copy *Curr-block-depth 0/imm32
9001     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9002     (flush _test-output-buffered-file)
9003 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9009     # check output
9010     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
9011     # . epilogue
9012     89/<- %esp 5/r32/ebp
9013     5d/pop-to-ebp
9014     c3/return
9015 
9016 test-add-mem-to-reg:
9017     #   var1/reg <- add var2
9018     # =>
9019     #   03/add *(ebp+__) var1
9020     #
9021     # . prologue
9022     55/push-ebp
9023     89/<- %ebp 4/r32/esp
9024     # setup
9025     (clear-stream _test-output-stream)
9026     (clear-stream $_test-output-buffered-file->buffer)
9027     # var type/ecx: (handle tree type-id) = int
9028     68/push 0/imm32/right/null
9029     68/push 1/imm32/left/int
9030     89/<- %ecx 4/r32/esp
9031     # var var-var1/ecx: var in eax
9032     68/push "eax"/imm32/register
9033     68/push 0/imm32/no-stack-offset
9034     68/push 1/imm32/block-depth
9035     51/push-ecx
9036     68/push "var1"/imm32
9037     89/<- %ecx 4/r32/esp
9038     # var var-var2/edx: var
9039     68/push 0/imm32/no-register
9040     68/push 8/imm32/stack-offset
9041     68/push 1/imm32/block-depth
9042     ff 6/subop/push *(ecx+4)  # Var-type
9043     68/push "var2"/imm32
9044     89/<- %edx 4/r32/esp
9045     # var inouts/esi: (handle stmt-var) = [var2]
9046     68/push 0/imm32/is-deref:false
9047     68/push 0/imm32/next
9048     52/push-edx/var-var2
9049     89/<- %esi 4/r32/esp
9050     # var outputs/edi = (handle stmt-var) = [var1]
9051     68/push 0/imm32/is-deref:false
9052     68/push 0/imm32/next
9053     51/push-ecx/var-var1
9054     89/<- %edi 4/r32/esp
9055     # var stmt/esi: statement
9056     68/push 0/imm32/next
9057     57/push-edi/outputs
9058     56/push-esi/inouts
9059     68/push "add"/imm32/operation
9060     68/push 1/imm32
9061     89/<- %esi 4/r32/esp
9062     # convert
9063     c7 0/subop/copy *Curr-block-depth 0/imm32
9064     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9065     (flush _test-output-buffered-file)
9066 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9072     # check output
9073     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
9074     # . epilogue
9075     89/<- %esp 5/r32/ebp
9076     5d/pop-to-ebp
9077     c3/return
9078 
9079 test-add-literal-to-eax:
9080     #   var1/eax <- add 0x34
9081     # =>
9082     #   05/add-to-eax 0x34/imm32
9083     #
9084     # . prologue
9085     55/push-ebp
9086     89/<- %ebp 4/r32/esp
9087     # setup
9088     (clear-stream _test-output-stream)
9089     (clear-stream $_test-output-buffered-file->buffer)
9090     # var type/ecx: (handle tree type-id) = int
9091     68/push 0/imm32/right/null
9092     68/push 1/imm32/left/int
9093     89/<- %ecx 4/r32/esp
9094     # var var-var1/ecx: var in eax
9095     68/push "eax"/imm32/register
9096     68/push 0/imm32/no-stack-offset
9097     68/push 1/imm32/block-depth
9098     51/push-ecx
9099     68/push "var1"/imm32
9100     89/<- %ecx 4/r32/esp
9101     # var type/edx: (handle tree type-id) = literal
9102     68/push 0/imm32/right/null
9103     68/push 0/imm32/left/literal
9104     89/<- %edx 4/r32/esp
9105     # var var-var2/edx: var literal
9106     68/push 0/imm32/no-register
9107     68/push 0/imm32/no-stack-offset
9108     68/push 1/imm32/block-depth
9109     52/push-edx
9110     68/push "0x34"/imm32
9111     89/<- %edx 4/r32/esp
9112     # var inouts/esi: (handle stmt-var) = [var2]
9113     68/push 0/imm32/is-deref:false
9114     68/push 0/imm32/next
9115     52/push-edx/var-var2
9116     89/<- %esi 4/r32/esp
9117     # var outputs/edi: (handle stmt-var) = [var1]
9118     68/push 0/imm32/is-deref:false
9119     68/push 0/imm32/next
9120     51/push-ecx/var-var1
9121     89/<- %edi 4/r32/esp
9122     # var stmt/esi: statement
9123     68/push 0/imm32/next
9124     57/push-edi/outputs
9125     56/push-esi/inouts
9126     68/push "add"/imm32/operation
9127     68/push 1/imm32
9128     89/<- %esi 4/r32/esp
9129     # convert
9130     c7 0/subop/copy *Curr-block-depth 0/imm32
9131     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9132     (flush _test-output-buffered-file)
9133 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9139     # check output
9140     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
9141     # . epilogue
9142     89/<- %esp 5/r32/ebp
9143     5d/pop-to-ebp
9144     c3/return
9145 
9146 test-add-literal-to-reg:
9147     #   var1/ecx <- add 0x34
9148     # =>
9149     #   81 0/subop/add %ecx 0x34/imm32
9150     #
9151     # . prologue
9152     55/push-ebp
9153     89/<- %ebp 4/r32/esp
9154     # setup
9155     (clear-stream _test-output-stream)
9156     (clear-stream $_test-output-buffered-file->buffer)
9157     # var type/ecx: (handle tree type-id) = int
9158     68/push 0/imm32/right/null
9159     68/push 1/imm32/left/int
9160     89/<- %ecx 4/r32/esp
9161     # var var-var1/ecx: var in ecx
9162     68/push "ecx"/imm32/register
9163     68/push 0/imm32/no-stack-offset
9164     68/push 1/imm32/block-depth
9165     51/push-ecx
9166     68/push "var1"/imm32
9167     89/<- %ecx 4/r32/esp
9168     # var type/edx: (handle tree type-id) = literal
9169     68/push 0/imm32/right/null
9170     68/push 0/imm32/left/literal
9171     89/<- %edx 4/r32/esp
9172     # var var-var2/edx: var literal
9173     68/push 0/imm32/no-register
9174     68/push 0/imm32/no-stack-offset
9175     68/push 1/imm32/block-depth
9176     52/push-edx
9177     68/push "0x34"/imm32
9178     89/<- %edx 4/r32/esp
9179     # var inouts/esi: (handle stmt-var) = [var2]
9180     68/push 0/imm32/is-deref:false
9181     68/push 0/imm32/next
9182     52/push-edx/var-var2
9183     89/<- %esi 4/r32/esp
9184     # var outputs/edi: (handle stmt-var) = [var1]
9185     68/push 0/imm32/is-deref:false
9186     68/push 0/imm32/next
9187     51/push-ecx/var-var1
9188     89/<- %edi 4/r32/esp
9189     # var stmt/esi: statement
9190     68/push 0/imm32/next
9191     57/push-edi/outputs
9192     56/push-esi/inouts
9193     68/push "add"/imm32/operation
9194     68/push 1/imm32
9195     89/<- %esi 4/r32/esp
9196     # convert
9197     c7 0/subop/copy *Curr-block-depth 0/imm32
9198     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9199     (flush _test-output-buffered-file)
9200 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9206     # check output
9207     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
9208     # . epilogue
9209     89/<- %esp 5/r32/ebp
9210     5d/pop-to-ebp
9211     c3/return
9212 
9213 test-add-literal-to-mem:
9214     #   add-to var1, 0x34
9215     # =>
9216     #   81 0/subop/add %eax 0x34/imm32
9217     #
9218     # . prologue
9219     55/push-ebp
9220     89/<- %ebp 4/r32/esp
9221     # setup
9222     (clear-stream _test-output-stream)
9223     (clear-stream $_test-output-buffered-file->buffer)
9224     # var type/ecx: (handle tree type-id) = int
9225     68/push 0/imm32/right/null
9226     68/push 1/imm32/left/int
9227     89/<- %ecx 4/r32/esp
9228     # var var-var1/ecx: var
9229     68/push 0/imm32/no-register
9230     68/push 8/imm32/stack-offset
9231     68/push 1/imm32/block-depth
9232     51/push-ecx
9233     68/push "var1"/imm32
9234     89/<- %ecx 4/r32/esp
9235     # var type/edx: (handle tree type-id) = literal
9236     68/push 0/imm32/right/null
9237     68/push 0/imm32/left/literal
9238     89/<- %edx 4/r32/esp
9239     # var var-var2/edx: var literal
9240     68/push 0/imm32/no-register
9241     68/push 0/imm32/no-stack-offset
9242     68/push 1/imm32/block-depth
9243     52/push-edx
9244     68/push "0x34"/imm32
9245     89/<- %edx 4/r32/esp
9246     # var inouts/esi: (handle stmt-var) = [var2]
9247     68/push 0/imm32/is-deref:false
9248     68/push 0/imm32/next
9249     52/push-edx/var-var2
9250     89/<- %esi 4/r32/esp
9251     # var inouts = (handle stmt-var) = [var1, var2]
9252     68/push 0/imm32/is-deref:false
9253     56/push-esi/next
9254     51/push-ecx/var-var1
9255     89/<- %esi 4/r32/esp
9256     # var stmt/esi: statement
9257     68/push 0/imm32/next
9258     68/push 0/imm32/outputs
9259     56/push-esi/inouts
9260     68/push "add-to"/imm32/operation
9261     68/push 1/imm32
9262     89/<- %esi 4/r32/esp
9263     # convert
9264     c7 0/subop/copy *Curr-block-depth 0/imm32
9265     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9266     (flush _test-output-buffered-file)
9267 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9273     # check output
9274     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
9275     # . epilogue
9276     89/<- %esp 5/r32/ebp
9277     5d/pop-to-ebp
9278     c3/return
9279 
9280 test-compare-mem-with-reg:
9281     #   compare var1, var2/eax
9282     # =>
9283     #   39/compare *(ebp+___) 0/r32/eax
9284     #
9285     # . prologue
9286     55/push-ebp
9287     89/<- %ebp 4/r32/esp
9288     # setup
9289     (clear-stream _test-output-stream)
9290     (clear-stream $_test-output-buffered-file->buffer)
9291     # var type/ecx: (handle tree type-id) = int
9292     68/push 0/imm32/right/null
9293     68/push 1/imm32/left/int
9294     89/<- %ecx 4/r32/esp
9295     # var var-var2/ecx: var in eax
9296     68/push "eax"/imm32/register
9297     68/push 0/imm32/no-stack-offset
9298     68/push 1/imm32/block-depth
9299     51/push-ecx
9300     68/push "var2"/imm32
9301     89/<- %ecx 4/r32/esp
9302     # var var-var1/edx: var
9303     68/push 0/imm32/no-register
9304     68/push 8/imm32/stack-offset
9305     68/push 1/imm32/block-depth
9306     ff 6/subop/push *(ecx+4)  # Var-type
9307     68/push "var1"/imm32
9308     89/<- %edx 4/r32/esp
9309     # var inouts/esi: (handle stmt-var) = [var2]
9310     68/push 0/imm32/is-deref:false
9311     68/push 0/imm32/next
9312     51/push-ecx/var-var2
9313     89/<- %esi 4/r32/esp
9314     # inouts = [var1, var2]
9315     68/push 0/imm32/is-deref:false
9316     56/push-esi
9317     52/push-edx/var-var1
9318     89/<- %esi 4/r32/esp
9319     # var stmt/esi: statement
9320     68/push 0/imm32/next
9321     68/push 0/imm32/outputs
9322     56/push-esi/inouts
9323     68/push "compare"/imm32/operation
9324     68/push 1/imm32
9325     89/<- %esi 4/r32/esp
9326     # convert
9327     c7 0/subop/copy *Curr-block-depth 0/imm32
9328     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9329     (flush _test-output-buffered-file)
9330 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9336     # check output
9337     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
9338     # . epilogue
9339     89/<- %esp 5/r32/ebp
9340     5d/pop-to-ebp
9341     c3/return
9342 
9343 test-compare-reg-with-mem:
9344     #   compare var1/eax, var2
9345     # =>
9346     #   3b/compare *(ebp+___) 0/r32/eax
9347     #
9348     # . prologue
9349     55/push-ebp
9350     89/<- %ebp 4/r32/esp
9351     # setup
9352     (clear-stream _test-output-stream)
9353     (clear-stream $_test-output-buffered-file->buffer)
9354     # var type/ecx: (handle tree type-id) = int
9355     68/push 0/imm32/right/null
9356     68/push 1/imm32/left/int
9357     89/<- %ecx 4/r32/esp
9358     # var var-var1/ecx: var in eax
9359     68/push "eax"/imm32/register
9360     68/push 0/imm32/no-stack-offset
9361     68/push 1/imm32/block-depth
9362     51/push-ecx
9363     68/push "var1"/imm32
9364     89/<- %ecx 4/r32/esp
9365     # var var-var2/edx: var
9366     68/push 0/imm32/no-register
9367     68/push 8/imm32/stack-offset
9368     68/push 1/imm32/block-depth
9369     ff 6/subop/push *(ecx+4)  # Var-type
9370     68/push "var2"/imm32
9371     89/<- %edx 4/r32/esp
9372     # var inouts/esi: (handle stmt-var) = [var2]
9373     68/push 0/imm32/is-deref:false
9374     68/push 0/imm32/next
9375     52/push-edx/var-var2
9376     89/<- %esi 4/r32/esp
9377     # inouts = [var1, var2]
9378     68/push 0/imm32/is-deref:false
9379     56/push-esi
9380     51/push-ecx/var-var1
9381     89/<- %esi 4/r32/esp
9382     # var stmt/esi: statement
9383     68/push 0/imm32/next
9384     68/push 0/imm32/outputs
9385     56/push-esi/inouts
9386     68/push "compare"/imm32/operation
9387     68/push 1/imm32
9388     89/<- %esi 4/r32/esp
9389     # convert
9390     c7 0/subop/copy *Curr-block-depth 0/imm32
9391     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9392     (flush _test-output-buffered-file)
9393 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9399     # check output
9400     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
9401     # . epilogue
9402     89/<- %esp 5/r32/ebp
9403     5d/pop-to-ebp
9404     c3/return
9405 
9406 test-compare-mem-with-literal:
9407     #   compare var1, 0x34
9408     # =>
9409     #   81 7/subop/compare *(ebp+___) 0x34/imm32
9410     #
9411     # . prologue
9412     55/push-ebp
9413     89/<- %ebp 4/r32/esp
9414     # setup
9415     (clear-stream _test-output-stream)
9416     (clear-stream $_test-output-buffered-file->buffer)
9417     # var type/ecx: (handle tree type-id) = int
9418     68/push 0/imm32/right/null
9419     68/push 1/imm32/left/int
9420     89/<- %ecx 4/r32/esp
9421     # var var-var1/ecx: var
9422     68/push 0/imm32/no-register
9423     68/push 8/imm32/stack-offset
9424     68/push 1/imm32/block-depth
9425     51/push-ecx
9426     68/push "var1"/imm32
9427     89/<- %ecx 4/r32/esp
9428     # var type/edx: (handle tree type-id) = literal
9429     68/push 0/imm32/right/null
9430     68/push 0/imm32/left/literal
9431     89/<- %edx 4/r32/esp
9432     # var var-var2/edx: var literal
9433     68/push 0/imm32/no-register
9434     68/push 0/imm32/no-stack-offset
9435     68/push 1/imm32/block-depth
9436     52/push-edx
9437     68/push "0x34"/imm32
9438     89/<- %edx 4/r32/esp
9439     # var inouts/esi: (handle stmt-var) = [var2]
9440     68/push 0/imm32/is-deref:false
9441     68/push 0/imm32/next
9442     52/push-edx/var-var2
9443     89/<- %esi 4/r32/esp
9444     # inouts = [var1, var2]
9445     68/push 0/imm32/is-deref:false
9446     56/push-esi/next
9447     51/push-ecx/var-var1
9448     89/<- %esi 4/r32/esp
9449     # var stmt/esi: statement
9450     68/push 0/imm32/next
9451     68/push 0/imm32/outputs
9452     56/push-esi/inouts
9453     68/push "compare"/imm32/operation
9454     68/push 1/imm32
9455     89/<- %esi 4/r32/esp
9456     # convert
9457     c7 0/subop/copy *Curr-block-depth 0/imm32
9458     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9459     (flush _test-output-buffered-file)
9460 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9466     # check output
9467     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
9468     # . epilogue
9469     89/<- %esp 5/r32/ebp
9470     5d/pop-to-ebp
9471     c3/return
9472 
9473 test-compare-eax-with-literal:
9474     #   compare var1/eax 0x34
9475     # =>
9476     #   3d/compare-eax-with 0x34/imm32
9477     #
9478     # . prologue
9479     55/push-ebp
9480     89/<- %ebp 4/r32/esp
9481     # setup
9482     (clear-stream _test-output-stream)
9483     (clear-stream $_test-output-buffered-file->buffer)
9484     # var type/ecx: (handle tree type-id) = int
9485     68/push 0/imm32/right/null
9486     68/push 1/imm32/left/int
9487     89/<- %ecx 4/r32/esp
9488     # var var-var1/ecx: var in eax
9489     68/push "eax"/imm32/register
9490     68/push 0/imm32/no-stack-offset
9491     68/push 1/imm32/block-depth
9492     51/push-ecx
9493     68/push "var1"/imm32
9494     89/<- %ecx 4/r32/esp
9495     # var type/edx: (handle tree type-id) = literal
9496     68/push 0/imm32/right/null
9497     68/push 0/imm32/left/literal
9498     89/<- %edx 4/r32/esp
9499     # var var-var2/edx: var literal
9500     68/push 0/imm32/no-register
9501     68/push 0/imm32/no-stack-offset
9502     68/push 1/imm32/block-depth
9503     52/push-edx
9504     68/push "0x34"/imm32
9505     89/<- %edx 4/r32/esp
9506     # var inouts/esi: (handle stmt-var) = [var2]
9507     68/push 0/imm32/is-deref:false
9508     68/push 0/imm32/next
9509     52/push-edx/var-var2
9510     89/<- %esi 4/r32/esp
9511     # inouts = [var1, var2]
9512     68/push 0/imm32/is-deref:false
9513     56/push-esi/next
9514     51/push-ecx/var-var1
9515     89/<- %esi 4/r32/esp
9516     # var stmt/esi: statement
9517     68/push 0/imm32/next
9518     68/push 0/imm32/outputs
9519     56/push-esi/inouts
9520     68/push "compare"/imm32/operation
9521     68/push 1/imm32/regular-stmt
9522     89/<- %esi 4/r32/esp
9523     # convert
9524     c7 0/subop/copy *Curr-block-depth 0/imm32
9525     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9526     (flush _test-output-buffered-file)
9527 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9533     # check output
9534     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
9535     # . epilogue
9536     89/<- %esp 5/r32/ebp
9537     5d/pop-to-ebp
9538     c3/return
9539 
9540 test-compare-reg-with-literal:
9541     #   compare var1/ecx 0x34
9542     # =>
9543     #   81 7/subop/compare %ecx 0x34/imm32
9544     #
9545     # . prologue
9546     55/push-ebp
9547     89/<- %ebp 4/r32/esp
9548     # setup
9549     (clear-stream _test-output-stream)
9550     (clear-stream $_test-output-buffered-file->buffer)
9551     # var type/ecx: (handle tree type-id) = int
9552     68/push 0/imm32/right/null
9553     68/push 1/imm32/left/int
9554     89/<- %ecx 4/r32/esp
9555     # var var-var1/ecx: var in ecx
9556     68/push "ecx"/imm32/register
9557     68/push 0/imm32/no-stack-offset
9558     68/push 1/imm32/block-depth
9559     51/push-ecx
9560     68/push "var1"/imm32
9561     89/<- %ecx 4/r32/esp
9562     # var type/edx: (handle tree type-id) = literal
9563     68/push 0/imm32/right/null
9564     68/push 0/imm32/left/literal
9565     89/<- %edx 4/r32/esp
9566     # var var-var2/edx: var literal
9567     68/push 0/imm32/no-register
9568     68/push 0/imm32/no-stack-offset
9569     68/push 1/imm32/block-depth
9570     52/push-edx
9571     68/push "0x34"/imm32
9572     89/<- %edx 4/r32/esp
9573     # var inouts/esi: (handle stmt-var) = [var2]
9574     68/push 0/imm32/is-deref:false
9575     68/push 0/imm32/next
9576     52/push-edx/var-var2
9577     89/<- %esi 4/r32/esp
9578     # inouts = [var1, var2]
9579     68/push 0/imm32/is-deref:false
9580     56/push-esi/next
9581     51/push-ecx/var-var1
9582     89/<- %esi 4/r32/esp
9583     # var stmt/esi: statement
9584     68/push 0/imm32/next
9585     68/push 0/imm32/outputs
9586     56/push-esi/inouts
9587     68/push "compare"/imm32/operation
9588     68/push 1/imm32/regular-stmt
9589     89/<- %esi 4/r32/esp
9590     # convert
9591     c7 0/subop/copy *Curr-block-depth 0/imm32
9592     (emit-subx-stmt _test-output-buffered-file %esi Primitives 0)
9593     (flush _test-output-buffered-file)
9594 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9600     # check output
9601     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
9602     # . epilogue
9603     89/<- %esp 5/r32/ebp
9604     5d/pop-to-ebp
9605     c3/return
9606 
9607 test-emit-subx-stmt-function-call:
9608     # Call a function on a variable on the stack.
9609     #   f foo
9610     # =>
9611     #   (f2 *(ebp-8))
9612     # (Changing the function name supports overloading in general, but here it
9613     # just serves to help disambiguate things.)
9614     #
9615     # There's a variable on the var stack as follows:
9616     #   name: 'foo'
9617     #   type: int
9618     #   stack-offset: -8
9619     #
9620     # There's nothing in primitives.
9621     #
9622     # There's a function with this info:
9623     #   name: 'f'
9624     #   inout: int/mem
9625     #   value: 'f2'
9626     #
9627     # . prologue
9628     55/push-ebp
9629     89/<- %ebp 4/r32/esp
9630     # setup
9631     (clear-stream _test-output-stream)
9632     (clear-stream $_test-output-buffered-file->buffer)
9633     # var type/ecx: (handle tree type-id) = int
9634     68/push 0/imm32/right/null
9635     68/push 1/imm32/left/int
9636     89/<- %ecx 4/r32/esp
9637     # var var-foo/ecx: var
9638     68/push 0/imm32/no-register
9639     68/push -8/imm32/stack-offset
9640     68/push 0/imm32/block-depth
9641     51/push-ecx
9642     68/push "foo"/imm32
9643     89/<- %ecx 4/r32/esp
9644     # var inouts/esi: (handle stmt-var)
9645     68/push 0/imm32/is-deref:false
9646     68/push 0/imm32/next
9647     51/push-ecx/var-foo
9648     89/<- %esi 4/r32/esp
9649     # var stmt/esi: statement
9650     68/push 0/imm32/next
9651     68/push 0/imm32/outputs
9652     56/push-esi/inouts
9653     68/push "f"/imm32/operation
9654     68/push 1/imm32
9655     89/<- %esi 4/r32/esp
9656     # var functions/ebx: function
9657     68/push 0/imm32/next
9658     68/push 0/imm32/body
9659     68/push 0/imm32/outputs
9660     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
9661     68/push "f2"/imm32/subx-name
9662     68/push "f"/imm32/name
9663     89/<- %ebx 4/r32/esp
9664     # convert
9665     c7 0/subop/copy *Curr-block-depth 0/imm32
9666     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
9667     (flush _test-output-buffered-file)
9668 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9674     # check output
9675     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
9676     # . epilogue
9677     89/<- %esp 5/r32/ebp
9678     5d/pop-to-ebp
9679     c3/return
9680 
9681 test-emit-subx-stmt-function-call-with-literal-arg:
9682     # Call a function on a literal.
9683     #   f 34
9684     # =>
9685     #   (f2 34)
9686     #
9687     # . prologue
9688     55/push-ebp
9689     89/<- %ebp 4/r32/esp
9690     # setup
9691     (clear-stream _test-output-stream)
9692     (clear-stream $_test-output-buffered-file->buffer)
9693     # var type/ecx: (handle tree type-id) = literal
9694     68/push 0/imm32/right/null
9695     68/push 0/imm32/left/literal
9696     89/<- %ecx 4/r32/esp
9697     # var var-foo/ecx: var literal
9698     68/push 0/imm32/no-register
9699     68/push 0/imm32/no-stack-offset
9700     68/push 0/imm32/block-depth
9701     51/push-ecx
9702     68/push "34"/imm32
9703     89/<- %ecx 4/r32/esp
9704     # var inouts/esi: (handle stmt-var)
9705     68/push 0/imm32/is-deref:false
9706     68/push 0/imm32/next
9707     51/push-ecx/var-foo
9708     89/<- %esi 4/r32/esp
9709     # var stmt/esi: statement
9710     68/push 0/imm32/next
9711     68/push 0/imm32/outputs
9712     56/push-esi/inouts
9713     68/push "f"/imm32/operation
9714     68/push 1/imm32
9715     89/<- %esi 4/r32/esp
9716     # var functions/ebx: function
9717     68/push 0/imm32/next
9718     68/push 0/imm32/body
9719     68/push 0/imm32/outputs
9720     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
9721     68/push "f2"/imm32/subx-name
9722     68/push "f"/imm32/name
9723     89/<- %ebx 4/r32/esp
9724     # convert
9725     c7 0/subop/copy *Curr-block-depth 0/imm32
9726     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
9727     (flush _test-output-buffered-file)
9728 +--  6 lines: #?     # dump _test-output-stream -----------------------------------------------------------------------------------------------------------------------------------------
9734     # check output
9735     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
9736     # . epilogue
9737     89/<- %esp 5/r32/ebp
9738     5d/pop-to-ebp
9739     c3/return
9740 
9741 emit-indent:  # out: (addr buffered-file), n: int
9742     # . prologue
9743     55/push-ebp
9744     89/<- %ebp 4/r32/esp
9745     # . save registers
9746     50/push-eax
9747     # var i/eax: int = n
9748     8b/-> *(ebp+0xc) 0/r32/eax
9749     {
9750       # if (i <= 0) break
9751       3d/compare-eax-with 0/imm32
9752       7e/jump-if-<= break/disp8
9753       (write-buffered *(ebp+8) "  ")
9754       48/decrement-eax
9755       eb/jump loop/disp8
9756     }
9757 $emit-indent:end:
9758     # . restore registers
9759     58/pop-to-eax
9760     # . epilogue
9761     89/<- %esp 5/r32/ebp
9762     5d/pop-to-ebp
9763     c3/return
9764 
9765 emit-subx-prologue:  # out: (addr buffered-file)
9766     # . prologue
9767     55/push-ebp
9768     89/<- %ebp 4/r32/esp
9769     #
9770     (write-buffered *(ebp+8) "  # . prologue\n")
9771     (write-buffered *(ebp+8) "  55/push-ebp\n")
9772     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
9773 $emit-subx-prologue:end:
9774     # . epilogue
9775     89/<- %esp 5/r32/ebp
9776     5d/pop-to-ebp
9777     c3/return
9778 
9779 emit-subx-epilogue:  # out: (addr buffered-file)
9780     # . prologue
9781     55/push-ebp
9782     89/<- %ebp 4/r32/esp
9783     #
9784     (write-buffered *(ebp+8) "  # . epilogue\n")
9785     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
9786     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
9787     (write-buffered *(ebp+8) "  c3/return\n")
9788 $emit-subx-epilogue:end:
9789     # . epilogue
9790     89/<- %esp 5/r32/ebp
9791     5d/pop-to-ebp
9792     c3/return