# The Mu computer's level-2 language, also called Mu. # http://akkartik.name/post/mu-2019-2 # # To run: # $ ./translate_subx init.linux [0-9]*.subx apps/mu.subx # == Goals # 1. Be memory safe. It should be impossible to corrupt the heap, or to create # a bad pointer. (Requires strong type safety.) # 2. Do as little as possible to achieve goal 1. The translator should be # implementable in machine code. # - minimize impedance mismatch between source language and SubX target # (e.g. programmer manages registers manually) # - checks over syntax # (e.g. programmer's register allocation is checked) # - runtime checks to avoid complex static analysis # (e.g. array indexing always checks bounds) # == Language description # A program is a sequence of function definitions. # # Function example: # fn foo n: int -> result/eax: int { # ... # } # # Functions consist of a name, optional inputs, optional outputs and a block. # # Function inputs and outputs are variables. All variables have a type and # storage specifier. They can be placed either in memory (on the stack) or in # one of 6 named registers. # eax ecx edx ebx esi edi # Variables in registers must be primitive 32-bit types. # Variables not explicitly placed in a register are on the stack. # Variables in registers need not have a name; in that case you refer to them # directly by the register name. # # Function inputs are always passed in memory (on the stack), while outputs # are always returned in registers. # # Blocks mostly consist of statements. # # Statements mostly consist of a name, optional inputs and optional outputs. # # Statement inputs are variables or literals. Variables need to specify type # (and storage) the first time they're mentioned but not later. # # Statement outputs, like function outputs, must be variables in registers. # # Statement names must be either primitives or user-defined functions. # # Primitives can write to any register. # User-defined functions only write to hard-coded registers. Outputs of each # call must have the same registers as in the function definition. # # There are some other statement types: # - blocks. Multiple statements surrounded by '{...}' and optionally # prefixed with a label name and ':' # - { # ... # } # - foo: { # ... # } # # - variable definitions on the stack. E.g.: # - var foo: int # - var bar: (array int 3) # There's no initializer; variables are automatically initialized. # The type of a local variable is either word-length (4 bytes) or starts with 'ref'. # # - variables definitions in a register. E.g.: # - var foo/eax: int <- add bar 1 # The initializer is mandatory and must be a valid instruction that writes # a single output to the right register. In practice registers will # usually be either initialized by primitives or copied from eax. # - var eax: int <- foo bar quux # var floo/ecx: int <- copy eax # # Still todo: # global variables # heap allocations (planned name: 'handle') # user-defined types: 'type' for structs, 'choice' for unions # short-lived 'address' type for efficiently writing inside nested structs # # We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle' # and 'address' in comments. Their definitions are in layer 50, but really you # can ignore the distinctions on a first reading of this program. # # Formal types: # A program is a linked list of functions # A function contains: # name: (handle array byte) # inouts: linked list of vars <-- 'inouts' is more precise than 'inputs' # data: (handle var) # next: (handle list) # outputs: linked list of vars # data: (handle var) # next: (handle list) # body: (handle block) # A var-type contains: # name: (handle array byte) # type: (handle tree type-id) # # A statement can be: # tag 0: a block # tag 1: a simple statement (stmt1) # tag 2: a variable defined on the stack # tag 3: a variable defined in a register # # A block contains: # tag: 0 # statements: (handle list statement) # name: (handle array byte) -- starting with '$' # # A regular statement contains: # tag: 1 # operation: (handle array byte) # inouts: (handle list operand) # outputs: (handle list var) # # A variable defined on the stack contains: # tag: 2 # name: (handle array byte) # type: (handle tree type-id) # # A variable defined in a register contains: # tag: 3 # name: (handle array byte) # type: (handle tree type-id) # reg: (handle array byte) # == Translation: managing the stack # Now that we know what the language looks like in the large, let's think # about how translation happens from the bottom up. One crucial piece of the # puzzle is how Mu will clean up variables defined on the stack for you. # # Assume that we maintain a 'functions' list while parsing source code. And a # 'primitives' list is a global constant. Both these contain enough information # to perform type-checking on function calls or primitive statements, respectively. # # Defining variables pushes them on a stack with the current block depth and # enough information about their location (stack offset or register). # Starting a block increments the current block id. # Each statement now has enough information to emit code for it. # Ending a block is where the magic happens: # pop all variables at the current block depth # emit code to restore all register variables introduced at the current depth # emit code to clean up all stack variables at the current depth (just increment esp) # decrement the current block depth # # Formal types: # live-vars: stack of vars # var: # name: (handle array byte) # type: (handle tree type-id) # block: int # stack-offset: int (added to ebp) # register: (handle array byte) # either usual register names # or '*' to indicate any register # At most one of stack-offset or register-index must be non-zero. # A register of '*' designates a variable _template_. Only legal in formal # parameters for primitives. # == Translating a single function call # This one's easy. Assuming we've already checked things, we just drop the # outputs (which use hard-coded registers) and emit inputs in a standard format. # # out1, out2, out3, ... <- name inout1, inout2, inout3, ... # => # (subx-name inout1 inout2 inout3) # # Formal types: # functions: linked list of info # name: (handle array byte) # inouts: linked list of vars # outputs: linked list of vars # body: block (singleton linked list) # subx-name: (handle array byte) # == Translating a single primitive instruction # A second crucial piece of the puzzle is how Mu converts fairly regular # primitives with their uniform syntax to SubX instructions with their gnarly # x86 details. # # Mu instructions have inputs and outputs. Primitives can have up to 2 of # them. # SubX instructions have rm32 and r32 operands. # The translation between them covers almost all the possibilities. # Instructions with 1 inout may turn into ones with 1 rm32 # (e.g. incrementing a var on the stack) # Instructions with 1 output may turn into ones with 1 rm32 # (e.g. incrementing a var in a register) # 1 inout and 1 output may turn into 1 rm32 and 1 r32 # (e.g. adding a var to a reg) # 2 inouts may turn into 1 rm32 and 1 r32 # (e.g. adding a reg to a var) # 1 inout and 1 literal may turn into 1 rm32 and 1 imm32 # (e.g. adding a constant to a var) # 1 output and 1 literal may turn into 1 rm32 and 1 imm32 # (e.g. adding a constant to a reg) # 2 outputs to hardcoded registers and 1 inout may turn into 1 rm32 # (special-case: divide edx:eax by a var or reg) # Observations: # We always emit rm32. It may be the first inout or the first output. # We may emit r32 or imm32 or neither. # When we emit r32 it may come from first inout or second inout or first output. # # Accordingly, the formal data structure for a primitive looks like this: # primitives: linked list of info # name: (handle array byte) # mu-inouts: linked list of vars to check # mu-outputs: linked list of vars to check; at most a singleton # subx-name: (handle array byte) # subx-rm32: enum arg-location # subx-r32: enum arg-location # subx-imm32: enum arg-location # subx-disp32: enum arg-location # output-is-write-only: boolean # arg-location: enum # 0 means none # 1 means first inout # 2 means second inout # 3 means first output # == Translating a block # Emit block name if necessary # Emit '{' # When you encounter a statement, emit it as above # When you encounter a variable declaration # emit any code needed for it (bzeros) # push it on the var stack # update register dict if necessary # When you encounter '}' # While popping variables off the var stack until block id changes # Emit code needed to clean up the stack # either increment esp # or pop into appropriate register # The rest is straightforward. == data Program: # (handle function) 0/imm32 Function-name: 0/imm32 Function-subx-name: 4/imm32 Function-inouts: # (handle list var) 8/imm32 Function-outputs: # (handle list var) 0xc/imm32 Function-body: # (handle block) 0x10/imm32 Function-next: # (handle function) 0x14/imm32 Function-size: # (addr int) 0x18/imm32/24 Primitive-name: 0/imm32 Primitive-inouts: # (handle list var) 4/imm32 Primitive-outputs: # (handle list var) 8/imm32 Primitive-subx-name: # (handle array byte) 0xc/imm32 Primitive-subx-rm32: # enum arg-location 0x10/imm32 Primitive-subx-r32: # enum arg-location 0x14/imm32 Primitive-subx-imm32: # enum arg-location 0x18/imm32 Primitive-subx-disp32: # enum arg-location -- only for branches 0x1c/imm32 Primitive-output-is-write-only: # boolean 0x20/imm32 Primitive-next: # (handle function) 0x24/imm32 Primitive-size: # (addr int) 0x28/imm32/36 Stmt-tag: 0/imm32 Block-statements: # (handle list statement) 4/imm32 Block-var: # (handle var) 8/imm32 Stmt1-operation: # (handle array byte) 4/imm32 Stmt1-inouts: # (handle stmt-var) 8/imm32 Stmt1-outputs: # (handle stmt-var) 0xc/imm32 Vardef-var: # (handle var) 4/imm32 Regvardef-operation: # (handle array byte) 4/imm32 Regvardef-inouts: # (handle stmt-var) 8/imm32 Regvardef-outputs: # (handle stmt-var) # will have exactly one element 0xc/imm32 Stmt-size: # (addr int) 0x10/imm32 Var-name: 0/imm32 Var-type: 4/imm32 Var-block-depth: 8/imm32 Var-stack-offset: 0xc/imm32 Var-register: 0x10/imm32 Var-size: # (addr int) 0x14/imm32 Any-register: # wildcard # size 1/imm32 # data 2a/asterisk List-value: 0/imm32 List-next: 4/imm32 List-size: # (addr int) 8/imm32 # A stmt-var is like a list of vars with call-site specific metadata Stmt-var-value: # (handle var) 0/imm32 Stmt-var-next: # (handle stmt-var) 4/imm32 Stmt-var-is-deref: # boolean 8/imm32 Stmt-var-size: # (addr int) 0xc/imm32 # Types are expressed as trees (s-expressions) of type-ids (ints). # However, there's no need for singletons, so we can assume (int) == int # - if x->right == nil, x is an atom # - x->left contains either a pointer to a pair, or an atomic type-id directly. # type ids will be less than 0x10000 (MAX_TYPE_ID). Tree-left: # either type-id or (addr tree type-id) 0/imm32 Tree-right: # (addr tree type-id) 4/imm32 Tree-size: # (addr int) 8/imm32 Max-type-id: 0x10000/imm32 == code Entry: # . prologue 89/<- %ebp 4/r32/esp (new-segment *Heap-size Heap) # if (argv[1] == "test') run-tests() { # if (argc <= 1) break 81 7/subop/compare *ebp 1/imm32 7e/jump-if-<= break/disp8 # if (argv[1] != "test") break (kernel-string-equal? *(ebp+8) "test") # => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 # (run-tests) # syscall(exit, *Num-test-failures) 8b/-> *Num-test-failures 3/r32/ebx eb/jump $mu-main:end/disp8 } # otherwise convert Stdin (convert-mu Stdin Stdout) (flush Stdout) # syscall(exit, 0) bb/copy-to-ebx 0/imm32 $mu-main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 convert-mu: # in: (addr buffered-file), out: (addr buffered-file) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (parse-mu *(ebp+8)) (check-mu-types) (emit-subx *(ebp+0xc)) $convert-mu:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-empty-input: # empty input => empty output # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) (check-stream-equal _test-output-stream "" "F - test-convert-empty-input") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-skeleton: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # (write _test-input-stream "fn foo {\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-skeleton/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-skeleton/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-skeleton/4") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-skeleton/6") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-skeleton/7") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-multiple-function-skeletons: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # (write _test-input-stream "fn foo {\n") (write _test-input-stream "}\n") (write _test-input-stream "fn bar {\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check first function (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-multiple-function-skeletons/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-multiple-function-skeletons/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-multiple-function-skeletons/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/3") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-multiple-function-skeletons/4") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/5") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/6") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-multiple-function-skeletons/7") # check second function (check-next-stream-line-equal _test-output-stream "bar:" "F - test-convert-multiple-function-skeletons/10") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-multiple-function-skeletons/11") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-multiple-function-skeletons/12") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/13") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-multiple-function-skeletons/14") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/15") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/16") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-multiple-function-skeletons/17") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-arg: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # (write _test-input-stream "fn foo n: int {\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-arg/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-arg/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-arg/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-arg/3") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-arg/4") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-arg/5") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-arg/6") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-arg/7") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-arg-and-body: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo n: int {\n") (write _test-input-stream " increment n\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-arg-and-body/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-arg-and-body/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-arg-and-body/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-arg-and-body/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-arg-and-body/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-arg-and-body/5") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x00000008)" "F - test-convert-function-with-arg-and-body/6") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-arg-and-body/7") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-arg-and-body/8") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-arg-and-body/9") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-arg-and-body/10") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-arg-and-body/11") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-arg-and-body/12") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-distinguishes-args: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo a: int, b: int {\n") (write _test-input-stream " increment b\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-distinguishes-args/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-distinguishes-args/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-distinguishes-args/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-distinguishes-args/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-distinguishes-args/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-distinguishes-args/5") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x0000000c)" "F - test-convert-function-distinguishes-args/6") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-distinguishes-args/7") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-distinguishes-args/8") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-distinguishes-args/9") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-distinguishes-args/10") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-distinguishes-args/11") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-distinguishes-args/12") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-returns-result: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n") (write _test-input-stream " result <- copy a\n") (write _test-input-stream " result <- increment\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-returns-result/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-returns-result/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-returns-result/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-returns-result/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-returns-result/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-returns-result/5") (check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-function-returns-result/6") (check-next-stream-line-equal _test-output-stream " 40/increment-eax" "F - test-convert-function-returns-result/7") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-returns-result/8") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-returns-result/9") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-returns-result/10") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-returns-result/11") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-returns-result/12") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-returns-result/13") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-literal-arg: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n") (write _test-input-stream " result <- copy a\n") (write _test-input-stream " result <- add 1\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-literal-arg/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-literal-arg/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-literal-arg/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-literal-arg/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-literal-arg/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-literal-arg/5") (check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-function-literal-arg/6") (check-next-stream-line-equal _test-output-stream " 05/add-to-eax 1/imm32" "F - test-convert-function-literal-arg/7") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-literal-arg/8") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-literal-arg/9") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-literal-arg/10") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-literal-arg/11") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-literal-arg/12") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-literal-arg/13") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-literal-arg-2: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n") (write _test-input-stream " result <- copy a\n") (write _test-input-stream " result <- add 1\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-literal-arg-2/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-literal-arg-2/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-literal-arg-2/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-literal-arg-2/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-literal-arg-2/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-literal-arg-2/5") (check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000003/r32" "F - test-convert-function-literal-arg-2/6") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %ebx 1/imm32" "F - test-convert-function-literal-arg-2/7") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-literal-arg-2/8") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-literal-arg-2/9") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-literal-arg-2/10") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-literal-arg-2/11") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-literal-arg-2/12") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-literal-arg-2/13") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-call-with-literal-arg: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn main -> result/ebx: int {\n") (write _test-input-stream " result <- do-add 3 4\n") (write _test-input-stream "}\n") (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n") (write _test-input-stream " result <- copy a\n") (write _test-input-stream " result <- add b\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "main:" "F - test-convert-function-call-with-literal-arg/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-call-with-literal-arg/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-call-with-literal-arg/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-call-with-literal-arg/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-call-with-literal-arg/4") (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:" "F - test-convert-function-call-with-literal-arg/5") (check-next-stream-line-equal _test-output-stream " (do-add 3 4)" "F - test-convert-function-call-with-literal-arg/6") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-call-with-literal-arg/7") (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-call-with-literal-arg/9") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-call-with-literal-arg/10") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-call-with-literal-arg/11") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-call-with-literal-arg/12") (check-next-stream-line-equal _test-output-stream "do-add:" "F - test-convert-function-call-with-literal-arg/13") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-call-with-literal-arg/14") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-call-with-literal-arg/15") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-call-with-literal-arg/16") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-call-with-literal-arg/17") (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:" "F - test-convert-function-call-with-literal-arg/18") (check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/19") (check-next-stream-line-equal _test-output-stream " 03/add *(ebp+0x0000000c) 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/20") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-call-with-literal-arg/21") (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:" "F - test-convert-function-call-with-literal-arg/22") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-call-with-literal-arg/23") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-call-with-literal-arg/24") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-call-with-literal-arg/25") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-call-with-literal-arg/26") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-local-var-in-mem: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " increment x\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-mem/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-mem/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-mem/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-mem/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-mem/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-mem/5") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-local-var-in-mem/6") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-local-var-in-mem/7") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-local-var-in-mem/8") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-mem/9") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-mem/10") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-mem/11") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-mem/12") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-mem/13") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-mem/14") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-local-var-in-reg: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " var x/ecx: int <- copy 3\n") (write _test-input-stream " x <- increment\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-reg/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-reg/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-reg/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-reg/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-reg/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-reg/5") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-function-with-local-var-in-reg/6") (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-convert-function-with-local-var-in-reg/7") (check-next-stream-line-equal _test-output-stream " 41/increment-ecx" "F - test-convert-function-with-local-var-in-reg/8") (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-reg/10") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-reg/11") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-reg/12") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-reg/13") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-reg/14") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-reg/15") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-local-var-dereferenced: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " var x/ecx: (addr int) <- copy 0\n") (write _test-input-stream " increment *x\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-dereferenced/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-dereferenced/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-dereferenced/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-dereferenced/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-dereferenced/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-dereferenced/5") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-function-with-local-var-dereferenced/6") (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 0/imm32" "F - test-convert-function-with-local-var-dereferenced/7") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *ecx" "F - test-convert-function-with-local-var-dereferenced/8") (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-dereferenced/10") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-dereferenced/11") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-dereferenced/12") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-dereferenced/13") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-dereferenced/14") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-dereferenced/15") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-local-var-in-block: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-block/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-block/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-block/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-block/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-block/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-block/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-block/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-local-var-in-block/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-local-var-in-block/8") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-local-var-in-block/9") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-local-var-in-block/10") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-block/11") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-local-var-in-block/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-block/13") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-block/14") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-block/15") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-block/16") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-block/17") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-block/18") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-local-var-in-named-block: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " $bar: {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-local-var-in-named-block/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-local-var-in-named-block/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-local-var-in-named-block/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-local-var-in-named-block/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-named-block/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-local-var-in-named-block/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-local-var-in-named-block/6") (check-next-stream-line-equal _test-output-stream "$bar:loop:" "F - test-convert-function-with-local-var-in-named-block/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-local-var-in-named-block/8") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-local-var-in-named-block/9") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-local-var-in-named-block/10") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-named-block/11") (check-next-stream-line-equal _test-output-stream "$bar:break:" "F - test-convert-function-with-local-var-in-named-block/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-local-var-in-named-block/13") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-local-var-in-named-block/14") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-local-var-in-named-block/15") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-local-var-in-named-block/16") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-local-var-in-named-block/17") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-local-var-in-named-block/18") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-branches-in-block: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo x: int {\n") (write _test-input-stream " {\n") (write _test-input-stream " break-if->=\n") (write _test-input-stream " loop-if-addr<\n") (write _test-input-stream " increment x\n") (write _test-input-stream " loop\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-in-block/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-in-block/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-in-block/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-in-block/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-block/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-in-block/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-block/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-branches-in-block/7") (check-next-stream-line-equal _test-output-stream " 0f 8d/jump-if->= break/disp32" "F - test-convert-function-with-branches-in-block/8") (check-next-stream-line-equal _test-output-stream " 0f 82/jump-if-addr< loop/disp32" "F - test-convert-function-with-branches-in-block/9") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x00000008)" "F - test-convert-function-with-branches-in-block/10") (check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-branches-in-block/11") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-block/12") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-branches-in-block/13") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-block/14") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-in-block/15") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-in-block/16") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-in-block/17") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-in-block/18") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-in-block/19") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-branches-in-named-block: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo x: int {\n") (write _test-input-stream " $bar: {\n") (write _test-input-stream " break-if->= $bar\n") (write _test-input-stream " loop-if-addr< $bar\n") (write _test-input-stream " increment x\n") (write _test-input-stream " loop\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-in-named-block/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-in-named-block/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-in-named-block/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-in-named-block/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-named-block/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-in-named-block/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-in-named-block/6") (check-next-stream-line-equal _test-output-stream "$bar:loop:" "F - test-convert-function-with-branches-in-named-block/7") (check-next-stream-line-equal _test-output-stream " 0f 8d/jump-if->= $bar:break/disp32" "F - test-convert-function-with-branches-in-named-block/8") (check-next-stream-line-equal _test-output-stream " 0f 82/jump-if-addr< $bar:loop/disp32" "F - test-convert-function-with-branches-in-named-block/9") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0x00000008)" "F - test-convert-function-with-branches-in-named-block/10") (check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-branches-in-named-block/11") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-named-block/12") (check-next-stream-line-equal _test-output-stream "$bar:break:" "F - test-convert-function-with-branches-in-named-block/13") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-in-named-block/14") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-in-named-block/15") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-in-named-block/16") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-in-named-block/17") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-in-named-block/18") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-in-named-block/19") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-var-in-nested-block: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo x: int {\n") (write _test-input-stream " {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-var-in-nested-block/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-var-in-nested-block/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-var-in-nested-block/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-var-in-nested-block/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-var-in-nested-block/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-var-in-nested-block/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-var-in-nested-block/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-var-in-nested-block/7") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-var-in-nested-block/8") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-var-in-nested-block/9") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-var-in-nested-block/11") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-var-in-nested-block/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-var-in-nested-block/13") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-var-in-nested-block/14") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-var-in-nested-block/15") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-var-in-nested-block/16") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-var-in-nested-block/17") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-var-in-nested-block/18") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-var-in-nested-block/19") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-var-in-nested-block/20") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-var-in-nested-block/21") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-var-in-nested-block/22") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-multiple-vars-in-nested-blocks: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo x: int {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x/eax: int <- copy 0\n") (write _test-input-stream " {\n") (write _test-input-stream " var y: int\n") (write _test-input-stream " x <- add y\n") (write _test-input-stream " }\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-multiple-vars-in-nested-blocks/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-multiple-vars-in-nested-blocks/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-multiple-vars-in-nested-blocks/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/7") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-function-with-multiple-vars-in-nested-blocks/8") (check-next-stream-line-equal _test-output-stream " b8/copy-to-eax 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/9") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-multiple-vars-in-nested-blocks/10") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/11") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12") (check-next-stream-line-equal _test-output-stream " 03/add *(ebp+0xfffffff8) 0x00000000/r32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/13") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/14") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-multiple-vars-in-nested-blocks/15") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/16") (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-function-with-multiple-vars-in-nested-blocks/17") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-multiple-vars-in-nested-blocks/18") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/19") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-multiple-vars-in-nested-blocks/20") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-multiple-vars-in-nested-blocks/21") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-multiple-vars-in-nested-blocks/22") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/23") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-multiple-vars-in-nested-blocks/24") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-multiple-vars-in-nested-blocks/25") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-branches-and-local-vars: # A conditional 'break' after a 'var' in a block is converted into a # nested block that performs all necessary cleanup before jumping. This # results in some ugly code duplication. # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " break-if->=\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-branches-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-branches-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-local-vars/9") (check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-branches-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " e9/jump $foo:0x00000002:break/disp32" "F - test-convert-function-with-branches-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-local-vars/13") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-branches-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-local-vars/15") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-local-vars/16") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-branches-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-local-vars/18") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-and-local-vars/19") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-and-local-vars/20") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-and-local-vars/21") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-and-local-vars/23") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-conditional-loops-and-local-vars: # A conditional 'loop' after a 'var' in a block is converted into a nested # block that performs all necessary cleanup before jumping. This results # in some ugly code duplication. # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " loop-if->=\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-conditional-loops-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-conditional-loops-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-conditional-loops-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-conditional-loops-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-conditional-loops-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-conditional-loops-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-conditional-loops-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-conditional-loops-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-conditional-loops-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-conditional-loops-and-local-vars/9") (check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-conditional-loops-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-conditional-loops-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " e9/jump $foo:0x00000002:loop/disp32" "F - test-convert-function-with-conditional-loops-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-conditional-loops-and-local-vars/13") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-conditional-loops-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-conditional-loops-and-local-vars/15") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-conditional-loops-and-local-vars/16") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-conditional-loops-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-conditional-loops-and-local-vars/18") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-conditional-loops-and-local-vars/19") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-conditional-loops-and-local-vars/20") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-conditional-loops-and-local-vars/21") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-conditional-loops-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-conditional-loops-and-local-vars/23") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-unconditional-loops-and-local-vars: # An unconditional 'loop' after a 'var' in a block is emitted _after_ the # regular block cleanup. Any instructions after 'loop' are dead and # therefore skipped. # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " loop\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-unconditional-loops-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-unconditional-loops-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-unconditional-loops-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-unconditional-loops-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-loops-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-unconditional-loops-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-loops-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-unconditional-loops-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-loops-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-loops-and-local-vars/9") (check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-unconditional-loops-and-local-vars/10") # not emitted: ff 0/subop/increment *(ebp+0xfffffffc) (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-loops-and-local-vars/11") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-unconditional-loops-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-loops-and-local-vars/13") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-unconditional-loops-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-unconditional-loops-and-local-vars/15") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-unconditional-loops-and-local-vars/16") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-unconditional-loops-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-unconditional-loops-and-local-vars/18") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-branches-and-loops-and-local-vars: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " break-if->=\n") (write _test-input-stream " increment x\n") (write _test-input-stream " loop\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-branches-and-loops-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-branches-and-loops-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-branches-and-loops-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-branches-and-loops-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-loops-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-branches-and-loops-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-loops-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-branches-and-loops-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-branches-and-loops-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-branches-and-loops-and-local-vars/9") (check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-branches-and-loops-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-loops-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " e9/jump $foo:0x00000002:break/disp32" "F - test-convert-function-with-branches-and-loops-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-loops-and-local-vars/13") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-branches-and-loops-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-branches-and-loops-and-local-vars/15") (check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-branches-and-loops-and-local-vars/16") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-loops-and-local-vars/17") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-branches-and-loops-and-local-vars/18") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-branches-and-loops-and-local-vars/19") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-branches-and-loops-and-local-vars/20") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-branches-and-loops-and-local-vars/21") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-branches-and-loops-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-branches-and-loops-and-local-vars/23") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-branches-and-loops-and-local-vars/24") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-nonlocal-branches-and-loops-and-local-vars: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " a: {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " {\n") (write _test-input-stream " var y: int\n") (write _test-input-stream " break-if->= a\n") (write _test-input-stream " increment x\n") (write _test-input-stream " loop\n") (write _test-input-stream " }\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " 0f 8c/jump-if-< break/disp32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/13") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/15") (check-next-stream-line-equal _test-output-stream " e9/jump a:break/disp32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/16") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " ff 0/subop/increment *(ebp+0xfffffffc)" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/18") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/19") (check-next-stream-line-equal _test-output-stream " e9/jump loop/disp32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/20") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/23") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24") (check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/29") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/30") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-nonlocal-unconditional-break-and-local-vars: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " a: {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " {\n") (write _test-input-stream " var y: int\n") (write _test-input-stream " break a\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/13") (check-next-stream-line-equal _test-output-stream " e9/jump a:break/disp32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18") (check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/23") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-unconditional-break-and-local-vars: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " {\n") (write _test-input-stream " var y: int\n") (write _test-input-stream " break\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-unconditional-break-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-unconditional-break-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-unconditional-break-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/9") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/13") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/15") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/16") (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/18") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/19") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-unconditional-break-and-local-vars/20") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/21") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-unconditional-break-and-local-vars/23") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-function-with-nonlocal-unconditional-loop-and-local-vars: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo {\n") (write _test-input-stream " a: {\n") (write _test-input-stream " var x: int\n") (write _test-input-stream " {\n") (write _test-input-stream " var y: int\n") (write _test-input-stream " loop a\n") (write _test-input-stream " increment x\n") (write _test-input-stream " }\n") (write _test-input-stream " }\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6") (check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10") (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/12") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/13") (check-next-stream-line-equal _test-output-stream " e9/jump a:loop/disp32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/14") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15") (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16") (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18") (check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/23") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-convert-length-of-array: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (clear-stream $_test-input-buffered-file->buffer) (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) c7 0/subop/copy *Next-block-index 1/imm32 # (write _test-input-stream "fn foo a: (addr array int) {\n") (write _test-input-stream " var b/eax: (addr array int) <- copy a\n") (write _test-input-stream " var c/eax: int <- length b\n") (write _test-input-stream "}\n") # convert (convert-mu _test-input-buffered-file _test-output-buffered-file) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-length-of-array/0") (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-length-of-array/1") (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-length-of-array/2") (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-length-of-array/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-length-of-array/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-length-of-array/5") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/6") (check-next-stream-line-equal _test-output-stream " 8b/copy-from *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-length-of-array/7") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/8") (check-next-stream-line-equal _test-output-stream " 8b/copy-from *eax 0x00000000/r32" "F - test-convert-length-of-array/9") (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/10") (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/11") (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array/12") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array/13") (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array/14") (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array/15") (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array/16") (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array/17") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return ####################################################### # Parsing ####################################################### parse-mu: # in: (addr buffered-file) # pseudocode # var curr-function: (addr (handle function)) = Program # var line: (stream byte 512) # var word-slice: slice # while true # line loop # clear-stream(line) # read-line-buffered(in, line) # if (line->write == 0) break # end of file # word-slice = next-mu-token(line) # if slice-empty?(word-slice) # end of line # continue # else if slice-starts-with?(word-slice, "#") # comment # continue # end of line # else if slice-equal(word-slice, "fn") # var new-function: (handle function) = allocate(function) # var vars: (stack (addr var) 256) # populate-mu-function-header(in, new-function, vars) # populate-mu-function-body(in, new-function, vars) # assert(vars->top == 0) # *curr-function = new-function # curr-function = &new-function->next # else # abort() # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx 57/push-edi # var line/ecx: (stream byte 512) 81 5/subop/subtract %esp 0x200/imm32 68/push 0x200/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %ecx 4/r32/esp # var word-slice/edx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %edx 4/r32/esp # var curr-function/edi: (addr (handle function)) = Program bf/copy-to-edi Program/imm32 # var vars/ebx: (stack (addr var) 256) 81 5/subop/subtract %esp 0x400/imm32 68/push 0x400/imm32/length 68/push 0/imm32/top 89/<- %ebx 4/r32/esp { $parse-mu:line-loop: (clear-stream %ecx) (read-line-buffered *(ebp+8) %ecx) # if (line->write == 0) break 81 7/subop/compare *ecx 0/imm32 0f 84/jump-if-= break/disp32 #? # dump line {{{ #? (write 2 "parse-mu: ^") #? (write-stream 2 %ecx) #? (write 2 "$\n") #? (rewind-stream %ecx) #? # }}} (next-mu-token %ecx %edx) # if slice-empty?(word-slice) continue (slice-empty? %edx) 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= loop/disp32 # if (*word-slice->start == "#") continue # . eax = *word-slice->start 8b/-> *edx 0/r32/eax 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # . if (eax == '#') continue 3d/compare-eax-and 0x23/imm32/hash 0f 84/jump-if-= loop/disp32 # if (slice-equal?(word-slice, "fn")) parse a function { $parse-mu:fn: (slice-equal? %edx "fn") 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= break/disp32 # var new-function/eax: (handle function) = populate-mu-function(in, new-function, vars) (allocate Heap *Function-size) # => eax (zero-out %eax *Function-size) (clear-stack %ebx) (populate-mu-function-header %ecx %eax %ebx) (populate-mu-function-body *(ebp+8) %eax %ebx) # *curr-function = new-function 89/<- *edi 0/r32/eax # curr-function = &new-function->next 8d/address-> *(eax+0x14) 7/r32/edi # Function-next e9/jump $parse-mu:line-loop/disp32 } # otherwise abort e9/jump $parse-mu:error1/disp32 } # end line loop $parse-mu:end: # . reclaim locals 81 0/subop/add %esp 0x630/imm32 # . restore registers 5f/pop-to-edi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $parse-mu:error1: # error("unexpected top-level command: " word-slice "\n") (write-buffered Stderr "unexpected top-level command: ") (write-slice-buffered Stderr %edx) (write-buffered Stderr "\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $parse-mu:error2: # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n") (print-int32-buffered Stderr *ebx) (write-buffered Stderr " vars not reclaimed after fn '") (write-slice-buffered Stderr *eax) # Function-name (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here # scenarios considered: # ✗ fn foo # no block # ✓ fn foo { # ✗ fn foo { { # ✗ fn foo { } # ✗ fn foo { } { # ✗ fn foo x { # ✗ fn foo x: { # ✓ fn foo x: int { # ✓ fn foo x: int { # ✓ fn foo x: int -> y/eax: int { populate-mu-function-header: # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var)) # pseudocode: # var name: slice # next-mu-token(first-line, name) # assert(name not in '{' '}' '->') # out->name = slice-to-string(name) # var next-offset: int = 8 # ## inouts # while true # ## name # name = next-mu-token(first-line) # if (name == '{') goto done # if (name == '->') break # assert(name != '}') # var v: (handle var) = parse-var-with-type(name, first-line) # assert(v->register == null) # v->stack-offset = next-offset # next-offset += size-of(v) # # v->block-depth is implicitly 0 # out->inouts = append(out->inouts, v) # push(vars, v) # ## outputs # while true # ## name # name = next-mu-token(first-line) # assert(name not in '{' '}' '->') # var v: (handle var) = parse-var-with-type(name, first-line) # assert(v->register != null) # out->outputs = append(out->outputs, v) # done: # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx 57/push-edi # edi = out 8b/-> *(ebp+0xc) 7/r32/edi # var word-slice/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # var next-offset/edx = 8 ba/copy-to-edx 8/imm32 # read function name (next-mu-token *(ebp+8) %ecx) # error checking # TODO: error if name starts with 'break' or 'loop' # if (word-slice == '{') abort (slice-equal? %ecx "{") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:error1/disp32 # if (word-slice == '->') abort (slice-equal? %ecx "->") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:error1/disp32 # if (word-slice == '}') abort (slice-equal? %ecx "}") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:error1/disp32 # save function name (slice-to-string Heap %ecx) # => eax 89/<- *edi 0/r32/eax # Function-name # initialize default subx-name as well 89/<- *(edi+4) 0/r32/eax # Function-subx-name # save function inouts { $populate-mu-function-header:check-for-inout: (next-mu-token *(ebp+8) %ecx) # if (word-slice == '{') goto done (slice-equal? %ecx "{") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:done/disp32 # if (word-slice == '->') break (slice-equal? %ecx "->") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # if (word-slice == '}') abort (slice-equal? %ecx "}") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:error1/disp32 # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line) (parse-var-with-type %ecx *(ebp+8)) # => eax 89/<- %ebx 0/r32/eax # assert(v->register == null) 81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register 0f 85/jump-if-!= $populate-mu-function-header:error2/disp32 # v->stack-offset = next-offset 89/<- *(ebx+0xc) 2/r32/edx # Var-stack-offset # next-offset += size-of(v) (size-of %ebx) # => eax 01/add %edx 0/r32/eax # v->block-depth is implicitly 0 # (append-list Heap %ebx *(edi+8)) # Function-inouts => eax 89/<- *(edi+8) 0/r32/eax # Function-inouts (push *(ebp+0x10) %ebx) # e9/jump loop/disp32 } # save function outputs { $populate-mu-function-header:check-for-out: (next-mu-token *(ebp+8) %ecx) # if (word-slice == '{') break (slice-equal? %ecx "{") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # if (word-slice == '->') abort (slice-equal? %ecx "->") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:error1/disp32 # if (word-slice == '}') abort (slice-equal? %ecx "}") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $populate-mu-function-header:error1/disp32 # (parse-var-with-type %ecx *(ebp+8)) # => eax 89/<- %ebx 0/r32/eax # assert(var->register != null) 81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register 0f 84/jump-if-= $populate-mu-function-header:error3/disp32 (append-list Heap %ebx *(edi+0xc)) # Function-outputs => eax 89/<- *(edi+0xc) 0/r32/eax # Function-outputs e9/jump loop/disp32 } $populate-mu-function-header:done: (check-no-tokens-left *(ebp+8)) $populate-mu-function-header:end: # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 5f/pop-to-edi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $populate-mu-function-header:error1: # error("function header not in form 'fn {'") (write-buffered Stderr "function header not in form 'fn [inouts] [-> outputs] {' -- '") (flush Stderr) (rewind-stream *(ebp+8)) (write-stream 2 *(ebp+8)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $populate-mu-function-header:error2: # error("function input '" var "' cannot be in a register") (write-buffered Stderr "function input '") (write-buffered Stderr *ebx) # Var-name (write-buffered Stderr "' cannot be in a register") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $populate-mu-function-header:error3: # error("function input '" var "' must be in a register") (write-buffered Stderr "function input '") (write-buffered Stderr *eax) # Var-name (write-buffered Stderr " must be in a register'") (flush Stderr) (rewind-stream *(ebp+8)) (write-stream 2 *(ebp+8)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here test-function-header-with-arg: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "foo n: int {\n") # var result/ecx: function 2b/subtract-> *Function-size 4/r32/esp 89/<- %ecx 4/r32/esp (zero-out %ecx *Function-size) # var vars/ebx: (stack (addr var) 16) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ebx 4/r32/esp # convert (populate-mu-function-header _test-input-stream %ecx %ebx) # check result (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name") # Function-name # edx: (handle list var) = result->inouts 8b/-> *(ecx+8) 2/r32/edx # Function-inouts # ebx: (handle var) = result->inouts->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-arg/inout:0/type:1") # Tree-right # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-function-header-with-multiple-args: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "foo a: int, b: int c: int {\n") # result/ecx: (handle function) 2b/subtract-> *Function-size 4/r32/esp 89/<- %ecx 4/r32/esp (zero-out %ecx *Function-size) # var vars/ebx: (stack (addr var) 16) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ebx 4/r32/esp # convert (populate-mu-function-header _test-input-stream %ecx %ebx) # check result (check-strings-equal *ecx "foo") # Function-name # edx: (handle list var) = result->inouts 8b/-> *(ecx+8) 2/r32/edx # Function-inouts $test-function-header-with-multiple-args:inout0: # ebx: (handle var) = result->inouts->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:0/type:1") # Tree-right # edx = result->inouts->next 8b/-> *(edx+4) 2/r32/edx # List-next $test-function-header-with-multiple-args:inout1: # ebx = result->inouts->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:1/type:1") # Tree-right # edx = result->inouts->next->next 8b/-> *(edx+4) 2/r32/edx # List-next $test-function-header-with-multiple-args:inout2: # ebx = result->inouts->next->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args/inout:2/type:1") # Tree-right # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-function-with-multiple-args-and-outputs: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n") # result/ecx: (handle function) 2b/subtract-> *Function-size 4/r32/esp 89/<- %ecx 4/r32/esp (zero-out %ecx *Function-size) # var vars/ebx: (stack (addr var) 16) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ebx 4/r32/esp # convert (populate-mu-function-header _test-input-stream %ecx %ebx) # check result (check-strings-equal *ecx "foo") # Function-name # edx: (handle list var) = result->inouts 8b/-> *(ecx+8) 2/r32/edx # Function-inouts # ebx: (handle var) = result->inouts->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1") # Tree-right # edx = result->inouts->next 8b/-> *(edx+4) 2/r32/edx # List-next # ebx = result->inouts->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1") # Tree-right # edx = result->inouts->next->next 8b/-> *(edx+4) 2/r32/edx # List-next # ebx = result->inouts->next->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2") # Var-name 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1") # Tree-right # edx: (handle list var) = result->outputs 8b/-> *(ecx+0xc) 2/r32/edx # Function-outputs # ebx: (handle var) = result->outputs->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0") # Var-name (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register") # Var-register 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1") # Tree-right # edx = result->outputs->next 8b/-> *(edx+4) 2/r32/edx # List-next # ebx = result->outputs->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1") # Var-name (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register") # Var-register 8b/-> *(ebx+4) 3/r32/ebx # Var-type (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1") # Tree-left (check-ints-equal *(ebx+4) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1") # Tree-right # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # format for variables with types # x: int # x: int, # x/eax: int # x/eax: int, # ignores at most one trailing comma # WARNING: modifies name parse-var-with-type: # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var) # pseudocode: # var v: (handle var) = allocate(Heap, Var-size) # var s: slice # if (!slice-ends-with(name, ":")) # abort # --name->end to skip ':' # next-token-from-slice(name->start, name->end, '/', s) # v->name = slice-to-string(s) # ## register # next-token-from-slice(s->end, name->end, '/', s) # if (!slice-empty?(s)) # v->register = slice-to-string(s) # ## type # var type: (handle tree type-id) = parse-type(first-line) # v->type = type # return v # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # esi = name 8b/-> *(ebp+8) 6/r32/esi # if (!slice-ends-with?(name, ":")) abort 8b/-> *(esi+4) 1/r32/ecx # Slice-end 49/decrement-ecx 8a/copy-byte *ecx 1/r32/CL 81 4/subop/and %ecx 0xff/imm32 81 7/subop/compare %ecx 0x3a/imm32/colon 0f 85/jump-if-!= $parse-var-with-type:abort/disp32 # --name->end to skip ':' ff 1/subop/decrement *(esi+4) # var result/edi: (handle var) = allocate(Heap, Var-size) (allocate Heap *Var-size) # => eax (zero-out %eax *Var-size) 89/<- %edi 0/r32/eax # var s/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp $parse-var-with-type:save-name: # save v->name (next-token-from-slice *esi *(esi+4) 0x2f %ecx) # Slice-start, Slice-end, '/' # . end/edx = s->end 8b/-> *(ecx+4) 2/r32/edx $parse-var-with-type:write-name: (slice-to-string Heap %ecx) # => eax 89/<- *edi 0/r32/eax # Var-name # save v->register $parse-var-with-type:save-register: (next-token-from-slice %edx *(esi+4) 0x2f %ecx) # end, name->end, '/' # if (!slice-empty?(s)) v->register = slice-to-string(s) { $parse-var-with-type:write-register: (slice-empty? %ecx) # => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 (slice-to-string Heap %ecx) 89/<- *(edi+0x10) 0/r32/eax # Var-register } $parse-var-with-type:save-type: (parse-type Heap *(ebp+0xc)) # => eax 89/<- *(edi+4) 0/r32/eax # Var-type $parse-var-with-type:end: # return result 89/<- %eax 7/r32/edi # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $parse-var-with-type:abort: # error("var should have form 'name: type' in '" line "'\n") (write-buffered Stderr "var should have form 'name: type' in '") (flush Stderr) (rewind-stream *(ebp+0xc)) (write-stream 2 *(ebp+0xc)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here parse-type: # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id) # pseudocode: # var s: slice = next-mu-token(in) # assert s != "" # assert s != "->" # assert s != "{" # assert s != "}" # if s == ")" # return 0 # result = allocate(Tree) # zero-out(result, *Tree-size) # if s != "(" # result->left = pos-slice(Type-id, s) # return # result->left = parse-type(ad, in) # result->right = parse-type-tree(ad, in) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx # var s/ecx: slice 68/push 0/imm32 68/push 0/imm32 89/<- %ecx 4/r32/esp # s = next-mu-token(in) (next-mu-token *(ebp+0xc) %ecx) #? (write-buffered Stderr "tok: ") #? (write-slice-buffered Stderr %ecx) #? (write-buffered Stderr "$\n") #? (flush Stderr) # assert s != "" (slice-equal? %ecx "") 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $parse-type:abort/disp32 # assert s != "{" (slice-equal? %ecx "{") 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $parse-type:abort/disp32 # assert s != "}" (slice-equal? %ecx "}") 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $parse-type:abort/disp32 # assert s != "->" (slice-equal? %ecx "->") 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $parse-type:abort/disp32 # if (s == ")") return 0 (slice-equal? %ecx ")") 3d/compare-eax-and 0/imm32/false b8/copy-to-eax 0/imm32 0f 85/jump-if-!= $parse-type:end/disp32 # var result/edx: (handle tree type-id) (allocate *(ebp+8) *Tree-size) # => eax (zero-out %eax *Tree-size) 89/<- %edx 0/r32/eax { # if (s != "(") break (slice-equal? %ecx "(") 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 # result->left = pos-slice(Type-id, s) (pos-slice Type-id %ecx) #? (write-buffered Stderr "=> {") #? (print-int32-buffered Stderr %eax) #? (write-buffered Stderr ", 0}\n") #? (flush Stderr) 89/<- *edx 0/r32/eax # Tree-left e9/jump $parse-type:return-edx/disp32 } # otherwise s == "(" # result->left = parse-type(ad, in) (parse-type *(ebp+8) *(ebp+0xc)) #? (write-buffered Stderr "=> {") #? (print-int32-buffered Stderr %eax) 89/<- *edx 0/r32/eax # Tree-left # result->right = parse-type-tree(ad, in) (parse-type-tree *(ebp+8) *(ebp+0xc)) #? (write-buffered Stderr Space) #? (print-int32-buffered Stderr %eax) #? (write-buffered Stderr "}\n") #? (flush Stderr) 89/<- *(edx+4) 0/r32/eax # Tree-right $parse-type:return-edx: 89/<- %eax 2/r32/edx $parse-type:end: # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $parse-type:abort: # error("unexpected token when parsing type: '" s "'\n") (write-buffered Stderr "unexpected token when parsing type: '") (write-slice-buffered Stderr %ecx) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here parse-type-tree: # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id) # pseudocode: # var tmp: (handle tree type-id) = parse-type(ad, in) # if tmp == 0 # return 0 # result = allocate(Tree) # zero-out(result, *Tree-size) # result->left = tmp # result->right = parse-type-tree(ad, in) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx # var tmp/eax: (handle tree type-id) = parse-type(ad, in) (parse-type *(ebp+8) *(ebp+0xc)) # if (tmp == 0) return tmp 3d/compare-eax-and 0/imm32 74/jump-if-= $parse-type-tree:end/disp8 # var tmp2/ecx = tmp 89/<- %ecx 0/r32/eax # var result/edx: (handle tree type-id) (allocate *(ebp+8) *Tree-size) # => eax (zero-out %eax *Tree-size) 89/<- %edx 0/r32/eax # result->left = tmp2 89/<- *edx 1/r32/ecx # Tree-left # result->right = parse-type-tree(ad, in) (parse-type-tree *(ebp+8) *(ebp+0xc)) 89/<- *(edx+4) 0/r32/eax # Tree-right $parse-type-tree:return-edx: 89/<- %eax 2/r32/edx $parse-type-tree:end: # . restore registers 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return next-mu-token: # in: (addr stream byte), out: (addr slice) # pseudocode: # start: # skip-chars-matching-whitespace(in) # if in->read >= in->write # end of in # out = {0, 0} # return # out->start = &in->data[in->read] # var curr-byte/eax: byte = in->data[in->read] # if curr->byte == ',' # comment token # ++in->read # goto start # if curr-byte == '#' # comment # goto done # treat as eof # if curr-byte == '"' # string literal # skip-string(in) # goto done # no metadata # if curr-byte == '(' # ++in->read # goto done # if curr-byte == ')' # ++in->read # goto done # # read a word # while true # if in->read >= in->write # break # curr-byte = in->data[in->read] # if curr-byte == ' ' # break # if curr-byte == '\r' # break # if curr-byte == '\n' # break # if curr-byte == '(' # break # if curr-byte == ')' # break # if curr-byte == ',' # break # ++in->read # done: # out->end = &in->data[in->read] # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi 57/push-edi # esi = in 8b/-> *(ebp+8) 6/r32/esi # edi = out 8b/-> *(ebp+0xc) 7/r32/edi $next-mu-token:start: (skip-chars-matching-whitespace %esi) $next-mu-token:check0: # if (in->read >= in->write) return out = {0, 0} # . ecx = in->read 8b/-> *(esi+4) 1/r32/ecx # . if (ecx >= in->write) return out = {0, 0} 3b/compare 1/r32/ecx *esi c7 0/subop/copy *edi 0/imm32 c7 0/subop/copy *(edi+4) 0/imm32 0f 8d/jump-if->= $next-mu-token:end/disp32 # out->start = &in->data[in->read] 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 89/<- *edi 0/r32/eax # var curr-byte/eax: byte = in->data[in->read] 31/xor %eax 0/r32/eax 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL { $next-mu-token:check-for-comma: # if (curr-byte != ',') break 3d/compare-eax-and 0x2c/imm32/comma 75/jump-if-!= break/disp8 # ++in->read ff 0/subop/increment *(esi+4) # restart e9/jump $next-mu-token:start/disp32 } { $next-mu-token:check-for-comment: # if (curr-byte != '#') break 3d/compare-eax-and 0x23/imm32/pound 75/jump-if-!= break/disp8 # return eof e9/jump $next-mu-token:done/disp32 } { $next-mu-token:check-for-string-literal: # if (curr-byte != '"') break 3d/compare-eax-and 0x22/imm32/dquote 75/jump-if-!= break/disp8 (skip-string %esi) # return e9/jump $next-mu-token:done/disp32 } { $next-mu-token:check-for-open-paren: # if (curr-byte != '(') break 3d/compare-eax-and 0x28/imm32/open-paren 75/jump-if-!= break/disp8 # ++in->read ff 0/subop/increment *(esi+4) # return e9/jump $next-mu-token:done/disp32 } { $next-mu-token:check-for-close-paren: # if (curr-byte != ')') break 3d/compare-eax-and 0x29/imm32/close-paren 75/jump-if-!= break/disp8 # ++in->read ff 0/subop/increment *(esi+4) # return e9/jump $next-mu-token:done/disp32 } { $next-mu-token:regular-word-without-metadata: # if (in->read >= in->write) break # . ecx = in->read 8b/-> *(esi+4) 1/r32/ecx # . if (ecx >= in->write) break 3b/compare *esi 1/r32/ecx 7d/jump-if->= break/disp8 # var c/eax: byte = in->data[in->read] 31/xor %eax 0/r32/eax 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL # if (c == ' ') break 3d/compare-eax-and 0x20/imm32/space 74/jump-if-= break/disp8 # if (c == '\r') break 3d/compare-eax-and 0xd/imm32/carriage-return 74/jump-if-= break/disp8 # if (c == '\n') break 3d/compare-eax-and 0xa/imm32/newline 74/jump-if-= break/disp8 # if (c == '(') break 3d/compare-eax-and 0x28/imm32/open-paren 0f 84/jump-if-= break/disp32 # if (c == ')') break 3d/compare-eax-and 0x29/imm32/close-paren 0f 84/jump-if-= break/disp32 # if (c == ',') break 3d/compare-eax-and 0x2c/imm32/comma 0f 84/jump-if-= break/disp32 # ++in->read ff 0/subop/increment *(esi+4) # e9/jump loop/disp32 } $next-mu-token:done: # out->end = &in->data[in->read] 8b/-> *(esi+4) 1/r32/ecx 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 89/<- *(edi+4) 0/r32/eax $next-mu-token:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # return the index in an array of strings matching 's' # index is denominated in elements, not bytes pos-slice: # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi #? (write-buffered Stderr "pos-slice: ") #? (write-slice-buffered Stderr *(ebp+0xc)) #? (write-buffered Stderr "\n") #? (flush Stderr) # esi = arr 8b/-> *(ebp+8) 6/r32/esi # var index/ecx: int = 0 b9/copy-to-ecx 0/imm32 # var curr/edx: (addr (addr array byte)) = arr->data 8d/copy-address *(esi+0xc) 2/r32/edx # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write] 8b/-> *esi 3/r32/ebx 8d/copy-address *(esi+ebx+0xc) 3/r32/ebx { #? (write-buffered Stderr " ") #? (print-int32-buffered Stderr %ecx) #? (write-buffered Stderr "\n") #? (flush Stderr) # if (curr >= max) return -1 39/compare %edx 3/r32/ebx b8/copy-to-eax -1/imm32 73/jump-if-addr>= $pos-slice:end/disp8 # if (slice-equal?(s, *curr)) break (slice-equal? *(ebp+0xc) *edx) # => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 # ++index 41/increment-ecx # curr += 4 81 0/subop/add %edx 4/imm32 # eb/jump loop/disp8 } # return index 89/<- %eax 1/r32/ecx $pos-slice:end: #? (write-buffered Stderr "=> ") #? (print-int32-buffered Stderr %eax) #? (write-buffered Stderr "\n") # . restore registers 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return == data Type-id: # (stream (address array byte)) 0x18/imm32/write 0/imm32/read 0x100/imm32/length # data "literal"/imm32 # 0 "int"/imm32 # 1 "addr"/imm32 # 2 "array"/imm32 # 3 "handle"/imm32 # 4 "bool"/imm32 # 5 0/imm32 0/imm32 # 0x20 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 == code test-parse-var-with-type: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "x:" b8/copy-to-eax "x:"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # _test-input-stream contains "int" (clear-stream _test-input-stream) (write _test-input-stream "int") # (parse-var-with-type %ecx _test-input-stream) 8b/-> *eax 2/r32/edx # Var-name (check-strings-equal %edx "x" "F - test-var-with-type/name") 8b/-> *(eax+4) 2/r32/edx # Var-type (check-ints-equal *edx 1 "F - test-var-with-type/type") (check-ints-equal *(edx+4) 0 "F - test-var-with-type/type") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-var-with-type-and-register: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "x/eax:" b8/copy-to-eax "x/eax:"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # _test-input-stream contains "int" (clear-stream _test-input-stream) (write _test-input-stream "int") # (parse-var-with-type %ecx _test-input-stream) 8b/-> *eax 2/r32/edx # Var-name (check-strings-equal %edx "x" "F - test-var-with-type-and-register/name") 8b/-> *(eax+0x10) 2/r32/edx # Var-register (check-strings-equal %edx "eax" "F - test-var-with-type-and-register/register") 8b/-> *(eax+4) 2/r32/edx # Var-type (check-ints-equal *edx 1 "F - test-var-with-type-and-register/type") (check-ints-equal *(edx+4) 0 "F - test-var-with-type-and-register/type") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-var-with-trailing-characters: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "x:" b8/copy-to-eax "x:"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # _test-input-stream contains "int," (clear-stream _test-input-stream) (write _test-input-stream "int,") # (parse-var-with-type %ecx _test-input-stream) 8b/-> *eax 2/r32/edx # Var-name (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name") 8b/-> *(eax+0x10) 2/r32/edx # Var-register (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register") 8b/-> *(eax+4) 2/r32/edx # Var-type (check-ints-equal *edx 1 "F - test-var-with-trailing-characters/type") (check-ints-equal *(edx+4) 0 "F - test-var-with-trailing-characters/type") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-var-with-register-and-trailing-characters: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "x/eax:" b8/copy-to-eax "x/eax:"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # _test-input-stream contains "int," (clear-stream _test-input-stream) (write _test-input-stream "int,") # (parse-var-with-type %ecx _test-input-stream) 8b/-> *eax 2/r32/edx # Var-name (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name") 8b/-> *(eax+0x10) 2/r32/edx # Var-register (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register") 8b/-> *(eax+4) 2/r32/edx # Var-type (check-ints-equal *edx 1 "F - test-var-with-register-and-trailing-characters/type") (check-ints-equal *(edx+4) 0 "F - test-var-with-register-and-trailing-characters/type") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-var-with-compound-type: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "x:" b8/copy-to-eax "x:"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # _test-input-stream contains "(addr int)" (clear-stream _test-input-stream) (write _test-input-stream "(addr int)") # (parse-var-with-type %ecx _test-input-stream) 8b/-> *eax 2/r32/edx # Var-name (check-strings-equal %edx "x" "F - test-var-with-compound-type/name") 8b/-> *(eax+0x10) 2/r32/edx # Var-register (check-ints-equal %edx 0 "F - test-var-with-compound-type/register") # var type/edx: (handle tree type-id) = var->type 8b/-> *(eax+4) 2/r32/edx # Var-type # type->left == atom(addr) 8b/-> *edx 0/r32/eax # Atom-value (check-ints-equal *eax 2 "F - test-var-with-compound-type/type:0") # Tree-left # type->right->left == atom(int) 8b/-> *(edx+4) 2/r32/edx # Tree-right 8b/-> *edx 0/r32/eax # Tree-left (check-ints-equal *eax 1 "F - test-var-with-compound-type/type:1") # Atom-value # type->right->right == null (check-ints-equal *(edx+4) 0 "F - test-var-with-compound-type/type:2") # Tree-right # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # identifier starts with a letter or '$' or '_' # no constraints at the moment on later letters # all we really want to do so far is exclude '{', '}' and '->' is-identifier?: # in: (addr slice) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # if (slice-empty?(in)) return false (slice-empty? *(ebp+8)) # => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= $is-identifier?:false/disp8 # var c/eax: byte = *in->start 8b/-> *(ebp+8) 0/r32/eax 8b/-> *eax 0/r32/eax 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # if (c == '$') return true 3d/compare-eax-and 0x24/imm32/$ 74/jump-if-= $is-identifier?:true/disp8 # if (c == '_') return true 3d/compare-eax-and 0x5f/imm32/_ 74/jump-if-= $is-identifier?:true/disp8 # drop case 25/and-eax-with 0x5f/imm32 # if (c < 'A') return false 3d/compare-eax-and 0x41/imm32/A 7c/jump-if-< $is-identifier?:false/disp8 # if (c > 'Z') return false 3d/compare-eax-and 0x5a/imm32/Z 7f/jump-if-> $is-identifier?:false/disp8 # otherwise return true $is-identifier?:true: b8/copy-to-eax 1/imm32/true eb/jump $is-identifier?:end/disp8 $is-identifier?:false: b8/copy-to-eax 0/imm32/false $is-identifier?:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-dollar: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "$a" b8/copy-to-eax "$a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 1 "F - test-is-identifier-dollar") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-underscore: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "_a" b8/copy-to-eax "_a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 1 "F - test-is-identifier-underscore") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-a: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "a$" b8/copy-to-eax "a$"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 1 "F - test-is-identifier-a") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-z: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "z$" b8/copy-to-eax "z$"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 1 "F - test-is-identifier-z") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-A: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "A$" b8/copy-to-eax "A$"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 1 "F - test-is-identifier-A") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-Z: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "Z$" b8/copy-to-eax "Z$"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 1 "F - test-is-identifier-Z") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-@: # character before 'A' is invalid # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "@a" b8/copy-to-eax "@a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 0 "F - test-is-identifier-@") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-square-bracket: # character after 'Z' is invalid # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "[a" b8/copy-to-eax "[a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 0 "F - test-is-identifier-@") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-backtick: # character before 'a' is invalid # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "`a" b8/copy-to-eax "`a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 0 "F - test-is-identifier-backtick") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-curly-brace-open: # character after 'z' is invalid; also used for blocks # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "{a" b8/copy-to-eax "{a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-curly-brace-close: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "}a" b8/copy-to-eax "}a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-is-identifier-hyphen: # disallow leading '-' since '->' has special meaning # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (eax..ecx) = "-a" b8/copy-to-eax "-a"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 # var slice/ecx: slice = {eax, ecx} 51/push-ecx 50/push-eax 89/<- %ecx 4/r32/esp # (is-identifier? %ecx) (check-ints-equal %eax 0 "F - test-is-identifier-hyphen") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return populate-mu-function-body: # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var)) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 56/push-esi 57/push-edi # esi = in 8b/-> *(ebp+8) 6/r32/esi # edi = out 8b/-> *(ebp+0xc) 7/r32/edi # initialize some global state c7 0/subop/copy *Curr-block-depth 1/imm32 c7 0/subop/copy *Next-local-stack-offset -4/imm32 # var eax: (handle block) = parse-mu-block(in, vars, fn) (parse-mu-block %esi *(ebp+0x10) %edi) # => eax # out->body = eax 89/<- *(edi+0x10) 0/r32/eax # Function-body $populate-mu-function-body:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return == data # Global state added to each var record when parsing a function Curr-block-depth: # (addr int) 0/imm32 Next-local-stack-offset: # (addr int) -4/imm32 Next-block-index: # (addr int) 1/imm32 == code # parses a block, assuming that the leading '{' has already been read by the caller parse-mu-block: # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block) # pseudocode: # var line: (stream byte 512) # var word-slice: slice # increment *Curr-block-depth # result/eax = allocate(Heap, Stmt-size) # result->tag = 0/block # result->name = some unique name # while true # line loop # clear-stream(line) # read-line-buffered(in, line) # if (line->write == 0) break # end of file # word-slice = next-mu-token(line) # if slice-empty?(word-slice) # end of line # continue # else if slice-starts-with?(word-slice, "#") # continue # else if slice-equal?(word-slice, "{") # assert(no-tokens-in(line)) # block = parse-mu-block(in, vars, fn) # append-to-block(result, block) # else if slice-equal?(word-slice, "}") # break # else if slice-ends-with?(word-slice, ":") # # TODO: error-check the rest of 'line' # --word-slice->end to skip ':' # named-block = parse-mu-named-block(word-slice, in, vars, fn) # append-to-block(result, named-block) # else if slice-equal?(word-slice, "var") # var-def = parse-mu-var-def(line, vars) # append-to-block(result, var-def) # else # stmt = parse-mu-stmt(line, vars, fn) # append-to-block(result, stmt) # decrement *Curr-block-depth # return result # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx 53/push-ebx 57/push-edi # var line/ecx: (stream byte 512) 81 5/subop/subtract %esp 0x200/imm32 68/push 0x200/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %ecx 4/r32/esp # var word-slice/edx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %edx 4/r32/esp # edi = result (allocate Heap *Stmt-size) # => eax (zero-out %eax *Stmt-size) 89/<- %edi 0/r32/eax # set result->tag c7 0/subop/copy *edi 0/imm32/block # Stmt-tag # set result->var (new-block-name *(ebp+0x10)) # => eax 89/<- *(edi+8) 0/r32/eax # Block-var # push result->var to vars (push *(ebp+0xc) %eax) # increment *Curr-block-depth ff 0/subop/increment *Curr-block-depth { $parse-mu-block:line-loop: # line = read-line-buffered(in) (clear-stream %ecx) (read-line-buffered *(ebp+8) %ecx) #? (write-buffered Stderr "line: ") #? (write-stream-data Stderr %ecx) #? (write-buffered Stderr Newline) #? (flush Stderr) # if (line->write == 0) break 81 7/subop/compare *ecx 0/imm32 0f 84/jump-if-= break/disp32 # word-slice = next-mu-token(line) (next-mu-token %ecx %edx) #? (write-buffered Stderr "word: ") #? (write-slice-buffered Stderr %edx) #? (write-buffered Stderr Newline) #? (flush Stderr) # if slice-empty?(word-slice) continue (slice-empty? %edx) 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= loop/disp32 # if (slice-starts-with?(word-slice, '#') continue # . eax = *word-slice->start 8b/-> *edx 0/r32/eax 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # . if (eax == '#') continue 3d/compare-eax-and 0x23/imm32/hash 0f 84/jump-if-= loop/disp32 # if slice-equal?(word-slice, "{") { $parse-mu-block:check-for-block: (slice-equal? %edx "{") 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 (check-no-tokens-left %ecx) # parse new block and append (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10)) # => eax (append-to-block Heap %edi %eax) e9/jump $parse-mu-block:line-loop/disp32 } # if slice-equal?(word-slice, "}") break $parse-mu-block:check-for-end: (slice-equal? %edx "}") 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # if slice-ends-with?(word-slice, ":") parse named block and append { $parse-mu-block:check-for-named-block: # . eax = *(word-slice->end-1) 8b/-> *(edx+4) 0/r32/eax 48/decrement-eax 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # . if (eax != ':') break 3d/compare-eax-and 0x3a/imm32/colon 0f 85/jump-if-!= break/disp32 # TODO: error-check the rest of 'line' # # skip ':' ff 1/subop/decrement *(edx+4) # Slice-end # (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10)) # => eax (append-to-block Heap %edi %eax) e9/jump $parse-mu-block:line-loop/disp32 } # if slice-equal?(word-slice, "var") { $parse-mu-block:check-for-var: (slice-equal? %edx "var") 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 # (parse-mu-var-def %ecx *(ebp+0xc)) # => eax (append-to-block Heap %edi %eax) e9/jump $parse-mu-block:line-loop/disp32 } $parse-mu-block:regular-stmt: # otherwise (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10)) # => eax (append-to-block Heap %edi %eax) e9/jump loop/disp32 } # end line loop # decrement *Curr-block-depth ff 1/subop/decrement *Curr-block-depth # (pop *(ebp+0xc)) # => eax # return result 89/<- %eax 7/r32/edi $parse-mu-block:end: # . reclaim locals 81 0/subop/add %esp 0x214/imm32 # . restore registers 5f/pop-to-edi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $parse-mu-block:abort: # error("'{' or '}' should be on its own line, but got '") (write-buffered Stderr "'{' or '}' should be on its own line, but got '") (rewind-stream %ecx) (write-stream 2 %ecx) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here new-block-name: # fn: (handle function) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:' 8b/-> *(ebp+8) 0/r32/eax 8b/-> *eax 0/r32/eax # Function-name 8b/-> *eax 0/r32/eax # String-length 05/add-to-eax 0xd/imm32 # 10 + 2 for '$:' 89/<- %ecx 0/r32/eax # var name/edx: (stream byte n) 29/subtract %esp 1/r32/ecx ff 6/subop/push %ecx 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %edx 4/r32/esp (clear-stream %edx) # eax = fn->name 8b/-> *(ebp+8) 0/r32/eax 8b/-> *eax 0/r32/eax # Function-name # construct result using Next-block-index (and increment it) (write %edx "$") (write %edx %eax) (write %edx ":") (print-int32 %edx *Next-block-index) ff 0/subop/increment *Next-block-index # var s/eax: slice = {name->data, name->data + name->write} (clobbering edx) # . eax = name->write 8b/-> *edx 0/r32/eax # . edx = name->data 8d/copy-address *(edx+0xc) 2/r32/edx # . eax = name->write + name->data 01/add %eax 2/r32/edx # . push {edx, eax} ff 6/subop/push %eax ff 6/subop/push %edx 89/<- %eax 4/r32/esp # var final-name/edx: (addr array byte) = slice-to-string(s) (slice-to-string Heap %eax) # => eax 89/<- %edx 0/r32/eax # set result->var # . var type/eax: (handle tree type-id) = literal (allocate Heap *Tree-size) # => eax (zero-out %eax *Tree-size) # default type is 'literal' # . var result/eax: (handle var) = new-var(final-name, type) (new-var Heap %edx %eax *Curr-block-depth 0 0) # => eax $new-block-name:end: # . reclaim locals 81 0/subop/add %ecx 0xc/imm32 # name.{read/write/len} 81 0/subop/add %ecx 8/imm32 # slice 01/add %esp 1/r32/ecx # . restore registers 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return check-no-tokens-left: # line: (addr stream byte) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # var s/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # (next-mu-token *(ebp+8) %ecx) # if slice-empty?(s) return (slice-empty? %ecx) 3d/compare-eax-and 0/imm32/false 75/jump-if-!= $check-no-tokens-left:end/disp8 # if (slice-starts-with?(s, '#') return # . eax = *s->start 8b/-> *edx 0/r32/eax 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # . if (eax == '#') continue 3d/compare-eax-and 0x23/imm32/hash 74/jump-if-= $check-no-tokens-left:end/disp8 # abort (write-buffered Stderr "'{' or '}' should be on its own line, but got '") (rewind-stream %ecx) (write-stream 2 %ecx) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $check-no-tokens-left:end: # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return parse-mu-named-block: # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt) # pseudocode: # var s: (addr array byte) = slice-to-string(name) # var v: (handle var) = new-var(s, 0) # v->block-depth = *Curr-block-depth # containing block depth # push(vars, v) # result = parse-mu-block(in, vars, fn) # pop(vars) # result->name = s # return result # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var s/ecx: (addr array byte) = slice-to-string(name) (slice-to-string Heap *(ebp+8)) # => eax 89/<- %ecx 0/r32/eax # var type/eax: (handle tree type-id) = literal (allocate Heap *Tree-size) # => eax (zero-out %eax *Tree-size) # default type is 'literal' # var v/ecx: (handle var) = new-var(s, type) (new-var Heap %ecx %eax *Curr-block-depth 0 0) # => eax 89/<- %ecx 0/r32/eax # push(vars, v) (push *(ebp+0x10) %ecx) # eax = result (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14)) # => eax # pop the var 50/push-eax (pop *(ebp+0x10)) # => eax 58/pop-to-eax # result->tag = named-block c7 0/subop/copy *eax 0/imm32/block # Stmt-tag # result->var = v 89/<- *(eax+8) 1/r32/ecx # Block-var $parse-mu-named-block:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return parse-mu-var-def: # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx # var word-slice/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # var v/edx: (handle var) = parse-var-with-type(line) (next-mu-token *(ebp+8) %ecx) (parse-var-with-type %ecx *(ebp+8)) # => eax 89/<- %edx 0/r32/eax # v->block-depth = *Curr-block-depth 8b/-> *Curr-block-depth 0/r32/eax 89/<- *(edx+8) 0/r32/eax # (push *(ebp+0xc) %edx) # either v has no register and there's no more to this line 8b/-> *(edx+0x10) 0/r32/eax # Var-register 3d/compare-eax-and 0/imm32 { 75/jump-if-!= break/disp8 # v->stack-offset = *Next-local-stack-offset 8b/-> *Next-local-stack-offset 0/r32/eax 89/<- *(edx+0xc) 0/r32/eax # Var-stack-offset # TODO: ensure that there's nothing else on this line (new-vardef Heap %edx) # => eax eb/jump $parse-mu-var-def:end/disp8 } # or v has a register and there's more to this line { 74/jump-if-= break/disp8 # ensure that the next word is '<-' (next-mu-token *(ebp+8) %ecx) (slice-equal? %ecx "<-") # => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= $parse-mu-var-def:abort/disp8 # (new-regvardef Heap %edx) # => eax (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc)) } $parse-mu-var-def:end: # *Next-local-stack-offset -= size-of(v) 50/push-eax (size-of %edx) # => eax 29/subtract-from *Next-local-stack-offset 0/r32/eax 58/pop-to-eax # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $parse-mu-var-def:abort: (rewind-stream *(ebp+8)) # error("register variable requires a valid instruction to initialize but got '" line "'\n") (write-buffered Stderr "register variable requires a valid instruction to initialize but got '") (flush Stderr) (write-stream 2 *(ebp+8)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here test-parse-mu-var-def: # 'var n: int' # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "n: int\n") # caller has consumed the 'var' c7 0/subop/copy *Curr-block-depth 1/imm32 c7 0/subop/copy *Next-local-stack-offset -4/imm32 # var vars/ecx: (stack (addr var) 4) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ecx 4/r32/esp (clear-stack %ecx) # convert (parse-mu-var-def _test-input-stream %ecx) # => eax # check result (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag") # Stmt-tag is vardef 8b/-> *(eax+4) 0/r32/eax # Vardef-var (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name") # Var-name (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register") # Var-register (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth") # Var-block-depth (check-ints-equal *(eax+0xc) -4 "F - test-parse-mu-reg-var-def/output-stack-offset") # Var-stack-offset # ensure type is int 8b/-> *(eax+4) 0/r32/eax # Var-type (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0") # Tree-left (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0") # Tree-right # globals (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-mu-reg-var-def: # 'var n/eax: int <- copy 0' # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "n/eax: int <- copy 0\n") # caller has consumed the 'var' c7 0/subop/copy *Curr-block-depth 1/imm32 c7 0/subop/copy *Next-local-stack-offset -4/imm32 # var vars/ecx: (stack (addr var) 4) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ecx 4/r32/esp (clear-stack %ecx) # convert (parse-mu-var-def _test-input-stream %ecx) # => eax # check result (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag") # Stmt-tag is regvardef 8b/-> *(eax+0xc) 0/r32/eax # Regvardef-outputs (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output") # List-next 8b/-> *eax 0/r32/eax # Stmt-var-value (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name") # Var-name (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register") # Var-register (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth") # Var-block-depth (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-stack-offset") # Var-stack-offset # ensure type is int 8b/-> *(eax+4) 0/r32/eax # Var-type (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0") # Tree-left (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0") # Tree-right # globals (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return parse-mu-stmt: # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt) # pseudocode: # var name: slice # result = allocate(Heap, Stmt-size) # if stmt-has-outputs?(line) # while true # name = next-mu-token(line) # if (name == '<-') break # assert(is-identifier?(name)) # var v: (handle var) = lookup-or-define-var(name, vars, fn) # regular stmts may define vars in fn outputs # result->outputs = append(result->outputs, v) # add-operation-and-inputs-to-stmt(result, line, vars) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx 57/push-edi # var name/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # var is-deref?/edx: boolean = false ba/copy-to-edx 0/imm32/false # result/edi: (handle stmt) (allocate Heap *Stmt-size) # => eax (zero-out %eax *Stmt-size) 89/<- %edi 0/r32/eax # result->tag = 1/stmt c7 0/subop/copy *edi 1/imm32/stmt1 # Stmt-tag { (stmt-has-outputs? *(ebp+8)) 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= break/disp32 { $parse-mu-stmt:read-outputs: # name = next-mu-token(line) (next-mu-token *(ebp+8) %ecx) # if slice-empty?(word-slice) break (slice-empty? %ecx) # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # if (name == "<-") break (slice-equal? %ecx "<-") # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # is-deref? = false ba/copy-to-edx 0/imm32/false # if (slice-starts-with?(name, '*')) ++name->start and set is-deref? 8b/-> *ecx 0/r32/eax # Slice-start 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 3d/compare-eax-and 0x2a/imm32/asterisk { 75/jump-if-!= break/disp8 ff 0/subop/increment *ecx ba/copy-to-edx 1/imm32/true } # assert(is-identifier?(name)) (is-identifier? %ecx) # => eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= $parse-mu-stmt:abort/disp32 # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?) (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10)) # => eax (append-stmt-var Heap %eax *(edi+0xc) %edx) # Stmt1-outputs => eax 89/<- *(edi+0xc) 0/r32/eax # Stmt1-outputs e9/jump loop/disp32 } } (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc)) $parse-mu-stmt:end: # return result 89/<- %eax 7/r32/edi # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 5f/pop-to-edi 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $parse-mu-stmt:abort: # error("invalid identifier '" name "'\n") (write-buffered Stderr "invalid identifier '") (write-slice-buffered Stderr %ecx) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here add-operation-and-inputs-to-stmt: # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var)) # pseudocode: # stmt->name = slice-to-string(next-mu-token(line)) # while true # name = next-mu-token(line) # v = lookup-var-or-literal(name) # stmt->inouts = append(stmt->inouts, v) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 57/push-edi # edi = stmt 8b/-> *(ebp+8) 7/r32/edi # var name/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # var is-deref?/edx: boolean = false ba/copy-to-edx 0/imm32/false $add-operation-and-inputs-to-stmt:read-operation: (next-mu-token *(ebp+0xc) %ecx) (slice-to-string Heap %ecx) # => eax 89/<- *(edi+4) 0/r32/eax # Stmt1-operation or Regvardef-operation { $add-operation-and-inputs-to-stmt:read-inouts: # name = next-mu-token(line) (next-mu-token *(ebp+0xc) %ecx) # if slice-empty?(word-slice) break (slice-empty? %ecx) # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # if (name == "<-") abort (slice-equal? %ecx "<-") 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32 # is-deref? = false ba/copy-to-edx 0/imm32/false # if (slice-starts-with?(name, '*')) ++name->start and set is-deref? 8b/-> *ecx 0/r32/eax # Slice-start 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 3d/compare-eax-and 0x2a/imm32/asterisk { 75/jump-if-!= break/disp8 ff 0/subop/increment *ecx ba/copy-to-edx 1/imm32/true } (lookup-var-or-literal %ecx *(ebp+0x10)) # => eax (append-list Heap %eax *(edi+8)) # Stmt1-inouts or Regvardef-inouts => eax 89/<- *(edi+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts e9/jump loop/disp32 } $add-operation-and-inputs-to-stmt:end: # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 5f/pop-to-edi 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $add-operation-and-inputs-to-stmt:abort: # error("invalid statement '" line "'\n") (rewind-stream *(ebp+8)) (write-buffered Stderr "invalid identifier '") (flush Stderr) (write-stream 2 *(ebp+8)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here stmt-has-outputs?: # line: (addr stream byte) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var word-slice/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # result = false b8/copy-to-eax 0/imm32/false (rewind-stream *(ebp+8)) { (next-mu-token *(ebp+8) %ecx) # if slice-empty?(word-slice) break (slice-empty? %ecx) 3d/compare-eax-and 0/imm32/false b8/copy-to-eax 0/imm32/false/result # restore result (if we're here it's still false) 0f 85/jump-if-!= break/disp32 # if slice-starts-with?(word-slice, '#') break # . eax = *word-slice->start 8b/-> *ecx 0/r32/eax 8a/copy-byte *eax 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # . if (eax == '#') break 3d/compare-eax-and 0x23/imm32/hash b8/copy-to-eax 0/imm32/false/result # restore result (if we're here it's still false) 0f 84/jump-if-= break/disp32 # if slice-equal?(word-slice, '<-') return true (slice-equal? %ecx "<-") 3d/compare-eax-and 0/imm32/false 74/jump-if-= loop/disp8 b8/copy-to-eax 1/imm32/true } $stmt-has-outputs:end: (rewind-stream *(ebp+8)) # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # if 'name' starts with a digit, create a new literal var for it # otherwise return first 'name' from the top (back) of 'vars' and abort if not found lookup-var-or-literal: # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 56/push-esi # esi = name 8b/-> *(ebp+8) 6/r32/esi # if slice-empty?(name) abort (slice-empty? %esi) # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32 # var c/ecx: byte = *name->start 8b/-> *esi 1/r32/ecx 8a/copy-byte *ecx 1/r32/CL 81 4/subop/and %ecx 0xff/imm32 # if is-decimal-digit?(c) return new var(name) { (is-decimal-digit? %ecx) # => eax 81 7/subop/compare %eax 0/imm32/false 74/jump-if-= break/disp8 (new-literal-integer Heap %esi) # => eax eb/jump $lookup-var-or-literal:end/disp8 } # else if (c == '"') return new var(name) { 81 7/subop/compare %ecx 0x22/imm32/dquote 75/jump-if-!= break/disp8 (new-literal-string Heap %esi) # => eax eb/jump $lookup-var-or-literal:end/disp8 } # otherwise return lookup-var(name, vars) { (lookup-var %esi *(ebp+0xc)) # => eax } $lookup-var-or-literal:end: # . restore registers 5e/pop-to-esi 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $lookup-var-or-literal:abort: (write-buffered Stderr "empty variable!") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here # return first 'name' from the top (back) of 'vars' and abort if not found lookup-var: # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # var target/eax: (handle array byte) = slice-to-string(name) (slice-to-string Heap *(ebp+8)) # => eax # (lookup-var-helper %eax *(ebp+0xc)) # => eax # if (result == 0) abort 3d/compare-eax-and 0/imm32 74/jump-if-= $lookup-var:abort/disp8 $lookup-var:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $lookup-var:abort: (write-buffered Stderr "unknown variable '") (write-slice-buffered Stderr *(ebp+8)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here # return first 'name' from the top (back) of 'vars', and 0/null if not found lookup-var-helper: # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var) # pseudocode: # var curr: (addr handle var) = &vars->data[vars->top - 4] # var min = vars->data # while curr >= min # var v: (handle var) = *curr # if v->name == name # return v # return 0 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 52/push-edx 53/push-ebx 56/push-esi # esi = vars 8b/-> *(ebp+0xc) 6/r32/esi # ebx = vars->top 8b/-> *esi 3/r32/ebx # if (vars->top > vars->length) abort 3b/compare 0/r32/eax *(esi+4) 0f 8f/jump-if-> $lookup-var-helper:error1/disp32 # var min/edx: (addr handle var) = vars->data 8d/copy-address *(esi+8) 2/r32/edx # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4] 81 5/subop/subtract %ebx 4/imm32 8d/copy-address *(esi+ebx+8) 3/r32/ebx { # if (curr < min) return 0 39/compare %ebx 2/r32/edx b8/copy-to-eax 0/imm32 0f 82/jump-if-addr< break/disp32 # var v/eax: (handle var) = *curr 8b/-> *ebx 0/r32/eax # if (v->name == name) return v (string-equal? *eax *(ebp+8)) # Var-name 3d/compare-eax-and 0/imm32/false 8b/-> *ebx 0/r32/eax 75/jump-if-!= break/disp8 # curr -= 4 81 5/subop/subtract %ebx 4/imm32 e9/jump loop/disp32 } $lookup-var-helper:end: # . restore registers 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $lookup-var-helper:error1: (write-buffered Stderr "malformed stack when looking up '") (write-slice-buffered Stderr *(ebp+8)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found lookup-or-define-var: # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var target/ecx: (handle array byte) = slice-to-string(name) (slice-to-string Heap *(ebp+8)) # => eax 89/<- %ecx 0/r32/eax # (lookup-var-helper %ecx *(ebp+0xc)) # => eax { # if (result != 0) return 3d/compare-eax-and 0/imm32 75/jump-if-!= break/disp8 # if name is one of fn's outputs, return it { (find-in-function-outputs *(ebp+0x10) %ecx) # => eax 3d/compare-eax-and 0/imm32 # otherwise abort 0f 84/jump-if-!= $lookup-var:abort/disp32 } } $lookup-or-define-var:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return find-in-function-outputs: # fn: (handle function), name: (handle array byte) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var curr/ecx: (handle list var) = fn->outputs 8b/-> *(ebp+8) 1/r32/ecx 8b/-> *(ecx+0xc) 1/r32/ecx # while curr != null { 81 7/subop/compare %ecx 0/imm32 74/jump-if-= break/disp8 # var v: (handle var) = *curr 8b/-> *ecx 0/r32/eax # List-value # if (curr->name == name) return curr 50/push-eax (string-equal? *eax *(ebp+0xc)) 3d/compare-eax-and 0/imm32/false 58/pop-to-eax 75/jump-if-!= $find-in-function-outputs:end/disp8 # curr = curr->next 8b/-> *(ecx+4) 1/r32/ecx # List-next eb/jump loop/disp8 } b8/copy-to-eax 0/imm32 $find-in-function-outputs:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-mu-stmt: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "increment n\n") # var vars/ecx: (stack (addr var) 4) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ecx 4/r32/esp (clear-stack %ecx) # var v/edx: var 81 5/subop/subtract %esp 0x14/imm32 # Var-size 89/<- %edx 4/r32/esp (zero-out %edx 0x14) # Var-size # v->name = "n" c7 0/subop/copy *edx "n"/imm32 # Var-name # (push %ecx %edx) # convert (parse-mu-stmt _test-input-stream %ecx) # => eax # check result (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag") # Stmt-tag is Stmt1 (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name") # Stmt1-operation # edx: (handle list var) = result->inouts 8b/-> *(eax+8) 2/r32/edx # Stmt1-inouts # ebx: (handle var) = result->inouts->value 8b/-> *edx 3/r32/ebx # Stmt-var-value (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0") # Var-name # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-parse-mu-stmt-with-comma: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-input-stream) (write _test-input-stream "copy-to n, 3\n") # var vars/ecx: (stack (addr var) 4) 81 5/subop/subtract %esp 0x10/imm32 68/push 0x10/imm32/length 68/push 0/imm32/top 89/<- %ecx 4/r32/esp (clear-stack %ecx) # var v/edx: var 81 5/subop/subtract %esp 0x14/imm32 # Var-size 89/<- %edx 4/r32/esp (zero-out %edx 0x14) # Var-size # v->name = "n" c7 0/subop/copy *edx "n"/imm32 # Var-name # (push %ecx %edx) # convert (parse-mu-stmt _test-input-stream %ecx) # => eax # check result (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag") # Stmt-tag is Stmt1 (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name") # Stmt1-operation # edx: (handle list var) = result->inouts 8b/-> *(eax+8) 2/r32/edx # Stmt1-inouts # ebx: (handle var) = result->inouts->value 8b/-> *edx 3/r32/ebx # Stmt-var-value (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0") # Var-name # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-function: # ad: (addr allocation-descriptor), name: (addr array byte), subx-name: (addr array byte), inouts: (handle list var), outputs: (handle list var), body: (handle block), next: (handle function) -> result/eax: (handle function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (allocate *(ebp+8) *Function-size) # => eax 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *eax 1/r32/ecx # Function-name 8b/-> *(ebp+0x10) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Function-subx-name 8b/-> *(ebp+0x14) 1/r32/ecx 89/<- *(eax+8) 1/r32/ecx # Function-inouts 8b/-> *(ebp+0x18) 1/r32/ecx 89/<- *(eax+0xc) 1/r32/ecx # Function-outputs 8b/-> *(ebp+0x1c) 1/r32/ecx 89/<- *(eax+0x10) 1/r32/ecx # Function-body 8b/-> *(ebp+0x20) 1/r32/ecx 89/<- *(eax+0x14) 1/r32/ecx # Function-next $new-function:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-var: # ad: (addr allocation-descriptor), name: (addr array byte), type: (addr tree type-id), block: int, stack-offset: int, register: (addr array byte) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (allocate *(ebp+8) *Var-size) # => eax 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *eax 1/r32/ecx # Var-name 8b/-> *(ebp+0x10) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Var-type 8b/-> *(ebp+0x14) 1/r32/ecx 89/<- *(eax+8) 1/r32/ecx # Var-block-depth 8b/-> *(ebp+0x18) 1/r32/ecx 89/<- *(eax+0xc) 1/r32/ecx # Var-stack-offset 8b/-> *(ebp+0x1c) 1/r32/ecx 89/<- *(eax+0x10) 1/r32/ecx # Var-register $new-var:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-literal-integer: # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # if (!is-hex-int?(name)) abort (is-hex-int? *(ebp+0xc)) # => eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= $new-literal-integer:abort/disp32 # var s/ecx: (addr array byte) (slice-to-string Heap *(ebp+0xc)) # => eax 89/<- %ecx 0/r32/eax # result/ecx = new var(s) (allocate *(ebp+8) *Var-size) # => eax (zero-out %eax *Var-size) 89/<- *eax 1/r32/ecx # Var-name 89/<- %ecx 0/r32/eax # result->type = new type() (allocate *(ebp+8) *Tree-size) # => eax (zero-out %eax *Tree-size) # default type is 'literal' 89/<- *(ecx+4) 0/r32/eax # Var-type # move result to eax 89/<- %eax 1/r32/ecx $new-literal-integer:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $new-literal-integer:abort: (write-buffered Stderr "variable cannot begin with a digit '") (write-slice-buffered Stderr *(ebp+0xc)) (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here new-literal-string: # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var s/ecx: (addr array byte) (slice-to-string Heap *(ebp+0xc)) # => eax 89/<- %ecx 0/r32/eax # result/ecx = new var(s) (allocate *(ebp+8) *Var-size) # => eax (zero-out %eax *Var-size) 89/<- *eax 1/r32/ecx # Var-name 89/<- %ecx 0/r32/eax # result->type = new type() (allocate *(ebp+8) *Tree-size) # => eax (zero-out %eax *Tree-size) # default type is 'literal' 89/<- *(ecx+4) 0/r32/eax # Var-type # move result to eax 89/<- %eax 1/r32/ecx $new-literal-string:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-label: # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var s/ecx: (addr array byte) (slice-to-string Heap *(ebp+0xc)) # => eax 89/<- %ecx 0/r32/eax # (allocate *(ebp+8) *Var-size) # => eax 89/<- *eax 1/r32/ecx # Var-name 89/<- %ecx 0/r32/eax (allocate *(ebp+8) *Tree-size) # => eax (zero-out %eax *Tree-size) # labels are literals 89/<- *(ecx+4) 0/r32/eax # Var-type 89/<- %eax 1/r32/ecx c7 0/subop/copy *(eax+8) 0/imm32 # Var-block-depth c7 0/subop/copy *(eax+0xc) 0/imm32 # Var-stack-offset c7 0/subop/copy *(eax+0x10) 0/imm32 # Var-register $new-label:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-block: # ad: (addr allocation-descriptor), data: (handle list statement) -> result/eax: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 0/imm32/tag/block # Stmt-tag 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Block-statements $new-block:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-vardef: # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 2/imm32/tag/var-on-stack # Stmt-tag # result->var = var 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Vardef-var $new-vardef:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return new-regvardef: # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 57/push-edi # ecx = var 8b/-> *(ebp+0xc) 1/r32/ecx # edi = result (allocate *(ebp+8) *Stmt-size) # => eax 89/<- %edi 0/r32/eax (zero-out %edi *Stmt-size) # set tag c7 0/subop/copy *edi 3/imm32/tag/var-in-register # Stmt-tag # set output (append-stmt-var Heap %ecx *(edi+0xc) 0) # Regvardef-outputs => eax 89/<- *(edi+0xc) 0/r32/eax # Regvardef-outputs $new-regvardef:end: 89/<- %eax 7/r32/edi # . restore registers 5f/pop-to-edi 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return append-list: # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (allocate *(ebp+8) *List-size) # => eax (zero-out %eax *List-size) 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *eax 1/r32/ecx # List-value # if (list == null) return result 81 7/subop/compare *(ebp+0x10) 0/imm32 74/jump-if-= $append-list:end/disp8 # otherwise append # var curr/ecx = list 8b/-> *(ebp+0x10) 1/r32/ecx # while (curr->next != null) curr = curr->next { 81 7/subop/compare *(ecx+4) 0/imm32 # List-next 74/jump-if-= break/disp8 # curr = curr->next 8b/-> *(ecx+4) 1/r32/ecx eb/jump loop/disp8 } # curr->next = result 89/<- *(ecx+4) 0/r32/eax # return list 8b/-> *(ebp+0x10) 0/r32/eax $append-list:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return append-stmt-var: # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (allocate *(ebp+8) *Stmt-var-size) # => eax (zero-out %eax *Stmt-var-size) 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *eax 1/r32/ecx # Stmt-var-value 8b/-> *(ebp+0x14) 1/r32/ecx 89/<- *(eax+8) 1/r32/ecx # Stmt-var-is-deref # if (list == null) return result 81 7/subop/compare *(ebp+0x10) 0/imm32 74/jump-if-= $append-stmt-var:end/disp8 # otherwise append # var curr/ecx: (handle stmt-var) = vars 8b/-> *(ebp+0x10) 1/r32/ecx # while (curr->next != null) curr = curr->next { 81 7/subop/compare *(ecx+4) 0/imm32 # List-next 74/jump-if-= break/disp8 # curr = curr->next 8b/-> *(ecx+4) 1/r32/ecx eb/jump loop/disp8 } # curr->next = result 89/<- *(ecx+4) 0/r32/eax # return vars 8b/-> *(ebp+0x10) 0/r32/eax $append-stmt-var:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return append-to-block: # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 56/push-esi # esi = block 8b/-> *(ebp+0xc) 6/r32/esi (append-list *(ebp+8) *(ebp+0x10) *(esi+4)) # ad, x, Block-statements 89/<- *(esi+4) 0/r32/eax # Block-statements $append-to-block:end: # . restore registers 5e/pop-to-esi # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return ####################################################### # Type-checking ####################################################### check-mu-types: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # $check-mu-types:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return size-of: # v: (addr var) -> result/eax: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # if v is a literal, return 0 8b/-> *(ebp+8) 0/r32/eax 8b/-> *(eax+4) 0/r32/eax # Var-type 81 7/subop/compare *eax 0/imm32 # Tree-left b8/copy-to-eax 0/imm32 74/jump-if-= $size-of:end/disp8 # hard-coded since we only support 'int' types for now b8/copy-to-eax 4/imm32 $size-of:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return type-equal?: # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx # ecx = a 8b/-> *(ebp+8) 1/r32/ecx # edx = b 8b/-> *(ebp+0xc) 2/r32/edx # if (a == b) return true 8b/-> %ecx 0/r32/eax # Var-type 39/compare %edx 0/r32/eax # Var-type b8/copy-to-eax 1/imm32/true 74/jump-if-= $type-equal?:end/disp8 # if (a < MAX_TYPE_ID) return false 81 7/subop/compare %ecx 0x10000/imm32 b8/copy-to-eax 0/imm32/false 72/jump-if-addr< $type-equal?:end/disp8 # if (b < MAX_TYPE_ID) return false 81 7/subop/compare %edx 0x10000/imm32 b8/copy-to-eax 0/imm32/false 72/jump-if-addr< $type-equal?:end/disp8 # if (!type-equal?(a->left, b->left)) return false (type-equal? *ecx *edx) # Tree-left, Tree-left => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= $type-equal?:end/disp8 # return type-equal?(a->right, b->right) (type-equal? *(ecx+4) *(edx+4)) # Tree-right, Tree-right => eax $type-equal?:end: # . restore registers 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return == data # not yet used, but it will be Type-size: # (stream int) 0x18/imm32/write 0/imm32/read 0x100/imm32/length # data 4/imm32 # literal 4/imm32 # int 4/imm32 # addr 0/imm32 # array (logic elsewhere) 8/imm32 # handle (fat pointer) 4/imm32 # bool 0/imm32 0/imm32 # 0x20 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 == code ####################################################### # Code-generation ####################################################### emit-subx: # out: (addr buffered-file) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 57/push-edi # edi = out 8b/-> *(ebp+8) 7/r32/edi # var curr/ecx: (handle function) = *Program 8b/-> *Program 1/r32/ecx { # if (curr == null) break 81 7/subop/compare %ecx 0/imm32 0f 84/jump-if-= break/disp32 (emit-subx-function %edi %ecx) # curr = curr->next 8b/-> *(ecx+0x14) 1/r32/ecx # Function-next e9/jump loop/disp32 } $emit-subx:end: # . restore registers 5f/pop-to-edi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-function: # out: (addr buffered-file), f: (handle function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 57/push-edi # edi = out 8b/-> *(ebp+8) 7/r32/edi # ecx = f 8b/-> *(ebp+0xc) 1/r32/ecx # var vars/edx: (stack (addr var) 256) 81 5/subop/subtract %esp 0x400/imm32 68/push 0x400/imm32/length 68/push 0/imm32/top 89/<- %edx 4/r32/esp # (write-buffered %edi *ecx) (write-buffered %edi ":\n") # Important: each block's depth during code-generation should be identical # to what it was during parsing. c7 0/subop/copy *Curr-block-depth 1/imm32 (emit-subx-prologue %edi) (emit-subx-block %edi *(ecx+0x10) %edx) # Function-body (emit-subx-epilogue %edi) $emit-subx-function:end: # . reclaim locals 81 0/subop/add %esp 408/imm32 # . restore registers 5f/pop-to-edi 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-stmt-list: # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var)) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi # esi = stmts 8b/-> *(ebp+0xc) 6/r32/esi # var var-seen?/edx: boolean <- copy false ba/copy-to-edx 0/imm32/false # { $emit-subx-stmt-list:loop: 81 7/subop/compare %esi 0/imm32 0f 84/jump-if-= break/disp32 # var curr-stmt/ecx = stmts->value 8b/-> *esi 1/r32/ecx # List-value { $emit-subx-stmt-list:check-for-block: 81 7/subop/compare *ecx 0/imm32/block # Stmt-tag 75/jump-if-!= break/disp8 $emit-subx-stmt-list:block: (emit-subx-block *(ebp+8) %ecx *(ebp+0x10)) } { $emit-subx-stmt-list:check-for-stmt: 81 7/subop/compare *ecx 1/imm32/stmt1 # Stmt-tag 0f 85/jump-if-!= break/disp32 $emit-subx-stmt-list:stmt1: { (is-mu-branch? %ecx) # => eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= break/disp32 $emit-subx-stmt-list:branch-stmt: # if !var-seen? break 81 7/subop/compare %edx 0/imm32/false 0f 84/jump-if-= break/disp32 $emit-subx-stmt-list:branch-stmt-and-var-seen: # unconditional loops {{{ { # if (!string-equal?(var->operation, "loop")) break (string-equal? *(ecx+4) "loop") # Stmt1-operation => eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= break/disp32 $emit-subx-stmt-list:unconditional-loop: 81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts # simple unconditional loops without a target { 0f 85/jump-if-!= break/disp32 $emit-subx-stmt-list:zero-arg-unconditional-loop: (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth) (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "e9/jump loop/disp32") (write-buffered *(ebp+8) Newline) e9/jump $emit-subx-stmt-list:cleanup/disp32 # skip remaining statements; they're dead code } # unconditional loops with a target { 0f 84/jump-if-= break/disp32 (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10)) e9/jump $emit-subx-stmt-list:cleanup/disp32 } } # }}} # unconditional breaks {{{ { # if (!string-equal?(curr-stmt->operation, "break")) break (string-equal? *(ecx+4) "break") # Stmt1-operation => eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= break/disp32 $emit-subx-stmt-list:unconditional-break: 81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts # simple unconditional breaks without a target 0f 84/jump-if-= $emit-subx-stmt-list:emit-cleanup/disp32 # easy: just skip remaining statements # unconditional breaks with a target (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10)) e9/jump $emit-subx-stmt-list:cleanup/disp32 } # }}} # simple conditional branches without a target {{{ 81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts { 0f 85/jump-if-!= break/disp32 $emit-subx-stmt-list:zero-arg-conditional-branch: # var old-block-depth/eax: int = Curr-block-depth - 1 8b/-> *Curr-block-depth 3/r32/ebx # cleanup prologue (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "{\n") ff 0/subop/increment *Curr-block-depth # (emit-reverse-break *(ebp+8) %ecx) # clean up until old block depth (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) %ebx) # var target-block-depth/ebx: int = Curr-block-depth - 1 4b/decrement-ebx # emit jump to target block (string-starts-with? *(ecx+4) "break") 3d/compare-eax-and 0/imm32/false { 74/jump-if-= break/disp8 (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "break") } 3d/compare-eax-and 0/imm32/false # just in case the function call modified flags { 75/jump-if-!= break/disp8 (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "loop") } # cleanup epilogue ff 1/subop/decrement *Curr-block-depth (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "}\n") # continue e9/jump $emit-subx-stmt-list:continue/disp32 } # }}} # conditional branches with an explicit target {{{ { 0f 84/jump-if-= break/disp32 $emit-subx-stmt-list:conditional-branch-with-target: # cleanup prologue (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "{\n") ff 0/subop/increment *Curr-block-depth # (emit-reverse-break *(ebp+8) %ecx) (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10)) # cleanup epilogue ff 1/subop/decrement *Curr-block-depth (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "}\n") # continue e9/jump $emit-subx-stmt-list:continue/disp32 } # }}} } $emit-subx-stmt-list:1-to-1: (emit-subx-statement *(ebp+8) %ecx Primitives *Program) } { $emit-subx-stmt-list:check-for-vardef: 81 7/subop/compare *ecx 2/imm32/vardef # Stmt-tag 75/jump-if-!= break/disp8 $emit-subx-stmt-list:vardef: (emit-subx-var-def *(ebp+8) %ecx) (push *(ebp+0x10) *(ecx+4)) # Vardef-var # var-seen? = true ba/copy-to-edx 1/imm32/true } { $emit-subx-stmt-list:check-for-regvardef: 81 7/subop/compare *ecx 3/imm32/regvardef # Stmt-tag 0f 85/jump-if-!= break/disp32 $emit-subx-stmt-list:regvardef: # TODO: ensure that there's exactly one output # var output/eax: (handle var) = curr-stmt->outputs->value 8b/-> *(ecx+0xc) 0/r32/eax 8b/-> *eax 0/r32/eax # ensure that output is in a register 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register 0f 84/jump-if-= $emit-subx-stmt-list:abort-regvardef-without-register/disp32 # emit spill (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "ff 6/subop/push %") (write-buffered *(ebp+8) *(eax+0x10)) (write-buffered *(ebp+8) Newline) # register variable definition (push *(ebp+0x10) %eax) # emit the instruction as usual (emit-subx-statement *(ebp+8) %ecx Primitives *Program) # var-seen? = true ba/copy-to-edx 1/imm32/true } $emit-subx-stmt-list:continue: # TODO: raise an error on unrecognized Stmt-tag 8b/-> *(esi+4) 6/r32/esi # List-next e9/jump loop/disp32 } $emit-subx-stmt-list:emit-cleanup: (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth) $emit-subx-stmt-list:cleanup: (clean-up-blocks *(ebp+0x10) *Curr-block-depth) $emit-subx-stmt-list:end: # . restore registers 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $emit-subx-stmt-list:abort-regvardef-without-register: # error("var '" var->name "' initialized from an instruction must live in a register\n") (write-buffered Stderr "var '") (write-buffered Stderr *eax) # Var-name (write-buffered Stderr "' initialized from an instruction must live in a register\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here emit-subx-cleanup-and-unconditional-nonlocal-branch: # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var)) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx # ecx = stmt 8b/-> *(ebp+0xc) 1/r32/ecx # var target/edx: (addr array byte) = curr-stmt->inouts->value->name 8b/-> *(ecx+8) 2/r32/edx # Stmt1-inouts 8b/-> *edx 2/r32/edx # Stmt-var-value 8b/-> *edx 2/r32/edx # Var-name # clean up until target block (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx) # emit jump to target block (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "e9/jump ") (write-buffered *(ebp+8) %edx) (string-starts-with? *(ecx+4) "break") 3d/compare-eax-and 0/imm32/false { 74/jump-if-= break/disp8 (write-buffered *(ebp+8) ":break/disp32\n") } 3d/compare-eax-and 0/imm32/false # just in case the function call modified flags { 75/jump-if-!= break/disp8 (write-buffered *(ebp+8) ":loop/disp32\n") } $emit-subx-cleanup-and-unconditional-nonlocal-branch:end: # . restore registers 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return is-mu-branch?: # stmt: (addr stmt1) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # ecx = stmt 8b/-> *(ebp+8) 1/r32/ecx # if (stmt->operation starts with "loop") return true (string-starts-with? *(ecx+4) "loop") # Stmt1-operation => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-not-equal $is-mu-branch?:end/disp8 # otherwise return (stmt->operation starts with "break") (string-starts-with? *(ecx+4) "break") # Stmt1-operation => eax $is-mu-branch?:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-reverse-break: # out: (addr buffered-file), stmt: (addr stmt1) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax # eax = stmt 8b/-> *(ebp+0xc) 0/r32/eax # (get Reverse-branch *(eax+4) 8 "reverse-branch: ") # Stmt1-operation => eax: (addr addr array byte) (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) *eax) (write-buffered *(ebp+8) " break/disp32\n") $emit-reverse-break:end: # . restore registers 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return == data Reverse-branch: # (table string string) # a table is a stream 0xa0/imm32/write 0/imm32/read 0xa0/imm32/length # data "break-if-="/imm32 "0f 85/jump-if-!="/imm32 "loop-if-="/imm32 "0f 85/jump-if-!="/imm32 "break-if-!="/imm32 "0f 84/jump-if-="/imm32 "loop-if-!="/imm32 "0f 84/jump-if-="/imm32 "break-if-<"/imm32 "0f 8d/jump-if->="/imm32 "loop-if-<"/imm32 "0f 8d/jump-if->="/imm32 "break-if->"/imm32 "0f 8e/jump-if-<="/imm32 "loop-if->"/imm32 "0f 8e/jump-if-<="/imm32 "break-if-<="/imm32 "0f 87/jump-if->"/imm32 "loop-if-<="/imm32 "0f 87/jump-if->"/imm32 "break-if->="/imm32 "0f 8c/jump-if-<"/imm32 "loop-if->="/imm32 "0f 8c/jump-if-<"/imm32 "break-if-addr<"/imm32 "0f 83/jump-if-addr>="/imm32 "loop-if-addr<"/imm32 "0f 83/jump-if-addr>="/imm32 "break-if-addr>"/imm32 "0f 86/jump-if-addr<="/imm32 "loop-if-addr>"/imm32 "0f 86/jump-if-addr<="/imm32 "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32 "loop-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32 "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32 "loop-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32 == code emit-unconditional-jump-to-depth: # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx # ecx = vars 8b/-> *(ebp+0xc) 1/r32/ecx # var eax: int = vars->top 8b/-> *ecx 0/r32/eax # var min/ecx: (address (handle var)) = vars->data 81 0/subop/add %ecx 8/imm32 # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4] 81 5/subop/subtract %eax 4/imm32 8d/copy-address *(ecx+eax) 0/r32/eax # edx = depth 8b/-> *(ebp+0x10) 2/r32/edx { $emit-unconditional-jump-to-depth:loop: # if (curr < min) break 39/compare %eax 1/r32/ecx 0f 82/jump-if-addr< break/disp32 # var v/ebx: (handle var) = *curr 8b/-> *eax 3/r32/ebx # if (v->block-depth < until-block-depth) break 39/compare *(ebx+8) 2/r32/edx # Var-block-depth 0f 8c/jump-if-< break/disp32 { $emit-unconditional-jump-to-depth:check: # if v->block-depth != until-block-depth, continue 39/compare *(ebx+8) 2/r32/edx # Var-block-depth 0f 85/jump-if-!= break/disp32 $emit-unconditional-jump-to-depth:depth-found: # if v is not a literal, continue # . var eax: int = size-of(v) 50/push-eax (size-of %ebx) # => eax # . if (eax != 0) continue 3d/compare-eax-and 0/imm32 58/pop-to-eax # 0f 85/jump-if-!= break/disp32 $emit-unconditional-jump-to-depth:label-found: # emit unconditional jump, then return (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "e9/jump ") (write-buffered *(ebp+8) *ebx) # Var-name (write-buffered *(ebp+8) ":") (write-buffered *(ebp+8) *(ebp+0x14)) (write-buffered *(ebp+8) "/disp32\n") eb/jump $emit-unconditional-jump-to-depth:end/disp8 } # curr -= 4 2d/subtract-from-eax 4/imm32 e9/jump loop/disp32 } # TODO: error if no label at 'depth' was found $emit-unconditional-jump-to-depth:end: # . restore registers 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # emit clean-up code for 'vars' until some block depth # doesn't actually modify 'vars' so we need traverse manually inside the stack emit-cleanup-code-until-depth: # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx # ecx = vars 8b/-> *(ebp+0xc) 1/r32/ecx # var eax: int = vars->top 8b/-> *ecx 0/r32/eax # var min/ecx: (address (handle var)) = vars->data 81 0/subop/add %ecx 8/imm32 # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4] 81 5/subop/subtract %eax 4/imm32 8d/copy-address *(ecx+eax) 0/r32/eax # edx = until-block-depth 8b/-> *(ebp+0x10) 2/r32/edx { $emit-cleanup-code-until-depth:loop: # if (curr < min) break 39/compare %eax 1/r32/ecx 0f 82/jump-if-addr< break/disp32 # var v/ebx: (handle var) = *curr 8b/-> *eax 3/r32/ebx # if (v->block-depth < until-block-depth) break 39/compare *(ebx+8) 2/r32/edx # Var-block-depth 0f 8c/jump-if-< break/disp32 # if v is in a register 81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register { 74/jump-if-= break/disp8 $emit-cleanup-code-until-depth:reclaim-var-in-register: (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "8f 0/subop/pop %") (write-buffered *(ebp+8) *(ebx+0x10)) (write-buffered *(ebp+8) Newline) } # otherwise v is on the stack { 75/jump-if-!= break/disp8 $emit-cleanup-code-until-depth:reclaim-var-on-stack: 50/push-eax (size-of %ebx) # => eax # don't emit code for labels 3d/compare-eax-and 0/imm32 74/jump-if-= break/disp8 # (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "81 0/subop/add %esp ") (print-int32-buffered *(ebp+8) %eax) (write-buffered *(ebp+8) "/imm32\n") 58/pop-to-eax } # curr -= 4 2d/subtract-from-eax 4/imm32 e9/jump loop/disp32 } $emit-cleanup-code-until-depth:end: # . restore registers 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # emit clean-up code for 'vars' until a given label is encountered # doesn't actually modify 'vars' so we need traverse manually inside the stack emit-cleanup-code-until-target: # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx # ecx = vars 8b/-> *(ebp+0xc) 1/r32/ecx # var eax: int = vars->top 8b/-> *ecx 0/r32/eax # var min/ecx: (address (handle var)) = vars->data 81 0/subop/add %ecx 8/imm32 # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4] 81 5/subop/subtract %eax 4/imm32 8d/copy-address *(ecx+eax) 2/r32/edx { $emit-cleanup-code-until-target:loop: # if (curr < min) break 39/compare %edx 1/r32/ecx 0f 82/jump-if-addr< break/disp32 # var v/ebx: (handle var) = *curr 8b/-> *edx 3/r32/ebx # if (v->name == until-block-label) break (string-equal? *ebx *(ebp+0x10)) # => eax 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= break/disp32 # if v is in a register 81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register { 74/jump-if-= break/disp8 $emit-cleanup-code-until-target:reclaim-var-in-register: (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "8f 0/subop/pop %") (write-buffered *(ebp+8) *(ebx+0x10)) (write-buffered *(ebp+8) Newline) } # otherwise v is on the stack { 75/jump-if-!= break/disp8 $emit-cleanup-code-until-target:reclaim-var-on-stack: (size-of %ebx) # => eax # don't emit code for labels 3d/compare-eax-and 0/imm32 74/jump-if-= break/disp8 # (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "81 0/subop/add %esp ") (print-int32-buffered *(ebp+8) %eax) (write-buffered *(ebp+8) "/imm32\n") } # curr -= 4 81 5/subop/subtract %edx 4/imm32 e9/jump loop/disp32 } $emit-cleanup-code-until-target:end: # . restore registers 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # clean up global state for 'vars' until some block depth clean-up-blocks: # vars: (addr stack (handle var)), until-block-depth: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi # esi = vars 8b/-> *(ebp+8) 6/r32/esi # ecx = until-block-depth 8b/-> *(ebp+0xc) 1/r32/ecx { $clean-up-blocks:reclaim-loop: # if (vars->top <= 0) break 81 7/subop/compare *esi 0/imm32 # Stack-top 7e/jump-if-<= break/disp8 # var v/eax: (handle var) = top(vars) (top %esi) # => eax # if (v->block-depth < until-block-depth) break 39/compare *(eax+8) 1/r32/ecx # Var-block-depth 7c/jump-if-< break/disp8 # if v is on the stack, update Next-local-stack-offset 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register { 75/jump-if-!= break/disp8 $clean-up-blocks:reclaim-var-on-stack: (size-of %eax) # => eax 01/add *Next-local-stack-offset 0/r32/eax } (pop %esi) e9/jump loop/disp32 } $clean-up-blocks:end: # . restore registers 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-var-def: # out: (addr buffered-file), stmt: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # eax = stmt 8b/-> *(ebp+0xc) 0/r32/eax # var n/eax: int = size-of(stmt->var) (size-of *(eax+4)) # Vardef-var => eax # while n > 0 { 3d/compare-eax-with 0/imm32 7e/jump-if-<= break/disp8 (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "68/push 0/imm32\n") # n -= 4 2d/subtract-from-eax 4/imm32 # eb/jump loop/disp8 } $emit-subx-var-def:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-statement: # out: (addr buffered-file), stmt: (handle statement), primitives: (handle primitive), functions: (handle function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # handle some special cases # ecx = stmt 8b/-> *(ebp+0xc) 1/r32/ecx # array length {{{ { # if (!string-equal?(stmt->operation, "length")) break (string-equal? *(ecx+4) "length") # Stmt1-operation => eax 3d/compare-eax-and 0/imm32 0f 84/jump-if-= break/disp32 $emit-subx-stmt-list:array-length: (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "8b/copy-from *") # inouts[0]->register 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts 8b/-> *eax 0/r32/eax # Stmt-var-value (write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax # (write-buffered *(ebp+8) " ") # outputs[0] "/r32" 8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs 8b/-> *eax 0/r32/eax # Stmt-var-value (get Registers *(eax+0x10) 8 "Registers") # Var-register => eax (print-int32-buffered *(ebp+8) *eax) (write-buffered *(ebp+8) "/r32\n") e9/jump $emit-subx-statement:end/disp32 } # }}} # if stmt matches a primitive, emit it { $emit-subx-statement:check-for-primitive: (find-matching-primitive *(ebp+0x10) *(ebp+0xc)) # primitives, stmt => curr/eax 3d/compare-eax-and 0/imm32 74/jump-if-= break/disp8 $emit-subx-statement:primitive: (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax) # out, stmt, curr e9/jump $emit-subx-statement:end/disp32 } # else if stmt matches a function, emit a call to it { $emit-subx-statement:check-for-call: (find-matching-function *(ebp+0x14) *(ebp+0xc)) # functions, stmt => curr/eax 3d/compare-eax-and 0/imm32 74/jump-if-= break/disp8 $emit-subx-statement:call: (emit-subx-call *(ebp+8) *(ebp+0xc) %eax) # out, stmt, curr e9/jump $emit-subx-statement:end/disp32 } # else assume it's a SubX function (TODO: how to type-check?!) (emit-hailmary-call *(ebp+8) *(ebp+0xc)) $emit-subx-statement:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $emit-subx-statement:abort: # error("couldn't translate '" stmt "'\n") (write-buffered Stderr "couldn't translate an instruction with operation '") 8b/-> *(ebp+0xc) 0/r32/eax (write-buffered Stderr *(eax+4)) # Stmt1-operation (write-buffered Stderr "'\n") (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here emit-subx-block: # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var)) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi # esi = block 8b/-> *(ebp+0xc) 6/r32/esi # var stmts/eax: (handle list statement) = block->statements 8b/-> *(esi+4) 0/r32/eax # Block-statements # { $emit-subx-block:check-empty: 81 7/subop/compare %eax 0/imm32 0f 84/jump-if-= break/disp32 (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "{\n") # var v/ecx: (addr array byte) = block->var->name 8b/-> *(esi+8) 1/r32/ecx # Block-var (write-buffered *(ebp+8) *ecx) # Var-name (write-buffered *(ebp+8) ":loop:\n") ff 0/subop/increment *Curr-block-depth (push *(ebp+0x10) %ecx) (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10)) (pop *(ebp+0x10)) # => eax ff 1/subop/decrement *Curr-block-depth (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "}\n") (write-buffered *(ebp+8) *ecx) # Var-name (write-buffered *(ebp+8) ":break:\n") } $emit-subx-block:end: # . restore registers 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # Primitives supported # For each operation, put variants with hard-coded registers before flexible ones. == data Primitives: # - increment/decrement _Primitive-inc-eax: # var/eax <- increment => 40/increment-eax "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-eax/imm32/outputs "40/increment-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-ecx/imm32/next _Primitive-inc-ecx: # var/ecx <- increment => 41/increment-ecx "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-ecx/imm32/outputs "41/increment-ecx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-edx/imm32/next _Primitive-inc-edx: # var/edx <- increment => 42/increment-edx "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-edx/imm32/outputs "42/increment-edx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-ebx/imm32/next _Primitive-inc-ebx: # var/ebx <- increment => 43/increment-ebx "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-ebx/imm32/outputs "43/increment-ebx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-esi/imm32/next _Primitive-inc-esi: # var/esi <- increment => 46/increment-esi "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-esi/imm32/outputs "46/increment-esi"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-edi/imm32/next _Primitive-inc-edi: # var/edi <- increment => 47/increment-edi "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-edi/imm32/outputs "47/increment-edi"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-eax/imm32/next _Primitive-dec-eax: # var/eax <- decrement => 48/decrement-eax "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-eax/imm32/outputs "48/decrement-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-ecx/imm32/next _Primitive-dec-ecx: # var/ecx <- decrement => 49/decrement-ecx "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-ecx/imm32/outputs "49/decrement-ecx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-edx/imm32/next _Primitive-dec-edx: # var/edx <- decrement => 4a/decrement-edx "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-edx/imm32/outputs "4a/decrement-edx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-ebx/imm32/next _Primitive-dec-ebx: # var/ebx <- decrement => 4b/decrement-ebx "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-ebx/imm32/outputs "4b/decrement-ebx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-esi/imm32/next _Primitive-dec-esi: # var/esi <- decrement => 4e/decrement-esi "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-esi/imm32/outputs "4e/decrement-esi"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-edi/imm32/next _Primitive-dec-edi: # var/edi <- decrement => 4f/decrement-edi "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-edi/imm32/outputs "4f/decrement-edi"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-mem/imm32/next _Primitive-inc-mem: # increment var => ff 0/subop/increment *(ebp+__) "increment"/imm32/name Single-int-var-in-mem/imm32/inouts 0/imm32/no-outputs "ff 0/subop/increment"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-inc-reg/imm32/next _Primitive-inc-reg: # var/reg <- increment => ff 0/subop/increment %__ "increment"/imm32/name 0/imm32/no-inouts Single-int-var-in-some-register/imm32/outputs "ff 0/subop/increment"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-mem/imm32/next _Primitive-dec-mem: # decrement var => ff 1/subop/decrement *(ebp+__) "decrement"/imm32/name Single-int-var-in-mem/imm32/inouts 0/imm32/no-outputs "ff 1/subop/decrement"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-dec-reg/imm32/next _Primitive-dec-reg: # var/reg <- decrement => ff 1/subop/decrement %__ "decrement"/imm32/name 0/imm32/no-inouts Single-int-var-in-some-register/imm32/outputs "ff 1/subop/decrement"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-add-to-eax/imm32/next # - add _Primitive-add-to-eax: # var/eax <- add lit => 05/add-to-eax lit/imm32 "add"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-eax/imm32/outputs "05/add-to-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-add-reg-to-reg/imm32/next _Primitive-add-reg-to-reg: # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32 "add"/imm32/name Single-int-var-in-some-register/imm32/inouts Single-int-var-in-some-register/imm32/outputs "01/add-to"/imm32/subx-name 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-add-reg-to-mem/imm32/next _Primitive-add-reg-to-mem: # add-to var1 var2/reg => 01/add-to var1 var2/r32 "add-to"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "01/add-to"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-add-mem-to-reg/imm32/next _Primitive-add-mem-to-reg: # var1/reg <- add var2 => 03/add var2/rm32 var1/r32 "add"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "03/add"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-add-lit-to-reg/imm32/next _Primitive-add-lit-to-reg: # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32 "add"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-some-register/imm32/outputs "81 0/subop/add"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-add-lit-to-mem/imm32/next _Primitive-add-lit-to-mem: # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32 "add-to"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "81 0/subop/add"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-second-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-subtract-from-eax/imm32/next # - subtract _Primitive-subtract-from-eax: # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32 "subtract"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-eax/imm32/outputs "2d/subtract-from-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-subtract-reg-from-reg/imm32/next _Primitive-subtract-reg-from-reg: # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32 "subtract"/imm32/name Single-int-var-in-some-register/imm32/inouts Single-int-var-in-some-register/imm32/outputs "29/subtract-from"/imm32/subx-name 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-subtract-reg-from-mem/imm32/next _Primitive-subtract-reg-from-mem: # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32 "subtract-from"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "29/subtract-from"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-subtract-mem-from-reg/imm32/next _Primitive-subtract-mem-from-reg: # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32 "subtract"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "2b/subtract"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-subtract-lit-from-reg/imm32/next _Primitive-subtract-lit-from-reg: # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32 "subtract"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-some-register/imm32/outputs "81 5/subop/subtract"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-subtract-lit-from-mem/imm32/next _Primitive-subtract-lit-from-mem: # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32 "subtract-from"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "81 5/subop/subtract"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-and-with-eax/imm32/next # - and _Primitive-and-with-eax: # var/eax <- and lit => 25/and-with-eax lit/imm32 "and"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-eax/imm32/outputs "25/and-with-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-and-reg-with-reg/imm32/next _Primitive-and-reg-with-reg: # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32 "and"/imm32/name Single-int-var-in-some-register/imm32/inouts Single-int-var-in-some-register/imm32/outputs "21/and-with"/imm32/subx-name 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-and-reg-with-mem/imm32/next _Primitive-and-reg-with-mem: # and-with var1 var2/reg => 21/and-with var1 var2/r32 "and-with"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "21/and-with"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-and-mem-with-reg/imm32/next _Primitive-and-mem-with-reg: # var1/reg <- and var2 => 23/and var2/rm32 var1/r32 "and"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "23/and"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-and-lit-with-reg/imm32/next _Primitive-and-lit-with-reg: # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32 "and"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-some-register/imm32/outputs "81 4/subop/and"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-and-lit-with-mem/imm32/next _Primitive-and-lit-with-mem: # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32 "and-with"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "81 4/subop/and"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-or-with-eax/imm32/next # - or _Primitive-or-with-eax: # var/eax <- or lit => 0d/or-with-eax lit/imm32 "or"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-eax/imm32/outputs "0d/or-with-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-or-reg-with-reg/imm32/next _Primitive-or-reg-with-reg: # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32 "or"/imm32/name Single-int-var-in-some-register/imm32/inouts Single-int-var-in-some-register/imm32/outputs "09/or-with"/imm32/subx-name 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-or-reg-with-mem/imm32/next _Primitive-or-reg-with-mem: # or-with var1 var2/reg => 09/or-with var1 var2/r32 "or-with"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "09/or-with"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-or-mem-with-reg/imm32/next _Primitive-or-mem-with-reg: # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32 "or"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "0b/or"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-or-lit-with-reg/imm32/next _Primitive-or-lit-with-reg: # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32 "or"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-some-register/imm32/outputs "81 1/subop/or"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-or-lit-with-mem/imm32/next _Primitive-or-lit-with-mem: # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32 "or-with"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "81 1/subop/or"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-second-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-xor-with-eax/imm32/next # - xor _Primitive-xor-with-eax: # var/eax <- xor lit => 35/xor-with-eax lit/imm32 "xor"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-eax/imm32/outputs "35/xor-with-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-xor-reg-with-reg/imm32/next _Primitive-xor-reg-with-reg: # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32 "xor"/imm32/name Single-int-var-in-some-register/imm32/inouts Single-int-var-in-some-register/imm32/outputs "31/xor-with"/imm32/subx-name 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-xor-reg-with-mem/imm32/next _Primitive-xor-reg-with-mem: # xor-with var1 var2/reg => 31/xor-with var1 var2/r32 "xor-with"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "31/xor-with"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-xor-mem-with-reg/imm32/next _Primitive-xor-mem-with-reg: # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32 "xor"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "33/xor"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-xor-lit-with-reg/imm32/next _Primitive-xor-lit-with-reg: # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32 "xor"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-some-register/imm32/outputs "81 6/subop/xor"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-xor-lit-with-mem/imm32/next _Primitive-xor-lit-with-mem: # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32 "xor-with"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "81 6/subop/xor"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-first-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-copy-to-eax/imm32/next # - copy _Primitive-copy-to-eax: # var/eax <- copy lit => b8/copy-to-eax lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-eax/imm32/outputs "b8/copy-to-eax"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-to-ecx/imm32/next _Primitive-copy-to-ecx: # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-ecx/imm32/outputs "b9/copy-to-ecx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-to-edx/imm32/next _Primitive-copy-to-edx: # var/edx <- copy lit => ba/copy-to-edx lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-edx/imm32/outputs "ba/copy-to-edx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-to-ebx/imm32/next _Primitive-copy-to-ebx: # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-ebx/imm32/outputs "bb/copy-to-ebx"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-to-esi/imm32/next _Primitive-copy-to-esi: # var/esi <- copy lit => be/copy-to-esi lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-esi/imm32/outputs "be/copy-to-esi"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-to-edi/imm32/next _Primitive-copy-to-edi: # var/edi <- copy lit => bf/copy-to-edi lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-edi/imm32/outputs "bf/copy-to-edi"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-reg-to-reg/imm32/next _Primitive-copy-reg-to-reg: # var1/reg <- copy var2/reg => 89/copy-to var1/rm32 var2/r32 "copy"/imm32/name Single-int-var-in-some-register/imm32/inouts Single-int-var-in-some-register/imm32/outputs "89/copy-to"/imm32/subx-name 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-reg-to-mem/imm32/next _Primitive-copy-reg-to-mem: # copy-to var1 var2/reg => 89/copy-to var1 var2/r32 "copy-to"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "89/copy-to"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-mem-to-reg/imm32/next _Primitive-copy-mem-to-reg: # var1/reg <- copy var2 => 8b/copy-from var2/rm32 var1/r32 "copy"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "8b/copy-from"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-lit-to-reg/imm32/next _Primitive-copy-lit-to-reg: # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32 "copy"/imm32/name Single-lit-var/imm32/inouts Single-int-var-in-some-register/imm32/outputs "c7 0/subop/copy"/imm32/subx-name 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-copy-lit-to-mem/imm32/next _Primitive-copy-lit-to-mem: # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32 "copy-to"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "c7 0/subop/copy"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-first-inout 0/imm32/no-disp32 1/imm32/output-is-write-only _Primitive-compare-mem-with-reg/imm32/next # - compare _Primitive-compare-mem-with-reg: # compare var1 var2/reg => 39/compare-> var1/rm32 var2/r32 "compare"/imm32/name Two-args-int-stack-int-reg/imm32/inouts 0/imm32/outputs "39/compare->"/imm32/subx-name 1/imm32/rm32-is-first-inout 2/imm32/r32-is-second-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-compare-reg-with-mem/imm32/next _Primitive-compare-reg-with-mem: # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32 "compare"/imm32/name Two-args-int-reg-int-stack/imm32/inouts 0/imm32/outputs "3b/compare<-"/imm32/subx-name 2/imm32/rm32-is-second-inout 1/imm32/r32-is-first-inout 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-compare-eax-with-literal/imm32/next _Primitive-compare-eax-with-literal: # compare var1/eax n => 3d/compare-eax-with n/imm32 "compare"/imm32/name Two-args-int-eax-int-literal/imm32/inouts 0/imm32/outputs "3d/compare-eax-with"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 2/imm32/imm32-is-second-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-compare-reg-with-literal/imm32/next _Primitive-compare-reg-with-literal: # compare var1/reg n => 81 7/subop/compare %reg n/imm32 "compare"/imm32/name Int-var-in-register-and-literal/imm32/inouts 0/imm32/outputs "81 7/subop/compare"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-second-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-compare-mem-with-literal/imm32/next _Primitive-compare-mem-with-literal: # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32 "compare"/imm32/name Int-var-and-literal/imm32/inouts 0/imm32/outputs "81 7/subop/compare"/imm32/subx-name 1/imm32/rm32-is-first-inout 0/imm32/no-r32 2/imm32/imm32-is-second-inout 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-multiply-reg-by-mem/imm32/next # - multiply _Primitive-multiply-reg-by-mem: # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32 "multiply"/imm32/name Single-int-var-in-mem/imm32/inouts Single-int-var-in-some-register/imm32/outputs "0f af/multiply"/imm32/subx-name 1/imm32/rm32-is-first-inout 3/imm32/r32-is-first-output 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/output-is-write-only _Primitive-break-if-addr=/imm32/next _Primitive-break-if-addr>=: "break-if-addr>="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-=/imm32/next _Primitive-break-if-=: "break-if-="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 84/jump-if-= break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-!=/imm32/next _Primitive-break-if-!=: "break-if-!="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 85/jump-if-!= break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-addr<=/imm32/next _Primitive-break-if-addr<=: "break-if-addr<="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-addr>/imm32/next _Primitive-break-if-addr>: "break-if-addr>"/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 87/jump-if-addr> break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-=/imm32/next _Primitive-break-if->=: "break-if->="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 8d/jump-if->= break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-<=/imm32/next _Primitive-break-if-<=: "break-if-<="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 8e/jump-if-<= break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if->/imm32/next _Primitive-break-if->: "break-if->"/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 8f/jump-if-> break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break/imm32/next _Primitive-break: "break"/imm32/name 0/imm32/inouts 0/imm32/outputs "e9/jump break/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-addr=/imm32/next _Primitive-loop-if-addr>=: "loop-if-addr>="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-=/imm32/next _Primitive-loop-if-=: "loop-if-="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 84/jump-if-= loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-!=/imm32/next _Primitive-loop-if-!=: "loop-if-!="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 85/jump-if-!= loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-addr<=/imm32/next _Primitive-loop-if-addr<=: "loop-if-addr<="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-addr>/imm32/next _Primitive-loop-if-addr>: "loop-if-addr>"/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-=/imm32/next _Primitive-loop-if->=: "loop-if->="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 8d/jump-if->= loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if-<=/imm32/next _Primitive-loop-if-<=: "loop-if-<="/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop-if->/imm32/next _Primitive-loop-if->: "loop-if->"/imm32/name 0/imm32/inouts 0/imm32/outputs "0f 8f/jump-if-> loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-loop/imm32/next # we probably don't need an unconditional break _Primitive-loop: "loop"/imm32/name 0/imm32/inouts 0/imm32/outputs "e9/jump loop/disp32"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 0/imm32/no-disp32 0/imm32/no-output _Primitive-break-if-addr<-named/imm32/next # - branches to named blocks _Primitive-break-if-addr<-named: "break-if-addr<"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 82/jump-if-addr<"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-addr>=-named/imm32/next _Primitive-break-if-addr>=-named: "break-if-addr>="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 83/jump-if-addr>="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-=-named/imm32/next _Primitive-break-if-=-named: "break-if-="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 84/jump-if-="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-!=-named/imm32/next _Primitive-break-if-!=-named: "break-if-!="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 85/jump-if-!="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-addr<=-named/imm32/next _Primitive-break-if-addr<=-named: "break-if-addr<="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 86/jump-if-addr<="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-addr>-named/imm32/next _Primitive-break-if-addr>-named: "break-if-addr>"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 87/jump-if-addr>"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-<-named/imm32/next _Primitive-break-if-<-named: "break-if-<"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8c/jump-if-<"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if->=-named/imm32/next _Primitive-break-if->=-named: "break-if->="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8d/jump-if->="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if-<=-named/imm32/next _Primitive-break-if-<=-named: "break-if-<="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8e/jump-if-<="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-if->-named/imm32/next _Primitive-break-if->-named: "break-if->"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8f/jump-if->"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-break-named/imm32/next _Primitive-break-named: "break"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "e9/jump"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-addr<-named/imm32/next _Primitive-loop-if-addr<-named: "loop-if-addr<"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 82/jump-if-addr<"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-addr>=-named/imm32/next _Primitive-loop-if-addr>=-named: "loop-if-addr>="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 83/jump-if-addr>="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-=-named/imm32/next _Primitive-loop-if-=-named: "loop-if-="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 84/jump-if-="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-!=-named/imm32/next _Primitive-loop-if-!=-named: "loop-if-!="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 85/jump-if-!="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-addr<=-named/imm32/next _Primitive-loop-if-addr<=-named: "loop-if-addr<="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 86/jump-if-addr<="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-addr>-named/imm32/next _Primitive-loop-if-addr>-named: "loop-if-addr>"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 87/jump-if-addr>"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-<-named/imm32/next _Primitive-loop-if-<-named: "loop-if-<"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8c/jump-if-<"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if->=-named/imm32/next _Primitive-loop-if->=-named: "loop-if->="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8d/jump-if->="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if-<=-named/imm32/next _Primitive-loop-if-<=-named: "loop-if-<="/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8e/jump-if-<="/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-if->-named/imm32/next _Primitive-loop-if->-named: "loop-if->"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "0f 8f/jump-if->"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output _Primitive-loop-named/imm32/next # we probably don't need an unconditional break _Primitive-loop-named: "loop"/imm32/name Single-lit-var/imm32/inouts 0/imm32/outputs "e9/jump"/imm32/subx-name 0/imm32/no-rm32 0/imm32/no-r32 0/imm32/no-imm32 1/imm32/disp32-is-first-inout 0/imm32/no-output 0/imm32/next Single-int-var-in-mem: Int-var-in-mem/imm32 0/imm32/next Int-var-in-mem: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 1/imm32/some-stack-offset 0/imm32/no-register Two-args-int-stack-int-reg: Int-var-in-mem/imm32 Single-int-var-in-some-register/imm32/next Two-args-int-reg-int-stack: Int-var-in-some-register/imm32 Single-int-var-in-mem/imm32/next Two-args-int-eax-int-literal: Int-var-in-eax/imm32 Single-lit-var/imm32/next Int-var-and-literal: Int-var-in-mem/imm32 Single-lit-var/imm32/next Int-var-in-register-and-literal: Int-var-in-some-register/imm32 Single-lit-var/imm32/next Single-int-var-in-some-register: Int-var-in-some-register/imm32 0/imm32/next Int-var-in-some-register: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset Any-register/imm32 Single-int-var-in-eax: Int-var-in-eax/imm32 0/imm32/next Int-var-in-eax: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset "eax"/imm32/register Single-int-var-in-ecx: Int-var-in-ecx/imm32 0/imm32/next Int-var-in-ecx: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset "ecx"/imm32/register Single-int-var-in-edx: Int-var-in-edx/imm32 0/imm32/next Int-var-in-edx: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset "edx"/imm32/register Single-int-var-in-ebx: Int-var-in-ebx/imm32 0/imm32/next Int-var-in-ebx: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset "ebx"/imm32/register Single-int-var-in-esi: Int-var-in-esi/imm32 0/imm32/next Int-var-in-esi: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset "esi"/imm32/register Single-int-var-in-edi: Int-var-in-edi/imm32 0/imm32/next Int-var-in-edi: "arg1"/imm32/name Type-int/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset "edi"/imm32/register Single-lit-var: Lit-var/imm32 0/imm32/next Lit-var: "literal"/imm32/name Type-literal/imm32 1/imm32/some-block-depth 0/imm32/no-stack-offset 0/imm32/no-register Type-int: 1/imm32/left/int 0/imm32/right/null Type-literal: 0/imm32/left/literal 0/imm32/right/null == code emit-subx-primitive: # out: (addr buffered-file), stmt: (handle statement), primitive: (handle function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # ecx = primitive 8b/-> *(ebp+0x10) 1/r32/ecx # emit primitive name (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) *(ecx+0xc)) # Primitive-subx-name # emit rm32 if necessary (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc)) # out, Primitive-subx-rm32, stmt # emit r32 if necessary (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc)) # out, Primitive-subx-r32, stmt # emit imm32 if necessary (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc)) # out, Primitive-subx-imm32, stmt # emit disp32 if necessary (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc)) # out, Primitive-subx-disp32, stmt (write-buffered *(ebp+8) Newline) $emit-subx-primitive:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-rm32: # out: (addr buffered-file), l: arg-location, stmt: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax # if (l == 0) return 81 7/subop/compare *(ebp+0xc) 0/imm32 74/jump-if-= $emit-subx-rm32:end/disp8 # var v/eax: (handle var) (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax (emit-subx-var-as-rm32 *(ebp+8) %eax) $emit-subx-rm32:end: # . restore registers 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return get-stmt-operand-from-arg-location: # stmt: (handle statement), l: arg-location -> var/eax: (handle stmt-var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # eax = l 8b/-> *(ebp+0xc) 0/r32/eax # ecx = stmt 8b/-> *(ebp+8) 1/r32/ecx # if (l == 1) return stmt->inouts { 3d/compare-eax-and 1/imm32 75/jump-if-!= break/disp8 $get-stmt-operand-from-arg-location:1: 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts eb/jump $get-stmt-operand-from-arg-location:end/disp8 } # if (l == 2) return stmt->inouts->next { 3d/compare-eax-and 2/imm32 75/jump-if-!= break/disp8 $get-stmt-operand-from-arg-location:2: 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts 8b/-> *(eax+4) 0/r32/eax # Stmt-var-next eb/jump $get-stmt-operand-from-arg-location:end/disp8 } # if (l == 3) return stmt->outputs { 3d/compare-eax-and 3/imm32 75/jump-if-!= break/disp8 $get-stmt-operand-from-arg-location:3: 8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs eb/jump $get-stmt-operand-from-arg-location:end/disp8 } # abort e9/jump $get-stmt-operand-from-arg-location:abort/disp32 $get-stmt-operand-from-arg-location:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $get-stmt-operand-from-arg-location:abort: # error("invalid arg-location " eax) (write-buffered Stderr "invalid arg-location ") (print-int32-buffered Stderr %eax) (write-buffered Stderr Newline) (flush Stderr) # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here emit-subx-r32: # out: (addr buffered-file), l: arg-location, stmt: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # if (location == 0) return 81 7/subop/compare *(ebp+0xc) 0/imm32 0f 84/jump-if-= $emit-subx-r32:end/disp32 # var v/eax: (handle var) (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax 8b/-> *eax 0/r32/eax # Stmt-var-value (maybe-get Registers *(eax+0x10) 8) # Var-register => eax: (addr register-index) (write-buffered *(ebp+8) Space) (print-int32-buffered *(ebp+8) *eax) (write-buffered *(ebp+8) "/r32") $emit-subx-r32:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-imm32: # out: (addr buffered-file), l: arg-location, stmt: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # if (location == 0) return 81 7/subop/compare *(ebp+0xc) 0/imm32 74/jump-if-= $emit-subx-imm32:end/disp8 # var v/eax: (handle var) (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax 8b/-> *eax 0/r32/eax # Stmt-var-value (write-buffered *(ebp+8) Space) (write-buffered *(ebp+8) *eax) # Var-name (write-buffered *(ebp+8) "/imm32") $emit-subx-imm32:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-disp32: # out: (addr buffered-file), l: arg-location, stmt: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # if (location == 0) return 81 7/subop/compare *(ebp+0xc) 0/imm32 0f 84/jump-if-= $emit-subx-disp32:end/disp32 # var v/eax: (handle var) (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # => eax 8b/-> *eax 0/r32/eax # Stmt-var-value (write-buffered *(ebp+8) Space) (write-buffered *(ebp+8) *eax) # Var-name # hack: if instruction operation starts with "break", emit ":break" # var name/ecx: (addr array byte) = stmt->operation 8b/-> *(ebp+0x10) 0/r32/eax 8b/-> *(eax+4) 1/r32/ecx { (string-starts-with? %ecx "break") # => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 (write-buffered *(ebp+8) ":break") } # hack: if instruction operation starts with "loop", emit ":loop" { (string-starts-with? %ecx "loop") # => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 (write-buffered *(ebp+8) ":loop") } (write-buffered *(ebp+8) "/disp32") $emit-subx-disp32:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-call: # out: (addr buffered-file), stmt: (handle statement), callee: (handle function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "(") # - emit function name 8b/-> *(ebp+0x10) 1/r32/ecx (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name # - emit arguments # var curr/ecx: (handle stmt-var) = stmt->inouts 8b/-> *(ebp+0xc) 1/r32/ecx 8b/-> *(ecx+8) 1/r32/ecx # Stmt1-inouts { # if (curr == null) break 81 7/subop/compare %ecx 0/imm32 74/jump-if-= break/disp8 # (emit-subx-call-operand *(ebp+8) %ecx) # curr = curr->next 8b/-> *(ecx+4) 1/r32/ecx # Stmt-var-next eb/jump loop/disp8 } # (write-buffered *(ebp+8) ")\n") $emit-subx-call:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # like a function call, except we have no idea what function it is # we hope it's defined in SubX and that the types are ok emit-hailmary-call: # out: (addr buffered-file), stmt: (handle statement) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "(") # ecx = stmt 8b/-> *(ebp+0xc) 1/r32/ecx # - emit function name (write-buffered *(ebp+8) *(ecx+4)) # Stmt1-operation # - emit arguments # var curr/ecx: (handle stmt-var) = stmt->inouts 8b/-> *(ecx+8) 1/r32/ecx # Stmt1-inouts { # if (curr == null) break 81 7/subop/compare %ecx 0/imm32 74/jump-if-= break/disp8 # (emit-subx-call-operand *(ebp+8) %ecx) # curr = curr->next 8b/-> *(ecx+4) 1/r32/ecx # Stmt-var-next eb/jump loop/disp8 } # (write-buffered *(ebp+8) ")\n") $emit-hailmary-call:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-call-operand: # out: (addr buffered-file), s: (handle stmt-var) # shares code with emit-subx-var-as-rm32 # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi # ecx = s 8b/-> *(ebp+0xc) 1/r32/ecx # var operand/esi: (handle var) = s->value 8b/-> *ecx 6/r32/esi # Stmt-var-value # if (operand->register) emit "%__" { 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 $emit-subx-call-operand:register: (write-buffered *(ebp+8) " %") (write-buffered *(ebp+8) *(esi+0x10)) # Var-register e9/jump $emit-subx-call-operand:end/disp32 } # else if (operand->stack-offset) emit "*(ebp+__)" { 81 7/subop/compare *(esi+0xc) 0/imm32 # Var-stack-offset 74/jump-if-= break/disp8 $emit-subx-call-operand:stack: (write-buffered *(ebp+8) Space) (write-buffered *(ebp+8) "*(ebp+") (print-int32-buffered *(ebp+8) *(esi+0xc)) # Var-stack-offset (write-buffered *(ebp+8) ")") e9/jump $emit-subx-call-operand:end/disp32 } # else if (operand->type == literal) emit "__" { 8b/-> *(esi+4) 0/r32/eax # Var-type 81 7/subop/compare *eax 0/imm32 # Tree-left 75/jump-if-!= break/disp8 $emit-subx-call-operand:literal: (write-buffered *(ebp+8) Space) (write-buffered *(ebp+8) *esi) } $emit-subx-call-operand:end: # . restore registers 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-var-as-rm32: # out: (addr buffered-file), s: (handle stmt-var) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi # ecx = s 8b/-> *(ebp+0xc) 1/r32/ecx # var operand/esi: (handle var) = s->value 8b/-> *ecx 6/r32/esi # Stmt-var-value # if (operand->register && s->is-deref?) emit "*__" { 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref 74/jump-if-= break/disp8 $emit-subx-var-as-rm32:register-indirect: (write-buffered *(ebp+8) " *") (write-buffered *(ebp+8) *(esi+0x10)) # Var-register } # if (operand->register && !s->is-deref?) emit "%__" { 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref 75/jump-if-!= break/disp8 $emit-subx-var-as-rm32:register-direct: (write-buffered *(ebp+8) " %") (write-buffered *(ebp+8) *(esi+0x10)) # Var-register } # else if (operand->stack-offset) emit "*(ebp+__)" { 81 7/subop/compare *(esi+0xc) 0/imm32 # Var-stack-offset 74/jump-if-= break/disp8 $emit-subx-var-as-rm32:stack: (write-buffered *(ebp+8) Space) (write-buffered *(ebp+8) "*(ebp+") (print-int32-buffered *(ebp+8) *(esi+0xc)) # Var-stack-offset (write-buffered *(ebp+8) ")") } $emit-subx-var-as-rm32:end: # . restore registers 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return find-matching-function: # functions: (addr function), stmt: (handle statement) -> result/eax: (handle function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var curr/ecx: (handle function) = functions 8b/-> *(ebp+8) 1/r32/ecx { # if (curr == null) break 81 7/subop/compare %ecx 0/imm32 74/jump-if-= break/disp8 # if match(stmt, curr) return curr { (mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 89/<- %eax 1/r32/ecx eb/jump $find-matching-function:end/disp8 } # curr = curr->next 8b/-> *(ecx+0x14) 1/r32/ecx # Function-next eb/jump loop/disp8 } # return null b8/copy-to-eax 0/imm32 $find-matching-function:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return find-matching-primitive: # primitives: (handle primitive), stmt: (handle statement) -> result/eax: (handle primitive) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var curr/ecx: (handle primitive) = primitives 8b/-> *(ebp+8) 1/r32/ecx { $find-matching-primitive:loop: # if (curr == null) break 81 7/subop/compare %ecx 0/imm32 0f 84/jump-if-= break/disp32 #? (write-buffered Stderr "prim: ") #? (write-buffered Stderr *ecx) # Primitive-name #? (write-buffered Stderr " => ") #? (write-buffered Stderr *(ecx+0xc)) # Primitive-subx-name #? (write-buffered Stderr Newline) #? (flush Stderr) # if match(curr, stmt) return curr { (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 89/<- %eax 1/r32/ecx eb/jump $find-matching-primitive:end/disp8 } $find-matching-primitive:next-primitive: # curr = curr->next 8b/-> *(ecx+0x24) 1/r32/ecx # Primitive-next e9/jump loop/disp32 } # return null b8/copy-to-eax 0/imm32 $find-matching-primitive:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return mu-stmt-matches-function?: # stmt: (handle statement), function: (handle function) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # return function->name == stmt->operation 8b/-> *(ebp+8) 1/r32/ecx 8b/-> *(ebp+0xc) 0/r32/eax (string-equal? *(ecx+4) *eax) # Stmt1-operation, Function-name => eax $mu-stmt-matches-function?:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return mu-stmt-matches-primitive?: # stmt: (handle statement), primitive: (handle primitive) -> result/eax: boolean # A mu stmt matches a primitive if the name matches, all the inout vars # match, and all the output vars match. # Vars match if types match and registers match. # In addition, a stmt output matches a primitive's output if types match # and the primitive has a wildcard register. # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # ecx = stmt 8b/-> *(ebp+8) 1/r32/ecx # edx = primitive 8b/-> *(ebp+0xc) 2/r32/edx { $mu-stmt-matches-primitive?:check-name: # if (primitive->name != stmt->operation) return false (string-equal? *(ecx+4) *edx) # Stmt1-operation, Primitive-name => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 b8/copy-to-eax 0/imm32 e9/jump $mu-stmt-matches-primitive?:end/disp32 } $mu-stmt-matches-primitive?:check-inouts: # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts) 8b/-> *(ecx+8) 6/r32/esi # Stmt1-inouts or Regvardef-inouts 8b/-> *(edx+4) 7/r32/edi # Primitive-inouts { # if (curr == 0 && curr2 == 0) move on to check outputs { 81 7/subop/compare %esi 0/imm32 75/jump-if-!= break/disp8 $mu-stmt-matches-primitive?:stmt-inout-is-null: { 81 7/subop/compare %edi 0/imm32 75/jump-if-!= break/disp8 # e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32 } # return false b8/copy-to-eax 0/imm32/false e9/jump $mu-stmt-matches-primitive?:end/disp32 } # if (curr2 == 0) return false { 81 7/subop/compare %edi 0/imm32 75/jump-if-!= break/disp8 $mu-stmt-matches-primitive?:prim-inout-is-null: b8/copy-to-eax 0/imm32/false e9/jump $mu-stmt-matches-primitive?:end/disp32 } # if (curr != curr2) return false { (operand-matches-primitive? %esi *edi) # List-value => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 b8/copy-to-eax 0/imm32/false e9/jump $mu-stmt-matches-primitive?:end/disp32 } # curr=curr->next 8b/-> *(esi+4) 6/r32/esi # Stmt-var-next # curr2=curr2->next 8b/-> *(edi+4) 7/r32/edi # Stmt-var-next eb/jump loop/disp8 } $mu-stmt-matches-primitive?:check-outputs: # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs) 8b/-> *(ecx+0xc) 6/r32/esi # Stmt1-outputs 8b/-> *(edx+8) 7/r32/edi # Primitive-outputs { # if (curr == 0) return (curr2 == 0) { $mu-stmt-matches-primitive?:check-output: 81 7/subop/compare %esi 0/imm32 75/jump-if-!= break/disp8 { 81 7/subop/compare %edi 0/imm32 75/jump-if-!= break/disp8 # return true b8/copy-to-eax 1/imm32 e9/jump $mu-stmt-matches-primitive?:end/disp32 } # return false b8/copy-to-eax 0/imm32 e9/jump $mu-stmt-matches-primitive?:end/disp32 } # if (curr2 == 0) return false { 81 7/subop/compare %edi 0/imm32 75/jump-if-!= break/disp8 b8/copy-to-eax 0/imm32 e9/jump $mu-stmt-matches-primitive?:end/disp32 } # if (curr != curr2) return false { (operand-matches-primitive? %esi *edi) # List-value => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 b8/copy-to-eax 0/imm32 e9/jump $mu-stmt-matches-primitive?:end/disp32 } # curr=curr->next 8b/-> *(esi+4) 6/r32/esi # Stmt-var-next # curr2=curr2->next 8b/-> *(edi+4) 7/r32/edi # Stmt-var-next eb/jump loop/disp8 } $mu-stmt-matches-primitive?:return-true: b8/copy-to-eax 1/imm32 $mu-stmt-matches-primitive?:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return operand-matches-primitive?: # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 56/push-esi 57/push-edi # ecx = s 8b/-> *(ebp+8) 1/r32/ecx # var var/esi : (handle var) = s->value 8b/-> *ecx 6/r32/esi # Stmt-var-value # edi = prim-var 8b/-> *(ebp+0xc) 7/r32/edi $operand-matches-primitive?:check-type: # if (var->type != prim-var->type) return false (subx-type-equal? *(esi+4) *(edi+4)) # Var-type, Var-type => eax 3d/compare-eax-and 0/imm32/false b8/copy-to-eax 0/imm32/false 0f 84/jump-if-= $operand-matches-primitive?:end/disp32 { $operand-matches-primitive?:check-register: # if prim-var is in memory and var is in register but dereference, match { 81 7/subop/compare *(edi+0x10) 0/imm32 # Var-register 0f 85/jump-if-!= break/disp32 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref 74/jump-if-= break/disp8 e9/jump $operand-matches-primitive?:return-true/disp32 } # if prim-var is in register and var is in register but dereference, no match { 81 7/subop/compare *(edi+0x10) 0/imm32 # Var-register 0f 84/jump-if-= break/disp32 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 0f 84/jump-if-= break/disp32 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref 74/jump-if-= break/disp8 e9/jump $operand-matches-primitive?:return-false/disp32 } # return false if var->register doesn't match prim-var->register { # if register addresses are equal, it's a match 8b/-> *(esi+0x10) 0/r32/eax # Var-register 39/compare *(edi+0x10) 0/r32/eax # Var-register 74/jump-if-= break/disp8 # if either address is 0, return false 3d/compare-eax-and 0/imm32 74/jump-if-= $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result 81 7/subop/compare *(edi+0x10) 0/imm32 # Var-register 74/jump-if-= $operand-matches-primitive?:return-false/disp8 # if prim-var->register is wildcard, it's a match (string-equal? *(edi+0x10) Any-register) # Var-register => eax 3d/compare-eax-and 0/imm32/false 75/jump-if-!= break/disp8 # if string contents aren't equal, return false (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register => eax 3d/compare-eax-and 0/imm32/false 74/jump-if-= $operand-matches-primitive?:return-false/disp8 } } $operand-matches-primitive?:return-true: b8/copy-to-eax 1/imm32/true eb/jump $operand-matches-primitive?:end/disp8 $operand-matches-primitive?:return-false: b8/copy-to-eax 0/imm32/false $operand-matches-primitive?:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return subx-type-equal?: # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var alit/ecx: boolean = is-literal-type?(a) (is-literal-type? *(ebp+8)) # => eax 89/<- %ecx 0/r32/eax # var blit/eax: boolean = is-literal-type?(b) (is-literal-type? *(ebp+0xc)) # => eax # return alit == blit 39/compare %eax 1/r32/ecx 74/jump-if-= $subx-type-equal?:true/disp8 $subx-type-equal?:false: b8/copy-to-eax 0/imm32/false eb/jump $subx-type-equal?:end/disp8 $subx-type-equal?:true: b8/copy-to-eax 1/imm32/true $subx-type-equal?:end: # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return is-literal-type?: # a: (handle tree type-id) -> result/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # 8b/-> *(ebp+8) 0/r32/eax 8b/-> *eax 0/r32/eax # Atom-value 3d/compare-eax-and 0/imm32/false 74/jump-if-equal $is-literal-type?:end/disp8 b8/copy-to-eax 1/imm32/true $is-literal-type?:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-emit-subx-statement-primitive: # Primitive operation on a variable on the stack. # increment foo # => # ff 0/subop/increment *(ebp-8) # # There's a variable on the var stack as follows: # name: 'foo' # type: int # stack-offset: -8 # # There's a primitive with this info: # name: 'increment' # inouts: int/mem # value: 'ff 0/subop/increment' # # There's nothing in functions. # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var 68/push 0/imm32/no-register 68/push -8/imm32/stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var operand/ebx: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %ebx 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 53/push-ebx/operands 68/push "increment"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # var primitives/ebx: primitive 68/push 0/imm32/next 68/push 0/imm32/output-is-write-only 68/push 0/imm32/no-disp32 68/push 0/imm32/no-imm32 68/push 0/imm32/no-r32 68/push 1/imm32/rm32-is-first-inout 68/push "ff 0/subop/increment"/imm32/subx-name 68/push 0/imm32/outputs 53/push-ebx/inouts # hack; in practice we won't have the same var in function definition and call 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi %ebx 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-emit-subx-statement-primitive-register: # Primitive operation on a variable in a register. # foo <- increment # => # ff 0/subop/increment %eax # sub-optimal, but should suffice # # There's a variable on the var stack as follows: # name: 'foo' # type: int # register: 'eax' # # There's a primitive with this info: # name: 'increment' # out: int/reg # value: 'ff 0/subop/increment' # # There's nothing in functions. # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var operand/ebx: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %ebx 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 53/push-ebx/outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # var formal-var/ebx: var in any register 68/push Any-register/imm32 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "dummy"/imm32 89/<- %ebx 4/r32/esp # var operand/ebx: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 53/push-ebx/formal-var 89/<- %ebx 4/r32/esp # var primitives/ebx: primitive 68/push 0/imm32/next 68/push 0/imm32/output-is-write-only 68/push 0/imm32/no-disp32 68/push 0/imm32/no-imm32 68/push 0/imm32/no-r32 68/push 3/imm32/rm32-in-first-output 68/push "ff 0/subop/increment"/imm32/subx-name 53/push-ebx/outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi %ebx 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-emit-subx-statement-select-primitive: # Select the right primitive between overloads. # foo <- increment # => # ff 0/subop/increment %eax # sub-optimal, but should suffice # # There's a variable on the var stack as follows: # name: 'foo' # type: int # register: 'eax' # # There's two primitives, as follows: # - name: 'increment' # out: int/reg # value: 'ff 0/subop/increment' # - name: 'increment' # inout: int/mem # value: 'ff 0/subop/increment' # # There's nothing in functions. # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var real-outputs/edi: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # var formal-var/ebx: var in any register 68/push Any-register/imm32 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "dummy"/imm32 89/<- %ebx 4/r32/esp # var formal-outputs/ebx: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 53/push-ebx/formal-var 89/<- %ebx 4/r32/esp # var primitive1/ebx: primitive 68/push 0/imm32/next 68/push 0/imm32/output-is-write-only 68/push 0/imm32/no-disp32 68/push 0/imm32/no-imm32 68/push 0/imm32/no-r32 68/push 3/imm32/rm32-in-first-output 68/push "ff 0/subop/increment"/imm32/subx-name 53/push-ebx/outputs/formal-outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # var primitives/ebx: primitive 53/push-ebx/next 68/push 0/imm32/output-is-write-only 68/push 0/imm32/no-disp32 68/push 0/imm32/no-imm32 68/push 0/imm32/no-r32 68/push 1/imm32/rm32-is-first-inout 68/push "ff 0/subop/increment"/imm32/subx-name 68/push 0/imm32/outputs 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi %ebx 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-emit-subx-statement-select-primitive-2: # Select the right primitive between overloads. # foo <- increment # => # ff 0/subop/increment %eax # sub-optimal, but should suffice # # There's a variable on the var stack as follows: # name: 'foo' # type: int # register: 'eax' # # There's two primitives, as follows: # - name: 'increment' # out: int/reg # value: 'ff 0/subop/increment' # - name: 'increment' # inout: int/mem # value: 'ff 0/subop/increment' # # There's nothing in functions. # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var inouts/edi: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 57/push-edi/inouts 68/push "increment"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # var formal-var/ebx: var in any register 68/push Any-register/imm32 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "dummy"/imm32 89/<- %ebx 4/r32/esp # var operand/ebx: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 53/push-ebx/formal-var 89/<- %ebx 4/r32/esp # var primitive1/ebx: primitive 68/push 0/imm32/next 68/push 0/imm32/output-is-write-only 68/push 0/imm32/no-disp32 68/push 0/imm32/no-imm32 68/push 0/imm32/no-r32 68/push 3/imm32/rm32-in-first-output 68/push "ff 0/subop/increment"/imm32/subx-name 53/push-ebx/outputs/formal-outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # var primitives/ebx: primitive 53/push-ebx/next 68/push 0/imm32/output-is-write-only 68/push 0/imm32/no-disp32 68/push 0/imm32/no-imm32 68/push 0/imm32/no-r32 68/push 1/imm32/rm32-is-first-inout 68/push "ff 0/subop/increment"/imm32/subx-name 68/push 0/imm32/outputs 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi %ebx 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-increment-register: # Select the right register between overloads. # foo <- increment # => # 50/increment-eax # # There's a variable on the var stack as follows: # name: 'foo' # type: int # register: 'eax' # # Primitives are the global definitions. # # There are no functions defined. # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var real-outputs/edi: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/operation 68/push 1/imm32/regular-statement 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-increment-var: # Select the right primitive between overloads. # foo <- increment # => # ff 0/subop/increment %eax # sub-optimal, but should suffice # # There's a variable on the var stack as follows: # name: 'foo' # type: int # register: 'eax' # # Primitives are the global definitions. # # There are no functions defined. # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var inouts/edi: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-add-reg-to-reg: # var1/reg <- add var2/reg # => # 01/add %var1 var2 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var var-var2/edx: var in ecx 68/push "ecx"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "var2"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # var outputs/edi: (handle stmt-var) = [var1, var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-var1 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 56/push-esi/inouts 68/push "add"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-add-reg-to-mem: # add-to var1 var2/reg # => # 01/add *(ebp+__) var2 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var 68/push 0/imm32/no-register 68/push 8/imm32/stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var var-var2/edx: var in ecx 68/push "ecx"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "var2"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # var inouts = (handle stmt-var) = [var1, var2] 56/push-esi/next 51/push-ecx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "add-to"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-add-mem-to-reg: # var1/reg <- add var2 # => # 03/add *(ebp+__) var1 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var var-var2/edx: var 68/push 0/imm32/no-register 68/push 8/imm32/stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "var2"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # var outputs/edi = (handle stmt-var) = [var1] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-var1 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 56/push-esi/inouts 68/push "add"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-add-literal-to-eax: # var1/eax <- add 0x34 # => # 05/add-to-eax 0x34/imm32 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var type/edx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %edx 4/r32/esp # var var-var2/edx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 52/push-edx 68/push "0x34"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # var outputs/edi: (handle stmt-var) = [var1] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-var1 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 56/push-esi/inouts 68/push "add"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-add-literal-to-reg: # var1/ecx <- add 0x34 # => # 81 0/subop/add %ecx 0x34/imm32 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in ecx 68/push "ecx"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var type/edx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %edx 4/r32/esp # var var-var2/edx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 52/push-edx 68/push "0x34"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # var outputs/edi: (handle stmt-var) = [var1] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-var1 89/<- %edi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 57/push-edi/outputs 56/push-esi/inouts 68/push "add"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-add-literal-to-mem: # add-to var1, 0x34 # => # 81 0/subop/add %eax 0x34/imm32 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var 68/push 0/imm32/no-register 68/push 8/imm32/stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var type/edx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %edx 4/r32/esp # var var-var2/edx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 52/push-edx 68/push "0x34"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # var inouts = (handle stmt-var) = [var1, var2] 68/push 0/imm32/is-deref:false 56/push-esi/next 51/push-ecx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "add-to"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-compare-mem-with-reg: # compare var1, var2/eax # => # 39/compare *(ebp+___) 0/r32/eax # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var2/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var2"/imm32 89/<- %ecx 4/r32/esp # var var-var1/edx: var 68/push 0/imm32/no-register 68/push 8/imm32/stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "var1"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-var2 89/<- %esi 4/r32/esp # inouts = [var1, var2] 68/push 0/imm32/is-deref:false 56/push-esi 52/push-edx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "compare"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-compare-reg-with-mem: # compare var1/eax, var2 # => # 3b/compare *(ebp+___) 0/r32/eax # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var var-var2/edx: var 68/push 0/imm32/no-register 68/push 8/imm32/stack-offset 68/push 1/imm32/block-depth ff 6/subop/push *(ecx+4) # Var-type 68/push "var2"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # inouts = [var1, var2] 68/push 0/imm32/is-deref:false 56/push-esi 51/push-ecx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "compare"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-compare-mem-with-literal: # compare var1, 0x34 # => # 81 7/subop/compare *(ebp+___) 0x34/imm32 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var 68/push 0/imm32/no-register 68/push 8/imm32/stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var type/edx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %edx 4/r32/esp # var var-var2/edx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 52/push-edx 68/push "0x34"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # inouts = [var1, var2] 68/push 0/imm32/is-deref:false 56/push-esi/next 51/push-ecx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "compare"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-compare-eax-with-literal: # compare var1/eax 0x34 # => # 3d/compare-eax-with 0x34/imm32 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in eax 68/push "eax"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var type/edx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %edx 4/r32/esp # var var-var2/edx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 52/push-edx 68/push "0x34"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # inouts = [var1, var2] 68/push 0/imm32/is-deref:false 56/push-esi/next 51/push-ecx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "compare"/imm32/operation 68/push 1/imm32/regular-stmt 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-compare-reg-with-literal: # compare var1/ecx 0x34 # => # 81 7/subop/compare %ecx 0x34/imm32 # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-var1/ecx: var in ecx 68/push "ecx"/imm32/register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 51/push-ecx 68/push "var1"/imm32 89/<- %ecx 4/r32/esp # var type/edx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %edx 4/r32/esp # var var-var2/edx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 1/imm32/block-depth 52/push-edx 68/push "0x34"/imm32 89/<- %edx 4/r32/esp # var inouts/esi: (handle stmt-var) = [var2] 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 52/push-edx/var-var2 89/<- %esi 4/r32/esp # inouts = [var1, var2] 68/push 0/imm32/is-deref:false 56/push-esi/next 51/push-ecx/var-var1 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "compare"/imm32/operation 68/push 1/imm32/regular-stmt 89/<- %esi 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi Primitives 0) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-emit-subx-statement-function-call: # Call a function on a variable on the stack. # f foo # => # (f2 *(ebp-8)) # (Changing the function name supports overloading in general, but here it # just serves to help disambiguate things.) # # There's a variable on the var stack as follows: # name: 'foo' # type: int # stack-offset: -8 # # There's nothing in primitives. # # There's a function with this info: # name: 'f' # inout: int/mem # value: 'f2' # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = int 68/push 0/imm32/right/null 68/push 1/imm32/left/int 89/<- %ecx 4/r32/esp # var var-foo/ecx: var 68/push 0/imm32/no-register 68/push -8/imm32/stack-offset 68/push 0/imm32/block-depth 51/push-ecx 68/push "foo"/imm32 89/<- %ecx 4/r32/esp # var inouts/esi: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "f"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # var functions/ebx: function 68/push 0/imm32/next 68/push 0/imm32/body 68/push 0/imm32/outputs 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call 68/push "f2"/imm32/subx-name 68/push "f"/imm32/name 89/<- %ebx 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi 0 %ebx) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return test-emit-subx-statement-function-call-with-literal-arg: # Call a function on a literal. # f 34 # => # (f2 34) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # setup (clear-stream _test-output-stream) (clear-stream $_test-output-buffered-file->buffer) # var type/ecx: (handle tree type-id) = literal 68/push 0/imm32/right/null 68/push 0/imm32/left/literal 89/<- %ecx 4/r32/esp # var var-foo/ecx: var literal 68/push 0/imm32/no-register 68/push 0/imm32/no-stack-offset 68/push 0/imm32/block-depth 51/push-ecx 68/push "34"/imm32 89/<- %ecx 4/r32/esp # var inouts/esi: (handle stmt-var) 68/push 0/imm32/is-deref:false 68/push 0/imm32/next 51/push-ecx/var-foo 89/<- %esi 4/r32/esp # var stmt/esi: statement 68/push 0/imm32/next 68/push 0/imm32/outputs 56/push-esi/inouts 68/push "f"/imm32/operation 68/push 1/imm32 89/<- %esi 4/r32/esp # var functions/ebx: function 68/push 0/imm32/next 68/push 0/imm32/body 68/push 0/imm32/outputs 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call 68/push "f2"/imm32/subx-name 68/push "f"/imm32/name 89/<- %ebx 4/r32/esp # convert c7 0/subop/copy *Curr-block-depth 0/imm32 (emit-subx-statement _test-output-buffered-file %esi 0 %ebx) (flush _test-output-buffered-file) #? # dump _test-output-stream {{{ #? (write 2 "^") #? (write-stream 2 _test-output-stream) #? (write 2 "$\n") #? (rewind-stream _test-output-stream) #? # }}} # check output (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-indent: # out: (addr buffered-file), n: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax # var i/eax: int = n 8b/-> *(ebp+0xc) 0/r32/eax { # if (i <= 0) break 3d/compare-eax-with 0/imm32 7e/jump-if-<= break/disp8 (write-buffered *(ebp+8) " ") 48/decrement-eax eb/jump loop/disp8 } $emit-indent:end: # . restore registers 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-prologue: # out: (addr buffered-file) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered *(ebp+8) " # . prologue\n") (write-buffered *(ebp+8) " 55/push-ebp\n") (write-buffered *(ebp+8) " 89/<- %ebp 4/r32/esp\n") $emit-subx-prologue:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-subx-epilogue: # out: (addr buffered-file) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (write-buffered *(ebp+8) " # . epilogue\n") (write-buffered *(ebp+8) " 89/<- %esp 5/r32/ebp\n") (write-buffered *(ebp+8) " 5d/pop-to-ebp\n") (write-buffered *(ebp+8) " c3/return\n") $emit-subx-epilogue:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return