commit a19048e596632b05d785590daad0a04d4e4b0bc2
parent 16357fa6dce0888a80d9dcf0a2aad94042a6220c
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Tue, 30 Nov 2021 13:38:37 -0600
expression evaluation at assignment
This required making the data segment RW, and adding an instruction to
move from a register to memory.
Diffstat:
5 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/elf.c b/elf.c
@@ -48,7 +48,7 @@ elf(char *text, size_t len, char* data, size_t dlen, FILE *f)
phdr_data.p_paddr = DATA_OFFSET;
phdr_data.p_filesz = dlen;
phdr_data.p_memsz = dlen;
- phdr_data.p_flags = PF_R;
+ phdr_data.p_flags = PF_R | PF_W;
phdr_data.p_align = 0x1000;
fwrite(&ehdr, 1, sizeof(Elf64_Ehdr), f);
diff --git a/main.c b/main.c
@@ -505,15 +505,32 @@ main(int argc, char *argv[])
}
} else if (item->kind == ITEM_DECL) {
struct expr *expr = &exprs.data[decls.data[item->idx].val];
- if (expr->kind == EXPR_LIT) {
- if (expr->class == C_INT) {
+ switch (expr->class) {
+ case C_INT:
+ // this is sort of an optimization, since we write at compile-time instead of evaluating and storing. should this happen here in the long term?
+ if (expr->kind == EXPR_LIT) {
decls.data[item->idx].addr = data_pushint(expr->d.v.v.val);
- } else if (expr->class == C_STR) {
- size_t addr = data_push(expr->d.v.v.s.ptr, expr->d.v.v.s.len);
- decls.data[item->idx].addr = data_pushint(addr);
+ } else {
+ decls.data[item->idx].addr = data_pushint(0);
+ enum reg reg = getreg();
+ size_t exprlen = genexpr(NULL, decls.data[item->idx].val, reg);
+ size_t movlen = mov_m64_r64(NULL, decls.data[item->idx].addr, reg);
+ char *code = malloc(exprlen + movlen);
+ if (!code)
+ error("genexpr malloc failed");
+
+ genexpr(code, decls.data[item->idx].val, reg);
+ mov_m64_r64(code + exprlen, decls.data[item->idx].addr, reg);
+ array_push((&text), code, exprlen + movlen);
+ freereg(reg);
}
- } else {
- error("cannot allocate memory for expression");
+ break;
+ // FIXME: we assume that any string is a literal, may break if we add binary operands on strings in the future.
+ case C_STR:
+ decls.data[item->idx].addr = data_pushint(data_push(expr->d.v.v.s.ptr, expr->d.v.v.s.len));
+ break;
+ default:
+ error("cannot generate code for unknown expression class");
}
} else {
error("cannot generate code for type");
diff --git a/prog.nc b/prog.nc
@@ -4,7 +4,7 @@ exit i64 = 60
len1 i64 = 11
hello str = "hello "
world str = "world"
-len2 i64 = 6
-syscall(write, stdout, hello, 6)
+len2 i64 = + 3 3
+syscall(write, stdout, hello, len2)
syscall(write, stdout, world, 5)
syscall(exit, 0)
diff --git a/x64.c b/x64.c
@@ -91,6 +91,26 @@ mov_r64_m64(char *buf, enum reg reg, uint64_t addr)
}
size_t
+mov_m64_r64(char *buf, uint64_t addr, enum reg reg)
+{
+ uint8_t mov[] = {0x48, 0x89};
+ uint8_t op1 = (MOD_INDIRECT << 6) | (reg << 3) | 4;
+ uint8_t sib = 0x25;
+ if (buf) {
+ memcpy(buf, mov, 2);
+ buf += 2;
+ *(buf++) = op1;
+ *(buf++) = sib;
+ *(buf++) = addr & 0xFF;
+ *(buf++) = (addr >> 8) & 0xFF;
+ *(buf++) = (addr >> 16) & 0xFF;
+ *(buf++) = (addr >> 24) & 0xFF;
+ }
+
+ return 8;
+}
+
+size_t
add_r64_r64(char *buf, enum reg reg1, enum reg reg2)
{
uint8_t mov[] = {0x48, 0x03};
diff --git a/x64.h b/x64.h
@@ -34,5 +34,6 @@ void freereg(enum reg reg);
size_t add_r_imm(char *buf, enum reg reg, uint64_t imm);
size_t mov_r_imm(char *buf, enum reg reg, uint64_t imm);
size_t mov_r64_m64(char *buf, enum reg reg, uint64_t addr);
+size_t mov_m64_r64(char *buf, uint64_t addr, enum reg reg);
size_t add_r64_r64(char *buf, enum reg reg1, enum reg reg2);
size_t sub_r64_r64(char *buf, enum reg reg1, enum reg reg2);