6041 - array indexing starting to work
And we're using it now in factorial.mu! In the process I had to fix a couple of bugs in pointer dereferencing. There are still some limitations: a) Indexing by a literal doesn't work yet. b) Only arrays of ints supported so far. Looking ahead, I'm not sure how I can support indexing arrays by non-literals (variables in registers) unless the element size is a power of 2.
This commit is contained in:
parent
1837f61176
commit
fee1bbd8b4
|
@ -1,7 +1,7 @@
|
||||||
# usage is finicky for now:
|
# usage:
|
||||||
# ./translate_mu apps/factorial.mu
|
# ./translate_mu apps/factorial.mu
|
||||||
# ./a.elf test # any args? run tests
|
# ./a.elf test # to run tests
|
||||||
# ./a.elf # no args? run factorial(5)
|
# ./a.elf # to run factorial(5)
|
||||||
fn main args: (addr array kernel-string) -> exit-status/ebx: int {
|
fn main args: (addr array kernel-string) -> exit-status/ebx: int {
|
||||||
var a/eax: (addr array kernel-string) <- copy args
|
var a/eax: (addr array kernel-string) <- copy args
|
||||||
var tmp/ecx: int <- length a
|
var tmp/ecx: int <- length a
|
||||||
|
@ -14,11 +14,15 @@ fn main args: (addr array kernel-string) -> exit-status/ebx: int {
|
||||||
exit-status <- copy tmp
|
exit-status <- copy tmp
|
||||||
break $main-body
|
break $main-body
|
||||||
}
|
}
|
||||||
# if (len(args) != 1) run-tests()
|
# if (args[1] == "test") run-tests()
|
||||||
|
var tmp2/ecx: int <- copy 1 # we need this just because we don't yet support `index` on literals; requires some translation-time computation
|
||||||
|
var tmp3/ecx: (addr kernel-string) <- index a, tmp2
|
||||||
|
var tmp4/eax: boolean <- kernel-string-equal? *tmp3, "test"
|
||||||
|
compare tmp4, 0
|
||||||
{
|
{
|
||||||
break-if-=
|
break-if-=
|
||||||
run-tests
|
run-tests
|
||||||
exit-status <- copy 0
|
exit-status <- copy 0 # TODO: get at Num-test-failures somehow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
104
apps/mu.subx
104
apps/mu.subx
|
@ -32,8 +32,6 @@
|
||||||
# eax ecx edx ebx esi edi
|
# eax ecx edx ebx esi edi
|
||||||
# Variables in registers must be primitive 32-bit types.
|
# Variables in registers must be primitive 32-bit types.
|
||||||
# Variables not explicitly placed in a register are on the stack.
|
# 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
|
# Function inputs are always passed in memory (on the stack), while outputs
|
||||||
# are always returned in registers.
|
# are always returned in registers.
|
||||||
|
@ -955,6 +953,52 @@ test-convert-function-with-local-var-dereferenced:
|
||||||
5d/pop-to-ebp
|
5d/pop-to-ebp
|
||||||
c3/return
|
c3/return
|
||||||
|
|
||||||
|
test-convert-compare-register-with-literal:
|
||||||
|
# . 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 0\n")
|
||||||
|
(write _test-input-stream " compare x, 0\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-compare-register-with-literal/0")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-compare-register-with-literal/1")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-compare-register-with-literal/2")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-compare-register-with-literal/3")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " {" "F - test-convert-compare-register-with-literal/4")
|
||||||
|
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-compare-register-with-literal/5")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-compare-register-with-literal/6")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 0/imm32" "F - test-convert-compare-register-with-literal/7")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " 81 7/subop/compare %ecx 0/imm32" "F - test-convert-compare-register-with-literal/8")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " }" "F - test-convert-compare-register-with-literal/10")
|
||||||
|
(check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-compare-register-with-literal/11")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-compare-register-with-literal/12")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-compare-register-with-literal/13")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-compare-register-with-literal/14")
|
||||||
|
(check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-compare-register-with-literal/15")
|
||||||
|
# . epilogue
|
||||||
|
89/<- %esp 5/r32/ebp
|
||||||
|
5d/pop-to-ebp
|
||||||
|
c3/return
|
||||||
|
|
||||||
test-convert-function-with-local-var-in-block:
|
test-convert-function-with-local-var-in-block:
|
||||||
# . prologue
|
# . prologue
|
||||||
55/push-ebp
|
55/push-ebp
|
||||||
|
@ -3958,11 +4002,12 @@ $add-operation-and-inputs-to-stmt:read-inouts:
|
||||||
3d/compare-eax-and 0x2a/imm32/asterisk
|
3d/compare-eax-and 0x2a/imm32/asterisk
|
||||||
{
|
{
|
||||||
75/jump-if-!= break/disp8
|
75/jump-if-!= break/disp8
|
||||||
|
$add-operation-and-inputs-to-stmt:inout-is-deref:
|
||||||
ff 0/subop/increment *ecx
|
ff 0/subop/increment *ecx
|
||||||
ba/copy-to-edx 1/imm32/true
|
ba/copy-to-edx 1/imm32/true
|
||||||
}
|
}
|
||||||
(lookup-var-or-literal %ecx *(ebp+0x10)) # => eax
|
(lookup-var-or-literal %ecx *(ebp+0x10)) # => eax
|
||||||
(append-list Heap %eax *(edi+8)) # Stmt1-inouts or Regvardef-inouts => eax
|
(append-stmt-var Heap %eax *(edi+8) %edx) # Stmt1-inouts or Regvardef-inouts => eax
|
||||||
89/<- *(edi+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts
|
89/<- *(edi+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts
|
||||||
e9/jump loop/disp32
|
e9/jump loop/disp32
|
||||||
}
|
}
|
||||||
|
@ -5467,6 +5512,38 @@ $emit-subx-stmt-list:array-length:
|
||||||
e9/jump $emit-subx-statement:end/disp32
|
e9/jump $emit-subx-statement:end/disp32
|
||||||
}
|
}
|
||||||
# }}}
|
# }}}
|
||||||
|
# array index {{{
|
||||||
|
# TODO: support literal index
|
||||||
|
{
|
||||||
|
# if (!string-equal?(var->operation, "index")) break
|
||||||
|
(string-equal? *(ecx+4) "index") # Stmt1-operation => eax
|
||||||
|
3d/compare-eax-and 0/imm32
|
||||||
|
0f 84/jump-if-= break/disp32
|
||||||
|
$emit-subx-stmt-list:index:
|
||||||
|
(emit-indent *(ebp+8) *Curr-block-depth)
|
||||||
|
(write-buffered *(ebp+8) "8d/copy-address *(")
|
||||||
|
# inouts[0]->register " + "
|
||||||
|
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts
|
||||||
|
8b/-> *eax 0/r32/eax # List-value
|
||||||
|
(write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax
|
||||||
|
#
|
||||||
|
(write-buffered *(ebp+8) " + ")
|
||||||
|
# inouts[1]->register
|
||||||
|
8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts
|
||||||
|
8b/-> *(eax+4) 0/r32/eax # List-next
|
||||||
|
8b/-> *eax 0/r32/eax # List-value
|
||||||
|
(write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax
|
||||||
|
#
|
||||||
|
(write-buffered *(ebp+8) "<<2 + 4) ")
|
||||||
|
# outputs[0] "/r32"
|
||||||
|
8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs
|
||||||
|
8b/-> *eax 0/r32/eax # List-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
|
# if stmt matches a primitive, emit it
|
||||||
{
|
{
|
||||||
$emit-subx-statement:check-for-primitive:
|
$emit-subx-statement:check-for-primitive:
|
||||||
|
@ -7225,11 +7302,26 @@ emit-subx-call-operand: # out: (addr buffered-file), s: (handle stmt-var)
|
||||||
8b/-> *(ebp+0xc) 1/r32/ecx
|
8b/-> *(ebp+0xc) 1/r32/ecx
|
||||||
# var operand/esi: (handle var) = s->value
|
# var operand/esi: (handle var) = s->value
|
||||||
8b/-> *ecx 6/r32/esi # Stmt-var-value
|
8b/-> *ecx 6/r32/esi # Stmt-var-value
|
||||||
# if (operand->register) emit "%__"
|
# if (operand->register && s->is-deref?) emit "*__"
|
||||||
{
|
{
|
||||||
|
$emit-subx-call-operand:check-for-register-indirect:
|
||||||
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
|
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
|
||||||
74/jump-if-= break/disp8
|
74/jump-if-= break/disp8
|
||||||
$emit-subx-call-operand:register:
|
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
|
||||||
|
74/jump-if-= break/disp8
|
||||||
|
$emit-subx-call-operand:register-indirect:
|
||||||
|
(write-buffered *(ebp+8) " *")
|
||||||
|
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
|
||||||
|
e9/jump $emit-subx-call-operand:end/disp32
|
||||||
|
}
|
||||||
|
# if (operand->register && !s->is-deref?) emit "%__"
|
||||||
|
{
|
||||||
|
$emit-subx-call-operand:check-for-register-direct:
|
||||||
|
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-call-operand:register-direct:
|
||||||
(write-buffered *(ebp+8) " %")
|
(write-buffered *(ebp+8) " %")
|
||||||
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
|
(write-buffered *(ebp+8) *(esi+0x10)) # Var-register
|
||||||
e9/jump $emit-subx-call-operand:end/disp32
|
e9/jump $emit-subx-call-operand:end/disp32
|
||||||
|
@ -7278,6 +7370,7 @@ emit-subx-var-as-rm32: # out: (addr buffered-file), s: (handle stmt-var)
|
||||||
8b/-> *ecx 6/r32/esi # Stmt-var-value
|
8b/-> *ecx 6/r32/esi # Stmt-var-value
|
||||||
# if (operand->register && s->is-deref?) emit "*__"
|
# if (operand->register && s->is-deref?) emit "*__"
|
||||||
{
|
{
|
||||||
|
$emit-subx-var-as-rm32:check-for-register-indirect:
|
||||||
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
|
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
|
||||||
74/jump-if-= break/disp8
|
74/jump-if-= break/disp8
|
||||||
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
|
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
|
||||||
|
@ -7288,6 +7381,7 @@ $emit-subx-var-as-rm32:register-indirect:
|
||||||
}
|
}
|
||||||
# if (operand->register && !s->is-deref?) emit "%__"
|
# if (operand->register && !s->is-deref?) emit "%__"
|
||||||
{
|
{
|
||||||
|
$emit-subx-var-as-rm32:check-for-register-direct:
|
||||||
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
|
81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register
|
||||||
74/jump-if-= break/disp8
|
74/jump-if-= break/disp8
|
||||||
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
|
81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref
|
||||||
|
|
|
@ -205,4 +205,14 @@ loop label {.name="loop", .inouts=[label],
|
||||||
break {.name="break", .subx-name="e9/jump break/disp32"}
|
break {.name="break", .subx-name="e9/jump break/disp32"}
|
||||||
break label {.name="break", .inouts=[label], .subx-name="e9/jump", .disp32=inouts[0] ":break"}
|
break label {.name="break", .inouts=[label], .subx-name="e9/jump", .disp32=inouts[0] ":break"}
|
||||||
|
|
||||||
|
Array operations
|
||||||
|
|
||||||
|
var/reg <- length var2/reg2: (addr array T)
|
||||||
|
{.name="length", .inouts=[reg2], .outputs=[reg1], .subx-name="8b/copy-from", .rm32="*" inouts[0], .r32=outputs[0]}
|
||||||
|
var/reg <- index arr/rega: (addr array T), idx/regi: int
|
||||||
|
{.name="index", .inouts=[rega, regi], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1] "<<2)", .r32=outputs[0]}
|
||||||
|
var/reg <- index arr/rega: (addr array T), n
|
||||||
|
compare var, n {.name="compare", .inouts=[var, n], .subx-name="81 7/subop/compare", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
|
||||||
|
{.name="index", .inouts=[rega, n], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1] "<<2)", .r32=outputs[0]}
|
||||||
|
|
||||||
vim:ft=c:nowrap
|
vim:ft=c:nowrap
|
||||||
|
|
|
@ -194,3 +194,9 @@ Similarly, conditional loops:
|
||||||
loop-if-addr<= label
|
loop-if-addr<= label
|
||||||
loop-if-addr>=
|
loop-if-addr>=
|
||||||
loop-if-addr>= label
|
loop-if-addr>= label
|
||||||
|
|
||||||
|
## Array operations
|
||||||
|
|
||||||
|
var/reg: int <- length var: (addr array T)
|
||||||
|
var/reg: (addr T) <- index var: (addr array T), idx: int
|
||||||
|
var/reg: (addr T) <- index var: (addr array T), n
|
||||||
|
|
Loading…
Reference in New Issue