diff --git a/apps/mu b/apps/mu index 9cd19eb8..d77798f9 100755 Binary files a/apps/mu and b/apps/mu differ diff --git a/apps/mu.subx b/apps/mu.subx index ddac7bb8..9a54e06e 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -1086,19 +1086,6 @@ $emit-subx-imm32:end: 5d/pop-to-ebp c3/return -#? # var args/ecx : (list var) = stmt->inouts -#? 8b/-> *(ebp+0xc) 1/r32/ecx -#? 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts -#? { -#? # if (curr == null) break -#? 81 7/subop/compare %ecx 0/imm32 -#? 74/jump-if-equal break/disp8 -#? # -#? (emit-subx-call-operand *(ebp+8) *ecx) -#? # args = args->next -#? 8b/-> *(ecx+4) 1/r32/ecx -#? } - emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) # . prologue 55/push-ebp @@ -1213,6 +1200,7 @@ find-matching-primitive: # primitives : (address primitive), stmt : (address st # var curr/ecx : (address primitive) = primitives 8b/-> *(ebp+8) 1/r32/ecx { +$find-matching-primitive:loop: # if (curr == null) break 81 7/subop/compare %ecx 0/imm32 74/jump-if-equal break/disp8 @@ -1224,8 +1212,9 @@ find-matching-primitive: # primitives : (address primitive), stmt : (address st 89/<- %eax 1/r32/ecx eb/jump $find-matching-function:end/disp8 } +$find-matching-primitive:next-primitive: # curr = curr->next - 8b/-> *(ecx+0x18) 1/r32/ecx # Primitive-next + 8b/-> *(ecx+0x1c) 1/r32/ecx # Primitive-next eb/jump loop/disp8 } # return null @@ -1257,23 +1246,219 @@ $mu-stmt-matches-function?:end: c3/return mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address 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 - # return primitive->name == stmt->operation + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # ecx = stmt 8b/-> *(ebp+8) 1/r32/ecx - 8b/-> *(ebp+0xc) 0/r32/eax - (string-equal? *ecx *eax) # => eax + # edx = primitive + 8b/-> *(ebp+0xc) 2/r32/edx + { +$mu-stmt-matches-primitive?:check-name: + # if (primitive->name != stmt->operation) return false + (string-equal? *ecx *edx) # => eax + 3d/compare-eax-and 0/imm32 + 75/jump-if-not-equal break/disp8 + b8/copy-to-eax 0/imm32 + e9/jump $mu-stmt-matches-primitive?:end/disp32 + } +$mu-stmt-matches-primitive?:check-inouts: + # curr = stmt->inouts + 8b/-> *(ecx+4) 6/r32/esi # Stmt-inouts + # curr2 = primitive->inouts + 8b/-> *(edx+4) 7/r32/edi # Primitive-inouts + { + # if (curr == 0) return (curr2 == 0) + { + 81 7/subop/compare %esi 0/imm32 + 75/jump-if-not-equal break/disp8 + { + 81 7/subop/compare %edi 0/imm32 + 75/jump-if-not-equal 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-not-equal 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) # => eax + 3d/compare-eax-and 0/imm32 + 75/jump-if-not-equal break/disp8 + b8/copy-to-eax 0/imm32 + e9/jump $mu-stmt-matches-primitive?:end/disp32 + } + # curr=curr->next + 8b/-> *(ecx+4) 1/r32/ecx # Operand-next + # curr2=curr2->next + 8b/-> *(edx+4) 2/r32/edx # Operand-next + } +$mu-stmt-matches-primitive?:check-outputs: + # ecx = stmt + 8b/-> *(ebp+8) 1/r32/ecx + # edx = primitive + 8b/-> *(ebp+0xc) 2/r32/edx + # curr = stmt->outputs + 8b/-> *(ecx+8) 6/r32/esi # Stmt-outputs + # curr2 = primitive->outputs + 8b/-> *(edx+8) 7/r32/edi # Primitive-outputs + { + # if (curr == 0) return (curr2 == 0) + { + 81 7/subop/compare %esi 0/imm32 + 75/jump-if-not-equal break/disp8 + { + 81 7/subop/compare %edi 0/imm32 + 75/jump-if-not-equal 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-not-equal break/disp8 + b8/copy-to-eax 0/imm32 + e9/jump $mu-stmt-matches-primitive?:end/disp32 + } + # if (curr != curr2) return false + { + (output-operand-matches-primitive? *esi *edi) # => eax + 3d/compare-eax-and 0/imm32 + 75/jump-if-not-equal break/disp8 + b8/copy-to-eax 0/imm32 + e9/jump $mu-stmt-matches-primitive?:end/disp32 + } + # curr=curr->next + 8b/-> *(ecx+4) 1/r32/ecx # Operand-next + # curr2=curr2->next + 8b/-> *(edx+4) 2/r32/edx # Operand-next + } +$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?: # var1 : (address var), var2 : (address var) => result/eax : boolean + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 56/push-esi + 57/push-edi + # esi = var1 + 8b/-> *(ebp+8) 6/r32/esi + # edi = var2 + 8b/-> *(ebp+0xc) 7/r32/edi + # if (var1->type != var2->type) return false + # if (var1->register != var1->register) return false + { + # if addresses are equal, don't return here + 8b/-> *(esi+0x10) 0/r32/eax + 39/compare *(edi+0x10) 0/r32/eax + 74/jump-if-equal break/disp8 + # if either address is 0, return false + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var1->register to result + 81 7/subop/compare *(edi+0x10) 0/imm32 + 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var1->register to result + # if string contents don't match, return false + (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 0/imm32/false + 74/jump-if-equal $operand-matches-primitive?:end/disp8 + } + # return true + b8/copy-to-eax 1/imm32/true +$operand-matches-primitive?:end: + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +# like operand-matches-primitive? but also handles "*" register in primitive +output-operand-matches-primitive?: # var : (address var), primout-var : (address var) => result/eax : boolean + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 56/push-esi + 57/push-edi + # esi = var + 8b/-> *(ebp+8) 6/r32/esi + # edi = primout-var + 8b/-> *(ebp+0xc) 7/r32/edi + # if (var->type != primout-var->type) return false + # return false if var->register doesn't match primout-var->register + { + # if addresses are equal, don't return here + 8b/-> *(esi+0x10) 0/r32/eax + 39/compare *(edi+0x10) 0/r32/eax + 74/jump-if-equal break/disp8 + # if either address is 0, return false + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result + 81 7/subop/compare *(edi+0x10) 0/imm32 + 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result + # if primout-var->register is "*", return true + (string-equal? *(edi+0x10) "*") # Var-register + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 1/imm32/true + 75/jump-if-not-equal $operand-matches-primitive?:end/disp8 + # if string contents don't match, return false + (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 0/imm32/false + 74/jump-if-equal $operand-matches-primitive?:end/disp8 + } + # return true + b8/copy-to-eax 1/imm32/true +$output-operand-matches-primitive?:end: + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + # . 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 @@ -1305,21 +1490,25 @@ test-emit-subx-statement-primitive: 68/push 1/imm32/type-int 68/push "foo"/imm32 89/<- %ecx 4/r32/esp +#? $aa-var-in-ecx: # vars/edx : (stack 1) 51/push-ecx/var-foo 68/push 1/imm32/data-length 68/push 1/imm32/top 89/<- %edx 4/r32/esp - # operand/esi : (list var) +#? $aa-vars-in-edx: + # operand/ebx : (list var) 68/push 0/imm32/next 51/push-ecx/var-foo - 89/<- %esi 4/r32/esp + 89/<- %ebx 4/r32/esp +#? $aa-stmt-operand-in-ebx: # stmt/esi : statement 68/push 0/imm32/next 68/push 0/imm32/outputs - 56/push-esi/operands + 53/push-ebx/operands 68/push "increment"/imm32/operation 89/<- %esi 4/r32/esp +#? $aa-stmt-in-esi: # primitives/ebx : primitive 68/push 0/imm32/next 68/push 0/imm32/no-imm32 @@ -1327,9 +1516,10 @@ test-emit-subx-statement-primitive: 68/push 1/imm32/rm32-is-first-inout 68/push "ff 0/subop/increment"/imm32/subx-name 68/push 0/imm32/outputs - 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call + 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 +$aa-primitive-in-ebx: # convert (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) (flush _test-output-buffered-file) @@ -1361,7 +1551,7 @@ test-emit-subx-statement-primitive-register: # # There's a primitive with this info: # name: 'increment' - # inout: int/reg + # out: int/reg # value: 'ff 0/subop/increment' # # There's nothing in functions. @@ -1384,13 +1574,13 @@ test-emit-subx-statement-primitive-register: 68/push 1/imm32/data-length 68/push 1/imm32/top 89/<- %edx 4/r32/esp - # operand/esi : (list var) + # operand/ebx : (list var) 68/push 0/imm32/next 51/push-ecx/var-foo - 89/<- %esi 4/r32/esp + 89/<- %ebx 4/r32/esp # stmt/esi : statement 68/push 0/imm32/next - 56/push-esi/outputs + 53/push-ebx/outputs 68/push 0/imm32/inouts 68/push "increment"/imm32/operation 89/<- %esi 4/r32/esp @@ -1433,6 +1623,202 @@ test-emit-subx-statement-primitive-register: 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-foo/ecx : var in eax + 68/push "eax"/imm32/register + 68/push 0/imm32/no-stack-offset + 68/push 1/imm32/block-depth + 68/push 1/imm32/type-int + 68/push "foo"/imm32 + 89/<- %ecx 4/r32/esp + # vars/edx : (stack 1) + 51/push-ecx/var-foo + 68/push 1/imm32/data-length + 68/push 1/imm32/top + 89/<- %edx 4/r32/esp + # real-outputs/edi : (list var) + 68/push 0/imm32/next + 51/push-ecx/var-foo + 89/<- %edi 4/r32/esp + # stmt/esi : statement + 68/push 0/imm32/next + 57/push-edi/outputs + 68/push 0/imm32/inouts + 68/push "increment"/imm32/operation + 89/<- %esi 4/r32/esp + # 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 + 68/push 1/imm32/type-int + 68/push "dummy"/imm32 + 89/<- %ebx 4/r32/esp + # formal-outputs/ebx : (list var) + 68/push 0/imm32/next + 53/push-ebx/formal-var + 89/<- %ebx 4/r32/esp + # primitive1/ebx : primitive + 68/push 0/imm32/next + 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 + # primitives/ebx : primitive + 53/push-ebx/next + 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 + (emit-subx-statement _test-output-buffered-file %esi %edx %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/0") + # . reclaim locals + 81 0/subop/add %esp 0x48/imm32 + # . 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-foo/ecx : var in eax + 68/push "eax"/imm32/register + 68/push 0/imm32/no-stack-offset + 68/push 1/imm32/block-depth + 68/push 1/imm32/type-int + 68/push "foo"/imm32 + 89/<- %ecx 4/r32/esp + # vars/edx : (stack 1) + 51/push-ecx/var-foo + 68/push 1/imm32/data-length + 68/push 1/imm32/top + 89/<- %edx 4/r32/esp + # inouts/edi : (list var) + 68/push 0/imm32/next + 51/push-ecx/var-foo + 89/<- %edi 4/r32/esp + # stmt/esi : statement + 68/push 0/imm32/next + 68/push 0/imm32/outputs + 57/push-edi/inouts + 68/push "increment"/imm32/operation + 89/<- %esi 4/r32/esp + # 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 + 68/push 1/imm32/type-int + 68/push "dummy"/imm32 + 89/<- %ebx 4/r32/esp + # operand/ebx : (list var) + 68/push 0/imm32/next + 53/push-ebx/formal-var + 89/<- %ebx 4/r32/esp + # primitive1/ebx : primitive + 68/push 0/imm32/next + 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 + # primitives/ebx : primitive + 53/push-ebx/next + 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 + (emit-subx-statement _test-output-buffered-file %esi %edx %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/0") + # . reclaim locals + 81 0/subop/add %esp 0x48/imm32 + # . 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 diff --git a/html/apps/mu.subx.html b/html/apps/mu.subx.html index a4adfb27..c33a1111 100644 --- a/html/apps/mu.subx.html +++ b/html/apps/mu.subx.html @@ -911,9 +911,9 @@ if ('onhashchange' in window) { 870 # 871 (write-buffered %edi *ecx) 872 (write-buffered %edi ":\n") - 873 (emit-subx-prologue %edi) + 873 (emit-subx-prologue %edi) 874 (emit-subx-block %edi *(ecx+0x10)) # Function-body - 875 (emit-subx-epilogue %edi) + 875 (emit-subx-epilogue %edi) 876 $emit-subx-function:end: 877 # . restore registers 878 5f/pop-to-edi @@ -945,7 +945,7 @@ if ('onhashchange' in window) { 904 # if stmt matches a primitive, emit it 905 { 906 $emit-subx-statement:primitive: - 907 (find-matching-primitive *(ebp+0x14) *(ebp+0xc)) # primitives, stmt => curr/eax + 907 (find-matching-primitive *(ebp+0x14) *(ebp+0xc)) # primitives, stmt => curr/eax 908 3d/compare-eax-and 0/imm32 909 74/jump-if-equal break/disp8 910 (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr @@ -954,10 +954,10 @@ if ('onhashchange' in window) { 913 # else if stmt matches a function, emit a call to it 914 { 915 $emit-subx-statement:call: - 916 (find-matching-function *(ebp+0x18) *(ebp+0xc)) # functions, stmt => curr/eax + 916 (find-matching-function *(ebp+0x18) *(ebp+0xc)) # functions, stmt => curr/eax 917 3d/compare-eax-and 0/imm32 918 74/jump-if-equal break/disp8 - 919 (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr + 919 (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr 920 e9/jump $emit-subx-statement:end/disp32 921 } 922 # else abort @@ -1020,7 +1020,7 @@ if ('onhashchange' in window) { 979 74/jump-if-equal $emit-subx-rm32:end/disp8 980 # 981 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax - 982 (emit-subx-call-operand *(ebp+8) %eax) # out, var + 982 (emit-subx-call-operand *(ebp+8) %eax) # out, var 983 $emit-subx-rm32:end: 984 # . restore registers 985 58/pop-to-eax @@ -1127,440 +1127,816 @@ if ('onhashchange' in window) { 1086 5d/pop-to-ebp 1087 c3/return 1088 -1089 #? # var args/ecx : (list var) = stmt->inouts -1090 #? 8b/-> *(ebp+0xc) 1/r32/ecx -1091 #? 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts -1092 #? { -1093 #? # if (curr == null) break -1094 #? 81 7/subop/compare %ecx 0/imm32 -1095 #? 74/jump-if-equal break/disp8 -1096 #? # -1097 #? (emit-subx-call-operand *(ebp+8) *ecx) -1098 #? # args = args->next -1099 #? 8b/-> *(ecx+4) 1/r32/ecx -1100 #? } -1101 -1102 emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) -1103 # . prologue -1104 55/push-ebp -1105 89/<- %ebp 4/r32/esp -1106 # . save registers -1107 50/push-eax -1108 51/push-ecx -1109 # -1110 (write-buffered *(ebp+8) "(") -1111 # - emit function name -1112 8b/-> *(ebp+0x14) 1/r32/ecx -1113 (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name -1114 # - emit arguments -1115 # var curr/ecx : (list var) = stmt->inouts -1116 8b/-> *(ebp+0xc) 1/r32/ecx -1117 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts -1118 { -1119 # if (curr == null) break -1120 81 7/subop/compare %ecx 0/imm32 -1121 74/jump-if-equal break/disp8 -1122 # -1123 (emit-subx-call-operand *(ebp+8) *ecx) -1124 # curr = curr->next -1125 8b/-> *(ecx+4) 1/r32/ecx -1126 } -1127 # -1128 (write-buffered *(ebp+8) ")") -1129 $emit-subx-call:end: -1130 # . restore registers -1131 59/pop-to-ecx -1132 58/pop-to-eax -1133 # . epilogue -1134 89/<- %esp 5/r32/ebp -1135 5d/pop-to-ebp -1136 c3/return -1137 -1138 emit-subx-call-operand: # out : (address buffered-file), operand : (address variable) -1139 # . prologue -1140 55/push-ebp -1141 89/<- %ebp 4/r32/esp -1142 # . save registers -1143 50/push-eax -1144 # eax = operand -1145 8b/-> *(ebp+0xc) 0/r32/eax -1146 # if (operand->register) emit "%__" -1147 { -1148 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register -1149 74/jump-if-equal break/disp8 -1150 $emit-subx-call-operand:register: -1151 (write-buffered *(ebp+8) " %") -1152 (write-buffered *(ebp+8) *(eax+0x10)) # Var-register -1153 } -1154 # else if (operand->stack-offset) emit "*(ebp+__)" -1155 { -1156 81 7/subop/compare *(eax+0xc) 0/imm32 # Var-stack-offset -1157 74/jump-if-equal break/disp8 -1158 $emit-subx-call-operand:stack: -1159 (write-buffered *(ebp+8) Space) -1160 (write-buffered *(ebp+8) "*(ebp+") -1161 8b/-> *(ebp+0xc) 0/r32/eax -1162 (print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-stack-offset -1163 (write-buffered *(ebp+8) ")") -1164 } -1165 $emit-subx-call-operand:end: -1166 # . restore registers -1167 58/pop-to-eax -1168 # . epilogue -1169 89/<- %esp 5/r32/ebp -1170 5d/pop-to-ebp -1171 c3/return -1172 -1173 find-matching-function: # functions : (address function), stmt : (address statement) -> result/eax : (address function) -1174 # . prologue -1175 55/push-ebp -1176 89/<- %ebp 4/r32/esp -1177 # . save registers -1178 51/push-ecx -1179 # var curr/ecx : (address function) = functions -1180 8b/-> *(ebp+8) 1/r32/ecx -1181 { -1182 # if (curr == null) break -1183 81 7/subop/compare %ecx 0/imm32 -1184 74/jump-if-equal break/disp8 -1185 # if match(curr, stmt) return curr -1186 { -1187 (mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax -1188 3d/compare-eax-and 0/imm32 -1189 74/jump-if-equal break/disp8 -1190 89/<- %eax 1/r32/ecx -1191 eb/jump $find-matching-function:end/disp8 -1192 } -1193 # curr = curr->next -1194 8b/-> *(ecx+0x10) 1/r32/ecx # Function-next -1195 eb/jump loop/disp8 -1196 } -1197 # return null -1198 b8/copy-to-eax 0/imm32 -1199 $find-matching-function:end: -1200 # . restore registers -1201 59/pop-to-ecx -1202 # . epilogue -1203 89/<- %esp 5/r32/ebp -1204 5d/pop-to-ebp -1205 c3/return -1206 -1207 find-matching-primitive: # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive) -1208 # . prologue -1209 55/push-ebp -1210 89/<- %ebp 4/r32/esp -1211 # . save registers -1212 51/push-ecx -1213 # var curr/ecx : (address primitive) = primitives -1214 8b/-> *(ebp+8) 1/r32/ecx -1215 { -1216 # if (curr == null) break -1217 81 7/subop/compare %ecx 0/imm32 -1218 74/jump-if-equal break/disp8 -1219 # if match(curr, stmt) return curr -1220 { -1221 (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax -1222 3d/compare-eax-and 0/imm32 -1223 74/jump-if-equal break/disp8 -1224 89/<- %eax 1/r32/ecx -1225 eb/jump $find-matching-function:end/disp8 -1226 } -1227 # curr = curr->next -1228 8b/-> *(ecx+0x18) 1/r32/ecx # Primitive-next -1229 eb/jump loop/disp8 -1230 } -1231 # return null -1232 b8/copy-to-eax 0/imm32 -1233 $find-matching-primitive:end: -1234 # . restore registers -1235 59/pop-to-ecx -1236 # . epilogue -1237 89/<- %esp 5/r32/ebp -1238 5d/pop-to-ebp -1239 c3/return -1240 -1241 mu-stmt-matches-function?: # stmt : (address statement), function : (address opcode-info) => result/eax : boolean -1242 # . prologue -1243 55/push-ebp -1244 89/<- %ebp 4/r32/esp -1245 # . save registers -1246 51/push-ecx -1247 # return primitive->name == stmt->operation -1248 8b/-> *(ebp+8) 1/r32/ecx -1249 8b/-> *(ebp+0xc) 0/r32/eax -1250 (string-equal? *ecx *eax) # => eax -1251 $mu-stmt-matches-function?:end: -1252 # . restore registers -1253 59/pop-to-ecx -1254 # . epilogue -1255 89/<- %esp 5/r32/ebp -1256 5d/pop-to-ebp -1257 c3/return -1258 -1259 mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address primitive) => result/eax : boolean -1260 # . prologue -1261 55/push-ebp -1262 89/<- %ebp 4/r32/esp -1263 # . save registers -1264 51/push-ecx -1265 # return primitive->name == stmt->operation -1266 8b/-> *(ebp+8) 1/r32/ecx -1267 8b/-> *(ebp+0xc) 0/r32/eax -1268 (string-equal? *ecx *eax) # => eax -1269 $mu-stmt-matches-primitive?:end: -1270 # . restore registers -1271 59/pop-to-ecx -1272 # . epilogue -1273 89/<- %esp 5/r32/ebp -1274 5d/pop-to-ebp -1275 c3/return -1276 -1277 test-emit-subx-statement-primitive: -1278 # Primitive operation on a variable on the stack. -1279 # increment foo -1280 # => -1281 # ff 0/subop/increment *(ebp-8) -1282 # -1283 # There's a variable on the var stack as follows: -1284 # name: 'foo' -1285 # type: int -1286 # stack-offset: -8 -1287 # -1288 # There's a primitive with this info: -1289 # name: 'increment' -1290 # inouts: int/mem -1291 # value: 'ff 0/subop/increment' -1292 # -1293 # There's nothing in functions. -1294 # -1295 # . prologue -1296 55/push-ebp -1297 89/<- %ebp 4/r32/esp -1298 # setup -1299 (clear-stream _test-output-stream) -1300 (clear-stream _test-output-buffered-file->buffer) -1301 # var-foo/ecx : var -1302 68/push 0/imm32/no-register -1303 68/push -8/imm32/stack-offset -1304 68/push 1/imm32/block-depth -1305 68/push 1/imm32/type-int -1306 68/push "foo"/imm32 -1307 89/<- %ecx 4/r32/esp -1308 # vars/edx : (stack 1) -1309 51/push-ecx/var-foo -1310 68/push 1/imm32/data-length -1311 68/push 1/imm32/top -1312 89/<- %edx 4/r32/esp -1313 # operand/esi : (list var) -1314 68/push 0/imm32/next -1315 51/push-ecx/var-foo -1316 89/<- %esi 4/r32/esp -1317 # stmt/esi : statement -1318 68/push 0/imm32/next -1319 68/push 0/imm32/outputs -1320 56/push-esi/operands -1321 68/push "increment"/imm32/operation -1322 89/<- %esi 4/r32/esp -1323 # primitives/ebx : primitive -1324 68/push 0/imm32/next -1325 68/push 0/imm32/no-imm32 -1326 68/push 0/imm32/no-r32 -1327 68/push 1/imm32/rm32-is-first-inout -1328 68/push "ff 0/subop/increment"/imm32/subx-name -1329 68/push 0/imm32/outputs -1330 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call -1331 68/push "increment"/imm32/name -1332 89/<- %ebx 4/r32/esp -1333 # convert -1334 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) -1335 (flush _test-output-buffered-file) -1336 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1342 # check output -1343 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive/0") -1344 # . reclaim locals -1345 81 0/subop/add %esp 0x48/imm32 -1346 # . epilogue -1347 89/<- %esp 5/r32/ebp -1348 5d/pop-to-ebp -1349 c3/return -1350 -1351 test-emit-subx-statement-primitive-register: -1352 # Primitive operation on a variable in a register. -1353 # foo <- increment -1354 # => -1355 # ff 0/subop/increment %eax # sub-optimal, but should suffice -1356 # -1357 # There's a variable on the var stack as follows: -1358 # name: 'foo' -1359 # type: int -1360 # register: 'eax' -1361 # -1362 # There's a primitive with this info: -1363 # name: 'increment' -1364 # inout: int/reg -1365 # value: 'ff 0/subop/increment' -1366 # -1367 # There's nothing in functions. -1368 # -1369 # . prologue -1370 55/push-ebp -1371 89/<- %ebp 4/r32/esp -1372 # setup -1373 (clear-stream _test-output-stream) -1374 (clear-stream _test-output-buffered-file->buffer) -1375 # var-foo/ecx : var in eax -1376 68/push "eax"/imm32/register -1377 68/push 0/imm32/no-stack-offset -1378 68/push 1/imm32/block-depth -1379 68/push 1/imm32/type-int -1380 68/push "foo"/imm32 -1381 89/<- %ecx 4/r32/esp -1382 # vars/edx : (stack 1) -1383 51/push-ecx/var-foo -1384 68/push 1/imm32/data-length -1385 68/push 1/imm32/top -1386 89/<- %edx 4/r32/esp -1387 # operand/esi : (list var) -1388 68/push 0/imm32/next -1389 51/push-ecx/var-foo -1390 89/<- %esi 4/r32/esp -1391 # stmt/esi : statement -1392 68/push 0/imm32/next -1393 56/push-esi/outputs -1394 68/push 0/imm32/inouts -1395 68/push "increment"/imm32/operation -1396 89/<- %esi 4/r32/esp -1397 # formal-var/ebx : var in any register -1398 68/push Any-register/imm32 -1399 68/push 0/imm32/no-stack-offset -1400 68/push 1/imm32/block-depth -1401 68/push 1/imm32/type-int -1402 68/push "dummy"/imm32 -1403 89/<- %ebx 4/r32/esp -1404 # operand/ebx : (list var) -1405 68/push 0/imm32/next -1406 53/push-ebx/formal-var -1407 89/<- %ebx 4/r32/esp -1408 # primitives/ebx : primitive -1409 68/push 0/imm32/next -1410 68/push 0/imm32/no-imm32 -1411 68/push 0/imm32/no-r32 -1412 68/push 3/imm32/rm32-in-first-output -1413 68/push "ff 0/subop/increment"/imm32/subx-name -1414 53/push-ebx/outputs -1415 68/push 0/imm32/inouts -1416 68/push "increment"/imm32/name -1417 89/<- %ebx 4/r32/esp -1418 # convert -1419 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) -1420 (flush _test-output-buffered-file) -1421 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1427 # check output -1428 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register/0") -1429 # . reclaim locals -1430 81 0/subop/add %esp 0x48/imm32 -1431 # . epilogue -1432 89/<- %esp 5/r32/ebp -1433 5d/pop-to-ebp -1434 c3/return -1435 -1436 test-emit-subx-statement-function-call: -1437 # Call a function on a variable on the stack. -1438 # f foo -1439 # => -1440 # (f2 *(ebp-8)) -1441 # (Changing the function name supports overloading in general, but here it -1442 # just serves to help disambiguate things.) -1443 # -1444 # There's a variable on the var stack as follows: -1445 # name: 'foo' -1446 # type: int -1447 # stack-offset: -8 -1448 # -1449 # There's nothing in primitives. -1450 # -1451 # There's a function with this info: -1452 # name: 'f' -1453 # inout: int/mem -1454 # value: 'f2' -1455 # -1456 # . prologue -1457 55/push-ebp -1458 89/<- %ebp 4/r32/esp -1459 # setup -1460 (clear-stream _test-output-stream) -1461 (clear-stream _test-output-buffered-file->buffer) -1462 # var-foo/ecx : var -1463 68/push 0/imm32/no-register -1464 68/push -8/imm32/stack-offset -1465 68/push 0/imm32/block-depth -1466 68/push 1/imm32/type-int -1467 68/push "foo"/imm32 -1468 89/<- %ecx 4/r32/esp -1469 # vars/edx = (stack 1) -1470 51/push-ecx/var-foo -1471 68/push 1/imm32/data-length -1472 68/push 1/imm32/top -1473 89/<- %edx 4/r32/esp -1474 # operands/esi : (list var) -1475 68/push 0/imm32/next -1476 51/push-ecx/var-foo -1477 89/<- %esi 4/r32/esp -1478 # stmt/esi : statement -1479 68/push 0/imm32/next -1480 68/push 0/imm32/outputs -1481 56/push-esi/inouts -1482 68/push "f"/imm32/operation -1483 89/<- %esi 4/r32/esp -1484 # functions/ebx : function -1485 68/push 0/imm32/next -1486 68/push 0/imm32/body -1487 68/push 0/imm32/outputs -1488 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call -1489 68/push "f2"/imm32/subx-name -1490 68/push "f"/imm32/name -1491 89/<- %ebx 4/r32/esp -1492 # convert -1493 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) -1494 (flush _test-output-buffered-file) -1495 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1501 # check output -1502 (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call/0") -1503 # . reclaim locals -1504 81 0/subop/add %esp 0x3c/imm32 -1505 # . epilogue -1506 89/<- %esp 5/r32/ebp -1507 5d/pop-to-ebp -1508 c3/return -1509 -1510 emit-subx-prologue: # out : (address buffered-file) -1511 # . prologue -1512 55/push-ebp -1513 89/<- %ebp 4/r32/esp -1514 # -1515 (write-buffered *(ebp+8) "# . prologue\n") -1516 (write-buffered *(ebp+8) "55/push-ebp\n") -1517 (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n") -1518 $emit-subx-prologue:end: -1519 # . epilogue -1520 89/<- %esp 5/r32/ebp -1521 5d/pop-to-ebp -1522 c3/return -1523 -1524 emit-subx-epilogue: # out : (address buffered-file) -1525 # . prologue -1526 55/push-ebp -1527 89/<- %ebp 4/r32/esp -1528 # -1529 (write-buffered *(ebp+8) "# . epilogue\n") -1530 (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n") -1531 (write-buffered *(ebp+8) "5d/pop-to-ebp\n") -1532 (write-buffered *(ebp+8) "c3/return\n") -1533 $emit-subx-epilogue:end: -1534 # . epilogue -1535 89/<- %esp 5/r32/ebp -1536 5d/pop-to-ebp -1537 c3/return +1089 emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) +1090 # . prologue +1091 55/push-ebp +1092 89/<- %ebp 4/r32/esp +1093 # . save registers +1094 50/push-eax +1095 51/push-ecx +1096 # +1097 (write-buffered *(ebp+8) "(") +1098 # - emit function name +1099 8b/-> *(ebp+0x14) 1/r32/ecx +1100 (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name +1101 # - emit arguments +1102 # var curr/ecx : (list var) = stmt->inouts +1103 8b/-> *(ebp+0xc) 1/r32/ecx +1104 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts +1105 { +1106 # if (curr == null) break +1107 81 7/subop/compare %ecx 0/imm32 +1108 74/jump-if-equal break/disp8 +1109 # +1110 (emit-subx-call-operand *(ebp+8) *ecx) +1111 # curr = curr->next +1112 8b/-> *(ecx+4) 1/r32/ecx +1113 } +1114 # +1115 (write-buffered *(ebp+8) ")") +1116 $emit-subx-call:end: +1117 # . restore registers +1118 59/pop-to-ecx +1119 58/pop-to-eax +1120 # . epilogue +1121 89/<- %esp 5/r32/ebp +1122 5d/pop-to-ebp +1123 c3/return +1124 +1125 emit-subx-call-operand: # out : (address buffered-file), operand : (address variable) +1126 # . prologue +1127 55/push-ebp +1128 89/<- %ebp 4/r32/esp +1129 # . save registers +1130 50/push-eax +1131 # eax = operand +1132 8b/-> *(ebp+0xc) 0/r32/eax +1133 # if (operand->register) emit "%__" +1134 { +1135 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register +1136 74/jump-if-equal break/disp8 +1137 $emit-subx-call-operand:register: +1138 (write-buffered *(ebp+8) " %") +1139 (write-buffered *(ebp+8) *(eax+0x10)) # Var-register +1140 } +1141 # else if (operand->stack-offset) emit "*(ebp+__)" +1142 { +1143 81 7/subop/compare *(eax+0xc) 0/imm32 # Var-stack-offset +1144 74/jump-if-equal break/disp8 +1145 $emit-subx-call-operand:stack: +1146 (write-buffered *(ebp+8) Space) +1147 (write-buffered *(ebp+8) "*(ebp+") +1148 8b/-> *(ebp+0xc) 0/r32/eax +1149 (print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-stack-offset +1150 (write-buffered *(ebp+8) ")") +1151 } +1152 $emit-subx-call-operand:end: +1153 # . restore registers +1154 58/pop-to-eax +1155 # . epilogue +1156 89/<- %esp 5/r32/ebp +1157 5d/pop-to-ebp +1158 c3/return +1159 +1160 find-matching-function: # functions : (address function), stmt : (address statement) -> result/eax : (address function) +1161 # . prologue +1162 55/push-ebp +1163 89/<- %ebp 4/r32/esp +1164 # . save registers +1165 51/push-ecx +1166 # var curr/ecx : (address function) = functions +1167 8b/-> *(ebp+8) 1/r32/ecx +1168 { +1169 # if (curr == null) break +1170 81 7/subop/compare %ecx 0/imm32 +1171 74/jump-if-equal break/disp8 +1172 # if match(curr, stmt) return curr +1173 { +1174 (mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax +1175 3d/compare-eax-and 0/imm32 +1176 74/jump-if-equal break/disp8 +1177 89/<- %eax 1/r32/ecx +1178 eb/jump $find-matching-function:end/disp8 +1179 } +1180 # curr = curr->next +1181 8b/-> *(ecx+0x10) 1/r32/ecx # Function-next +1182 eb/jump loop/disp8 +1183 } +1184 # return null +1185 b8/copy-to-eax 0/imm32 +1186 $find-matching-function:end: +1187 # . restore registers +1188 59/pop-to-ecx +1189 # . epilogue +1190 89/<- %esp 5/r32/ebp +1191 5d/pop-to-ebp +1192 c3/return +1193 +1194 find-matching-primitive: # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive) +1195 # . prologue +1196 55/push-ebp +1197 89/<- %ebp 4/r32/esp +1198 # . save registers +1199 51/push-ecx +1200 # var curr/ecx : (address primitive) = primitives +1201 8b/-> *(ebp+8) 1/r32/ecx +1202 { +1203 $find-matching-primitive:loop: +1204 # if (curr == null) break +1205 81 7/subop/compare %ecx 0/imm32 +1206 74/jump-if-equal break/disp8 +1207 # if match(curr, stmt) return curr +1208 { +1209 (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax +1210 3d/compare-eax-and 0/imm32 +1211 74/jump-if-equal break/disp8 +1212 89/<- %eax 1/r32/ecx +1213 eb/jump $find-matching-function:end/disp8 +1214 } +1215 $find-matching-primitive:next-primitive: +1216 # curr = curr->next +1217 8b/-> *(ecx+0x1c) 1/r32/ecx # Primitive-next +1218 eb/jump loop/disp8 +1219 } +1220 # return null +1221 b8/copy-to-eax 0/imm32 +1222 $find-matching-primitive:end: +1223 # . restore registers +1224 59/pop-to-ecx +1225 # . epilogue +1226 89/<- %esp 5/r32/ebp +1227 5d/pop-to-ebp +1228 c3/return +1229 +1230 mu-stmt-matches-function?: # stmt : (address statement), function : (address opcode-info) => result/eax : boolean +1231 # . prologue +1232 55/push-ebp +1233 89/<- %ebp 4/r32/esp +1234 # . save registers +1235 51/push-ecx +1236 # return primitive->name == stmt->operation +1237 8b/-> *(ebp+8) 1/r32/ecx +1238 8b/-> *(ebp+0xc) 0/r32/eax +1239 (string-equal? *ecx *eax) # => eax +1240 $mu-stmt-matches-function?:end: +1241 # . restore registers +1242 59/pop-to-ecx +1243 # . epilogue +1244 89/<- %esp 5/r32/ebp +1245 5d/pop-to-ebp +1246 c3/return +1247 +1248 mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address primitive) => result/eax : boolean +1249 # A mu stmt matches a primitive if the name matches, all the inout vars +1250 # match, and all the output vars match. +1251 # Vars match if types match and registers match. +1252 # In addition, a stmt output matches a primitive's output if types match +1253 # and the primitive has a wildcard register. +1254 # . prologue +1255 55/push-ebp +1256 89/<- %ebp 4/r32/esp +1257 # . save registers +1258 51/push-ecx +1259 52/push-edx +1260 53/push-ebx +1261 56/push-esi +1262 57/push-edi +1263 # ecx = stmt +1264 8b/-> *(ebp+8) 1/r32/ecx +1265 # edx = primitive +1266 8b/-> *(ebp+0xc) 2/r32/edx +1267 { +1268 $mu-stmt-matches-primitive?:check-name: +1269 # if (primitive->name != stmt->operation) return false +1270 (string-equal? *ecx *edx) # => eax +1271 3d/compare-eax-and 0/imm32 +1272 75/jump-if-not-equal break/disp8 +1273 b8/copy-to-eax 0/imm32 +1274 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1275 } +1276 $mu-stmt-matches-primitive?:check-inouts: +1277 # curr = stmt->inouts +1278 8b/-> *(ecx+4) 6/r32/esi # Stmt-inouts +1279 # curr2 = primitive->inouts +1280 8b/-> *(edx+4) 7/r32/edi # Primitive-inouts +1281 { +1282 # if (curr == 0) return (curr2 == 0) +1283 { +1284 81 7/subop/compare %esi 0/imm32 +1285 75/jump-if-not-equal break/disp8 +1286 { +1287 81 7/subop/compare %edi 0/imm32 +1288 75/jump-if-not-equal break/disp8 +1289 # return true +1290 b8/copy-to-eax 1/imm32 +1291 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1292 } +1293 # return false +1294 b8/copy-to-eax 0/imm32 +1295 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1296 } +1297 # if (curr2 == 0) return false +1298 { +1299 81 7/subop/compare %edi 0/imm32 +1300 75/jump-if-not-equal break/disp8 +1301 b8/copy-to-eax 0/imm32 +1302 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1303 } +1304 # if (curr != curr2) return false +1305 { +1306 (operand-matches-primitive? *esi *edi) # => eax +1307 3d/compare-eax-and 0/imm32 +1308 75/jump-if-not-equal break/disp8 +1309 b8/copy-to-eax 0/imm32 +1310 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1311 } +1312 # curr=curr->next +1313 8b/-> *(ecx+4) 1/r32/ecx # Operand-next +1314 # curr2=curr2->next +1315 8b/-> *(edx+4) 2/r32/edx # Operand-next +1316 } +1317 $mu-stmt-matches-primitive?:check-outputs: +1318 # ecx = stmt +1319 8b/-> *(ebp+8) 1/r32/ecx +1320 # edx = primitive +1321 8b/-> *(ebp+0xc) 2/r32/edx +1322 # curr = stmt->outputs +1323 8b/-> *(ecx+8) 6/r32/esi # Stmt-outputs +1324 # curr2 = primitive->outputs +1325 8b/-> *(edx+8) 7/r32/edi # Primitive-outputs +1326 { +1327 # if (curr == 0) return (curr2 == 0) +1328 { +1329 81 7/subop/compare %esi 0/imm32 +1330 75/jump-if-not-equal break/disp8 +1331 { +1332 81 7/subop/compare %edi 0/imm32 +1333 75/jump-if-not-equal break/disp8 +1334 # return true +1335 b8/copy-to-eax 1/imm32 +1336 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1337 } +1338 # return false +1339 b8/copy-to-eax 0/imm32 +1340 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1341 } +1342 # if (curr2 == 0) return false +1343 { +1344 81 7/subop/compare %edi 0/imm32 +1345 75/jump-if-not-equal break/disp8 +1346 b8/copy-to-eax 0/imm32 +1347 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1348 } +1349 # if (curr != curr2) return false +1350 { +1351 (output-operand-matches-primitive? *esi *edi) # => eax +1352 3d/compare-eax-and 0/imm32 +1353 75/jump-if-not-equal break/disp8 +1354 b8/copy-to-eax 0/imm32 +1355 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1356 } +1357 # curr=curr->next +1358 8b/-> *(ecx+4) 1/r32/ecx # Operand-next +1359 # curr2=curr2->next +1360 8b/-> *(edx+4) 2/r32/edx # Operand-next +1361 } +1362 $mu-stmt-matches-primitive?:return-true: +1363 b8/copy-to-eax 1/imm32 +1364 $mu-stmt-matches-primitive?:end: +1365 # . restore registers +1366 5f/pop-to-edi +1367 5e/pop-to-esi +1368 5b/pop-to-ebx +1369 5a/pop-to-edx +1370 59/pop-to-ecx +1371 # . epilogue +1372 89/<- %esp 5/r32/ebp +1373 5d/pop-to-ebp +1374 c3/return +1375 +1376 operand-matches-primitive?: # var1 : (address var), var2 : (address var) => result/eax : boolean +1377 # . prologue +1378 55/push-ebp +1379 89/<- %ebp 4/r32/esp +1380 # . save registers +1381 56/push-esi +1382 57/push-edi +1383 # esi = var1 +1384 8b/-> *(ebp+8) 6/r32/esi +1385 # edi = var2 +1386 8b/-> *(ebp+0xc) 7/r32/edi +1387 # if (var1->type != var2->type) return false +1388 # if (var1->register != var1->register) return false +1389 { +1390 # if addresses are equal, don't return here +1391 8b/-> *(esi+0x10) 0/r32/eax +1392 39/compare *(edi+0x10) 0/r32/eax +1393 74/jump-if-equal break/disp8 +1394 # if either address is 0, return false +1395 3d/compare-eax-and 0/imm32 +1396 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var1->register to result +1397 81 7/subop/compare *(edi+0x10) 0/imm32 +1398 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var1->register to result +1399 # if string contents don't match, return false +1400 (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register +1401 3d/compare-eax-and 0/imm32 +1402 b8/copy-to-eax 0/imm32/false +1403 74/jump-if-equal $operand-matches-primitive?:end/disp8 +1404 } +1405 # return true +1406 b8/copy-to-eax 1/imm32/true +1407 $operand-matches-primitive?:end: +1408 # . restore registers +1409 5f/pop-to-edi +1410 5e/pop-to-esi +1411 # . epilogue +1412 89/<- %esp 5/r32/ebp +1413 5d/pop-to-ebp +1414 c3/return +1415 +1416 # like operand-matches-primitive? but also handles "*" register in primitive +1417 output-operand-matches-primitive?: # var : (address var), primout-var : (address var) => result/eax : boolean +1418 # . prologue +1419 55/push-ebp +1420 89/<- %ebp 4/r32/esp +1421 # . save registers +1422 56/push-esi +1423 57/push-edi +1424 # esi = var +1425 8b/-> *(ebp+8) 6/r32/esi +1426 # edi = primout-var +1427 8b/-> *(ebp+0xc) 7/r32/edi +1428 # if (var->type != primout-var->type) return false +1429 # return false if var->register doesn't match primout-var->register +1430 { +1431 # if addresses are equal, don't return here +1432 8b/-> *(esi+0x10) 0/r32/eax +1433 39/compare *(edi+0x10) 0/r32/eax +1434 74/jump-if-equal break/disp8 +1435 # if either address is 0, return false +1436 3d/compare-eax-and 0/imm32 +1437 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result +1438 81 7/subop/compare *(edi+0x10) 0/imm32 +1439 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result +1440 # if primout-var->register is "*", return true +1441 (string-equal? *(edi+0x10) "*") # Var-register +1442 3d/compare-eax-and 0/imm32 +1443 b8/copy-to-eax 1/imm32/true +1444 75/jump-if-not-equal $operand-matches-primitive?:end/disp8 +1445 # if string contents don't match, return false +1446 (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register +1447 3d/compare-eax-and 0/imm32 +1448 b8/copy-to-eax 0/imm32/false +1449 74/jump-if-equal $operand-matches-primitive?:end/disp8 +1450 } +1451 # return true +1452 b8/copy-to-eax 1/imm32/true +1453 $output-operand-matches-primitive?:end: +1454 # . restore registers +1455 5f/pop-to-edi +1456 5e/pop-to-esi +1457 # . epilogue +1458 89/<- %esp 5/r32/ebp +1459 5d/pop-to-ebp +1460 c3/return +1461 +1462 test-emit-subx-statement-primitive: +1463 # Primitive operation on a variable on the stack. +1464 # increment foo +1465 # => +1466 # ff 0/subop/increment *(ebp-8) +1467 # +1468 # There's a variable on the var stack as follows: +1469 # name: 'foo' +1470 # type: int +1471 # stack-offset: -8 +1472 # +1473 # There's a primitive with this info: +1474 # name: 'increment' +1475 # inouts: int/mem +1476 # value: 'ff 0/subop/increment' +1477 # +1478 # There's nothing in functions. +1479 # +1480 # . prologue +1481 55/push-ebp +1482 89/<- %ebp 4/r32/esp +1483 # setup +1484 (clear-stream _test-output-stream) +1485 (clear-stream _test-output-buffered-file->buffer) +1486 # var-foo/ecx : var +1487 68/push 0/imm32/no-register +1488 68/push -8/imm32/stack-offset +1489 68/push 1/imm32/block-depth +1490 68/push 1/imm32/type-int +1491 68/push "foo"/imm32 +1492 89/<- %ecx 4/r32/esp +1493 #? $aa-var-in-ecx: +1494 # vars/edx : (stack 1) +1495 51/push-ecx/var-foo +1496 68/push 1/imm32/data-length +1497 68/push 1/imm32/top +1498 89/<- %edx 4/r32/esp +1499 #? $aa-vars-in-edx: +1500 # operand/ebx : (list var) +1501 68/push 0/imm32/next +1502 51/push-ecx/var-foo +1503 89/<- %ebx 4/r32/esp +1504 #? $aa-stmt-operand-in-ebx: +1505 # stmt/esi : statement +1506 68/push 0/imm32/next +1507 68/push 0/imm32/outputs +1508 53/push-ebx/operands +1509 68/push "increment"/imm32/operation +1510 89/<- %esi 4/r32/esp +1511 #? $aa-stmt-in-esi: +1512 # primitives/ebx : primitive +1513 68/push 0/imm32/next +1514 68/push 0/imm32/no-imm32 +1515 68/push 0/imm32/no-r32 +1516 68/push 1/imm32/rm32-is-first-inout +1517 68/push "ff 0/subop/increment"/imm32/subx-name +1518 68/push 0/imm32/outputs +1519 53/push-ebx/inouts # hack; in practice we won't have the same var in function definition and call +1520 68/push "increment"/imm32/name +1521 89/<- %ebx 4/r32/esp +1522 $aa-primitive-in-ebx: +1523 # convert +1524 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1525 (flush _test-output-buffered-file) +1526 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1532 # check output +1533 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive/0") +1534 # . reclaim locals +1535 81 0/subop/add %esp 0x48/imm32 +1536 # . epilogue +1537 89/<- %esp 5/r32/ebp +1538 5d/pop-to-ebp +1539 c3/return +1540 +1541 test-emit-subx-statement-primitive-register: +1542 # Primitive operation on a variable in a register. +1543 # foo <- increment +1544 # => +1545 # ff 0/subop/increment %eax # sub-optimal, but should suffice +1546 # +1547 # There's a variable on the var stack as follows: +1548 # name: 'foo' +1549 # type: int +1550 # register: 'eax' +1551 # +1552 # There's a primitive with this info: +1553 # name: 'increment' +1554 # out: int/reg +1555 # value: 'ff 0/subop/increment' +1556 # +1557 # There's nothing in functions. +1558 # +1559 # . prologue +1560 55/push-ebp +1561 89/<- %ebp 4/r32/esp +1562 # setup +1563 (clear-stream _test-output-stream) +1564 (clear-stream _test-output-buffered-file->buffer) +1565 # var-foo/ecx : var in eax +1566 68/push "eax"/imm32/register +1567 68/push 0/imm32/no-stack-offset +1568 68/push 1/imm32/block-depth +1569 68/push 1/imm32/type-int +1570 68/push "foo"/imm32 +1571 89/<- %ecx 4/r32/esp +1572 # vars/edx : (stack 1) +1573 51/push-ecx/var-foo +1574 68/push 1/imm32/data-length +1575 68/push 1/imm32/top +1576 89/<- %edx 4/r32/esp +1577 # operand/ebx : (list var) +1578 68/push 0/imm32/next +1579 51/push-ecx/var-foo +1580 89/<- %ebx 4/r32/esp +1581 # stmt/esi : statement +1582 68/push 0/imm32/next +1583 53/push-ebx/outputs +1584 68/push 0/imm32/inouts +1585 68/push "increment"/imm32/operation +1586 89/<- %esi 4/r32/esp +1587 # formal-var/ebx : var in any register +1588 68/push Any-register/imm32 +1589 68/push 0/imm32/no-stack-offset +1590 68/push 1/imm32/block-depth +1591 68/push 1/imm32/type-int +1592 68/push "dummy"/imm32 +1593 89/<- %ebx 4/r32/esp +1594 # operand/ebx : (list var) +1595 68/push 0/imm32/next +1596 53/push-ebx/formal-var +1597 89/<- %ebx 4/r32/esp +1598 # primitives/ebx : primitive +1599 68/push 0/imm32/next +1600 68/push 0/imm32/no-imm32 +1601 68/push 0/imm32/no-r32 +1602 68/push 3/imm32/rm32-in-first-output +1603 68/push "ff 0/subop/increment"/imm32/subx-name +1604 53/push-ebx/outputs +1605 68/push 0/imm32/inouts +1606 68/push "increment"/imm32/name +1607 89/<- %ebx 4/r32/esp +1608 # convert +1609 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1610 (flush _test-output-buffered-file) +1611 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1617 # check output +1618 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register/0") +1619 # . reclaim locals +1620 81 0/subop/add %esp 0x48/imm32 +1621 # . epilogue +1622 89/<- %esp 5/r32/ebp +1623 5d/pop-to-ebp +1624 c3/return +1625 +1626 test-emit-subx-statement-select-primitive: +1627 # Select the right primitive between overloads. +1628 # foo <- increment +1629 # => +1630 # ff 0/subop/increment %eax # sub-optimal, but should suffice +1631 # +1632 # There's a variable on the var stack as follows: +1633 # name: 'foo' +1634 # type: int +1635 # register: 'eax' +1636 # +1637 # There's two primitives, as follows: +1638 # - name: 'increment' +1639 # out: int/reg +1640 # value: 'ff 0/subop/increment' +1641 # - name: 'increment' +1642 # inout: int/mem +1643 # value: 'ff 0/subop/increment' +1644 # +1645 # There's nothing in functions. +1646 # +1647 # . prologue +1648 55/push-ebp +1649 89/<- %ebp 4/r32/esp +1650 # setup +1651 (clear-stream _test-output-stream) +1652 (clear-stream _test-output-buffered-file->buffer) +1653 # var-foo/ecx : var in eax +1654 68/push "eax"/imm32/register +1655 68/push 0/imm32/no-stack-offset +1656 68/push 1/imm32/block-depth +1657 68/push 1/imm32/type-int +1658 68/push "foo"/imm32 +1659 89/<- %ecx 4/r32/esp +1660 # vars/edx : (stack 1) +1661 51/push-ecx/var-foo +1662 68/push 1/imm32/data-length +1663 68/push 1/imm32/top +1664 89/<- %edx 4/r32/esp +1665 # real-outputs/edi : (list var) +1666 68/push 0/imm32/next +1667 51/push-ecx/var-foo +1668 89/<- %edi 4/r32/esp +1669 # stmt/esi : statement +1670 68/push 0/imm32/next +1671 57/push-edi/outputs +1672 68/push 0/imm32/inouts +1673 68/push "increment"/imm32/operation +1674 89/<- %esi 4/r32/esp +1675 # formal-var/ebx : var in any register +1676 68/push Any-register/imm32 +1677 68/push 0/imm32/no-stack-offset +1678 68/push 1/imm32/block-depth +1679 68/push 1/imm32/type-int +1680 68/push "dummy"/imm32 +1681 89/<- %ebx 4/r32/esp +1682 # formal-outputs/ebx : (list var) +1683 68/push 0/imm32/next +1684 53/push-ebx/formal-var +1685 89/<- %ebx 4/r32/esp +1686 # primitive1/ebx : primitive +1687 68/push 0/imm32/next +1688 68/push 0/imm32/no-imm32 +1689 68/push 0/imm32/no-r32 +1690 68/push 3/imm32/rm32-in-first-output +1691 68/push "ff 0/subop/increment"/imm32/subx-name +1692 53/push-ebx/outputs/formal-outputs +1693 68/push 0/imm32/inouts +1694 68/push "increment"/imm32/name +1695 89/<- %ebx 4/r32/esp +1696 # primitives/ebx : primitive +1697 53/push-ebx/next +1698 68/push 0/imm32/no-imm32 +1699 68/push 0/imm32/no-r32 +1700 68/push 1/imm32/rm32-is-first-inout +1701 68/push "ff 0/subop/increment"/imm32/subx-name +1702 68/push 0/imm32/outputs +1703 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call +1704 68/push "increment"/imm32/name +1705 89/<- %ebx 4/r32/esp +1706 # convert +1707 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1708 (flush _test-output-buffered-file) +1709 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1715 # check output +1716 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive/0") +1717 # . reclaim locals +1718 81 0/subop/add %esp 0x48/imm32 +1719 # . epilogue +1720 89/<- %esp 5/r32/ebp +1721 5d/pop-to-ebp +1722 c3/return +1723 +1724 test-emit-subx-statement-select-primitive-2: +1725 # Select the right primitive between overloads. +1726 # foo <- increment +1727 # => +1728 # ff 0/subop/increment %eax # sub-optimal, but should suffice +1729 # +1730 # There's a variable on the var stack as follows: +1731 # name: 'foo' +1732 # type: int +1733 # register: 'eax' +1734 # +1735 # There's two primitives, as follows: +1736 # - name: 'increment' +1737 # out: int/reg +1738 # value: 'ff 0/subop/increment' +1739 # - name: 'increment' +1740 # inout: int/mem +1741 # value: 'ff 0/subop/increment' +1742 # +1743 # There's nothing in functions. +1744 # +1745 # . prologue +1746 55/push-ebp +1747 89/<- %ebp 4/r32/esp +1748 # setup +1749 (clear-stream _test-output-stream) +1750 (clear-stream _test-output-buffered-file->buffer) +1751 # var-foo/ecx : var in eax +1752 68/push "eax"/imm32/register +1753 68/push 0/imm32/no-stack-offset +1754 68/push 1/imm32/block-depth +1755 68/push 1/imm32/type-int +1756 68/push "foo"/imm32 +1757 89/<- %ecx 4/r32/esp +1758 # vars/edx : (stack 1) +1759 51/push-ecx/var-foo +1760 68/push 1/imm32/data-length +1761 68/push 1/imm32/top +1762 89/<- %edx 4/r32/esp +1763 # inouts/edi : (list var) +1764 68/push 0/imm32/next +1765 51/push-ecx/var-foo +1766 89/<- %edi 4/r32/esp +1767 # stmt/esi : statement +1768 68/push 0/imm32/next +1769 68/push 0/imm32/outputs +1770 57/push-edi/inouts +1771 68/push "increment"/imm32/operation +1772 89/<- %esi 4/r32/esp +1773 # formal-var/ebx : var in any register +1774 68/push Any-register/imm32 +1775 68/push 0/imm32/no-stack-offset +1776 68/push 1/imm32/block-depth +1777 68/push 1/imm32/type-int +1778 68/push "dummy"/imm32 +1779 89/<- %ebx 4/r32/esp +1780 # operand/ebx : (list var) +1781 68/push 0/imm32/next +1782 53/push-ebx/formal-var +1783 89/<- %ebx 4/r32/esp +1784 # primitive1/ebx : primitive +1785 68/push 0/imm32/next +1786 68/push 0/imm32/no-imm32 +1787 68/push 0/imm32/no-r32 +1788 68/push 3/imm32/rm32-in-first-output +1789 68/push "ff 0/subop/increment"/imm32/subx-name +1790 53/push-ebx/outputs/formal-outputs +1791 68/push 0/imm32/inouts +1792 68/push "increment"/imm32/name +1793 89/<- %ebx 4/r32/esp +1794 # primitives/ebx : primitive +1795 53/push-ebx/next +1796 68/push 0/imm32/no-imm32 +1797 68/push 0/imm32/no-r32 +1798 68/push 1/imm32/rm32-is-first-inout +1799 68/push "ff 0/subop/increment"/imm32/subx-name +1800 68/push 0/imm32/outputs +1801 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call +1802 68/push "increment"/imm32/name +1803 89/<- %ebx 4/r32/esp +1804 # convert +1805 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1806 (flush _test-output-buffered-file) +1807 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1813 # check output +1814 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2/0") +1815 # . reclaim locals +1816 81 0/subop/add %esp 0x48/imm32 +1817 # . epilogue +1818 89/<- %esp 5/r32/ebp +1819 5d/pop-to-ebp +1820 c3/return +1821 +1822 test-emit-subx-statement-function-call: +1823 # Call a function on a variable on the stack. +1824 # f foo +1825 # => +1826 # (f2 *(ebp-8)) +1827 # (Changing the function name supports overloading in general, but here it +1828 # just serves to help disambiguate things.) +1829 # +1830 # There's a variable on the var stack as follows: +1831 # name: 'foo' +1832 # type: int +1833 # stack-offset: -8 +1834 # +1835 # There's nothing in primitives. +1836 # +1837 # There's a function with this info: +1838 # name: 'f' +1839 # inout: int/mem +1840 # value: 'f2' +1841 # +1842 # . prologue +1843 55/push-ebp +1844 89/<- %ebp 4/r32/esp +1845 # setup +1846 (clear-stream _test-output-stream) +1847 (clear-stream _test-output-buffered-file->buffer) +1848 # var-foo/ecx : var +1849 68/push 0/imm32/no-register +1850 68/push -8/imm32/stack-offset +1851 68/push 0/imm32/block-depth +1852 68/push 1/imm32/type-int +1853 68/push "foo"/imm32 +1854 89/<- %ecx 4/r32/esp +1855 # vars/edx = (stack 1) +1856 51/push-ecx/var-foo +1857 68/push 1/imm32/data-length +1858 68/push 1/imm32/top +1859 89/<- %edx 4/r32/esp +1860 # operands/esi : (list var) +1861 68/push 0/imm32/next +1862 51/push-ecx/var-foo +1863 89/<- %esi 4/r32/esp +1864 # stmt/esi : statement +1865 68/push 0/imm32/next +1866 68/push 0/imm32/outputs +1867 56/push-esi/inouts +1868 68/push "f"/imm32/operation +1869 89/<- %esi 4/r32/esp +1870 # functions/ebx : function +1871 68/push 0/imm32/next +1872 68/push 0/imm32/body +1873 68/push 0/imm32/outputs +1874 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call +1875 68/push "f2"/imm32/subx-name +1876 68/push "f"/imm32/name +1877 89/<- %ebx 4/r32/esp +1878 # convert +1879 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) +1880 (flush _test-output-buffered-file) +1881 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1887 # check output +1888 (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call/0") +1889 # . reclaim locals +1890 81 0/subop/add %esp 0x3c/imm32 +1891 # . epilogue +1892 89/<- %esp 5/r32/ebp +1893 5d/pop-to-ebp +1894 c3/return +1895 +1896 emit-subx-prologue: # out : (address buffered-file) +1897 # . prologue +1898 55/push-ebp +1899 89/<- %ebp 4/r32/esp +1900 # +1901 (write-buffered *(ebp+8) "# . prologue\n") +1902 (write-buffered *(ebp+8) "55/push-ebp\n") +1903 (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n") +1904 $emit-subx-prologue:end: +1905 # . epilogue +1906 89/<- %esp 5/r32/ebp +1907 5d/pop-to-ebp +1908 c3/return +1909 +1910 emit-subx-epilogue: # out : (address buffered-file) +1911 # . prologue +1912 55/push-ebp +1913 89/<- %ebp 4/r32/esp +1914 # +1915 (write-buffered *(ebp+8) "# . epilogue\n") +1916 (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n") +1917 (write-buffered *(ebp+8) "5d/pop-to-ebp\n") +1918 (write-buffered *(ebp+8) "c3/return\n") +1919 $emit-subx-epilogue:end: +1920 # . epilogue +1921 89/<- %esp 5/r32/ebp +1922 5d/pop-to-ebp +1923 c3/return