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.
This commit is contained in:
Kartik Agaram 2019-11-17 20:54:39 -08:00
parent eafdbfc103
commit 3b40e3c331
3 changed files with 1227 additions and 465 deletions

BIN
apps/mu

Binary file not shown.

View File

@ -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

File diff suppressed because it is too large Load Diff