https://github.com/akkartik/mu/blob/master/apps/mu.subx
   1 # The Mu computer's level-2 language, also called Mu.
   2 # http://akkartik.name/post/mu-2019-2
   3 #
   4 # To run:
   5 #   $ ./ntranslate init.linux 0*.subx apps/mu.subx
   6 
   7 # == Goals
   8 # 1. Be memory safe. It should be impossible to corrupt the heap, or to create
   9 # a bad pointer. (Requires strong type safety.)
  10 # 2. Do as little as possible to achieve goal 1.
  11 #   - runtime checks to avoid complex static analysis
  12 #   - minimize impedance mismatch between source language and SubX target
  13 
  14 # == Language description
  15 # A program is a sequence of function definitions.
  16 #
  17 # Function example:
  18 #   fn foo n: int -> result/eax: int {
  19 #     ...
  20 #   }
  21 #
  22 # Functions consist of a name, optional inputs, optional outputs and a block.
  23 #
  24 # Function inputs and outputs are variables. All variables have a type and
  25 # storage specifier. They can be placed either in memory (on the stack) or in
  26 # one of 6 named registers.
  27 #   eax ecx edx ebx esi edi
  28 # Variables in registers must be primitive 32-bit types.
  29 # Variables not explicitly placed in a register are on the stack.
  30 # Variables in registers need not have a name; in that case you refer to them
  31 # directly by the register name.
  32 #
  33 # Function inputs are always passed in memory (on the stack), while outputs
  34 # are always returned in registers.
  35 #
  36 # Blocks mostly consist of statements.
  37 #
  38 # Statements mostly consist of a name, optional inputs and optional outputs.
  39 #
  40 # Statement inputs are variables or literals. Variables need to specify type
  41 # (and storage) the first time they're mentioned but not later.
  42 #
  43 # Statement outputs, like function outputs, must be variables in registers.
  44 #
  45 # Statement names must be either primitives or user-defined functions.
  46 #
  47 # Primitives can write to any register.
  48 # User-defined functions only write to hard-coded registers. Outputs of each
  49 # call must have the same registers as in the function definition.
  50 #
  51 # There are some other statement types:
  52 #   - blocks. Multiple statements surrounded by '{...}' and optionally
  53 #     prefixed with a label name and ':'
  54 #       - {
  55 #           ...
  56 #         }
  57 #       - foo: {
  58 #           ...
  59 #         }
  60 #
  61 #   - variable definitions on the stack. E.g.:
  62 #       - var foo: int
  63 #       - var bar: (array int 3)
  64 #     There's no initializer; variables are automatically initialized.
  65 #
  66 #   - variables definitions in a register. E.g.:
  67 #       - var foo/eax : int <- add bar 1
  68 #     The initializer is mandatory and must be a valid instruction that writes
  69 #     a single output to the right register. In practice registers will
  70 #     usually be either initialized by primitives or copied from eax.
  71 #       - var eax : int <- foo bar quux
  72 #         var floo/ecx : int <- copy eax
  73 #
  74 # Still todo:
  75 #   global variables
  76 #   heap allocations (planned name: 'handle')
  77 #   user-defined types: 'type' for structs, 'choice' for unions
  78 #   short-lived 'address' type for efficiently writing inside nested structs
  79 #
  80 # Formal types:
  81 #   A program is a linked list of functions
  82 #   A function contains:
  83 #     name: string
  84 #     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
  85 #       data: (address var)
  86 #       next: (address list)
  87 #     outputs: linked list of vars
  88 #       data: (address var)
  89 #       next: (address list)
  90 #     body: block
  91 #   A var-type contains:
  92 #     name: string
  93 #     type: s-expression of type ids
  94 #
  95 #   A statement can be:
  96 #     tag 0: a block
  97 #     tag 1: a simple statement
  98 #     tag 2: a variable defined on the stack
  99 #     tag 3: a variable defined in a register
 100 #     tag 4: a named block
 101 #
 102 #   A block contains:
 103 #     tag: 0
 104 #     statements: (address list statement)
 105 #
 106 #   A regular statement contains:
 107 #     tag: 1
 108 #     operation: string
 109 #     inouts: (address list operand)
 110 #     outputs: (address list var)
 111 #
 112 #   A variable defined on the stack contains:
 113 #     tag: 2
 114 #     name: string
 115 #     type: type-tree
 116 #
 117 #   A variable defined in a register contains:
 118 #     tag: 3
 119 #     name: string
 120 #     type: type-tree
 121 #     reg: string
 122 #
 123 #   A named block contains:
 124 #     tag: 4
 125 #     name: string
 126 #     statements: (address list statement)
 127 
 128 # == Translation: managing the stack
 129 # Now that we know what the language looks like in the large, let's think
 130 # about how translation happens from the bottom up. One crucial piece of the
 131 # puzzle is how Mu will clean up variables defined on the stack for you.
 132 #
 133 # Assume that we maintain a 'functions' list while parsing source code. And a
 134 # 'primitives' list is a global constant. Both these contain enough information
 135 # to perform type-checking on function calls or primitive statements, respectively.
 136 #
 137 # Defining variables pushes them on a stack with the current block depth and
 138 # enough information about their location (stack offset or register).
 139 # Starting a block increments the current block id.
 140 # Each statement now has enough information to emit code for it.
 141 # Ending a block is where the magic happens:
 142 #   pop all variables at the current block depth
 143 #   emit code to restore all register variables introduced at the current depth
 144 #   emit code to clean up all stack variables at the current depth (just increment esp)
 145 #   decrement the current block depth
 146 #
 147 # Formal types:
 148 #   live-vars: stack of vars
 149 #   var:
 150 #     name: string
 151 #     type: s-expression? Just a type id for now.
 152 #     block: int
 153 #     stack-offset: int  (added to ebp)
 154 #     register: string
 155 #       either usual register names
 156 #       or '*' to indicate any register
 157 #   At most one of stack-offset or register-index must be non-zero.
 158 #   A register of '*' designates a variable _template_. Only legal in formal
 159 #   parameters for primitives.
 160 
 161 # == Translating a single function call
 162 # This one's easy. Assuming we've already checked things, we just drop the
 163 # outputs (which use hard-coded registers) and emit inputs in a standard format.
 164 #
 165 # out1, out2, out3, ... <- name inout1, inout2, inout3, ...
 166 # =>
 167 # (subx-name inout1 inout2 inout3)
 168 #
 169 # Formal types:
 170 #   functions: linked list of info
 171 #     name: string
 172 #     inouts: linked list of vars
 173 #     outputs: linked list of vars
 174 #     body: block (singleton linked list)
 175 #     subx-name: string
 176 
 177 # == Translating a single primitive instruction
 178 # A second crucial piece of the puzzle is how Mu converts fairly regular
 179 # primitives with their uniform syntax to SubX instructions with their gnarly
 180 # x86 details.
 181 #
 182 # Mu instructions have inputs and outputs. Primitives can have up to 2 of
 183 # them.
 184 # SubX instructions have rm32 and r32 operands.
 185 # The translation between them covers almost all the possibilities.
 186 #   Instructions with 1 inout may turn into ones with 1 rm32
 187 #     (e.g. incrementing a var on the stack)
 188 #   Instructions with 1 output may turn into ones with 1 rm32
 189 #     (e.g. incrementing a var in a register)
 190 #   1 inout and 1 output may turn into 1 rm32 and 1 r32
 191 #     (e.g. adding a var to a reg)
 192 #   2 inouts may turn into 1 rm32 and 1 r32
 193 #     (e.g. adding a reg to a var)
 194 #   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
 195 #     (e.g. adding a constant to a var)
 196 #   1 output and 1 literal may turn into 1 rm32 and 1 imm32
 197 #     (e.g. adding a constant to a reg)
 198 #   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
 199 #     (special-case: divide edx:eax by a var or reg)
 200 # Observations:
 201 #   We always emit rm32. It may be the first inout or the first output.
 202 #   We may emit r32 or imm32 or neither.
 203 #   When we emit r32 it may come from first inout or second inout or first output.
 204 #
 205 # Accordingly, the formal data structure for a primitive looks like this:
 206 #   primitives: linked list of info
 207 #     name: string
 208 #     mu-inouts: linked list of vars to check
 209 #     mu-outputs: linked list of vars to check
 210 #     subx-name: string
 211 #     subx-rm32: enum arg-location
 212 #     subx-r32: enum arg-location
 213 #     subx-imm32: enum arg-location
 214 #   arg-location: enum
 215 #     0 means none
 216 #     1 means first inout
 217 #     2 means second inout
 218 #     3 means first output
 219 
 220 # == Translating a block
 221 # Emit block name if necessary
 222 # Emit '{'
 223 # When you encounter a statement, emit it as above
 224 # When you encounter a variable declaration
 225 #   emit any code needed for it (bzeros)
 226 #   push it on the var stack
 227 #   update register dict if necessary
 228 # When you encounter '}'
 229 #   While popping variables off the var stack until block id changes
 230 #     Emit code needed to clean up the stack
 231 #       either increment esp
 232 #       or pop into appropriate register
 233 
 234 # The rest is straightforward.
 235 
 236 == data
 237 
 238 Program:  # (address function)
 239   0/imm32
 240 
 241 Function-name:
 242   0/imm32
 243 Function-subx-name:
 244   4/imm32
 245 Function-inouts:  # (address list var)
 246   8/imm32
 247 Function-outputs:  # (address list var)
 248   0xc/imm32
 249 Function-body:  # (address block)
 250   0x10/imm32
 251 Function-next:  # (address function)
 252   0x14/imm32
 253 Function-size:
 254   0x18/imm32/24
 255 
 256 Primitive-name:
 257   0/imm32
 258 Primitive-inouts:  # (address list var)
 259   4/imm32
 260 Primitive-outputs:  # (address list var)
 261   8/imm32
 262 Primitive-subx-name:  # (address string)
 263   0xc/imm32
 264 Primitive-subx-rm32:  # enum arg-location
 265   0x10/imm32
 266 Primitive-subx-r32:  # enum arg-location
 267   0x14/imm32
 268 Primitive-subx-imm32:  # enum arg-location
 269   0x18/imm32
 270 Primitive-next:  # (address function)
 271   0x1c/imm32
 272 Primitive-size:
 273   0x20/imm32/24
 274 
 275 Stmt-tag:
 276   0/imm32
 277 
 278 Block-statements:  # (address list statement)
 279   4/imm32
 280 
 281 Stmt1-operation:  # string
 282   4/imm32
 283 Stmt1-inouts:  # (address list var)
 284   8/imm32
 285 Stmt1-outputs:  # (address list var)
 286   0xc/imm32
 287 
 288 Vardef-name:  # string
 289   4/imm32
 290 Vardef-type:  # (address tree type-id)
 291   8/imm32
 292 
 293 Regvardef-name:  # string
 294   4/imm32
 295 Regvardef-type:  # (address tree type-id)
 296   8/imm32
 297 Regvardef-register:  # string
 298   0xc/imm32
 299 
 300 Named-block-name:
 301   4/imm32
 302 Named-block-statements:  # (address list statement)
 303   8/imm32
 304 
 305 Stmt-size:
 306   0x10/imm32
 307 
 308 Var-name:
 309   0/imm32
 310 Var-type:
 311   4/imm32
 312 Var-block:
 313   8/imm32
 314 Var-stack-offset:
 315   0xc/imm32
 316 Var-register:
 317   0x10/imm32
 318 Var-size:
 319   0x14/imm32
 320 
 321 Any-register:  # "*"
 322   # size
 323   1/imm32
 324   # data
 325   2a/asterisk
 326 
 327 == code
 328 
 329 Entry:
 330     # . prologue
 331     89/<- %ebp 4/r32/esp
 332     (new-segment Heap-size Heap)
 333     # if (argv[1] == "test') run-tests()
 334     {
 335       # if (argc <= 1) break
 336       81 7/subop/compare *ebp 1/imm32
 337       7e/jump-if-lesser-or-equal break/disp8
 338       # if (argv[1] != "test") break
 339       (kernel-string-equal? *(ebp+8) "test")  # => eax
 340       3d/compare-eax-and 0/imm32
 341       74/jump-if-equal break/disp8
 342       #
 343       (run-tests)
 344       # syscall(exit, *Num-test-failures)
 345       8b/-> *Num-test-failures 3/r32/ebx
 346       eb/jump $mu-main:end/disp8
 347     }
 348     # otherwise convert Stdin
 349     (convert-mu Stdin Stdout)
 350     (flush Stdout)
 351     # syscall(exit, 0)
 352     bb/copy-to-ebx 0/imm32
 353 $mu-main:end:
 354     b8/copy-to-eax 1/imm32/exit
 355     cd/syscall 0x80/imm8
 356 
 357 convert-mu:  # in : (address buffered-file), out : (address buffered-file)
 358     # . prologue
 359     55/push-ebp
 360     89/<- %ebp 4/r32/esp
 361     #
 362     (parse-mu *(ebp+8))
 363     (check-mu-types)
 364     (emit-subx *(ebp+0xc))
 365 $convert-mu:end:
 366     # . epilogue
 367     89/<- %esp 5/r32/ebp
 368     5d/pop-to-ebp
 369     c3/return
 370 
 371 test-convert-empty-input:
 372     # empty input => empty output
 373     # . prologue
 374     55/push-ebp
 375     89/<- %ebp 4/r32/esp
 376     # setup
 377     (clear-stream _test-input-stream)
 378     (clear-stream _test-input-buffered-file->buffer)
 379     (clear-stream _test-output-stream)
 380     (clear-stream _test-output-buffered-file->buffer)
 381     #
 382     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 383     (flush _test-output-buffered-file)
 384     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 385     # . epilogue
 386     89/<- %esp 5/r32/ebp
 387     5d/pop-to-ebp
 388     c3/return
 389 
 390 test-convert-function-skeleton:
 391     # empty function decl => function prologue and epilogue
 392     #   fn foo {
 393     #   }
 394     # =>
 395     #   foo:
 396     #     # . prologue
 397     #     55/push-ebp
 398     #     89/<- %ebp 4/r32/esp
 399     #     # . epilogue
 400     #     89/<- %esp 5/r32/ebp
 401     #     5d/pop-to-ebp
 402     #     c3/return
 403     # . prologue
 404     55/push-ebp
 405     89/<- %ebp 4/r32/esp
 406     # setup
 407     (clear-stream _test-input-stream)
 408     (clear-stream _test-input-buffered-file->buffer)
 409     (clear-stream _test-output-stream)
 410     (clear-stream _test-output-buffered-file->buffer)
 411     #
 412     (write _test-input-stream "fn foo {\n")
 413     (write _test-input-stream "}\n")
 414     # convert
 415     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 416     (flush _test-output-buffered-file)
 417 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 423     # check output
 424     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-skeleton/0")
 425     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-skeleton/1")
 426     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-skeleton/2")
 427     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 428     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-skeleton/4")
 429     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 430     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 431     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-skeleton/7")
 432     # . epilogue
 433     89/<- %esp 5/r32/ebp
 434     5d/pop-to-ebp
 435     c3/return
 436 
 437 test-convert-multiple-function-skeletons:
 438     # multiple functions correctly organized into a linked list
 439     #   fn foo {
 440     #   }
 441     #   fn bar {
 442     #   }
 443     # =>
 444     #   foo:
 445     #     # . prologue
 446     #     55/push-ebp
 447     #     89/<- %ebp 4/r32/esp
 448     #     # . epilogue
 449     #     89/<- %esp 5/r32/ebp
 450     #     5d/pop-to-ebp
 451     #     c3/return
 452     #   bar:
 453     #     # . prologue
 454     #     55/push-ebp
 455     #     89/<- %ebp 4/r32/esp
 456     #     # . epilogue
 457     #     89/<- %esp 5/r32/ebp
 458     #     5d/pop-to-ebp
 459     #     c3/return
 460     # . prologue
 461     55/push-ebp
 462     89/<- %ebp 4/r32/esp
 463     # setup
 464     (clear-stream _test-input-stream)
 465     (clear-stream _test-input-buffered-file->buffer)
 466     (clear-stream _test-output-stream)
 467     (clear-stream _test-output-buffered-file->buffer)
 468     #
 469     (write _test-input-stream "fn foo {\n")
 470     (write _test-input-stream "}\n")
 471     (write _test-input-stream "fn bar {\n")
 472     (write _test-input-stream "}\n")
 473     # convert
 474     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 475     (flush _test-output-buffered-file)
 476 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 482     # check first function
 483     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-multiple-function-skeletons/0")
 484     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/1")
 485     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 486     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 487     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 488     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 489     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 490     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/7")
 491     # check second function
 492     (check-next-stream-line-equal _test-output-stream "bar:"                  "F - test-convert-multiple-function-skeletons/10")
 493     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/11")
 494     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 495     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 496     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 497     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 498     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 499     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/17")
 500     # . epilogue
 501     89/<- %esp 5/r32/ebp
 502     5d/pop-to-ebp
 503     c3/return
 504 
 505 test-convert-function-with-arg:
 506     # function with one arg
 507     #   fn foo n : int {
 508     #   }
 509     # =>
 510     #   foo:
 511     #     # . prologue
 512     #     55/push-ebp
 513     #     89/<- %ebp 4/r32/esp
 514     #     # . epilogue
 515     #     89/<- %esp 5/r32/ebp
 516     #     5d/pop-to-ebp
 517     #     c3/return
 518     # . prologue
 519     55/push-ebp
 520     89/<- %ebp 4/r32/esp
 521     # setup
 522     (clear-stream _test-input-stream)
 523     (clear-stream _test-input-buffered-file->buffer)
 524     (clear-stream _test-output-stream)
 525     (clear-stream _test-output-buffered-file->buffer)
 526     #
 527     (write _test-input-stream "fn foo n : int {\n")
 528     (write _test-input-stream "}\n")
 529     # convert
 530     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 531     (flush _test-output-buffered-file)
 532 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 538     # check output
 539     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-skeleton/0")
 540     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-skeleton/1")
 541     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-skeleton/2")
 542     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 543     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-skeleton/4")
 544     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 545     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 546     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-skeleton/7")
 547     # . epilogue
 548     89/<- %esp 5/r32/ebp
 549     5d/pop-to-ebp
 550     c3/return
 551 
 552 #######################################################
 553 # Parsing
 554 #######################################################
 555 
 556 parse-mu:  # in : (address buffered-file)
 557     # pseudocode
 558     #   var curr-function = Program
 559     #   var line : (stream byte 512)
 560     #   var word-slice : slice
 561     #   while true                                  # line loop
 562     #     clear-stream(line)
 563     #     read-line-buffered(in, line)
 564     #     if (line->write == 0) break               # end of file
 565     #     while true                                # word loop
 566     #       word-slice = next-word-or-string(line)
 567     #       if slice-empty?(word-slice)             # end of line
 568     #         break
 569     #       else if slice-starts-with?(word-slice, "#")  # comment
 570     #         break                                 # end of line
 571     #       else if slice-equal(word-slice, "fn")
 572     #         var new-function : (address function) = new function
 573     #         populate-mu-function(in, new-function)
 574     #         *curr-function = new-function
 575     #         curr-function = &new-function->next
 576     #       else
 577     #         abort()
 578     #
 579     # . prologue
 580     55/push-ebp
 581     89/<- %ebp 4/r32/esp
 582     # . save registers
 583     50/push-eax
 584     51/push-ecx
 585     52/push-edx
 586     57/push-edi
 587     # var line/ecx : (stream byte 512)
 588     81 5/subop/subtract %esp 0x200/imm32
 589     68/push 0x200/imm32/length
 590     68/push 0/imm32/read
 591     68/push 0/imm32/write
 592     89/<- %ecx 4/r32/esp
 593     # var word-slice/edx : slice
 594     68/push 0/imm32/end
 595     68/push 0/imm32/start
 596     89/<- %edx 4/r32/esp
 597     # var curr-function/edi : (address function) = Program
 598     bf/copy-to-edi Program/imm32
 599     {
 600 $parse-mu:line-loop:
 601       (clear-stream %ecx)
 602       (read-line-buffered *(ebp+8) %ecx)
 603       # if (line->write == 0) break
 604       81 7/subop/compare *ecx 0/imm32
 605       0f 84/jump-if-equal break/disp32
 606 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------
 612       { # word loop
 613 $parse-mu:word-loop:
 614         (next-word-or-string %ecx %edx)
 615         # if slice-empty?(word-slice) break
 616         (slice-empty? %edx)
 617         3d/compare-eax-and 0/imm32
 618         0f 85/jump-if-not-equal break/disp32
 619         # if (*word-slice->start == "#") break
 620         # . eax = *word-slice->start
 621         8b/-> *edx 0/r32/eax
 622         8a/copy-byte *eax 0/r32/AL
 623         81 4/subop/and %eax 0xff/imm32
 624         # . if (eax == '#') break
 625         3d/compare-eax-and 0x23/imm32/hash
 626         0f 84/jump-if-equal break/disp32
 627         # if (slice-equal?(word-slice, "fn")) parse a function
 628         {
 629           (slice-equal? %edx "fn")
 630           3d/compare-eax-and 0/imm32
 631           0f 84/jump-if-equal break/disp32
 632           # var new-function/eax : (address function) = populate-mu-function()
 633           (allocate Heap *Function-size)  # => eax
 634           (populate-mu-function-header %ecx %eax)
 635           (populate-mu-function-body *(ebp+8) %eax)
 636           # *curr-function = new-function
 637           89/<- *edi 0/r32/eax
 638           # curr-function = &new-function->next
 639           8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
 640           e9/jump $parse-mu:word-loop/disp32
 641         }
 642         # otherwise abort
 643         e9/jump $parse-mu:abort/disp32
 644       } # end word loop
 645       e9/jump loop/disp32
 646     } # end line loop
 647 $parse-mu:end:
 648     # . reclaim locals
 649     81 0/subop/add %esp 0x214/imm32
 650     # . restore registers
 651     5f/pop-to-edi
 652     5a/pop-to-edx
 653     59/pop-to-ecx
 654     58/pop-to-eax
 655     # . epilogue
 656     89/<- %esp 5/r32/ebp
 657     5d/pop-to-ebp
 658     c3/return
 659 
 660 $parse-mu:abort:
 661     # error("unexpected top-level command: " word-slice "\n")
 662     (write-buffered Stderr "unexpected top-level command: ")
 663     (write-buffered Stderr %edx)
 664     (write-buffered Stderr "\n")
 665     (flush Stderr)
 666     # . syscall(exit, 1)
 667     bb/copy-to-ebx  1/imm32
 668     b8/copy-to-eax  1/imm32/exit
 669     cd/syscall  0x80/imm8
 670     # never gets here
 671 
 672 # errors considered:
 673 #   fn foo { {
 674 #   fn foo { }
 675 #   fn foo { } {
 676 #   fn foo  # no block
 677 populate-mu-function-header:  # first-line : (address stream byte), out : (address function)
 678     # . prologue
 679     55/push-ebp
 680     89/<- %ebp 4/r32/esp
 681     # . save registers
 682     50/push-eax
 683     51/push-ecx
 684     57/push-edi
 685     # edi = out
 686     8b/-> *(ebp+0xc) 7/r32/edi
 687     # var word-slice/ecx : slice
 688     68/push 0/imm32/end
 689     68/push 0/imm32/start
 690     89/<- %ecx 4/r32/esp
 691     # save function name
 692     (next-word *(ebp+8) %ecx)
 693     (slice-to-string Heap %ecx)  # => eax
 694     89/<- *edi 0/r32/eax  # Function-name
 695     # error checking
 696     # if (word-slice == '{') abort
 697     (slice-equal? %ecx "{")   # => eax
 698     3d/compare-eax-and 0/imm32
 699     0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32
 700     # if (word-slice == '->') abort
 701     (slice-equal? %ecx "->")   # => eax
 702     3d/compare-eax-and 0/imm32
 703     0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32
 704     # if (word-slice == '}') abort
 705     (slice-equal? %ecx "}")   # => eax
 706     3d/compare-eax-and 0/imm32
 707     0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32
 708     # save function inouts
 709     # while (word-slice not in "{" "->")
 710     {
 711       # if (word-slice == '{') break
 712       (slice-equal? %ecx "{")   # => eax
 713       3d/compare-eax-and 0/imm32
 714       75/jump-if-not-equal break/disp8
 715       # if (word-slice == '->') break
 716       (slice-equal? %ecx "->")   # => eax
 717       3d/compare-eax-and 0/imm32
 718       75/jump-if-not-equal break/disp8
 719       # if (word-slice == '}') abort
 720       (slice-equal? %ecx "}")   # => eax
 721       3d/compare-eax-and 0/imm32
 722       0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32
 723       #
 724       (next-word *(ebp+8) %ecx)
 725       e9/jump loop/disp32
 726     }
 727     # if (word-slice == "->") skip it
 728     {
 729       (slice-equal? %ecx "->")   # => eax
 730       3d/compare-eax-and 0/imm32
 731       74/jump-if-equal break/disp8
 732       (next-word *(ebp+8) %ecx)
 733     }
 734     # save function outputs
 735     # while (word-slice not == "{")
 736     {
 737       # if (word-slice == '{') break
 738       (slice-equal? %ecx "{")   # => eax
 739       3d/compare-eax-and 0/imm32
 740       75/jump-if-not-equal break/disp8
 741       # if (word-slice == '->') abort
 742       (slice-equal? %ecx "->")   # => eax
 743       3d/compare-eax-and 0/imm32
 744       0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32
 745       # if (word-slice == '}') abort
 746       (slice-equal? %ecx "}")   # => eax
 747       3d/compare-eax-and 0/imm32
 748       0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32
 749       #
 750       (next-word *(ebp+8) %ecx)
 751       e9/jump loop/disp32
 752     }
 753     # assert that there's no further token
 754     {
 755       # word-slice = next-word(line)
 756       (next-word *(ebp+8) %ecx)
 757       # if (word-slice == '') break
 758       (slice-empty? %ecx)
 759       3d/compare-eax-and 0/imm32
 760       75/jump-if-not-equal break/disp8
 761       # if (slice-starts-with?(word-slice, "#")) break
 762       # . eax = *word-slice->start
 763       8b/-> *edx 0/r32/eax
 764       8a/copy-byte *eax 0/r32/AL
 765       81 4/subop/and %eax 0xff/imm32
 766       # . if (eax == '#') break
 767       3d/compare-eax-and 0x23/imm32/hash
 768       74/jump-if-equal break/disp8
 769       # otherwise abort
 770       eb/jump $populate-mu-function-header:abort/disp8
 771     }
 772 $populate-mu-function-header:end:
 773     # . reclaim locals
 774     81 0/subop/add %esp 8/imm32
 775     # . restore registers
 776     5f/pop-to-edi
 777     59/pop-to-ecx
 778     58/pop-to-eax
 779     # . epilogue
 780     89/<- %esp 5/r32/ebp
 781     5d/pop-to-ebp
 782     c3/return
 783 
 784 $populate-mu-function-header:abort:
 785     # error("function header not in form 'fn <name> {'")
 786     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
 787     (flush Stderr)
 788     (rewind-stream *(ebp+8))
 789     (write-stream 2 *(ebp+8))
 790     (write-buffered Stderr "'\n")
 791     (flush Stderr)
 792     # . syscall(exit, 1)
 793     bb/copy-to-ebx  1/imm32
 794     b8/copy-to-eax  1/imm32/exit
 795     cd/syscall  0x80/imm8
 796     # never gets here
 797 
 798 # identifier starts with a letter or '$' or '_'
 799 # no constraints at the moment on later letters
 800 # all we really want to do so far is exclude '{', '}' and '->'
 801 is-identifier?:  # in : (address slice) -> result/eax : boolean
 802     # . prologue
 803     55/push-ebp
 804     89/<- %ebp 4/r32/esp
 805     # if (slice-empty?(in)) return false
 806     (slice-empty? *(ebp+8))  # => eax
 807     3d/compare-eax-and 0/imm32
 808     75/jump-if-not-equal $is-identifier?:false/disp8
 809     # var c/eax : char = *in->start
 810     8b/-> *(ebp+8) 0/r32/eax
 811     8b/-> *eax 0/r32/eax
 812     8a/copy-byte *eax 0/r32/AL
 813     81 4/subop/and %eax 0xff/imm32
 814     # if (c == '$') return true
 815     3d/compare-eax-and 0x24/imm32/$
 816     74/jump-if-equal $is-identifier?:true/disp8
 817     # if (c == '_') return true
 818     3d/compare-eax-and 0x5f/imm32/_
 819     74/jump-if-equal $is-identifier?:true/disp8
 820     # drop case
 821     25/and-eax-with 0x5f/imm32
 822     # if (c < 'A') return false
 823     3d/compare-eax-and 0x41/imm32/A
 824     7c/jump-if-lesser $is-identifier?:false/disp8
 825     # if (c > 'Z') return false
 826     3d/compare-eax-and 0x5a/imm32/Z
 827     7f/jump-if-greater $is-identifier?:false/disp8
 828     # otherwise return true
 829 $is-identifier?:true:
 830     b8/copy-to-eax 1/imm32/true
 831     eb/jump $is-identifier?:end/disp8
 832 $is-identifier?:false:
 833     b8/copy-to-eax 0/imm32/false
 834 $is-identifier?:end:
 835     # . epilogue
 836     89/<- %esp 5/r32/ebp
 837     5d/pop-to-ebp
 838     c3/return
 839 
 840 test-is-identifier-dollar:
 841     # . prologue
 842     55/push-ebp
 843     89/<- %ebp 4/r32/esp
 844     # (eax..ecx) = "$a"
 845     b8/copy-to-eax "$a"/imm32
 846     8b/-> *eax 1/r32/ecx
 847     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 848     05/add-to-eax 4/imm32
 849     # var slice/ecx = {eax, ecx}
 850     51/push-ecx
 851     50/push-eax
 852     89/<- %ecx 4/r32/esp
 853     #
 854     (is-identifier? %ecx)
 855     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
 856     # . epilogue
 857     89/<- %esp 5/r32/ebp
 858     5d/pop-to-ebp
 859     c3/return
 860 
 861 test-is-identifier-underscore:
 862     # . prologue
 863     55/push-ebp
 864     89/<- %ebp 4/r32/esp
 865     # (eax..ecx) = "_a"
 866     b8/copy-to-eax "_a"/imm32
 867     8b/-> *eax 1/r32/ecx
 868     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 869     05/add-to-eax 4/imm32
 870     # var slice/ecx = {eax, ecx}
 871     51/push-ecx
 872     50/push-eax
 873     89/<- %ecx 4/r32/esp
 874     #
 875     (is-identifier? %ecx)
 876     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
 877     # . epilogue
 878     89/<- %esp 5/r32/ebp
 879     5d/pop-to-ebp
 880     c3/return
 881 
 882 test-is-identifier-a:
 883     # . prologue
 884     55/push-ebp
 885     89/<- %ebp 4/r32/esp
 886     # (eax..ecx) = "a$"
 887     b8/copy-to-eax "a$"/imm32
 888     8b/-> *eax 1/r32/ecx
 889     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 890     05/add-to-eax 4/imm32
 891     # var slice/ecx = {eax, ecx}
 892     51/push-ecx
 893     50/push-eax
 894     89/<- %ecx 4/r32/esp
 895     #
 896     (is-identifier? %ecx)
 897     (check-ints-equal %eax 1 "F - test-is-identifier-a")
 898     # . epilogue
 899     89/<- %esp 5/r32/ebp
 900     5d/pop-to-ebp
 901     c3/return
 902 
 903 test-is-identifier-z:
 904     # . prologue
 905     55/push-ebp
 906     89/<- %ebp 4/r32/esp
 907     # (eax..ecx) = "z$"
 908     b8/copy-to-eax "z$"/imm32
 909     8b/-> *eax 1/r32/ecx
 910     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 911     05/add-to-eax 4/imm32
 912     # var slice/ecx = {eax, ecx}
 913     51/push-ecx
 914     50/push-eax
 915     89/<- %ecx 4/r32/esp
 916     #
 917     (is-identifier? %ecx)
 918     (check-ints-equal %eax 1 "F - test-is-identifier-z")
 919     # . epilogue
 920     89/<- %esp 5/r32/ebp
 921     5d/pop-to-ebp
 922     c3/return
 923 
 924 test-is-identifier-A:
 925     # . prologue
 926     55/push-ebp
 927     89/<- %ebp 4/r32/esp
 928     # (eax..ecx) = "A$"
 929     b8/copy-to-eax "A$"/imm32
 930     8b/-> *eax 1/r32/ecx
 931     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 932     05/add-to-eax 4/imm32
 933     # var slice/ecx = {eax, ecx}
 934     51/push-ecx
 935     50/push-eax
 936     89/<- %ecx 4/r32/esp
 937     #
 938     (is-identifier? %ecx)
 939     (check-ints-equal %eax 1 "F - test-is-identifier-A")
 940     # . epilogue
 941     89/<- %esp 5/r32/ebp
 942     5d/pop-to-ebp
 943     c3/return
 944 
 945 test-is-identifier-Z:
 946     # . prologue
 947     55/push-ebp
 948     89/<- %ebp 4/r32/esp
 949     # (eax..ecx) = "Z$"
 950     b8/copy-to-eax "Z$"/imm32
 951     8b/-> *eax 1/r32/ecx
 952     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 953     05/add-to-eax 4/imm32
 954     # var slice/ecx = {eax, ecx}
 955     51/push-ecx
 956     50/push-eax
 957     89/<- %ecx 4/r32/esp
 958     #
 959     (is-identifier? %ecx)
 960     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
 961     # . epilogue
 962     89/<- %esp 5/r32/ebp
 963     5d/pop-to-ebp
 964     c3/return
 965 
 966 test-is-identifier-@:
 967     # . prologue
 968     55/push-ebp
 969     89/<- %ebp 4/r32/esp
 970     # (eax..ecx) = "@a"
 971     b8/copy-to-eax "@a"/imm32
 972     8b/-> *eax 1/r32/ecx
 973     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 974     05/add-to-eax 4/imm32
 975     # var slice/ecx = {eax, ecx}
 976     51/push-ecx
 977     50/push-eax
 978     89/<- %ecx 4/r32/esp
 979     #
 980     (is-identifier? %ecx)
 981     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 982     # . epilogue
 983     89/<- %esp 5/r32/ebp
 984     5d/pop-to-ebp
 985     c3/return
 986 
 987 populate-mu-function-body:  # in : (address buffered-file), out : (address function)
 988     # . prologue
 989     55/push-ebp
 990     89/<- %ebp 4/r32/esp
 991     # . save registers
 992     50/push-eax
 993     56/push-esi
 994     # esi = in
 995     8b/-> *(ebp+8) 6/r32/esi
 996     # var eax : (address block) = parse-mu-block(in)
 997     (parse-mu-block %esi)
 998     # out->body = eax
 999     89/<- *(eax+0x10) 0/r32/eax  # Function-body
1000 $populate-mu-function-body:end:
1001     # . restore registers
1002     5e/pop-to-esi
1003     58/pop-to-eax
1004     # . epilogue
1005     89/<- %esp 5/r32/ebp
1006     5d/pop-to-ebp
1007     c3/return
1008 
1009 # parses a block, assuming that the leading '{' has already been read by the caller
1010 # errors considered:
1011 #   { abc
1012 parse-mu-block:  # in : (address buffered-file) -> result/eax : (address block)
1013     # . prologue
1014     55/push-ebp
1015     89/<- %ebp 4/r32/esp
1016     # . save registers
1017     50/push-eax
1018     51/push-ecx
1019     52/push-edx
1020     53/push-ebx
1021     # var line/ecx : (stream byte 512)
1022     81 5/subop/subtract %esp 0x200/imm32
1023     68/push 0x200/imm32/length
1024     68/push 0/imm32/read
1025     68/push 0/imm32/write
1026     89/<- %ecx 4/r32/esp
1027     # var word-slice/edx : slice
1028     68/push 0/imm32/end
1029     68/push 0/imm32/start
1030     89/<- %edx 4/r32/esp
1031     # var open-curly-count/ebx : int = 1
1032     bb/copy-to-ebx 1/imm32
1033     { # line loop
1034 $parse-mu-block:line-loop:
1035       # if (open-curly-count == 0) break
1036       81 7/subop/compare %ebx 0/imm32
1037       0f 84/jump-if-equal break/disp32
1038       # line = read-line-buffered(in)
1039       (clear-stream %ecx)
1040       (read-line-buffered *(ebp+8) %ecx)
1041       # if (line->write == 0) break
1042       81 7/subop/compare *ecx 0/imm32
1043       0f 84/jump-if-equal break/disp32
1044       # word-slice = next-word(line)
1045       (next-word %ecx %edx)
1046       # if slice-empty?(word-slice) continue
1047       (slice-empty? %ecx)
1048       3d/compare-eax-and 0/imm32
1049       75/jump-if-not-equal loop/disp8
1050       # if (slice-starts-with?(word-slice, '#') continue
1051       # . eax = *word-slice->start
1052       8b/-> *edx 0/r32/eax
1053       8a/copy-byte *eax 0/r32/AL
1054       81 4/subop/and %eax 0xff/imm32
1055       # . if (eax == '#') continue
1056       3d/compare-eax-and 0x23/imm32/hash
1057       74/jump-if-equal loop/disp8
1058       {
1059         # if slice-equal?(word-slice, "{") ++open-curly-count
1060         {
1061           (slice-equal? %ecx "{")
1062           3d/compare-eax-and 0/imm32
1063           74/jump-if-equal break/disp8
1064           43/increment-ebx
1065           eb/jump $curly-found:end/disp8
1066         }
1067         # else if slice-equal?(word-slice, "}") --open-curly-count
1068         {
1069           (slice-equal? %ecx "}")
1070           3d/compare-eax-and 0/imm32
1071           74/jump-if-equal break/disp8
1072           4b/decrement-ebx
1073           eb/jump $curly-found:end/disp8
1074         }
1075         # else break
1076         eb/jump $parse-mu-block:end/disp8
1077       }
1078       # - check for invalid tokens after curly
1079 $curly-found:end:
1080       # second-word-slice = next-word(line)
1081       (next-word %ecx %edx)
1082       # if slice-empty?(second-word-slice) continue
1083       (slice-empty? %ecx)
1084       3d/compare-eax-and 0/imm32
1085       0f 85/jump-if-not-equal loop/disp32
1086       # if (slice-starts-with?(second-word-slice, '#') continue
1087       # . eax = *second-word-slice->start
1088       8b/-> *edx 0/r32/eax
1089       8a/copy-byte *eax 0/r32/AL
1090       81 4/subop/and %eax 0xff/imm32
1091       # . if (eax == '#') continue
1092       3d/compare-eax-and 0x23/imm32/hash
1093       0f 84/jump-if-equal loop/disp32
1094       # abort
1095       eb/jump $parse-mu-block:abort/disp8
1096     } # end line loop
1097 $parse-mu-block:end:
1098     # . reclaim locals
1099     81 0/subop/add %esp 0x214/imm32
1100     # . restore registers
1101     5b/pop-to-ebx
1102     5a/pop-to-edx
1103     59/pop-to-ecx
1104     58/pop-to-eax
1105     # . epilogue
1106     89/<- %esp 5/r32/ebp
1107     5d/pop-to-ebp
1108     c3/return
1109 
1110 $parse-mu-block:abort:
1111     # error("'{' or '}' should be on its own line, but got '")
1112     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
1113     (rewind-stream %ecx)
1114     (write-stream 2 %ecx)
1115     (write-buffered Stderr "'\n")
1116     (flush Stderr)
1117     # . syscall(exit, 1)
1118     bb/copy-to-ebx  1/imm32
1119     b8/copy-to-eax  1/imm32/exit
1120     cd/syscall  0x80/imm8
1121     # never gets here
1122 
1123 new-function:  # ad: allocation-descriptor, name: string, subx-name: string, inouts: (address list var), outputs: (address list var), body: (address block), next: (address function) -> result/eax: (address function)
1124     # . prologue
1125     55/push-ebp
1126     89/<- %ebp 4/r32/esp
1127     # . save registers
1128     51/push-ecx
1129     #
1130     (allocate *(ebp+8) *Function-size)  # => eax
1131     8b/-> *(ebp+0xc) 1/r32/ecx
1132     89/<- *eax 1/r32/ecx  # Function-name
1133     8b/-> *(ebp+0x10) 1/r32/ecx
1134     89/<- *(eax+4) 1/r32/ecx  # Function-subx-name
1135     8b/-> *(ebp+0x14) 1/r32/ecx
1136     89/<- *(eax+8) 1/r32/ecx  # Function-inouts
1137     8b/-> *(ebp+0x18) 1/r32/ecx
1138     89/<- *(eax+0xc) 1/r32/ecx  # Function-outputs
1139     8b/-> *(ebp+0x1c) 1/r32/ecx
1140     89/<- *(eax+0x10) 1/r32/ecx  # Function-body
1141     8b/-> *(ebp+0x20) 1/r32/ecx
1142     89/<- *(eax+0x14) 1/r32/ecx  # Function-next
1143 $new-function:end:
1144     # . restore registers
1145     59/pop-to-ecx
1146     # . epilogue
1147     89/<- %esp 5/r32/ebp
1148     5d/pop-to-ebp
1149     c3/return
1150 
1151 new-var:  # ad: allocation-descriptor, name: string, type: int, block: int, stack-offset: int, register: string -> result/eax: (address var)
1152     # . prologue
1153     55/push-ebp
1154     89/<- %ebp 4/r32/esp
1155     # . save registers
1156     51/push-ecx
1157     #
1158     (allocate *(ebp+8) *Var-size)  # => eax
1159     8b/-> *(ebp+0xc) 1/r32/ecx
1160     89/<- *eax 1/r32/ecx  # Var-name
1161     8b/-> *(ebp+0x10) 1/r32/ecx
1162     89/<- *(eax+4) 1/r32/ecx  # Var-type
1163     8b/-> *(ebp+0x14) 1/r32/ecx
1164     89/<- *(eax+8) 1/r32/ecx  # Var-block
1165     8b/-> *(ebp+0x18) 1/r32/ecx
1166     89/<- *(eax+0xc) 1/r32/ecx  # Var-stack-offset
1167     8b/-> *(ebp+0x1c) 1/r32/ecx
1168     89/<- *(eax+0x10) 1/r32/ecx  # Var-register
1169 $new-var:end:
1170     # . restore registers
1171     59/pop-to-ecx
1172     # . epilogue
1173     89/<- %esp 5/r32/ebp
1174     5d/pop-to-ebp
1175     c3/return
1176 
1177 new-block:  # ad: allocation-descriptor, data: (address list statement)
1178     # . prologue
1179     55/push-ebp
1180     89/<- %ebp 4/r32/esp
1181     # . save registers
1182     51/push-ecx
1183     #
1184     (allocate *(ebp+8) *Stmt-size)  # => eax
1185     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
1186     8b/-> *(ebp+0xc) 1/r32/ecx
1187     89/<- *(eax+4) 1/r32/ecx  # Block-statements
1188 $new-block:end:
1189     # . restore registers
1190     59/pop-to-ecx
1191     # . epilogue
1192     89/<- %esp 5/r32/ebp
1193     5d/pop-to-ebp
1194     c3/return
1195 
1196 new-stmt:  # ad: allocation-descriptor, operation: string, inouts: (address list var), outputs: (address list var) -> result/eax: (address statement)
1197     # . prologue
1198     55/push-ebp
1199     89/<- %ebp 4/r32/esp
1200     # . save registers
1201     51/push-ecx
1202     #
1203     (allocate *(ebp+8) *Stmt-size)  # => eax
1204     c7 0/subop/copy *eax 1/imm32/tag/regular-stmt  # Stmt-tag
1205     8b/-> *(ebp+0xc) 1/r32/ecx
1206     89/<- *(eax+4) 1/r32/ecx  # Stmt1-operation
1207     8b/-> *(ebp+0x10) 1/r32/ecx
1208     89/<- *(eax+8) 1/r32/ecx  # Stmt1-inouts
1209     8b/-> *(ebp+0x14) 1/r32/ecx
1210     89/<- *(eax+0xc) 1/r32/ecx  # Stmt1-outputs
1211 $new-stmt:end:
1212     # . restore registers
1213     59/pop-to-ecx
1214     # . epilogue
1215     89/<- %esp 5/r32/ebp
1216     5d/pop-to-ebp
1217     c3/return
1218 
1219 new-vardef:  # ad: allocation-descriptor, name: string, type: int -> result/eax: (address statement)
1220     # . prologue
1221     55/push-ebp
1222     89/<- %ebp 4/r32/esp
1223     # . save registers
1224     51/push-ecx
1225     #
1226     (allocate *(ebp+8) *Stmt-size)  # => eax
1227     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
1228     8b/-> *(ebp+0xc) 1/r32/ecx
1229     89/<- *(eax+4) 1/r32/ecx  # Vardef-name
1230     8b/-> *(ebp+0x10) 1/r32/ecx
1231     89/<- *(eax+8) 1/r32/ecx  # Vardef-type
1232 $new-vardef:end:
1233     # . restore registers
1234     59/pop-to-ecx
1235     # . epilogue
1236     89/<- %esp 5/r32/ebp
1237     5d/pop-to-ebp
1238     c3/return
1239 
1240 new-regvardef:  # ad: allocation-descriptor, name: string, type: int, register: string -> result/eax: (address statement)
1241     # . prologue
1242     55/push-ebp
1243     89/<- %ebp 4/r32/esp
1244     # . save registers
1245     51/push-ecx
1246     #
1247     (allocate *(ebp+8) *Stmt-size)  # => eax
1248     c7 0/subop/copy *eax 3/imm32/tag/var-in-register
1249     8b/-> *(ebp+0xc) 1/r32/ecx
1250     89/<- *(eax+4) 1/r32/ecx  # Regvardef-name
1251     8b/-> *(ebp+0x10) 1/r32/ecx
1252     89/<- *(eax+8) 1/r32/ecx  # Regvardef-type
1253     8b/-> *(ebp+0x14) 1/r32/ecx
1254     89/<- *(eax+0xc) 1/r32/ecx  # Regvardef-register
1255 $new-regvardef:end:
1256     # . restore registers
1257     59/pop-to-ecx
1258     # . epilogue
1259     89/<- %esp 5/r32/ebp
1260     5d/pop-to-ebp
1261     c3/return
1262 
1263 new-named-block:  # ad: allocation-descriptor, name: string, data: (address list statement)
1264     # . prologue
1265     55/push-ebp
1266     89/<- %ebp 4/r32/esp
1267     # . save registers
1268     51/push-ecx
1269     #
1270     (allocate *(ebp+8) *Stmt-size)  # => eax
1271     c7 0/subop/copy *eax 4/imm32/tag/named-block
1272     8b/-> *(ebp+0xc) 1/r32/ecx
1273     89/<- *(eax+4) 1/r32/ecx  # Named-block-name
1274     8b/-> *(ebp+0x10) 1/r32/ecx
1275     89/<- *(eax+8) 1/r32/ecx  # Named-block-statements
1276 $new-named-block:end:
1277     # . restore registers
1278     59/pop-to-ecx
1279     # . epilogue
1280     89/<- %esp 5/r32/ebp
1281     5d/pop-to-ebp
1282     c3/return
1283 
1284 #######################################################
1285 # Type-checking
1286 #######################################################
1287 
1288 check-mu-types:
1289     # . prologue
1290     55/push-ebp
1291     89/<- %ebp 4/r32/esp
1292     #
1293 $check-mu-types:end:
1294     # . epilogue
1295     89/<- %esp 5/r32/ebp
1296     5d/pop-to-ebp
1297     c3/return
1298 
1299 #######################################################
1300 # Code-generation
1301 #######################################################
1302 
1303 emit-subx:  # out : (address buffered-file)
1304     # . prologue
1305     55/push-ebp
1306     89/<- %ebp 4/r32/esp
1307     # . save registers
1308     50/push-eax
1309     51/push-ecx
1310     57/push-edi
1311     # edi = out
1312     8b/-> *(ebp+8) 7/r32/edi
1313     # var curr/ecx : (address function) = Program
1314     8b/-> *Program 1/r32/ecx
1315     {
1316       # if (curr == NULL) break
1317       81 7/subop/compare %ecx 0/imm32
1318       0f 84/jump-if-equal break/disp32
1319       (emit-subx-function %edi %ecx)
1320       # curr = curr->next
1321       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
1322       e9/jump loop/disp32
1323     }
1324 $emit-subx:end:
1325     # . restore registers
1326     5f/pop-to-edi
1327     59/pop-to-ecx
1328     58/pop-to-eax
1329     # . epilogue
1330     89/<- %esp 5/r32/ebp
1331     5d/pop-to-ebp
1332     c3/return
1333 
1334 emit-subx-function:  # out : (address buffered-file), f : (address function)
1335     # . prologue
1336     55/push-ebp
1337     89/<- %ebp 4/r32/esp
1338     # . save registers
1339     50/push-eax
1340     51/push-ecx
1341     57/push-edi
1342     # edi = out
1343     8b/-> *(ebp+8) 7/r32/edi
1344     # ecx = f
1345     8b/-> *(ebp+0xc) 1/r32/ecx
1346     #
1347     (write-buffered %edi *ecx)
1348     (write-buffered %edi ":\n")
1349     (emit-subx-prologue %edi)
1350     (emit-subx-block %edi *(ecx+0x10))  # Function-body
1351     (emit-subx-epilogue %edi)
1352 $emit-subx-function:end:
1353     # . restore registers
1354     5f/pop-to-edi
1355     59/pop-to-ecx
1356     58/pop-to-eax
1357     # . epilogue
1358     89/<- %esp 5/r32/ebp
1359     5d/pop-to-ebp
1360     c3/return
1361 
1362 emit-subx-block:  # out : (address buffered-file), block : (address block)
1363     # . prologue
1364     55/push-ebp
1365     89/<- %ebp 4/r32/esp
1366     #
1367 $emit-subx-block:end:
1368     # . epilogue
1369     89/<- %esp 5/r32/ebp
1370     5d/pop-to-ebp
1371     c3/return
1372 
1373 emit-subx-statement:  # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address primitive), functions : (address function)
1374     # . prologue
1375     55/push-ebp
1376     89/<- %ebp 4/r32/esp
1377     # . save registers
1378     50/push-eax
1379     51/push-ecx
1380     # if stmt matches a primitive, emit it
1381     {
1382 $emit-subx-statement:primitive:
1383       (find-matching-primitive *(ebp+0x14) *(ebp+0xc))  # primitives, stmt => curr/eax
1384       3d/compare-eax-and 0/imm32
1385       74/jump-if-equal break/disp8
1386       (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax)  # out, stmt, vars, curr
1387       e9/jump $emit-subx-statement:end/disp32
1388     }
1389     # else if stmt matches a function, emit a call to it
1390     {
1391 $emit-subx-statement:call:
1392       (find-matching-function *(ebp+0x18) *(ebp+0xc))  # functions, stmt => curr/eax
1393       3d/compare-eax-and 0/imm32
1394       74/jump-if-equal break/disp8
1395       (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax)  # out, stmt, vars, curr
1396       e9/jump $emit-subx-statement:end/disp32
1397     }
1398     # else abort
1399     e9/jump $emit-subx-statement:abort/disp32
1400 $emit-subx-statement:end:
1401     # . restore registers
1402     59/pop-to-ecx
1403     58/pop-to-eax
1404     # . epilogue
1405     89/<- %esp 5/r32/ebp
1406     5d/pop-to-ebp
1407     c3/return
1408 
1409 $emit-subx-statement:abort:
1410     # error("couldn't translate '" stmt "'\n")
1411     (write-buffered Stderr "couldn't translate '")
1412 #?     (emit-string Stderr *(ebp+0xc))  # TODO
1413     (write-buffered Stderr "'\n")
1414     (flush Stderr)
1415     # . syscall(exit, 1)
1416     bb/copy-to-ebx  1/imm32
1417     b8/copy-to-eax  1/imm32/exit
1418     cd/syscall  0x80/imm8
1419     # never gets here
1420 
1421 # Primitives supported
1422 == data
1423 Primitives:
1424     # increment var => ff 0/subop/increment *(ebp+__)
1425     "increment"/imm32/name
1426     Single-int-var-on-stack/imm32/inouts
1427     0/imm32/no-outputs
1428     "ff 0/subop/increment"/imm32/subx-name
1429     1/imm32/rm32-is-first-inout
1430     0/imm32/no-r32
1431     0/imm32/no-imm32
1432     _Primitive-inc-reg/imm32/next
1433 _Primitive-inc-reg:
1434     # var/reg <- increment => ff 0/subop/increment %__
1435     "increment"/imm32/name
1436     0/imm32/no-inouts
1437     Single-int-var-in-some-register/imm32/outputs
1438     "ff 0/subop/increment"/imm32/subx-name
1439     3/imm32/rm32-is-first-output
1440     0/imm32/no-r32
1441     0/imm32/no-imm32
1442     _Primitive-add-reg-to-reg/imm32/next
1443 _Primitive-add-reg-to-reg:
1444     # var1/reg <- add var2/reg => 01 var1/rm32 var2/r32
1445     "add"/imm32/name
1446     Single-int-var-in-some-register/imm32/inouts
1447     Single-int-var-in-some-register/imm32/outputs
1448     "01"/imm32/subx-name
1449     3/imm32/rm32-is-first-output
1450     1/imm32/r32-is-first-inout
1451     0/imm32/no-imm32
1452     _Primitive-add-reg-to-mem/imm32/next
1453 _Primitive-add-reg-to-mem:
1454     # add-to var1 var2/reg => 01 var1 var2/r32
1455     "add-to"/imm32/name
1456     Int-var-and-second-int-var-in-some-register/imm32/inouts
1457     0/imm32/outputs
1458     "01"/imm32/subx-name
1459     1/imm32/rm32-is-first-inout
1460     2/imm32/r32-is-second-inout
1461     0/imm32/no-imm32
1462     _Primitive-add-mem-to-reg/imm32/next
1463 _Primitive-add-mem-to-reg:
1464     # var1/reg <- add var2 => 03 var2/rm32 var1/r32
1465     "add"/imm32/name
1466     Single-int-var-on-stack/imm32/inouts
1467     Single-int-var-in-some-register/imm32/outputs
1468     "03"/imm32/subx-name
1469     1/imm32/rm32-is-first-inout
1470     3/imm32/r32-is-first-output
1471     0/imm32/no-imm32
1472     _Primitive-add-lit-to-reg/imm32/next
1473 _Primitive-add-lit-to-reg:
1474     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
1475     "add"/imm32/name
1476     Single-lit-var/imm32/inouts
1477     Single-int-var-in-some-register/imm32/outputs
1478     "81 0/subop/add"/imm32/subx-name
1479     3/imm32/rm32-is-first-output
1480     0/imm32/no-r32
1481     1/imm32/imm32-is-first-inout
1482     _Primitive-add-lit-to-mem/imm32/next
1483 _Primitive-add-lit-to-mem:
1484     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
1485     "add-to"/imm32/name
1486     Int-var-and-literal/imm32/inouts
1487     0/imm32/outputs
1488     "81 0/subop/add"/imm32/subx-name
1489     1/imm32/rm32-is-first-inout
1490     0/imm32/no-r32
1491     2/imm32/imm32-is-first-inout
1492     0/imm32/next
1493 
1494 Single-int-var-on-stack:
1495     Int-var-on-stack/imm32
1496     0/imm32/next
1497 
1498 Int-var-on-stack:
1499     "arg1"/imm32/name
1500     1/imm32/type-int
1501     1/imm32/some-block-depth
1502     1/imm32/some-stack-offset
1503     0/imm32/no-register
1504 
1505 Int-var-and-second-int-var-in-some-register:
1506     Int-var-on-stack/imm32
1507     Single-int-var-in-some-register/imm32/next
1508 
1509 Int-var-and-literal:
1510     Int-var-on-stack/imm32
1511     Single-lit-var/imm32/next
1512 
1513 Single-int-var-in-some-register:
1514     Int-var-in-some-register/imm32
1515     0/imm32/next
1516 
1517 Int-var-in-some-register:
1518     "arg1"/imm32/name
1519     1/imm32/type-int
1520     1/imm32/some-block-depth
1521     0/imm32/no-stack-offset
1522     "*"/imm32/register
1523 
1524 Single-lit-var:
1525     Lit-var/imm32
1526     0/imm32/next
1527 
1528 Lit-var:
1529     "literal"/imm32/name
1530     0/imm32/type-literal
1531     1/imm32/some-block-depth
1532     0/imm32/no-stack-offset
1533     0/imm32/no-register
1534 
1535 == code
1536 emit-subx-primitive:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), primitive : (address function)
1537     # . prologue
1538     55/push-ebp
1539     89/<- %ebp 4/r32/esp
1540     # . save registers
1541     50/push-eax
1542     51/push-ecx
1543     # ecx = primitive
1544     8b/-> *(ebp+0x14) 1/r32/ecx
1545     # emit primitive name
1546     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
1547     # emit rm32 if necessary
1548     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
1549     # emit r32 if necessary
1550     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
1551     # emit imm32 if necessary
1552     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
1553 $emit-subx-primitive:end:
1554     # . restore registers
1555     59/pop-to-ecx
1556     58/pop-to-eax
1557     # . epilogue
1558     89/<- %esp 5/r32/ebp
1559     5d/pop-to-ebp
1560     c3/return
1561 
1562 emit-subx-rm32:  # out : (address buffered-file), l : arg-location, stmt : (address statement)
1563     # . prologue
1564     55/push-ebp
1565     89/<- %ebp 4/r32/esp
1566     # . save registers
1567     50/push-eax
1568     # if (l == 0) return
1569     81 7/subop/compare *(ebp+0xc) 0/imm32
1570     74/jump-if-equal $emit-subx-rm32:end/disp8
1571     #
1572     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
1573     (emit-subx-var-as-rm32 *(ebp+8) %eax)  # out, var
1574 $emit-subx-rm32:end:
1575     # . restore registers
1576     58/pop-to-eax
1577     # . epilogue
1578     89/<- %esp 5/r32/ebp
1579     5d/pop-to-ebp
1580     c3/return
1581 
1582 get-stmt-operand-from-arg-location:  # stmt : (address statement), l : arg-location -> var/eax : (address variable)
1583     # . prologue
1584     55/push-ebp
1585     89/<- %ebp 4/r32/esp
1586     # . save registers
1587     51/push-ecx
1588     # eax = l
1589     8b/-> *(ebp+0xc) 0/r32/eax
1590     # ecx = stmt
1591     8b/-> *(ebp+8) 1/r32/ecx
1592     # if (l == 1) return stmt->inouts->var
1593     {
1594       3d/compare-eax-and 1/imm32
1595       75/jump-if-not-equal break/disp8
1596 $get-stmt-operand-from-arg-location:1:
1597       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
1598       8b/-> *eax 0/r32/eax  # Operand-var
1599       eb/jump $get-stmt-operand-from-arg-location:end/disp8
1600     }
1601     # if (l == 2) return stmt->inouts->next->var
1602     {
1603       3d/compare-eax-and 2/imm32
1604       75/jump-if-not-equal break/disp8
1605 $get-stmt-operand-from-arg-location:2:
1606       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
1607       8b/-> *(eax+4) 0/r32/eax  # Operand-next
1608       8b/-> *eax 0/r32/eax  # Operand-var
1609       eb/jump $get-stmt-operand-from-arg-location:end/disp8
1610     }
1611     # if (l == 3) return stmt->outputs
1612     {
1613       3d/compare-eax-and 3/imm32
1614       75/jump-if-not-equal break/disp8
1615 $get-stmt-operand-from-arg-location:3:
1616       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
1617       8b/-> *eax 0/r32/eax  # Operand-var
1618       eb/jump $get-stmt-operand-from-arg-location:end/disp8
1619     }
1620     # abort
1621     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
1622 $get-stmt-operand-from-arg-location:end:
1623     # . restore registers
1624     59/pop-to-ecx
1625     # . epilogue
1626     89/<- %esp 5/r32/ebp
1627     5d/pop-to-ebp
1628     c3/return
1629 
1630 $get-stmt-operand-from-arg-location:abort:
1631     # error("invalid arg-location " eax)
1632     (write-buffered Stderr "invalid arg-location ")
1633     (print-int32-buffered Stderr %eax)
1634     (write-buffered Stderr "\n")
1635     (flush Stderr)
1636     # . syscall(exit, 1)
1637     bb/copy-to-ebx  1/imm32
1638     b8/copy-to-eax  1/imm32/exit
1639     cd/syscall  0x80/imm8
1640     # never gets here
1641 
1642 emit-subx-r32:  # out : (address buffered-file), l : arg-location, stmt : (address statement)
1643     # . prologue
1644     55/push-ebp
1645     89/<- %ebp 4/r32/esp
1646     # . save registers
1647     50/push-eax
1648     51/push-ecx
1649     # if (location == 0) return
1650     81 7/subop/compare *(ebp+0xc) 0/imm32
1651     0f 84/jump-if-equal $emit-subx-r32:end/disp32
1652     #
1653     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
1654     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax : (address register-index)
1655     (write-buffered *(ebp+8) Space)
1656     (print-int32-buffered *(ebp+8) *eax)
1657     (write-buffered *(ebp+8) "/r32")
1658 $emit-subx-r32:end:
1659     # . restore registers
1660     59/pop-to-ecx
1661     58/pop-to-eax
1662     # . epilogue
1663     89/<- %esp 5/r32/ebp
1664     5d/pop-to-ebp
1665     c3/return
1666 
1667 emit-subx-imm32:  # out : (address buffered-file), l : arg-location, stmt : (address statement)
1668     # . prologue
1669     55/push-ebp
1670     89/<- %ebp 4/r32/esp
1671     # . save registers
1672     50/push-eax
1673     51/push-ecx
1674     # if (location == 0) return
1675     81 7/subop/compare *(ebp+0xc) 0/imm32
1676     74/jump-if-equal $emit-subx-imm32:end/disp8
1677     #
1678     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # stmt, l => var/eax
1679     (write-buffered *(ebp+8) Space)
1680     (write-buffered *(ebp+8) *eax)  # Var-name
1681     (write-buffered *(ebp+8) "/imm32")
1682 $emit-subx-imm32:end:
1683     # . restore registers
1684     59/pop-to-ecx
1685     58/pop-to-eax
1686     # . epilogue
1687     89/<- %esp 5/r32/ebp
1688     5d/pop-to-ebp
1689     c3/return
1690 
1691 emit-subx-call:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function)
1692     # . prologue
1693     55/push-ebp
1694     89/<- %ebp 4/r32/esp
1695     # . save registers
1696     50/push-eax
1697     51/push-ecx
1698     #
1699     (write-buffered *(ebp+8) "(")
1700     # - emit function name
1701     8b/-> *(ebp+0x14) 1/r32/ecx
1702     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
1703     # - emit arguments
1704     # var curr/ecx : (list var) = stmt->inouts
1705     8b/-> *(ebp+0xc) 1/r32/ecx
1706     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
1707     {
1708       # if (curr == null) break
1709       81 7/subop/compare %ecx 0/imm32
1710       74/jump-if-equal break/disp8
1711       #
1712       (emit-subx-call-operand *(ebp+8) *ecx)
1713       # curr = curr->next
1714       8b/-> *(ecx+4) 1/r32/ecx
1715     }
1716     #
1717     (write-buffered *(ebp+8) ")")
1718 $emit-subx-call:end:
1719     # . restore registers
1720     59/pop-to-ecx
1721     58/pop-to-eax
1722     # . epilogue
1723     89/<- %esp 5/r32/ebp
1724     5d/pop-to-ebp
1725     c3/return
1726 
1727 emit-subx-call-operand:  # out : (address buffered-file), operand : (address variable)
1728     # . prologue
1729     55/push-ebp
1730     89/<- %ebp 4/r32/esp
1731     # . save registers
1732     50/push-eax
1733     # eax = operand
1734     8b/-> *(ebp+0xc) 0/r32/eax
1735     # if non-literal, emit appropriately
1736     (emit-subx-var-as-rm32 *(ebp+8) %eax)
1737     # else if (operand->type == literal) emit "__"
1738     {
1739       81 7/subop/compare *(eax+4) 0/imm32  # Var-type
1740       75/jump-if-not-equal break/disp8
1741 $emit-subx-call-operand:literal:
1742       (write-buffered *(ebp+8) Space)
1743       (write-buffered *(ebp+8) *eax)
1744     }
1745 $emit-subx-call-operand:end:
1746     # . restore registers
1747     58/pop-to-eax
1748     # . epilogue
1749     89/<- %esp 5/r32/ebp
1750     5d/pop-to-ebp
1751     c3/return
1752 
1753 emit-subx-var-as-rm32:  # out : (address buffered-file), operand : (address variable)
1754     # . prologue
1755     55/push-ebp
1756     89/<- %ebp 4/r32/esp
1757     # . save registers
1758     50/push-eax
1759     # eax = operand
1760     8b/-> *(ebp+0xc) 0/r32/eax
1761     # if (operand->register) emit "%__"
1762     {
1763       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
1764       74/jump-if-equal break/disp8
1765 $emit-subx-var-as-rm32:register:
1766       (write-buffered *(ebp+8) " %")
1767       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
1768     }
1769     # else if (operand->stack-offset) emit "*(ebp+__)"
1770     {
1771       81 7/subop/compare *(eax+0xc) 0/imm32  # Var-stack-offset
1772       74/jump-if-equal break/disp8
1773 $emit-subx-var-as-rm32:stack:
1774       (write-buffered *(ebp+8) Space)
1775       (write-buffered *(ebp+8) "*(ebp+")
1776       8b/-> *(ebp+0xc) 0/r32/eax
1777       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
1778       (write-buffered *(ebp+8) ")")
1779     }
1780 $emit-subx-var-as-rm32:end:
1781     # . restore registers
1782     58/pop-to-eax
1783     # . epilogue
1784     89/<- %esp 5/r32/ebp
1785     5d/pop-to-ebp
1786     c3/return
1787 
1788 find-matching-function:  # functions : (address function), stmt : (address statement) -> result/eax : (address function)
1789     # . prologue
1790     55/push-ebp
1791     89/<- %ebp 4/r32/esp
1792     # . save registers
1793     51/push-ecx
1794     # var curr/ecx : (address function) = functions
1795     8b/-> *(ebp+8) 1/r32/ecx
1796     {
1797       # if (curr == null) break
1798       81 7/subop/compare %ecx 0/imm32
1799       74/jump-if-equal break/disp8
1800       # if match(curr, stmt) return curr
1801       {
1802         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
1803         3d/compare-eax-and 0/imm32
1804         74/jump-if-equal break/disp8
1805         89/<- %eax 1/r32/ecx
1806         eb/jump $find-matching-function:end/disp8
1807       }
1808       # curr = curr->next
1809       8b/-> *(ecx+0x10) 1/r32/ecx  # Function-next
1810       eb/jump loop/disp8
1811     }
1812     # return null
1813     b8/copy-to-eax 0/imm32
1814 $find-matching-function:end:
1815     # . restore registers
1816     59/pop-to-ecx
1817     # . epilogue
1818     89/<- %esp 5/r32/ebp
1819     5d/pop-to-ebp
1820     c3/return
1821 
1822 find-matching-primitive:  # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive)
1823     # . prologue
1824     55/push-ebp
1825     89/<- %ebp 4/r32/esp
1826     # . save registers
1827     51/push-ecx
1828     # var curr/ecx : (address primitive) = primitives
1829     8b/-> *(ebp+8) 1/r32/ecx
1830     {
1831 $find-matching-primitive:loop:
1832       # if (curr == null) break
1833       81 7/subop/compare %ecx 0/imm32
1834       74/jump-if-equal break/disp8
1835       # if match(curr, stmt) return curr
1836       {
1837         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
1838         3d/compare-eax-and 0/imm32
1839         74/jump-if-equal break/disp8
1840         89/<- %eax 1/r32/ecx
1841         eb/jump $find-matching-function:end/disp8
1842       }
1843 $find-matching-primitive:next-primitive:
1844       # curr = curr->next
1845       8b/-> *(ecx+0x1c) 1/r32/ecx  # Primitive-next
1846       eb/jump loop/disp8
1847     }
1848     # return null
1849     b8/copy-to-eax 0/imm32
1850 $find-matching-primitive:end:
1851     # . restore registers
1852     59/pop-to-ecx
1853     # . epilogue
1854     89/<- %esp 5/r32/ebp
1855     5d/pop-to-ebp
1856     c3/return
1857 
1858 mu-stmt-matches-function?:  # stmt : (address statement), function : (address opcode-info) => result/eax : boolean
1859     # . prologue
1860     55/push-ebp
1861     89/<- %ebp 4/r32/esp
1862     # . save registers
1863     51/push-ecx
1864     # return primitive->name == stmt->operation
1865     8b/-> *(ebp+8) 1/r32/ecx
1866     8b/-> *(ebp+0xc) 0/r32/eax
1867     (string-equal? *(ecx+4) *eax)  # Stmt1-operation, Primitive-name => eax
1868 $mu-stmt-matches-function?:end:
1869     # . restore registers
1870     59/pop-to-ecx
1871     # . epilogue
1872     89/<- %esp 5/r32/ebp
1873     5d/pop-to-ebp
1874     c3/return
1875 
1876 mu-stmt-matches-primitive?:  # stmt : (address statement), primitive : (address primitive) => result/eax : boolean
1877     # A mu stmt matches a primitive if the name matches, all the inout vars
1878     # match, and all the output vars match.
1879     # Vars match if types match and registers match.
1880     # In addition, a stmt output matches a primitive's output if types match
1881     # and the primitive has a wildcard register.
1882     # . prologue
1883     55/push-ebp
1884     89/<- %ebp 4/r32/esp
1885     # . save registers
1886     51/push-ecx
1887     52/push-edx
1888     53/push-ebx
1889     56/push-esi
1890     57/push-edi
1891     # ecx = stmt
1892     8b/-> *(ebp+8) 1/r32/ecx
1893     # edx = primitive
1894     8b/-> *(ebp+0xc) 2/r32/edx
1895     {
1896 $mu-stmt-matches-primitive?:check-name:
1897       # if (primitive->name != stmt->operation) return false
1898       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
1899       3d/compare-eax-and 0/imm32
1900       75/jump-if-not-equal break/disp8
1901       b8/copy-to-eax 0/imm32
1902       e9/jump $mu-stmt-matches-primitive?:end/disp32
1903     }
1904 $mu-stmt-matches-primitive?:check-inouts:
1905     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
1906     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts
1907     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
1908     {
1909       # if (curr == 0) return (curr2 == 0)
1910       {
1911         81 7/subop/compare %esi 0/imm32
1912         75/jump-if-not-equal break/disp8
1913         {
1914           81 7/subop/compare %edi 0/imm32
1915           75/jump-if-not-equal break/disp8
1916           # return true
1917           b8/copy-to-eax 1/imm32
1918           e9/jump $mu-stmt-matches-primitive?:end/disp32
1919         }
1920         # return false
1921         b8/copy-to-eax 0/imm32
1922         e9/jump $mu-stmt-matches-primitive?:end/disp32
1923       }
1924       # if (curr2 == 0) return false
1925       {
1926         81 7/subop/compare %edi 0/imm32
1927         75/jump-if-not-equal break/disp8
1928         b8/copy-to-eax 0/imm32
1929         e9/jump $mu-stmt-matches-primitive?:end/disp32
1930       }
1931       # if (curr != curr2) return false
1932       {
1933         (operand-matches-primitive? *esi *edi)  # => eax
1934         3d/compare-eax-and 0/imm32
1935         75/jump-if-not-equal break/disp8
1936         b8/copy-to-eax 0/imm32
1937         e9/jump $mu-stmt-matches-primitive?:end/disp32
1938       }
1939       # curr=curr->next
1940       8b/-> *(esi+4) 6/r32/esi  # Operand-next
1941       # curr2=curr2->next
1942       8b/-> *(edi+4) 7/r32/edi  # Operand-next
1943       eb/jump loop/disp8
1944     }
1945 $mu-stmt-matches-primitive?:check-outputs:
1946     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
1947     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
1948     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
1949     {
1950       # if (curr == 0) return (curr2 == 0)
1951       {
1952         81 7/subop/compare %esi 0/imm32
1953         75/jump-if-not-equal break/disp8
1954         {
1955           81 7/subop/compare %edi 0/imm32
1956           75/jump-if-not-equal break/disp8
1957           # return true
1958           b8/copy-to-eax 1/imm32
1959           e9/jump $mu-stmt-matches-primitive?:end/disp32
1960         }
1961         # return false
1962         b8/copy-to-eax 0/imm32
1963         e9/jump $mu-stmt-matches-primitive?:end/disp32
1964       }
1965       # if (curr2 == 0) return false
1966       {
1967         81 7/subop/compare %edi 0/imm32
1968         75/jump-if-not-equal break/disp8
1969         b8/copy-to-eax 0/imm32
1970         e9/jump $mu-stmt-matches-primitive?:end/disp32
1971       }
1972       # if (curr != curr2) return false
1973       {
1974         (operand-matches-primitive? *esi *edi)  # => eax
1975         3d/compare-eax-and 0/imm32
1976         75/jump-if-not-equal break/disp8
1977         b8/copy-to-eax 0/imm32
1978         e9/jump $mu-stmt-matches-primitive?:end/disp32
1979       }
1980       # curr=curr->next
1981       8b/-> *(ecx+4) 1/r32/ecx  # Operand-next
1982       # curr2=curr2->next
1983       8b/-> *(edx+4) 2/r32/edx  # Operand-next
1984       eb/jump loop/disp8
1985     }
1986 $mu-stmt-matches-primitive?:return-true:
1987     b8/copy-to-eax 1/imm32
1988 $mu-stmt-matches-primitive?:end:
1989     # . restore registers
1990     5f/pop-to-edi
1991     5e/pop-to-esi
1992     5b/pop-to-ebx
1993     5a/pop-to-edx
1994     59/pop-to-ecx
1995     # . epilogue
1996     89/<- %esp 5/r32/ebp
1997     5d/pop-to-ebp
1998     c3/return
1999 
2000 operand-matches-primitive?:  # var : (address var), primout-var : (address var) => result/eax : boolean
2001     # . prologue
2002     55/push-ebp
2003     89/<- %ebp 4/r32/esp
2004     # . save registers
2005     56/push-esi
2006     57/push-edi
2007     # esi = var
2008     8b/-> *(ebp+8) 6/r32/esi
2009     # edi = primout-var
2010     8b/-> *(ebp+0xc) 7/r32/edi
2011     # if (var->type != primout-var->type) return false
2012     8b/-> *(esi+4) 0/r32/eax  # Var-type
2013     39/compare *(edi+4) 0/r32/eax  # Var-type
2014     b8/copy-to-eax 0/imm32/false
2015     75/jump-if-not-equal $operand-matches-primitive?:end/disp8
2016     # return false if var->register doesn't match primout-var->register
2017     {
2018       # if addresses are equal, don't return here
2019       8b/-> *(esi+0x10) 0/r32/eax
2020       39/compare *(edi+0x10) 0/r32/eax
2021       74/jump-if-equal break/disp8
2022       # if either address is 0, return false
2023       3d/compare-eax-and 0/imm32
2024       74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
2025       81 7/subop/compare *(edi+0x10) 0/imm32
2026       74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
2027       # if primout-var->register is "*", return true
2028       (string-equal? *(edi+0x10) "*")  # Var-register
2029       3d/compare-eax-and 0/imm32
2030       b8/copy-to-eax 1/imm32/true
2031       75/jump-if-not-equal $operand-matches-primitive?:end/disp8
2032       # if string contents don't match, return false
2033       (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register
2034       3d/compare-eax-and 0/imm32
2035       b8/copy-to-eax 0/imm32/false
2036       74/jump-if-equal $operand-matches-primitive?:end/disp8
2037     }
2038     # return true
2039     b8/copy-to-eax 1/imm32/true
2040 $operand-matches-primitive?:end:
2041     # . restore registers
2042     5f/pop-to-edi
2043     5e/pop-to-esi
2044     # . epilogue
2045     89/<- %esp 5/r32/ebp
2046     5d/pop-to-ebp
2047     c3/return
2048 
2049 test-emit-subx-statement-primitive:
2050     # Primitive operation on a variable on the stack.
2051     #   increment foo
2052     # =>
2053     #   ff 0/subop/increment *(ebp-8)
2054     #
2055     # There's a variable on the var stack as follows:
2056     #   name: 'foo'
2057     #   type: int
2058     #   stack-offset: -8
2059     #
2060     # There's a primitive with this info:
2061     #   name: 'increment'
2062     #   inouts: int/mem
2063     #   value: 'ff 0/subop/increment'
2064     #
2065     # There's nothing in functions.
2066     #
2067     # . prologue
2068     55/push-ebp
2069     89/<- %ebp 4/r32/esp
2070     # setup
2071     (clear-stream _test-output-stream)
2072     (clear-stream _test-output-buffered-file->buffer)
2073     # var-foo/ecx : var
2074     68/push 0/imm32/no-register
2075     68/push -8/imm32/stack-offset
2076     68/push 1/imm32/block-depth
2077     68/push 1/imm32/type-int
2078     68/push "foo"/imm32
2079     89/<- %ecx 4/r32/esp
2080     # vars/edx : (stack 1)
2081     51/push-ecx/var-foo
2082     68/push 1/imm32/data-length
2083     68/push 1/imm32/top
2084     89/<- %edx 4/r32/esp
2085     # operand/ebx : (list var)
2086     68/push 0/imm32/next
2087     51/push-ecx/var-foo
2088     89/<- %ebx 4/r32/esp
2089     # stmt/esi : statement
2090     68/push 0/imm32/next
2091     68/push 0/imm32/outputs
2092     53/push-ebx/operands
2093     68/push "increment"/imm32/operation
2094     68/push 1/imm32
2095     89/<- %esi 4/r32/esp
2096     # primitives/ebx : primitive
2097     68/push 0/imm32/next
2098     68/push 0/imm32/no-imm32
2099     68/push 0/imm32/no-r32
2100     68/push 1/imm32/rm32-is-first-inout
2101     68/push "ff 0/subop/increment"/imm32/subx-name
2102     68/push 0/imm32/outputs
2103     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
2104     68/push "increment"/imm32/name
2105     89/<- %ebx 4/r32/esp
2106     # convert
2107     (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
2108     (flush _test-output-buffered-file)
2109 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2115     # check output
2116     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive")
2117     # . epilogue
2118     89/<- %esp 5/r32/ebp
2119     5d/pop-to-ebp
2120     c3/return
2121 
2122 test-emit-subx-statement-primitive-register:
2123     # Primitive operation on a variable in a register.
2124     #   foo <- increment
2125     # =>
2126     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
2127     #
2128     # There's a variable on the var stack as follows:
2129     #   name: 'foo'
2130     #   type: int
2131     #   register: 'eax'
2132     #
2133     # There's a primitive with this info:
2134     #   name: 'increment'
2135     #   out: int/reg
2136     #   value: 'ff 0/subop/increment'
2137     #
2138     # There's nothing in functions.
2139     #
2140     # . prologue
2141     55/push-ebp
2142     89/<- %ebp 4/r32/esp
2143     # setup
2144     (clear-stream _test-output-stream)
2145     (clear-stream _test-output-buffered-file->buffer)
2146     # var-foo/ecx : var in eax
2147     68/push "eax"/imm32/register
2148     68/push 0/imm32/no-stack-offset
2149     68/push 1/imm32/block-depth
2150     68/push 1/imm32/type-int
2151     68/push "foo"/imm32
2152     89/<- %ecx 4/r32/esp
2153     # vars/edx : (stack 1)
2154     51/push-ecx/var-foo
2155     68/push 1/imm32/data-length
2156     68/push 1/imm32/top
2157     89/<- %edx 4/r32/esp
2158     # operand/ebx : (list var)
2159     68/push 0/imm32/next
2160     51/push-ecx/var-foo
2161     89/<- %ebx 4/r32/esp
2162     # stmt/esi : statement
2163     68/push 0/imm32/next
2164     53/push-ebx/outputs
2165     68/push 0/imm32/inouts
2166     68/push "increment"/imm32/operation
2167     68/push 1/imm32
2168     89/<- %esi 4/r32/esp
2169     # formal-var/ebx : var in any register
2170     68/push Any-register/imm32
2171     68/push 0/imm32/no-stack-offset
2172     68/push 1/imm32/block-depth
2173     68/push 1/imm32/type-int
2174     68/push "dummy"/imm32
2175     89/<- %ebx 4/r32/esp
2176     # operand/ebx : (list var)
2177     68/push 0/imm32/next
2178     53/push-ebx/formal-var
2179     89/<- %ebx 4/r32/esp
2180     # primitives/ebx : primitive
2181     68/push 0/imm32/next
2182     68/push 0/imm32/no-imm32
2183     68/push 0/imm32/no-r32
2184     68/push 3/imm32/rm32-in-first-output
2185     68/push "ff 0/subop/increment"/imm32/subx-name
2186     53/push-ebx/outputs
2187     68/push 0/imm32/inouts
2188     68/push "increment"/imm32/name
2189     89/<- %ebx 4/r32/esp
2190     # convert
2191     (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
2192     (flush _test-output-buffered-file)
2193 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2199     # check output
2200     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register")
2201     # . epilogue
2202     89/<- %esp 5/r32/ebp
2203     5d/pop-to-ebp
2204     c3/return
2205 
2206 test-emit-subx-statement-select-primitive:
2207     # Select the right primitive between overloads.
2208     #   foo <- increment
2209     # =>
2210     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
2211     #
2212     # There's a variable on the var stack as follows:
2213     #   name: 'foo'
2214     #   type: int
2215     #   register: 'eax'
2216     #
2217     # There's two primitives, as follows:
2218     #   - name: 'increment'
2219     #     out: int/reg
2220     #     value: 'ff 0/subop/increment'
2221     #   - name: 'increment'
2222     #     inout: int/mem
2223     #     value: 'ff 0/subop/increment'
2224     #
2225     # There's nothing in functions.
2226     #
2227     # . prologue
2228     55/push-ebp
2229     89/<- %ebp 4/r32/esp
2230     # setup
2231     (clear-stream _test-output-stream)
2232     (clear-stream _test-output-buffered-file->buffer)
2233     # var-foo/ecx : var in eax
2234     68/push "eax"/imm32/register
2235     68/push 0/imm32/no-stack-offset
2236     68/push 1/imm32/block-depth
2237     68/push 1/imm32/type-int
2238     68/push "foo"/imm32
2239     89/<- %ecx 4/r32/esp
2240     # vars/edx : (stack 1)
2241     51/push-ecx/var-foo
2242     68/push 1/imm32/data-length
2243     68/push 1/imm32/top
2244     89/<- %edx 4/r32/esp
2245     # real-outputs/edi : (list var)
2246     68/push 0/imm32/next
2247     51/push-ecx/var-foo
2248     89/<- %edi 4/r32/esp
2249     # stmt/esi : statement
2250     68/push 0/imm32/next
2251     57/push-edi/outputs
2252     68/push 0/imm32/inouts
2253     68/push "increment"/imm32/operation
2254     68/push 1/imm32
2255     89/<- %esi 4/r32/esp
2256     # formal-var/ebx : var in any register
2257     68/push Any-register/imm32
2258     68/push 0/imm32/no-stack-offset
2259     68/push 1/imm32/block-depth
2260     68/push 1/imm32/type-int
2261     68/push "dummy"/imm32
2262     89/<- %ebx 4/r32/esp
2263     # formal-outputs/ebx : (list var)
2264     68/push 0/imm32/next
2265     53/push-ebx/formal-var
2266     89/<- %ebx 4/r32/esp
2267     # primitive1/ebx : primitive
2268     68/push 0/imm32/next
2269     68/push 0/imm32/no-imm32
2270     68/push 0/imm32/no-r32
2271     68/push 3/imm32/rm32-in-first-output
2272     68/push "ff 0/subop/increment"/imm32/subx-name
2273     53/push-ebx/outputs/formal-outputs
2274     68/push 0/imm32/inouts
2275     68/push "increment"/imm32/name
2276     89/<- %ebx 4/r32/esp
2277     # primitives/ebx : primitive
2278     53/push-ebx/next
2279     68/push 0/imm32/no-imm32
2280     68/push 0/imm32/no-r32
2281     68/push 1/imm32/rm32-is-first-inout
2282     68/push "ff 0/subop/increment"/imm32/subx-name
2283     68/push 0/imm32/outputs
2284     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
2285     68/push "increment"/imm32/name
2286     89/<- %ebx 4/r32/esp
2287     # convert
2288     (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
2289     (flush _test-output-buffered-file)
2290 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2296     # check output
2297     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive")
2298     # . epilogue
2299     89/<- %esp 5/r32/ebp
2300     5d/pop-to-ebp
2301     c3/return
2302 
2303 test-emit-subx-statement-select-primitive-2:
2304     # Select the right primitive between overloads.
2305     #   foo <- increment
2306     # =>
2307     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
2308     #
2309     # There's a variable on the var stack as follows:
2310     #   name: 'foo'
2311     #   type: int
2312     #   register: 'eax'
2313     #
2314     # There's two primitives, as follows:
2315     #   - name: 'increment'
2316     #     out: int/reg
2317     #     value: 'ff 0/subop/increment'
2318     #   - name: 'increment'
2319     #     inout: int/mem
2320     #     value: 'ff 0/subop/increment'
2321     #
2322     # There's nothing in functions.
2323     #
2324     # . prologue
2325     55/push-ebp
2326     89/<- %ebp 4/r32/esp
2327     # setup
2328     (clear-stream _test-output-stream)
2329     (clear-stream _test-output-buffered-file->buffer)
2330     # var-foo/ecx : var in eax
2331     68/push "eax"/imm32/register
2332     68/push 0/imm32/no-stack-offset
2333     68/push 1/imm32/block-depth
2334     68/push 1/imm32/type-int
2335     68/push "foo"/imm32
2336     89/<- %ecx 4/r32/esp
2337     # vars/edx : (stack 1)
2338     51/push-ecx/var-foo
2339     68/push 1/imm32/data-length
2340     68/push 1/imm32/top
2341     89/<- %edx 4/r32/esp
2342     # inouts/edi : (list var)
2343     68/push 0/imm32/next
2344     51/push-ecx/var-foo
2345     89/<- %edi 4/r32/esp
2346     # stmt/esi : statement
2347     68/push 0/imm32/next
2348     68/push 0/imm32/outputs
2349     57/push-edi/inouts
2350     68/push "increment"/imm32/operation
2351     68/push 1/imm32
2352     89/<- %esi 4/r32/esp
2353     # formal-var/ebx : var in any register
2354     68/push Any-register/imm32
2355     68/push 0/imm32/no-stack-offset
2356     68/push 1/imm32/block-depth
2357     68/push 1/imm32/type-int
2358     68/push "dummy"/imm32
2359     89/<- %ebx 4/r32/esp
2360     # operand/ebx : (list var)
2361     68/push 0/imm32/next
2362     53/push-ebx/formal-var
2363     89/<- %ebx 4/r32/esp
2364     # primitive1/ebx : primitive
2365     68/push 0/imm32/next
2366     68/push 0/imm32/no-imm32
2367     68/push 0/imm32/no-r32
2368     68/push 3/imm32/rm32-in-first-output
2369     68/push "ff 0/subop/increment"/imm32/subx-name
2370     53/push-ebx/outputs/formal-outputs
2371     68/push 0/imm32/inouts
2372     68/push "increment"/imm32/name
2373     89/<- %ebx 4/r32/esp
2374     # primitives/ebx : primitive
2375     53/push-ebx/next
2376     68/push 0/imm32/no-imm32
2377     68/push 0/imm32/no-r32
2378     68/push 1/imm32/rm32-is-first-inout
2379     68/push "ff 0/subop/increment"/imm32/subx-name
2380     68/push 0/imm32/outputs
2381     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
2382     68/push "increment"/imm32/name
2383     89/<- %ebx 4/r32/esp
2384     # convert
2385     (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
2386     (flush _test-output-buffered-file)
2387 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2393     # check output
2394     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2")
2395     # . epilogue
2396     89/<- %esp 5/r32/ebp
2397     5d/pop-to-ebp
2398     c3/return
2399 
2400 test-increment-register:
2401     # Select the right primitive between overloads.
2402     #   foo <- increment
2403     # =>
2404     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
2405     #
2406     # There's a variable on the var stack as follows:
2407     #   name: 'foo'
2408     #   type: int
2409     #   register: 'eax'
2410     #
2411     # Primitives are the global definitions.
2412     #
2413     # There are no functions defined.
2414     #
2415     # . prologue
2416     55/push-ebp
2417     89/<- %ebp 4/r32/esp
2418     # setup
2419     (clear-stream _test-output-stream)
2420     (clear-stream _test-output-buffered-file->buffer)
2421     # var-foo/ecx : var in eax
2422     68/push "eax"/imm32/register
2423     68/push 0/imm32/no-stack-offset
2424     68/push 1/imm32/block-depth
2425     68/push 1/imm32/type-int
2426     68/push "foo"/imm32
2427     89/<- %ecx 4/r32/esp
2428     # vars/edx : (stack 1)
2429     51/push-ecx/var-foo
2430     68/push 1/imm32/data-length
2431     68/push 1/imm32/top
2432     89/<- %edx 4/r32/esp
2433     # real-outputs/edi : (list var)
2434     68/push 0/imm32/next
2435     51/push-ecx/var-foo
2436     89/<- %edi 4/r32/esp
2437     # stmt/esi : statement
2438     68/push 0/imm32/next
2439     57/push-edi/outputs
2440     68/push 0/imm32/inouts
2441     68/push "increment"/imm32/operation
2442     68/push 1/imm32
2443     89/<- %esi 4/r32/esp
2444     # convert
2445     (emit-subx-statement _test-output-buffered-file %esi %edx Primitives 0)
2446     (flush _test-output-buffered-file)
2447 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2453     # check output
2454     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-register")
2455     # . epilogue
2456     89/<- %esp 5/r32/ebp
2457     5d/pop-to-ebp
2458     c3/return
2459 
2460 test-increment-var:
2461     # Select the right primitive between overloads.
2462     #   foo <- increment
2463     # =>
2464     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
2465     #
2466     # There's a variable on the var stack as follows:
2467     #   name: 'foo'
2468     #   type: int
2469     #   register: 'eax'
2470     #
2471     # Primitives are the global definitions.
2472     #
2473     # There are no functions defined.
2474     #
2475     # . prologue
2476     55/push-ebp
2477     89/<- %ebp 4/r32/esp
2478     # setup
2479     (clear-stream _test-output-stream)
2480     (clear-stream _test-output-buffered-file->buffer)
2481     # var-foo/ecx : var in eax
2482     68/push "eax"/imm32/register
2483     68/push 0/imm32/no-stack-offset
2484     68/push 1/imm32/block-depth
2485     68/push 1/imm32/type-int
2486     68/push "foo"/imm32
2487     89/<- %ecx 4/r32/esp
2488     # vars/edx : (stack 1)
2489     51/push-ecx/var-foo
2490     68/push 1/imm32/data-length
2491     68/push 1/imm32/top
2492     89/<- %edx 4/r32/esp
2493     # inouts/edi : (list var)
2494     68/push 0/imm32/next
2495     51/push-ecx/var-foo
2496     89/<- %edi 4/r32/esp
2497     # stmt/esi : statement
2498     68/push 0/imm32/next
2499     68/push 0/imm32/outputs
2500     57/push-edi/inouts
2501     68/push "increment"/imm32/operation
2502     68/push 1/imm32
2503     89/<- %esi 4/r32/esp
2504     # convert
2505     (emit-subx-statement _test-output-buffered-file %esi %edx Primitives 0)
2506     (flush _test-output-buffered-file)
2507 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2513     # check output
2514     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-var")
2515     # . epilogue
2516     89/<- %esp 5/r32/ebp
2517     5d/pop-to-ebp
2518     c3/return
2519 
2520 test-add-reg-to-reg:
2521     #   var1/reg <- add var2/reg
2522     # =>
2523     #   01 %var1 var2
2524     #
2525     # . prologue
2526     55/push-ebp
2527     89/<- %ebp 4/r32/esp
2528     # setup
2529     (clear-stream _test-output-stream)
2530     (clear-stream _test-output-buffered-file->buffer)
2531     # var-var1/ecx : var in eax
2532     68/push "eax"/imm32/register
2533     68/push 0/imm32/no-stack-offset
2534     68/push 1/imm32/block-depth
2535     68/push 1/imm32/type-int
2536     68/push "var1"/imm32
2537     89/<- %ecx 4/r32/esp
2538     # var-var2/edx : var in ecx
2539     68/push "ecx"/imm32/register
2540     68/push 0/imm32/no-stack-offset
2541     68/push 1/imm32/block-depth
2542     68/push 1/imm32/type-int
2543     68/push "var2"/imm32
2544     89/<- %edx 4/r32/esp
2545     # inouts/esi : (list var2)
2546     68/push 0/imm32/next
2547     52/push-edx/var-var2
2548     89/<- %esi 4/r32/esp
2549     # outputs/edi : (list var1)
2550     68/push 0/imm32/next
2551     51/push-ecx/var-var1
2552     89/<- %edi 4/r32/esp
2553     # stmt/esi : statement
2554     68/push 0/imm32/next
2555     57/push-edi/outputs
2556     56/push-esi/inouts
2557     68/push "add"/imm32/operation
2558     68/push 1/imm32
2559     89/<- %esi 4/r32/esp
2560     # convert
2561     (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0)
2562     (flush _test-output-buffered-file)
2563 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2569     # check output
2570     (check-next-stream-line-equal _test-output-stream "01 %eax 0x00000001/r32" "F - test-add-reg-to-reg")
2571     # . epilogue
2572     89/<- %esp 5/r32/ebp
2573     5d/pop-to-ebp
2574     c3/return
2575 
2576 test-add-reg-to-mem:
2577     #   add-to var1 var2/reg
2578     # =>
2579     #   01 *(ebp+__) var2
2580     #
2581     # . prologue
2582     55/push-ebp
2583     89/<- %ebp 4/r32/esp
2584     # setup
2585     (clear-stream _test-output-stream)
2586     (clear-stream _test-output-buffered-file->buffer)
2587     # var-var1/ecx : var
2588     68/push 0/imm32/no-register
2589     68/push 8/imm32/stack-offset
2590     68/push 1/imm32/block-depth
2591     68/push 1/imm32/type-int
2592     68/push "var1"/imm32
2593     89/<- %ecx 4/r32/esp
2594     # var-var2/edx : var in ecx
2595     68/push "ecx"/imm32/register
2596     68/push 0/imm32/no-stack-offset
2597     68/push 1/imm32/block-depth
2598     68/push 1/imm32/type-int
2599     68/push "var2"/imm32
2600     89/<- %edx 4/r32/esp
2601     # inouts/esi : (list var2)
2602     68/push 0/imm32/next
2603     52/push-edx/var-var2
2604     89/<- %esi 4/r32/esp
2605     # inouts = (list var1 var2)
2606     56/push-esi/next
2607     51/push-ecx/var-var1
2608     89/<- %esi 4/r32/esp
2609     # stmt/esi : statement
2610     68/push 0/imm32/next
2611     68/push 0/imm32/outputs
2612     56/push-esi/inouts
2613     68/push "add-to"/imm32/operation
2614     68/push 1/imm32
2615     89/<- %esi 4/r32/esp
2616     # convert
2617     (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0)
2618     (flush _test-output-buffered-file)
2619 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2625     # check output
2626     (check-next-stream-line-equal _test-output-stream "01 *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
2627     # . epilogue
2628     89/<- %esp 5/r32/ebp
2629     5d/pop-to-ebp
2630     c3/return
2631 
2632 test-add-mem-to-reg:
2633     #   var1/reg <- add var2
2634     # =>
2635     #   03 *(ebp+__) var1
2636     #
2637     # . prologue
2638     55/push-ebp
2639     89/<- %ebp 4/r32/esp
2640     # setup
2641     (clear-stream _test-output-stream)
2642     (clear-stream _test-output-buffered-file->buffer)
2643     # var-var1/ecx : var in eax
2644     68/push "eax"/imm32/register
2645     68/push 0/imm32/no-stack-offset
2646     68/push 1/imm32/block-depth
2647     68/push 1/imm32/type-int
2648     68/push "var1"/imm32
2649     89/<- %ecx 4/r32/esp
2650     # var-var2/edx : var
2651     68/push 0/imm32/no-register
2652     68/push 8/imm32/stack-offset
2653     68/push 1/imm32/block-depth
2654     68/push 1/imm32/type-int
2655     68/push "var2"/imm32
2656     89/<- %edx 4/r32/esp
2657     # inouts/esi : (list var2)
2658     68/push 0/imm32/next
2659     52/push-edx/var-var2
2660     89/<- %esi 4/r32/esp
2661     # outputs/edi : (list var1)
2662     68/push 0/imm32/next
2663     51/push-ecx/var-var1
2664     89/<- %edi 4/r32/esp
2665     # stmt/esi : statement
2666     68/push 0/imm32/next
2667     57/push-edi/outputs
2668     56/push-esi/inouts
2669     68/push "add"/imm32/operation
2670     68/push 1/imm32
2671     89/<- %esi 4/r32/esp
2672     # convert
2673     (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0)
2674     (flush _test-output-buffered-file)
2675 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2681     # check output
2682     (check-next-stream-line-equal _test-output-stream "03 *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
2683     # . epilogue
2684     89/<- %esp 5/r32/ebp
2685     5d/pop-to-ebp
2686     c3/return
2687 
2688 test-add-literal-to-reg:
2689     #   var1/eax <- add 0x34
2690     # =>
2691     #   81 0/subop/add %eax 0x34/imm32
2692     #
2693     # . prologue
2694     55/push-ebp
2695     89/<- %ebp 4/r32/esp
2696     # setup
2697     (clear-stream _test-output-stream)
2698     (clear-stream _test-output-buffered-file->buffer)
2699     # var-var1/ecx : var in eax
2700     68/push "eax"/imm32/register
2701     68/push 0/imm32/no-stack-offset
2702     68/push 1/imm32/block-depth
2703     68/push 1/imm32/type-int
2704     68/push "var1"/imm32
2705     89/<- %ecx 4/r32/esp
2706     # var-var2/edx : var literal
2707     68/push 0/imm32/no-register
2708     68/push 0/imm32/no-stack-offset
2709     68/push 1/imm32/block-depth
2710     68/push 0/imm32/type-literal
2711     68/push "0x34"/imm32
2712     89/<- %edx 4/r32/esp
2713     # inouts/esi : (list var2)
2714     68/push 0/imm32/next
2715     52/push-edx/var-var2
2716     89/<- %esi 4/r32/esp
2717     # outputs/edi : (list var1)
2718     68/push 0/imm32/next
2719     51/push-ecx/var-var1
2720     89/<- %edi 4/r32/esp
2721     # stmt/esi : statement
2722     68/push 0/imm32/next
2723     57/push-edi/outputs
2724     56/push-esi/inouts
2725     68/push "add"/imm32/operation
2726     68/push 1/imm32
2727     89/<- %esi 4/r32/esp
2728     # convert
2729     (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0)
2730     (flush _test-output-buffered-file)
2731 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2737     # check output
2738     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %eax 0x34/imm32" "F - test-add-literal-to-reg")
2739     # . epilogue
2740     89/<- %esp 5/r32/ebp
2741     5d/pop-to-ebp
2742     c3/return
2743 
2744 test-add-literal-to-mem:
2745     #   add-to var1, 0x34
2746     # =>
2747     #   81 0/subop/add %eax 0x34/imm32
2748     #
2749     # . prologue
2750     55/push-ebp
2751     89/<- %ebp 4/r32/esp
2752     # setup
2753     (clear-stream _test-output-stream)
2754     (clear-stream _test-output-buffered-file->buffer)
2755     # var-var1/ecx : var
2756     68/push 0/imm32/no-register
2757     68/push 8/imm32/stack-offset
2758     68/push 1/imm32/block-depth
2759     68/push 1/imm32/type-int
2760     68/push "var1"/imm32
2761     89/<- %ecx 4/r32/esp
2762     # var-var2/edx : var literal
2763     68/push 0/imm32/no-register
2764     68/push 0/imm32/no-stack-offset
2765     68/push 1/imm32/block-depth
2766     68/push 0/imm32/type-literal
2767     68/push "0x34"/imm32
2768     89/<- %edx 4/r32/esp
2769     # inouts/esi : (list var2)
2770     68/push 0/imm32/next
2771     52/push-edx/var-var2
2772     89/<- %esi 4/r32/esp
2773     # inouts = (list var1 inouts)
2774     56/push-esi/next
2775     51/push-ecx/var-var1
2776     89/<- %esi 4/r32/esp
2777     # stmt/esi : statement
2778     68/push 0/imm32/next
2779     68/push 0/imm32/outputs
2780     56/push-esi/inouts
2781     68/push "add-to"/imm32/operation
2782     68/push 1/imm32
2783     89/<- %esi 4/r32/esp
2784     # convert
2785     (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0)
2786     (flush _test-output-buffered-file)
2787 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2793     # check output
2794     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
2795     # . epilogue
2796     89/<- %esp 5/r32/ebp
2797     5d/pop-to-ebp
2798     c3/return
2799 
2800 test-emit-subx-statement-function-call:
2801     # Call a function on a variable on the stack.
2802     #   f foo
2803     # =>
2804     #   (f2 *(ebp-8))
2805     # (Changing the function name supports overloading in general, but here it
2806     # just serves to help disambiguate things.)
2807     #
2808     # There's a variable on the var stack as follows:
2809     #   name: 'foo'
2810     #   type: int
2811     #   stack-offset: -8
2812     #
2813     # There's nothing in primitives.
2814     #
2815     # There's a function with this info:
2816     #   name: 'f'
2817     #   inout: int/mem
2818     #   value: 'f2'
2819     #
2820     # . prologue
2821     55/push-ebp
2822     89/<- %ebp 4/r32/esp
2823     # setup
2824     (clear-stream _test-output-stream)
2825     (clear-stream _test-output-buffered-file->buffer)
2826     # var-foo/ecx : var
2827     68/push 0/imm32/no-register
2828     68/push -8/imm32/stack-offset
2829     68/push 0/imm32/block-depth
2830     68/push 1/imm32/type-int
2831     68/push "foo"/imm32
2832     89/<- %ecx 4/r32/esp
2833     # vars/edx = (stack 1)
2834     51/push-ecx/var-foo
2835     68/push 1/imm32/data-length
2836     68/push 1/imm32/top
2837     89/<- %edx 4/r32/esp
2838     # operands/esi : (list var)
2839     68/push 0/imm32/next
2840     51/push-ecx/var-foo
2841     89/<- %esi 4/r32/esp
2842     # stmt/esi : statement
2843     68/push 0/imm32/next
2844     68/push 0/imm32/outputs
2845     56/push-esi/inouts
2846     68/push "f"/imm32/operation
2847     68/push 1/imm32
2848     89/<- %esi 4/r32/esp
2849     # functions/ebx : function
2850     68/push 0/imm32/next
2851     68/push 0/imm32/body
2852     68/push 0/imm32/outputs
2853     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
2854     68/push "f2"/imm32/subx-name
2855     68/push "f"/imm32/name
2856     89/<- %ebx 4/r32/esp
2857     # convert
2858     (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx)
2859     (flush _test-output-buffered-file)
2860 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2866     # check output
2867     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call")
2868     # . epilogue
2869     89/<- %esp 5/r32/ebp
2870     5d/pop-to-ebp
2871     c3/return
2872 
2873 test-emit-subx-statement-function-call-with-literal-arg:
2874     # Call a function on a literal.
2875     #   f 34
2876     # =>
2877     #   (f2 34)
2878     #
2879     # . prologue
2880     55/push-ebp
2881     89/<- %ebp 4/r32/esp
2882     # setup
2883     (clear-stream _test-output-stream)
2884     (clear-stream _test-output-buffered-file->buffer)
2885     # var-foo/ecx : literal
2886     68/push 0/imm32/no-register
2887     68/push 0/imm32/no-stack-offset
2888     68/push 0/imm32/block-depth
2889     68/push 0/imm32/type-literal
2890     68/push "34"/imm32
2891     89/<- %ecx 4/r32/esp
2892     # vars/edx = (stack 1)
2893     51/push-ecx/var-foo
2894     68/push 1/imm32/data-length
2895     68/push 1/imm32/top
2896     89/<- %edx 4/r32/esp
2897     # operands/esi : (list var)
2898     68/push 0/imm32/next
2899     51/push-ecx/var-foo
2900     89/<- %esi 4/r32/esp
2901     # stmt/esi : statement
2902     68/push 0/imm32/next
2903     68/push 0/imm32/outputs
2904     56/push-esi/inouts
2905     68/push "f"/imm32/operation
2906     68/push 1/imm32
2907     89/<- %esi 4/r32/esp
2908     # functions/ebx : function
2909     68/push 0/imm32/next
2910     68/push 0/imm32/body
2911     68/push 0/imm32/outputs
2912     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
2913     68/push "f2"/imm32/subx-name
2914     68/push "f"/imm32/name
2915     89/<- %ebx 4/r32/esp
2916     # convert
2917     (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx)
2918     (flush _test-output-buffered-file)
2919 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
2925     # check output
2926     (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg")
2927     # . epilogue
2928     89/<- %esp 5/r32/ebp
2929     5d/pop-to-ebp
2930     c3/return
2931 
2932 emit-subx-prologue:  # out : (address buffered-file)
2933     # . prologue
2934     55/push-ebp
2935     89/<- %ebp 4/r32/esp
2936     #
2937     (write-buffered *(ebp+8) "# . prologue\n")
2938     (write-buffered *(ebp+8) "55/push-ebp\n")
2939     (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n")
2940 $emit-subx-prologue:end:
2941     # . epilogue
2942     89/<- %esp 5/r32/ebp
2943     5d/pop-to-ebp
2944     c3/return
2945 
2946 emit-subx-epilogue:  # out : (address buffered-file)
2947     # . prologue
2948     55/push-ebp
2949     89/<- %ebp 4/r32/esp
2950     #
2951     (write-buffered *(ebp+8) "# . epilogue\n")
2952     (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n")
2953     (write-buffered *(ebp+8) "5d/pop-to-ebp\n")
2954     (write-buffered *(ebp+8) "c3/return\n")
2955 $emit-subx-epilogue:end:
2956     # . epilogue
2957     89/<- %esp 5/r32/ebp
2958     5d/pop-to-ebp
2959     c3/return