update docs about call convention for multi-value results (first is in A or AY, then R15...R0)

added sprites+coroutines+defer part to benchmark program
This commit is contained in:
Irmen de Jong 2025-02-19 21:05:33 +01:00
parent bc550a4549
commit fb1e89d9ef
7 changed files with 20 additions and 14 deletions

View File

@ -12,7 +12,7 @@ circles {
sub draw(bool use_kernal, uword max_time) -> uword {
if use_kernal
void cx16.set_screen_mode(128)
cx16.set_screen_mode(128)
else
gfx_lores.graphics_mode()
@ -33,7 +33,7 @@ circles {
}
if use_kernal
void cx16.set_screen_mode(3)
cx16.set_screen_mode(3)
else {
gfx_lores.text_mode()
}

View File

@ -15,6 +15,7 @@
%import b_queens
%import b_textelite
%import b_maze
%import b_sprites
%zeropage basicsafe
%option no_sysinit
@ -29,7 +30,7 @@ main {
sub start() {
ubyte benchmark_number
void cx16.set_screen_mode(3)
cx16.set_screen_mode(3)
txt.color2(1, 6)
txt.clear_screen()
@ -74,10 +75,14 @@ main {
benchmark_score[benchmark_number] = textelite.bench(120)
benchmark_number++
announce_benchmark("sprites-coroutines-defer")
benchmark_score[benchmark_number] = animsprites.benchmark(300)
benchmark_number++
benchmark_names[benchmark_number] = 0
benchmark_score[benchmark_number] = 0
void cx16.set_screen_mode(3)
cx16.set_screen_mode(3)
txt.uppercase()
txt.color2(1, 6)
uword final_score
@ -99,7 +104,7 @@ main {
sub announce_benchmark(str name) {
benchmark_names[benchmark_number] = name
void cx16.set_screen_mode(3)
cx16.set_screen_mode(3)
txt.uppercase()
txt.color2(1, 6)
txt.clear_screen()

View File

@ -635,7 +635,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
// return value(s)
val returnRegSpecs = if(fcall.void) emptyList() else {
// TODO: for current implemenation of the call convention in case of multiple return values,
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
// So we use an empty list to avoid confusion here. This may change in a future version.
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
callTarget.returns.map {
val returnIrType = irType(it)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null)

View File

@ -33,6 +33,7 @@
; - call killall() to kill all tasks.
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!!
coroutines {
%option ignore_unused

View File

@ -1170,9 +1170,9 @@ So for instance::
asmsub multisub() -> uword @AY, bool @Pc, ubyte @X { ... }
.. sidebar:: usage of cx16.r0-cx16.r15
.. sidebar:: register usage
Subroutines with multiple return values use the "virtual registers" to return those.
Subroutines with multiple return values use cpu registers A, Y, and the R0-R15 "virtual registers" to return those.
Using those virtual registers during the calculation of the values in the return statement should be avoided.
Otherwise you risk overwriting an earlier return value in the sequence.

View File

@ -155,8 +155,9 @@ Regular subroutines
- for an ``asmsub`` or ``extsub`` the subroutine's signature specifies the output registers that contain the values explicitly,
just as for a single return value.
- for regular subroutines, the compiler will use the "virtual registers" cx16.r0-cx16.r15, from r15 down to r0, for the
result values left to right. This may change in a future compiler version.
- for regular subroutines, the compiler will return the first of the return values via the cpu register ``A``` (or ``A + Y``` if it's a word value),
just like for subroutines that only return a single value.
The remainder of the return values are returned via the "virtual registers" cx16.r16-cx16.r0 (using R15 first and counting down to R0).
**Builtin functions can be different:**

View File

@ -1,10 +1,6 @@
TODO
====
- IR: call main.two():r4.w,r5.w the registers mentioned after the call are wrong/unused in case of multi-value returns. Better to clear this to avoid confusion? (they ARE correct for single value returns!)
- update docs about call convention for multi-value results (first is in A or AY, then R15...R0)
...