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 #   Statements are not yet fully designed.
  95 #   statement = var definition or simple statement or block
  96 #   simple statement:
  97 #     operation: string
  98 #     inouts: linked list of vars
  99 #     outputs: linked list of vars
 100 #   block = linked list of statements
 101 
 102 # == Translation: managing the stack
 103 # Now that we know what the language looks like in the large, let's think
 104 # about how translation happens from the bottom up. One crucial piece of the
 105 # puzzle is how Mu will clean up variables defined on the stack for you.
 106 #
 107 # Assume that we maintain a 'functions' list while parsing source code. And a
 108 # 'primitives' list is a global constant. Both these contain enough information
 109 # to perform type-checking on function calls or primitive statements, respectively.
 110 #
 111 # Defining variables pushes them on a stack with the current block depth and
 112 # enough information about their location (stack offset or register).
 113 # Starting a block increments the current block id.
 114 # Each statement now has enough information to emit code for it.
 115 # Ending a block is where the magic happens:
 116 #   pop all variables at the current block depth
 117 #   emit code to restore all register variables introduced at the current depth
 118 #   emit code to clean up all stack variables at the current depth (just increment esp)
 119 #   decrement the current block depth
 120 #
 121 # Formal types:
 122 #   live-vars: stack of vars
 123 #   var:
 124 #     name: string
 125 #     type: s-expression? Just a type id for now.
 126 #     block: int
 127 #     stack-offset: int  (added to ebp)
 128 #     register: string
 129 #       either usual register names
 130 #       or '*' to indicate any register
 131 #   At most one of stack-offset or register-index must be non-zero.
 132 #   A register of '*' designates a variable _template_. Only legal in formal
 133 #   parameters for primitives.
 134 
 135 # == Translating a single function call
 136 # This one's easy. Assuming we've already checked things, we just drop the
 137 # outputs (which use hard-coded registers) and emit inputs in a standard format.
 138 #
 139 # out1, out2, out3, ... <- name inout1, inout2, inout3, ...
 140 # =>
 141 # (subx-name inout1 inout2 inout3)
 142 #
 143 # Formal types:
 144 #   functions: linked list of info
 145 #     name: string
 146 #     inouts: linked list of vars
 147 #     outputs: linked list of vars
 148 #     body: block (singleton linked list)
 149 #     subx-name: string
 150 
 151 # == Translating a single primitive instruction
 152 # A second crucial piece of the puzzle is how Mu converts fairly regular
 153 # primitives with their uniform syntax to SubX instructions with their gnarly
 154 # x86 details.
 155 #
 156 # Mu instructions have inputs and outputs. Primitives can have up to 2 of
 157 # them.
 158 # SubX instructions have rm32 and r32 operands.
 159 # The translation between them covers almost all the possibilities.
 160 #   Instructions with 1 inout may turn into ones with 1 rm32
 161 #     (e.g. incrementing a var on the stack)
 162 #   Instructions with 1 output may turn into ones with 1 rm32
 163 #     (e.g. incrementing a var in a register)
 164 #   1 inout and 1 output may turn into 1 rm32 and 1 r32
 165 #     (e.g. adding a var to a reg)
 166 #   2 inouts may turn into 1 rm32 and 1 r32
 167 #     (e.g. adding a reg to a var)
 168 #   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
 169 #     (e.g. adding a constant to a var)
 170 #   1 output and 1 literal may turn into 1 rm32 and 1 imm32
 171 #     (e.g. adding a constant to a reg)
 172 #   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
 173 #     (special-case: divide edx:eax by a var or reg)
 174 # Observations:
 175 #   We always emit rm32. It may be the first inout or the first output.
 176 #   We may emit r32 or imm32 or neither.
 177 #   When we emit r32 it may come from first inout or second inout or first output.
 178 #
 179 # Accordingly, the formal data structure for a primitive looks like this:
 180 #   primitives: linked list of info
 181 #     name: string
 182 #     mu-inouts: linked list of vars to check
 183 #     mu-outputs: linked list of vars to check
 184 #     subx-name: string
 185 #     subx-rm32: enum of 2 states
 186 #     subx-r32: enum of 3 states
 187 
 188 # == Translating a block
 189 # Emit block name if necessary
 190 # Emit '{'
 191 # When you encounter a statement, emit it as above
 192 # When you encounter a variable declaration
 193 #   emit any code needed for it (bzeros)
 194 #   push it on the var stack
 195 #   update register dict if necessary
 196 # When you encounter '}'
 197 #   While popping variables off the var stack until block id changes
 198 #     Emit code needed to clean up the stack
 199 #       either increment esp
 200 #       or pop into appropriate register
 201 
 202 # The rest is straightforward.
 203 
 204 == data
 205 
 206 Program:  # (address function)
 207   0/imm32
 208 
 209 Function-name:
 210   0/imm32
 211 Function-subx-name:
 212   4/imm32
 213 Function-inouts:  # (address list var)
 214   8/imm32
 215 Function-outputs:  # (address list var)
 216   0xc/imm32
 217 Function-body:  # (address block)
 218   0x10/imm32
 219 Function-next:  # (address function)
 220   0x14/imm32
 221 Function-size:
 222   0x18/imm32/24
 223 
 224 Primitive-name:
 225   0/imm32
 226 Primitive-inouts:  # (address list var)
 227   8/imm32
 228 Primitive-outputs:  # (address list var)
 229   0xc/imm32
 230 Primitive-subx-name:
 231   4/imm32
 232 Primitive-next:  # (address function)
 233   0x14/imm32
 234 Primitive-size:
 235   0x18/imm32/24
 236 
 237 Stmt-operation:
 238   0/imm32
 239 Stmt-inouts:
 240   4/imm32
 241 Stmt-outputs:
 242   8/imm32
 243 Stmt-next:
 244   0xc/imm32
 245 Stmt-size:
 246   0x10/imm32
 247 
 248 Var-name:
 249   0/imm32
 250 Var-type:
 251   4/imm32
 252 Var-block:
 253   8/imm32
 254 Var-stack-offset:
 255   0xc/imm32
 256 Var-register-index:
 257   0x10/imm32
 258 Var-size:
 259   0x14/imm32
 260 
 261 == code
 262 
 263 Entry:
 264     # . prologue
 265     89/<- %ebp 4/r32/esp
 266     (new-segment Heap-size Heap)
 267     # if (argv[1] == "test') run-tests()
 268     {
 269       # if (argc <= 1) break
 270       81 7/subop/compare *ebp 1/imm32
 271       7e/jump-if-lesser-or-equal break/disp8
 272       # if (argv[1] != "test") break
 273       (kernel-string-equal? *(ebp+8) "test")  # => eax
 274       3d/compare-eax-and 0/imm32
 275       74/jump-if-equal break/disp8
 276       #
 277       (run-tests)
 278       # syscall(exit, *Num-test-failures)
 279       8b/-> *Num-test-failures 3/r32/ebx
 280       eb/jump $mu-main:end/disp8
 281     }
 282     # otherwise convert Stdin
 283     (convert-mu Stdin Stdout)
 284     (flush Stdout)
 285     # syscall(exit, 0)
 286     bb/copy-to-ebx 0/imm32
 287 $mu-main:end:
 288     b8/copy-to-eax 1/imm32/exit
 289     cd/syscall 0x80/imm8
 290 
 291 convert-mu:  # in : (address buffered-file), out : (address buffered-file)
 292     # . prologue
 293     55/push-ebp
 294     89/<- %ebp 4/r32/esp
 295     #
 296     (parse-mu *(ebp+8))
 297     (check-mu-types)
 298     (emit-subx *(ebp+0xc))
 299 $convert-mu:end:
 300     # . epilogue
 301     89/<- %esp 5/r32/ebp
 302     5d/pop-to-ebp
 303     c3/return
 304 
 305 test-convert-empty-input:
 306     # empty input => empty output
 307     # . prologue
 308     55/push-ebp
 309     89/<- %ebp 4/r32/esp
 310     # setup
 311     (clear-stream _test-input-stream)
 312     (clear-stream _test-input-buffered-file->buffer)
 313     (clear-stream _test-output-stream)
 314     (clear-stream _test-output-buffered-file->buffer)
 315     #
 316     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 317     (flush _test-output-buffered-file)
 318     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
 319     # . epilogue
 320     89/<- %esp 5/r32/ebp
 321     5d/pop-to-ebp
 322     c3/return
 323 
 324 test-convert-function-skeleton:
 325     # empty function decl => function prologue and epilogue
 326     #   fn foo {
 327     #   }
 328     # =>
 329     #   foo:
 330     #     # . prologue
 331     #     55/push-ebp
 332     #     89/<- %ebp 4/r32/esp
 333     #     # . epilogue
 334     #     89/<- %esp 5/r32/ebp
 335     #     5d/pop-to-ebp
 336     #     c3/return
 337     # . prologue
 338     55/push-ebp
 339     89/<- %ebp 4/r32/esp
 340     # setup
 341     (clear-stream _test-input-stream)
 342     (clear-stream _test-input-buffered-file->buffer)
 343     (clear-stream _test-output-stream)
 344     (clear-stream _test-output-buffered-file->buffer)
 345     #
 346     (write _test-input-stream "fn foo {\n")
 347     (write _test-input-stream "}\n")
 348     # convert
 349     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 350     (flush _test-output-buffered-file)
 351 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 357     # check output
 358     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-skeleton/0")
 359     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-skeleton/1")
 360     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-skeleton/2")
 361     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 362     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-skeleton/4")
 363     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 364     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 365     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-skeleton/7")
 366     # . epilogue
 367     89/<- %esp 5/r32/ebp
 368     5d/pop-to-ebp
 369     c3/return
 370 
 371 test-convert-multiple-function-skeletons:
 372     # multiple functions correctly organized into a linked list
 373     #   fn foo {
 374     #   }
 375     #   fn bar {
 376     #   }
 377     # =>
 378     #   foo:
 379     #     # . prologue
 380     #     55/push-ebp
 381     #     89/<- %ebp 4/r32/esp
 382     #     # . epilogue
 383     #     89/<- %esp 5/r32/ebp
 384     #     5d/pop-to-ebp
 385     #     c3/return
 386     #   bar:
 387     #     # . prologue
 388     #     55/push-ebp
 389     #     89/<- %ebp 4/r32/esp
 390     #     # . epilogue
 391     #     89/<- %esp 5/r32/ebp
 392     #     5d/pop-to-ebp
 393     #     c3/return
 394     # . prologue
 395     55/push-ebp
 396     89/<- %ebp 4/r32/esp
 397     # setup
 398     (clear-stream _test-input-stream)
 399     (clear-stream _test-input-buffered-file->buffer)
 400     (clear-stream _test-output-stream)
 401     (clear-stream _test-output-buffered-file->buffer)
 402     #
 403     (write _test-input-stream "fn foo {\n")
 404     (write _test-input-stream "}\n")
 405     (write _test-input-stream "fn bar {\n")
 406     (write _test-input-stream "}\n")
 407     # convert
 408     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 409     (flush _test-output-buffered-file)
 410 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 416     # check first function
 417     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-multiple-function-skeletons/0")
 418     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/1")
 419     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
 420     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
 421     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/4")
 422     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
 423     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
 424     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/7")
 425     # check second function
 426     (check-next-stream-line-equal _test-output-stream "bar:"                  "F - test-convert-multiple-function-skeletons/10")
 427     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-multiple-function-skeletons/11")
 428     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
 429     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
 430     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-multiple-function-skeletons/14")
 431     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
 432     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
 433     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-multiple-function-skeletons/17")
 434     # . epilogue
 435     89/<- %esp 5/r32/ebp
 436     5d/pop-to-ebp
 437     c3/return
 438 
 439 test-convert-function-with-arg:
 440     # function with one arg and a copy instruction
 441     #   fn foo n : int -> result/eax : int {
 442     #     result <- copy n
 443     #   }
 444     # =>
 445     #   foo:
 446     #     # . prologue
 447     #     55/push-ebp
 448     #     89/<- %ebp 4/r32/esp
 449     #     {
 450     #     # result <- copy n
 451     #     8b/-> *(ebp+8) 0/r32/eax
 452     #     }
 453     #     # . epilogue
 454     #     89/<- %esp 5/r32/ebp
 455     #     5d/pop-to-ebp
 456     #     c3/return
 457     # . prologue
 458     55/push-ebp
 459     89/<- %ebp 4/r32/esp
 460     # setup
 461     (clear-stream _test-input-stream)
 462     (clear-stream _test-input-buffered-file->buffer)
 463     (clear-stream _test-output-stream)
 464     (clear-stream _test-output-buffered-file->buffer)
 465     #
 466     (write _test-input-stream "fn foo {\n")
 467     (write _test-input-stream "}\n")
 468     # convert
 469     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 470     (flush _test-output-buffered-file)
 471 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 477     # check output
 478     (check-next-stream-line-equal _test-output-stream "foo:"                  "F - test-convert-function-skeleton/0")
 479     (check-next-stream-line-equal _test-output-stream "# . prologue"          "F - test-convert-function-skeleton/1")
 480     (check-next-stream-line-equal _test-output-stream "55/push-ebp"           "F - test-convert-function-skeleton/2")
 481     (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
 482     (check-next-stream-line-equal _test-output-stream "# . epilogue"          "F - test-convert-function-skeleton/4")
 483     (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
 484     (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
 485     (check-next-stream-line-equal _test-output-stream "c3/return"             "F - test-convert-function-skeleton/7")
 486     # . epilogue
 487     89/<- %esp 5/r32/ebp
 488     5d/pop-to-ebp
 489     c3/return
 490 
 491 parse-mu:  # in : (address buffered-file)
 492     # pseudocode
 493     #   var curr-function = Program
 494     #   var line : (stream byte 512)
 495     #   var word-slice : slice
 496     #   while true                                  # line loop
 497     #     clear-stream(line)
 498     #     read-line-buffered(in, line)
 499     #     if (line->write == 0) break               # end of file
 500     #     while true                                # word loop
 501     #       word-slice = next-word-or-string(line)
 502     #       if slice-empty?(word-slice)             # end of line
 503     #         break
 504     #       else if slice-starts-with?(word-slice, "#")  # comment
 505     #         break                                 # end of line
 506     #       else if slice-equal(word-slice, "fn")
 507     #         var new-function : (address function) = new function
 508     #         populate-mu-function(in, new-function)
 509     #         *curr-function = new-function
 510     #         curr-function = &new-function->next
 511     #       else
 512     #         abort()
 513     #
 514     # . prologue
 515     55/push-ebp
 516     89/<- %ebp 4/r32/esp
 517     # . save registers
 518     50/push-eax
 519     51/push-ecx
 520     52/push-edx
 521     57/push-edi
 522     # var line/ecx : (stream byte 512)
 523     81 5/subop/subtract %esp 0x200/imm32
 524     68/push 0x200/imm32/length
 525     68/push 0/imm32/read
 526     68/push 0/imm32/write
 527     89/<- %ecx 4/r32/esp
 528     # var word-slice/edx : slice
 529     68/push 0/imm32/end
 530     68/push 0/imm32/start
 531     89/<- %edx 4/r32/esp
 532     # var curr-function/edi : (address function) = Program
 533     bf/copy-to-edi Program/imm32
 534     {
 535 $parse-mu:line-loop:
 536       (clear-stream %ecx)
 537       (read-line-buffered *(ebp+8) %ecx)
 538       # if (line->write == 0) break
 539       81 7/subop/compare *ecx 0/imm32
 540       0f 84/jump-if-equal break/disp32
 541 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------
 547       { # word loop
 548 $parse-mu:word-loop:
 549         (next-word-or-string %ecx %edx)
 550         # if slice-empty?(word-slice) break
 551         (slice-empty? %edx)
 552         3d/compare-eax-and 0/imm32
 553         0f 85/jump-if-not-equal break/disp32
 554         # if (*word-slice->start == "#") break
 555         # . eax = *word-slice->start
 556         8b/-> *edx 0/r32/eax
 557         8a/copy-byte *eax 0/r32/AL
 558         81 4/subop/and %eax 0xff/imm32
 559         # . if (eax == '#') break
 560         3d/compare-eax-and 0x23/imm32/hash
 561         0f 84/jump-if-equal break/disp32
 562         # if (slice-equal?(word-slice, "fn")) parse a function
 563         {
 564           (slice-equal? %edx "fn")
 565           3d/compare-eax-and 0/imm32
 566           0f 84/jump-if-equal break/disp32
 567           # var new-function/eax : (address function) = populate-mu-function()
 568           (allocate Heap *Function-size)  # => eax
 569           (populate-mu-function-header %ecx %eax)
 570           (populate-mu-function-body *(ebp+8) %eax)
 571           # *curr-function = new-function
 572           89/<- *edi 0/r32/eax
 573           # curr-function = &new-function->next
 574           8d/address-> *(eax+0x10) 7/r32/edi
 575           e9/jump $parse-mu:word-loop/disp32
 576         }
 577         # otherwise abort
 578         e9/jump $parse-mu:abort/disp32
 579       } # end word loop
 580       e9/jump loop/disp32
 581     } # end line loop
 582 $parse-mu:end:
 583     # . reclaim locals
 584     81 0/subop/add %esp 0x214/imm32
 585     # . restore registers
 586     5f/pop-to-edi
 587     5a/pop-to-edx
 588     59/pop-to-ecx
 589     58/pop-to-eax
 590     # . epilogue
 591     89/<- %esp 5/r32/ebp
 592     5d/pop-to-ebp
 593     c3/return
 594 
 595 $parse-mu:abort:
 596     # error("unexpected top-level command: " word-slice "\n")
 597     (write-buffered Stderr "unexpected top-level command: ")
 598     (write-buffered Stderr %edx)
 599     (write-buffered Stderr "\n")
 600     (flush Stderr)
 601     # . syscall(exit, 1)
 602     bb/copy-to-ebx  1/imm32
 603     b8/copy-to-eax  1/imm32/exit
 604     cd/syscall  0x80/imm8
 605     # never gets here
 606 
 607 # errors considered:
 608 #   fn foo { {
 609 #   fn foo { }
 610 #   fn foo { } {
 611 #   fn foo  # no block
 612 populate-mu-function-header:  # first-line : (address stream byte), out : (address function)
 613     # . prologue
 614     55/push-ebp
 615     89/<- %ebp 4/r32/esp
 616     # . save registers
 617     50/push-eax
 618     51/push-ecx
 619     57/push-edi
 620     # edi = out
 621     8b/-> *(ebp+0xc) 7/r32/edi
 622     # var word-slice/ecx : slice
 623     68/push 0/imm32/end
 624     68/push 0/imm32/start
 625     89/<- %ecx 4/r32/esp
 626     # save function name
 627     (next-word *(ebp+8) %ecx)
 628     (slice-to-string Heap %ecx)  # => eax
 629     89/<- *edi 0/r32/eax
 630     # assert that next token is '{'
 631     (next-word *(ebp+8) %ecx)
 632     (slice-equal? %ecx "{")
 633     3d/compare-eax-and 0/imm32
 634     74/jump-if-equal $populate-mu-function-header:abort/disp8
 635     # assert that there's no further token
 636     {
 637       # word-slice = next-word(line)
 638       (next-word *(ebp+8) %ecx)
 639       # if (word-slice == '') break
 640       (slice-empty? %ecx)
 641       3d/compare-eax-and 0/imm32
 642       75/jump-if-not-equal break/disp8
 643       # if (slice-starts-with?(word-slice, "#")) break
 644       # . eax = *word-slice->start
 645       8b/-> *edx 0/r32/eax
 646       8a/copy-byte *eax 0/r32/AL
 647       81 4/subop/and %eax 0xff/imm32
 648       # . if (eax == '#') break
 649       3d/compare-eax-and 0x23/imm32/hash
 650       74/jump-if-equal break/disp8
 651       # otherwise abort
 652       eb/jump $populate-mu-function-header:abort/disp8
 653     }
 654 $populate-mu-function-header:end:
 655     # . reclaim locals
 656     81 0/subop/add %esp 8/imm32
 657     # . restore registers
 658     5f/pop-to-edi
 659     59/pop-to-ecx
 660     58/pop-to-eax
 661     # . epilogue
 662     89/<- %esp 5/r32/ebp
 663     5d/pop-to-ebp
 664     c3/return
 665 
 666 $populate-mu-function-header:abort:
 667     # error("function header not in form 'fn <name> {'")
 668     (write-buffered Stderr "function header not in form 'fn <name> {' -- '")
 669     (rewind-stream *(ebp+8))
 670     (write-stream 2 *(ebp+8))
 671     (write-buffered Stderr "'\n")
 672     (flush Stderr)
 673     # . syscall(exit, 1)
 674     bb/copy-to-ebx  1/imm32
 675     b8/copy-to-eax  1/imm32/exit
 676     cd/syscall  0x80/imm8
 677     # never gets here
 678 
 679 # errors considered:
 680 #   { abc
 681 populate-mu-function-body:  # in : (address buffered-file), out : (address function)
 682     # . prologue
 683     55/push-ebp
 684     89/<- %ebp 4/r32/esp
 685     # . save registers
 686     50/push-eax
 687     51/push-ecx
 688     52/push-edx
 689     53/push-ebx
 690     # var line/ecx : (stream byte 512)
 691     81 5/subop/subtract %esp 0x200/imm32
 692     68/push 0x200/imm32/length
 693     68/push 0/imm32/read
 694     68/push 0/imm32/write
 695     89/<- %ecx 4/r32/esp
 696     # var word-slice/edx : slice
 697     68/push 0/imm32/end
 698     68/push 0/imm32/start
 699     89/<- %edx 4/r32/esp
 700     # var open-curly-count/ebx : int = 1
 701     bb/copy-to-ebx 1/imm32
 702     { # line loop
 703 $populate-mu-function-body:line-loop:
 704       # if (open-curly-count == 0) break
 705       81 7/subop/compare %ebx 0/imm32
 706       0f 84/jump-if-equal break/disp32
 707       # line = read-line-buffered(in)
 708       (clear-stream %ecx)
 709       (read-line-buffered *(ebp+8) %ecx)
 710       # if (line->write == 0) break
 711       81 7/subop/compare *ecx 0/imm32
 712       0f 84/jump-if-equal break/disp32
 713       # word-slice = next-word(line)
 714       (next-word %ecx %edx)
 715       # if slice-empty?(word-slice) continue
 716       (slice-empty? %ecx)
 717       3d/compare-eax-and 0/imm32
 718       75/jump-if-not-equal loop/disp8
 719       # if (slice-starts-with?(word-slice, '#') continue
 720       # . eax = *word-slice->start
 721       8b/-> *edx 0/r32/eax
 722       8a/copy-byte *eax 0/r32/AL
 723       81 4/subop/and %eax 0xff/imm32
 724       # . if (eax == '#') continue
 725       3d/compare-eax-and 0x23/imm32/hash
 726       74/jump-if-equal loop/disp8
 727       {
 728         # if slice-equal?(word-slice, "{") ++open-curly-count
 729         {
 730           (slice-equal? %ecx "{")
 731           3d/compare-eax-and 0/imm32
 732           74/jump-if-equal break/disp8
 733           43/increment-ebx
 734           eb/jump $curly-found:end/disp8
 735         }
 736         # else if slice-equal?(word-slice, "}") --open-curly-count
 737         {
 738           (slice-equal? %ecx "}")
 739           3d/compare-eax-and 0/imm32
 740           74/jump-if-equal break/disp8
 741           4b/decrement-ebx
 742           eb/jump $curly-found:end/disp8
 743         }
 744         # else break
 745         eb/jump $populate-mu-function-body:end/disp8
 746       }
 747       # - check for invalid tokens after curly
 748 $curly-found:end:
 749       # second-word-slice = next-word(line)
 750       (next-word %ecx %edx)
 751       # if slice-empty?(second-word-slice) continue
 752       (slice-empty? %ecx)
 753       3d/compare-eax-and 0/imm32
 754       0f 85/jump-if-not-equal loop/disp32
 755       # if (slice-starts-with?(second-word-slice, '#') continue
 756       # . eax = *second-word-slice->start
 757       8b/-> *edx 0/r32/eax
 758       8a/copy-byte *eax 0/r32/AL
 759       81 4/subop/and %eax 0xff/imm32
 760       # . if (eax == '#') continue
 761       3d/compare-eax-and 0x23/imm32/hash
 762       0f 84/jump-if-equal loop/disp32
 763       # abort
 764       eb/jump $populate-mu-function-body:abort/disp8
 765     } # end line loop
 766 $populate-mu-function-body:end:
 767     # . reclaim locals
 768     81 0/subop/add %esp 0x214/imm32
 769     # . restore registers
 770     5b/pop-to-ebx
 771     5a/pop-to-edx
 772     59/pop-to-ecx
 773     58/pop-to-eax
 774     # . epilogue
 775     89/<- %esp 5/r32/ebp
 776     5d/pop-to-ebp
 777     c3/return
 778 
 779 $populate-mu-function-body:abort:
 780     # error("'{' or '}' should be on its own line, but got '")
 781     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 782     (rewind-stream %ecx)
 783     (write-stream 2 %ecx)
 784     (write-buffered Stderr "'\n")
 785     (flush Stderr)
 786     # . syscall(exit, 1)
 787     bb/copy-to-ebx  1/imm32
 788     b8/copy-to-eax  1/imm32/exit
 789     cd/syscall  0x80/imm8
 790     # never gets here
 791 
 792 check-mu-types:
 793     # . prologue
 794     55/push-ebp
 795     89/<- %ebp 4/r32/esp
 796     #
 797 $check-types:end:
 798     # . epilogue
 799     89/<- %esp 5/r32/ebp
 800     5d/pop-to-ebp
 801     c3/return
 802 
 803 emit-subx:  # out : (address buffered-file)
 804     # . prologue
 805     55/push-ebp
 806     89/<- %ebp 4/r32/esp
 807     # . save registers
 808     50/push-eax
 809     51/push-ecx
 810     57/push-edi
 811     # edi = out
 812     8b/-> *(ebp+8) 7/r32/edi
 813     # var curr/ecx : (address function) = Program
 814     8b/-> *Program 1/r32/ecx
 815     {
 816       # if (curr == NULL) break
 817       81 7/subop/compare %ecx 0/imm32
 818       0f 84/jump-if-equal break/disp32
 819       (emit-subx-function %edi %ecx)
 820       # curr = curr->next
 821       8b/-> *(ecx+0x10) 1/r32/ecx
 822       e9/jump loop/disp32
 823     }
 824 $emit-subx:end:
 825     # . restore registers
 826     5f/pop-to-edi
 827     59/pop-to-ecx
 828     58/pop-to-eax
 829     # . epilogue
 830     89/<- %esp 5/r32/ebp
 831     5d/pop-to-ebp
 832     c3/return
 833 
 834 # == Emitting a function
 835 # Emit function header
 836 # Emit function prologue
 837 # Translate function body
 838 # Emit function epilogue
 839 
 840 emit-subx-function:  # out : (address buffered-file), f : (address function)
 841     # . prologue
 842     55/push-ebp
 843     89/<- %ebp 4/r32/esp
 844     # . save registers
 845     50/push-eax
 846     51/push-ecx
 847     57/push-edi
 848     # edi = out
 849     8b/-> *(ebp+8) 7/r32/edi
 850     # ecx = f
 851     8b/-> *(ebp+0xc) 1/r32/ecx
 852     #
 853     (write-buffered %edi *ecx)
 854     (write-buffered %edi ":\n")
 855     (emit-subx-prologue %edi)
 856     (emit-subx-block %edi *(ecx+0x10))  # Function-body
 857     (emit-subx-epilogue %edi)
 858 $emit-subx-function:end:
 859     # . restore registers
 860     5f/pop-to-edi
 861     59/pop-to-ecx
 862     58/pop-to-eax
 863     # . epilogue
 864     89/<- %esp 5/r32/ebp
 865     5d/pop-to-ebp
 866     c3/return
 867 
 868 emit-subx-block:  # out : (address buffered-file), block : (address block)
 869     # . prologue
 870     55/push-ebp
 871     89/<- %ebp 4/r32/esp
 872     #
 873 $emit-subx-block:end:
 874     # . epilogue
 875     89/<- %esp 5/r32/ebp
 876     5d/pop-to-ebp
 877     c3/return
 878 
 879 emit-subx-statement:  # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address opcode-info), functions : (address function)
 880     # . prologue
 881     55/push-ebp
 882     89/<- %ebp 4/r32/esp
 883     # . save registers
 884     50/push-eax
 885     51/push-ecx
 886     # if stmt matches a primitive, emit it
 887     {
 888       (find-matching-function *(ebp+0x14) *(ebp+0xc))
 889       3d/compare-eax-and 0/imm32
 890       74/jump-if-equal break/disp8
 891       (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax)
 892       e9/jump $emit-subx-statement:end/disp32
 893     }
 894     # else if stmt matches a function, emit a call to it
 895     {
 896       (find-matching-function *(ebp+0x18) *(ebp+0xc))
 897       3d/compare-eax-and 0/imm32
 898       74/jump-if-equal break/disp8
 899       (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax)
 900       e9/jump $emit-subx-statement:end/disp32
 901     }
 902     # else abort
 903     e9/jump $emit-subx-statement:abort/disp32
 904 $emit-subx-statement:end:
 905     # . restore registers
 906     59/pop-to-ecx
 907     58/pop-to-eax
 908     # . epilogue
 909     89/<- %esp 5/r32/ebp
 910     5d/pop-to-ebp
 911     c3/return
 912 
 913 $emit-subx-statement:abort:
 914     # error("couldn't translate '" stmt "'\n")
 915     (write-buffered Stderr "couldn't translate '")
 916 #?     (emit-string Stderr *(ebp+0xc))  # TODO
 917     (write-buffered Stderr "'\n")
 918     (flush Stderr)
 919     # . syscall(exit, 1)
 920     bb/copy-to-ebx  1/imm32
 921     b8/copy-to-eax  1/imm32/exit
 922     cd/syscall  0x80/imm8
 923     # never gets here
 924 
 925 emit-subx-primitive:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), primitive : (address function)
 926     # . prologue
 927     55/push-ebp
 928     89/<- %ebp 4/r32/esp
 929     # . save registers
 930     50/push-eax
 931     51/push-ecx
 932     # - emit primitive name
 933     8b/-> *(ebp+0x14) 1/r32/ecx
 934     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
 935     # - emit arguments
 936     # var args/ecx : (list var) = stmt->inouts
 937     8b/-> *(ebp+0xc) 1/r32/ecx
 938     8b/-> *(ecx+4) 1/r32/ecx  # Stmt-inouts
 939     {
 940       # if (curr == null) break
 941       81 7/subop/compare %ecx 0/imm32
 942       74/jump-if-equal break/disp8
 943       #
 944       (emit-subx-call-operand *(ebp+8) *ecx)
 945       # args = args->next
 946       8b/-> *(ecx+4) 1/r32/ecx
 947     }
 948 $emit-subx-primitive:end:
 949     # . restore registers
 950     59/pop-to-ecx
 951     58/pop-to-eax
 952     # . epilogue
 953     89/<- %esp 5/r32/ebp
 954     5d/pop-to-ebp
 955     c3/return
 956 
 957 emit-subx-call:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function)
 958     # . prologue
 959     55/push-ebp
 960     89/<- %ebp 4/r32/esp
 961     # . save registers
 962     50/push-eax
 963     51/push-ecx
 964     #
 965     (write-buffered *(ebp+8) "(")
 966     # - emit function name
 967     8b/-> *(ebp+0x14) 1/r32/ecx
 968     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
 969     # - emit arguments
 970     # var curr/ecx : (list var) = stmt->inouts
 971     8b/-> *(ebp+0xc) 1/r32/ecx
 972     8b/-> *(ecx+4) 1/r32/ecx  # Stmt-inouts
 973     {
 974       # if (curr == null) break
 975       81 7/subop/compare %ecx 0/imm32
 976       74/jump-if-equal break/disp8
 977       #
 978       (emit-subx-call-operand *(ebp+8) *ecx)
 979       # curr = curr->next
 980       8b/-> *(ecx+4) 1/r32/ecx
 981     }
 982     #
 983     (write-buffered *(ebp+8) ")")
 984 $emit-subx-call:end:
 985     # . restore registers
 986     59/pop-to-ecx
 987     58/pop-to-eax
 988     # . epilogue
 989     89/<- %esp 5/r32/ebp
 990     5d/pop-to-ebp
 991     c3/return
 992 
 993 emit-subx-call-operand:  # out : (address buffered-file), operand : (address variable)
 994     # . prologue
 995     55/push-ebp
 996     89/<- %ebp 4/r32/esp
 997     # . save registers
 998     50/push-eax
 999     #
1000     (write-buffered *(ebp+8) Space)
1001     (write-buffered *(ebp+8) "*(ebp+")
1002     8b/-> *(ebp+0xc) 0/r32/eax
1003     (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-stack-offset
1004     (write-buffered *(ebp+8) ")")
1005 $emit-subx-call-operand:end:
1006     # . restore registers
1007     58/pop-to-eax
1008     # . epilogue
1009     89/<- %esp 5/r32/ebp
1010     5d/pop-to-ebp
1011     c3/return
1012 
1013 find-matching-function:  # functions : (address function), stmt : (address statement) -> result/eax : (address function)
1014     # . prologue
1015     55/push-ebp
1016     89/<- %ebp 4/r32/esp
1017     # . save registers
1018     51/push-ecx
1019     # var curr/ecx : (address function) = functions
1020     8b/-> *(ebp+8) 1/r32/ecx
1021     {
1022       # if (curr == null) break
1023       81 7/subop/compare %ecx 0/imm32
1024       74/jump-if-equal break/disp8
1025       # if match(curr, stmt) return curr
1026       {
1027         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
1028         3d/compare-eax-and 0/imm32
1029         74/jump-if-equal break/disp8
1030         89/<- %eax 1/r32/ecx
1031         eb/jump $find-matching-function:end/disp8
1032       }
1033       # curr = curr->next
1034       8b/-> *(ecx+0x10) 1/r32/ecx
1035       eb/jump loop/disp8
1036     }
1037     # return null
1038     b8/copy-to-eax 0/imm32
1039 $find-matching-function:end:
1040     # . restore registers
1041     59/pop-to-ecx
1042     # . epilogue
1043     89/<- %esp 5/r32/ebp
1044     5d/pop-to-ebp
1045     c3/return
1046 
1047 mu-stmt-matches-function?:  # stmt : (address statement), primitive : (address opcode-info) => result/eax : boolean
1048     # . prologue
1049     55/push-ebp
1050     89/<- %ebp 4/r32/esp
1051     # . save registers
1052     51/push-ecx
1053     # return primitive->name == stmt->operation
1054     8b/-> *(ebp+8) 1/r32/ecx
1055     8b/-> *(ebp+0xc) 0/r32/eax
1056     (string-equal? *ecx *eax)  # => eax
1057 $mu-stmt-matches-function?:end:
1058     # . restore registers
1059     59/pop-to-ecx
1060     # . epilogue
1061     89/<- %esp 5/r32/ebp
1062     5d/pop-to-ebp
1063     c3/return
1064 
1065 test-emit-subx-statement-primitive:
1066     # Primitive operation on a variable on the stack.
1067     #   increment foo
1068     # =>
1069     #   ff 0/subop/increment *(ebp-8)
1070     #
1071     # There's a variable on the var stack as follows:
1072     #   name: 'foo'
1073     #   type: int
1074     #   stack-offset: -8
1075     #
1076     # There's a primitive with this info:
1077     #   name: 'increment'
1078     #   inout: int/mem
1079     #   value: 'ff 0/subop/increment'
1080     #
1081     # There's nothing in functions.
1082     #
1083     # . prologue
1084     55/push-ebp
1085     89/<- %ebp 4/r32/esp
1086     # setup
1087     (clear-stream _test-output-stream)
1088     (clear-stream _test-output-buffered-file->buffer)
1089     # var-foo/ecx : var
1090     68/push 0/imm32/no-register
1091     68/push -8/imm32/stack-offset
1092     68/push 1/imm32/block-depth
1093     68/push 1/imm32/type-int
1094     68/push "foo"/imm32
1095     89/<- %ecx 4/r32/esp
1096     # vars/edx : (stack 1)
1097     51/push-ecx/var-foo
1098     68/push 1/imm32/data-length
1099     68/push 1/imm32/top
1100     89/<- %edx 4/r32/esp
1101     # operand/esi : (list var)
1102     68/push 0/imm32/next
1103     51/push-ecx/var-foo
1104     89/<- %esi 4/r32/esp
1105     # stmt/esi : statement
1106     68/push 0/imm32/next
1107     68/push 0/imm32/outputs
1108     56/push-esi/operands
1109     68/push "increment"/imm32/operation
1110     89/<- %esi 4/r32/esp
1111     # primitives/ebx : primitive
1112     68/push 0/imm32/next
1113     68/push 0/imm32/body
1114     68/push 0/imm32/outputs
1115     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
1116     68/push "ff 0/subop/increment"/imm32/subx-name
1117     68/push "increment"/imm32/name
1118     89/<- %ebx 4/r32/esp
1119     # convert
1120     (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
1121     (flush _test-output-buffered-file)
1122 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1128     # check output
1129     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive/0")
1130     # . reclaim locals
1131     81 0/subop/add %esp 0x48/imm32
1132     # . epilogue
1133     89/<- %esp 5/r32/ebp
1134     5d/pop-to-ebp
1135     c3/return
1136 
1137 test-emit-subx-statement-function-call:
1138     # Call a function on a variable on the stack.
1139     #   f foo
1140     # =>
1141     #   (f2 *(ebp-8))
1142     # (Changing the function name supports overloading in general, but here it
1143     # just serves to help disambiguate things.)
1144     #
1145     # There's a variable on the var stack as follows:
1146     #   name: 'foo'
1147     #   type: int
1148     #   stack-offset: -8
1149     #
1150     # There's nothing in primitives.
1151     #
1152     # There's a function with this info:
1153     #   name: 'f'
1154     #   inout: int/mem
1155     #   value: 'f2'
1156     #
1157     # . prologue
1158     55/push-ebp
1159     89/<- %ebp 4/r32/esp
1160     # setup
1161     (clear-stream _test-output-stream)
1162     (clear-stream _test-output-buffered-file->buffer)
1163     # var-foo/ecx : var
1164     68/push 0/imm32/no-register
1165     68/push -8/imm32/stack-offset
1166     68/push 0/imm32/block-depth
1167     68/push 1/imm32/type-int
1168     68/push "foo"/imm32
1169     89/<- %ecx 4/r32/esp
1170     # vars/edx = (stack 1)
1171     51/push-ecx/var-foo
1172     68/push 1/imm32/data-length
1173     68/push 1/imm32/top
1174     89/<- %edx 4/r32/esp
1175     # operands/esi : (list var)
1176     68/push 0/imm32/next
1177     51/push-ecx/var-foo
1178     89/<- %esi 4/r32/esp
1179     # stmt/esi : statement
1180     68/push 0/imm32/next
1181     68/push 0/imm32/outputs
1182     56/push-esi/inouts
1183     68/push "f"/imm32/operation
1184     89/<- %esi 4/r32/esp
1185     # functions/ebx : function
1186     68/push 0/imm32/next
1187     68/push 0/imm32/body
1188     68/push 0/imm32/outputs
1189     51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
1190     68/push "f2"/imm32/subx-name
1191     68/push "f"/imm32/name
1192     89/<- %ebx 4/r32/esp
1193     # convert
1194     (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx)
1195     (flush _test-output-buffered-file)
1196 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1202     # check output
1203     (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call/0")
1204     # . reclaim locals
1205     81 0/subop/add %esp 0x3c/imm32
1206     # . epilogue
1207     89/<- %esp 5/r32/ebp
1208     5d/pop-to-ebp
1209     c3/return
1210 
1211 emit-subx-prologue:  # out : (address buffered-file)
1212     # . prologue
1213     55/push-ebp
1214     89/<- %ebp 4/r32/esp
1215     #
1216     (write-buffered *(ebp+8) "# . prologue\n")
1217     (write-buffered *(ebp+8) "55/push-ebp\n")
1218     (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n")
1219 $emit-subx-prologue:end:
1220     # . epilogue
1221     89/<- %esp 5/r32/ebp
1222     5d/pop-to-ebp
1223     c3/return
1224 
1225 emit-subx-epilogue:  # out : (address buffered-file)
1226     # . prologue
1227     55/push-ebp
1228     89/<- %ebp 4/r32/esp
1229     #
1230     (write-buffered *(ebp+8) "# . epilogue\n")
1231     (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n")
1232     (write-buffered *(ebp+8) "5d/pop-to-ebp\n")
1233     (write-buffered *(ebp+8) "c3/return\n")
1234 $emit-subx-epilogue:end:
1235     # . epilogue
1236     89/<- %esp 5/r32/ebp
1237     5d/pop-to-ebp
1238     c3/return