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:
Kartik Agaram 2020-02-21 10:05:04 -08:00
parent 1837f61176
commit fee1bbd8b4
5 changed files with 124 additions and 10 deletions

View File

@ -1,7 +1,7 @@
# usage is finicky for now:
# usage:
# ./translate_mu apps/factorial.mu
# ./a.elf test # any args? run tests
# ./a.elf # no args? run factorial(5)
# ./a.elf test # to run tests
# ./a.elf # to run factorial(5)
fn main args: (addr array kernel-string) -> exit-status/ebx: int {
var a/eax: (addr array kernel-string) <- copy args
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
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-=
run-tests
exit-status <- copy 0
exit-status <- copy 0 # TODO: get at Num-test-failures somehow
}
}
}

BIN
apps/mu

Binary file not shown.

View File

@ -32,8 +32,6 @@
# eax ecx edx ebx esi edi
# Variables in registers must be primitive 32-bit types.
# Variables not explicitly placed in a register are on the stack.
# Variables in registers need not have a name; in that case you refer to them
# directly by the register name.
#
# Function inputs are always passed in memory (on the stack), while outputs
# are always returned in registers.
@ -955,6 +953,52 @@ test-convert-function-with-local-var-dereferenced:
5d/pop-to-ebp
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:
# . prologue
55/push-ebp
@ -3958,11 +4002,12 @@ $add-operation-and-inputs-to-stmt:read-inouts:
3d/compare-eax-and 0x2a/imm32/asterisk
{
75/jump-if-!= break/disp8
$add-operation-and-inputs-to-stmt:inout-is-deref:
ff 0/subop/increment *ecx
ba/copy-to-edx 1/imm32/true
}
(lookup-var-or-literal %ecx *(ebp+0x10)) # => eax
(append-list Heap %eax *(edi+8)) # Stmt1-inouts or Regvardef-inouts => eax
(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
e9/jump loop/disp32
}
@ -5467,6 +5512,38 @@ $emit-subx-stmt-list:array-length:
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
{
$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
# var operand/esi: (handle var) = s->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
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) *(esi+0x10)) # Var-register
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
# 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
74/jump-if-= break/disp8
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 "%__"
{
$emit-subx-var-as-rm32: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

View File

@ -205,4 +205,14 @@ loop label {.name="loop", .inouts=[label],
break {.name="break", .subx-name="e9/jump break/disp32"}
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

View File

@ -194,3 +194,9 @@ Similarly, conditional loops:
loop-if-addr<= label
loop-if-addr>=
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