zsdcc register usage

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

zsdcc register usage

Post by derekfountain »

I have a bit of C code I need to optimise, so I'm considering converting it to assembly language. I'm using the zsdcc compiler and newlib. I'm trying to find resources which tell me which Z80 registers I can work with and which I need to protect.

Assuming I write a new function for which the compiler generates a CALL in its usual convention, which registers can my function use? Do I have to save everything I'm going to change? I'm aware of the __preserves_regs thing which I can use to tell the compiler that my assembler code preserves some registers. That seems to imply the compiler assumes my code will trash everything unless I tell it I'm preserving something, which in turn implies the compiler will generate code to save and restore registers it's using around the CALL to my code. This doesn't seem right. I'm not seeing loads of pushes and pops in generated code around calls to functions.

So what are the rules? Do I need to save everything I change? Or nothing? What about the alternate registers? What are the rules for the index registers? This must be written down somewhere, but I can't find anything.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: zsdcc register usage

Post by jorgegv »

AFAICT function calls considers regular registers are thrashed, i.e. AF, BC, DE and HL, and the alternate ones, and also IY or IX, depending on some settings.

The issue about not seeing lots of pushes/pops around function calls I think obeys to the fact that the compiler is smartly allocating registers to minimize or avoid those operations for efficiency. I have seen _some_ pushes/pops around function calls in my Code that support this.

If you are really interested in this I can search for some of those cases to back my claims :-)
stefano
Well known member
Posts: 2137
Joined: Mon Jul 16, 2007 7:39 pm

Re: zsdcc register usage

Post by stefano »

you have several options to optimise like only real men do, assembler is only one of them =D
Have a look at the opt files here and how they can be applied to clisp.c (instructions are inside it):

https://github.com/z88dk/z88dk/tree/mas ... ples/clisp

Also, do not underestimate the callee trick, sometimes you can use it also without rewriting your functions in assembly
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: zsdcc register usage

Post by derekfountain »

I think I found the answer buried in the SDCC manual, section 3.16:

• callee_saves function1[,function2[,function3...]] - The compiler by default uses a caller saves convention for
register saving across function calls, however this can cause unnecessary register pushing and popping when
calling small functions from larger functions. This option can be used to switch off the register saving con-
vention for the function names specified. ...


From that I understand that the compiler will generate pushes and pops around function calls to save whatever register-held values it's currently got and which it knows it needs across the call. You can guide it with the __preserves_regs directive which can spare it the effort of pushing/popping a register it needs to preserve if you tell it you're not going to change that register. There's also the --callee-saves option and pragma if you have functions which look after the registers themselves.

It seems that in many (most?) cases, with local variables typically held on the stack in compiled code, the registers don't need to be saved. The registers are normally used to set up the function call (i.e. registers are pushed to get the arguments onto the stack) and to deal with the result (i.e. the return value comes back in registers).

So the bottom line appears to be that an assembly language function can use the registers as it pleases.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: zsdcc register usage

Post by jorgegv »

SDCC uses IX as its frame pointer, so it also needs to be preserved always (that is, if you use IX inside you function...or if you call some other function inside yours, which may use IX). I just worked on this when migrating SP1 sources to compile with SDCC in Classic mode.

Documented in section 4.3 of SDCC user manual.
Timmy
Well known member
Posts: 392
Joined: Sat Mar 10, 2012 4:18 pm

Re: zsdcc register usage

Post by Timmy »

What is the reason you cannot just push every register you use before using them?
There are about 20 registers or something.
It's also very unlikely your code will be much slower just because you pushed the registers.

I don't use SDCC myself so I'm not sure which registers it needs, but always push IX and IY when you use them.
In ZCC I don't care about registers much but it's always a good practice to push all the registers you need, unless you're more experienced.

And in ZCC FASTCALLs you probably shouldn't push/pop HL and A, but that's obvious.
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: zsdcc register usage

Post by derekfountain »

"PUSH rr" is 11 Ts, and I need 4 of them, plus PUSH IX and PUSH IY (15 Ts each), then an EXX (4 Ts), then 3 more PUSHes for the alternate set, then another EXX... 155 Ts. Plus about the same to put everything back when the routine exists. I'm rewriting the routine in ASM because it's called hundreds of times per second and I need it faster. 300-something Ts in unnecessary overhead doesn't seem like the best way to start!
Timmy
Well known member
Posts: 392
Joined: Sat Mar 10, 2012 4:18 pm

Re: zsdcc register usage

Post by Timmy »

This may surprise you, but those pushes are pretty much peanuts compared to your hundreds of calls to them, or to the things you do in the routine itself. And obviously you're not using all those alternate registers, are you? So you don't need to push everything.

Have you ever seen an interrupt routine? These are (if properly done) full of pushes and pops, and they are done 50 times a second. These things can play music, update screens, updating variables, everything. These are seriously not a big deal. Every single game that plays AY music during interrupts do this, it is not a problem to anyone at all.

On the other hand, it's also possible that what you're doing cannot be done on the spectrum. But I'm not going to speculate now.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: zsdcc register usage

Post by jorgegv »

Well for a pretty fast interrupt routine, I just code it to set a global flag, and then poll that flag in the main program loop. With that kind of interrupt routine, pushes and pops account for pretty much all of the execution time.

I prefer to have the interrupt routines quite bounded wrt execution time, but may be that's my personal prefeence...
Post Reply