From 3b40e3c331312c78d252fab02bd5f8dd53ce59f4 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 17 Nov 2019 20:54:39 -0800 Subject: 5750 We can now compile primitive statements while selecting the right template to code-gen each one from. Even when multiple templates have the same name. --- apps/mu.subx | 436 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 411 insertions(+), 25 deletions(-) (limited to 'apps/mu.subx') 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 -- cgit 1.4.1-2-gfad0