nooc

nooc programming language compiler
git clone git://git.nihaljere.xyz/nooc
Log | Files | Refs | LICENSE

commit b5bf20b8b3f55e81e08cbf9b96b871ce44cc2b17
parent 0061900f933341976ce447469d0570370bc62f5a
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Wed, 15 Dec 2021 23:58:06 -0600

gensyscall: correct register allocation

Previously, we ignored if a syscall arg register was currently
allocated. This is obviously bad, because we replace the value
without saving the old value. This could cause problems with nested
syscalls, and more generally, nested function calls (once we pass
parameters in registers). Now we push the value of used registers
to the stack, perform the syscall, and then pop the values back off
the stack.

Diffstat:
Mmain.c | 20++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/main.c b/main.c @@ -399,6 +399,7 @@ gencall(char *buf, size_t addr, struct expr *expr) size_t gensyscall(char *buf, struct expr *expr) { + unsigned short pushed = 0; size_t len = 0; struct fparams *params = &expr->d.call.params; if (params->len > 7) @@ -406,19 +407,30 @@ gensyscall(char *buf, struct expr *expr) // encoding for argument registers in ABI order for (int i = 0; i < params->len; i++) { - used_reg |= (1 << abi_arg[i]); + if (used_reg & (1 << abi_arg[i])) { + len += push_r64(buf ? buf + len : NULL, abi_arg[i]); + pushed |= (1 << abi_arg[i]); + } else { + used_reg |= (1 << abi_arg[i]); + } len += genexpr(buf ? buf + len : NULL, params->data[i], abi_arg[i]); } - // FIXME: what if an abi arg register has already been allocated before this executes? (ex. nested function call) - clearreg(); - if (buf) { char syscall[] = {0x0f, 0x05}; memcpy(buf + len, syscall, 2); } + len += 2; + for (int i = params->len - 1; i >= 0; i--) { + if (pushed & (1 << abi_arg[i])) { + len += pop_r64(buf ? buf + len : NULL, abi_arg[i]); + } else { + freereg(abi_arg[i]); + } + } + return len; }