Skip to content

Commit

Permalink
update docs about call convention for multi-value results (first is i…
Browse files Browse the repository at this point in the history
…n A or AY, then R15...R0)

added sprites+coroutines+defer part to benchmark program
  • Loading branch information
irmen committed Feb 19, 2025
1 parent bc550a4 commit fb1e89d
Show file tree
Hide file tree
Showing 7 changed files with 20 additions and 14 deletions.
4 changes: 2 additions & 2 deletions benchmark-program/b_circles.p8
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -33,7 +33,7 @@ circles {
}

if use_kernal
void cx16.set_screen_mode(3)
cx16.set_screen_mode(3)
else {
gfx_lores.text_mode()
}
Expand Down
11 changes: 8 additions & 3 deletions benchmark-program/benchmark.p8
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
%import b_queens
%import b_textelite
%import b_maze
%import b_sprites

%zeropage basicsafe
%option no_sysinit
Expand All @@ -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()

Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions compiler/res/prog8lib/coroutines.p8
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions docs/source/programming.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
5 changes: 3 additions & 2 deletions docs/source/technical.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand Down
4 changes: 0 additions & 4 deletions docs/source/todo.rst
Original file line number Diff line number Diff line change
@@ -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)

...


Expand Down

0 comments on commit fb1e89d

Please sign in to comment.