Age | Commit message (Collapse) | Author |
|
If the size of the struct is not a multiple of 8, the actual struct
size may be different from the size reserved on the stack.
This fixes the case where the struct is passed in memory, but we
still may over-read a struct passed in registers. A TODO is added
for now.
|
|
Michael found a bug where some copies
from registers to memory in the arm64
abi clobber the stack. The test case
is:
type :T = { w }
function w $f() {
@start
%p =:T call $g()
%x =w loadw %p
ret %x
}
qbe will write 4 bytes out of bounds
when pulling the result struct from
its register. The same bug can be
observed if :T's definition is {w 3};
in this case qbe writes 16 bytes in
a slot of 12 bytes.
This patch changes stkblob() to use
the rounded argument size if it is
going to be restored from registers.
Relatedly, mem->reg loads for structs
with size < 16 and != 8, are treated
a bit sloppily both in the arm64 and
in the sysv abis. That is much less
harmful than the present bug.
|
|
Some arm64 abi tests have been failing
for some time now. This fixes them by
being a bit more careful with liveset
management in spill.c.
A late bsclr() call in spill.c may drop
legitimately live registers in e.g.,
R12 =w add R12, 1
While it hurts for regs, it does not
matter for ssa temps because those cannot
be both in the arguments & return (by the
ssa invariant). I added a check before
bsclr() to make sure we are clearing
only ssa temps.
One might be surprised that any ssa temp
may be live at this point. The reason why
this is the case is the special handling
of dead return values earlier in spill().
I think that it is the only case where
the return value can be (awkwardly) live
at the same time as the arguments, and I
think this never happens with registers
(i.e., we never have dead register-
assigning instructions). I added an
assert to check the latter invariant.
Finally, there was a simple bug in the
arm64 abi which I fixed: In case the return
happens via a pointer, x8 needs to be marked
live at the beginning of the function. This
was caught by test/abi4.ssa.
|
|
Tested-by: Thomas Bracht Laumann Jespersen <t@laumann.xyz>
Fixes: https://todo.sr.ht/~sircmpwn/hare/312
|
|
Fixes #467. It assumes that the stack won't need to grow beyond 2^32 bytes.
If that were to happen, we'd need another or at most two more `movk` instructions.
Signed-off-by: Sudipto Mallick <smlckz@disroot.org>
|
|
If registers spill onto the stack, we may end up with SSA like
S320 =l copy 0
after rega(). Handle this case in arm64 emit().
|
|
|
|
|
|
Some abis, like the riscv one, treat
arguments differently depending on
whether they are variadic or not.
To prepare for the upcomming riscv
target, we change the variadic call
syntax and give meaning to the
location of the '...' marker.
# new syntax
%ret =w call $f(w %regular, ..., w %variadic)
By nature of their abis, the change
is backwards compatible for existing
targets.
|
|
|
|
The no-op `copy R0` is necessary in order to trigger dopm in spill.c
and rega.c, which assume that a call is always followed by one or
more copies from registers. However, the arm64 ABI does not actually
return the caller-passed pointer as in x86_64. This causes an
assertion failure
qbe: aarch64: Assertion failed: r == T.rglob || b == fn->start (spill.c: spill: 470)
for the following test program
type :t = { l 3 }
function $f() {
@start.1
@start.2
%ret =:t call $g()
ret
}
The assertion failure only triggers when the block containing the
call is not the first block, because the check is skipped for the
first block (since some registers may have been used for arguments).
To fix this, set R0 in the call data so that spill/rega can see
that this dummy "return" register was generated by the call. This
matches qbe's existing behavior when the function returns void,
another case where no register is used for the function result.
|
|
The immediate in the add instruction is only 12 bits. If the offset
does not fit, we must move it into a register first.
|
|
According to the ARMv8 overview document
However if SP is used as the base register then the value of the stack
pointer prior to adding any offset must be quadword (16 byte) aligned,
or else a stack alignment exception will be generated.
This manifests as a bus error on my system.
To resolve this, just save registers two at a time with stp.
|
|
|
|
In this case, the immediate is too large to use directly in the add/sub
instructions, so move it into a temporary register first.
Also, for clarity, rearrange the if-conditions so that they match the
constraints of the instructions that immediately follow.
|
|
|
|
The ldrb and ldrh instructions require a 32-bit register name for the
destination and will clear the upper 32-bits of that register.
|
|
In this case, the potential truncations
flagged by gcc are only affecting debug
information.
|
|
|
|
|
|
Compiler warned about comparison between signed and unsigned values.
|
|
The stashing of constants in gas.c was also
changed to support 16-bytes constants.
|
|
Symbols in the source file are still limited in
length because the rest of the code assumes that
strings always fit in NString bytes.
Regardless, there is already a benefit because
comparing/copying symbol names does not require
using strcmp()/strcpy() anymore.
|
|
|