English Amiga Board

English Amiga Board (http://eab.abime.net/index.php)
-   Coders. C/C++ (http://eab.abime.net/forumdisplay.php?f=118)
-   -   VBCC: Restrict usage of registers in C (http://eab.abime.net/showthread.php?t=99396)

Hedeon 28 October 2019 12:46

VBCC: Restrict usage of registers in C
 
Hi,

I am busy transferring the WarpOS compatible kernel I did for PCI PPC cards from assembly to C. Of course the kernel is very low level and it is unavoidable to use assembly here and there.

The 603 and e300 PPCs don't have hardware tlb lookup and make use of software exceptions to load the correct values from the page table.

To accomplish this the exceptions use four shadow registers called r0-r3. They are independent from the 'normal' registers which are also called r0-r3 (up to r31).

IF I want to make this in C, I need to restrict the usage of registers to these 4. Is this even possible with VBCC?

robinsonb5 28 October 2019 14:15

Quote:

Originally Posted by Hedeon (Post 1354719)
IF I want to make this in C, I need to restrict the usage of registers to these 4. Is this even possible with VBCC?

I'm not 100% sure this will work, but you would need to modify the backend to mark registers r4 - r31 as allocated in the init_cg function (regsa[n]=1; careful, the register number doesn't map directly to n - see the register names array.) - then modify the t1, t2 and t3 values to restrict the compiler's internal scratch registers to the allowed range. You'll leave just one register free for the compiler's use so expect to see lots of stack operations.

Hedeon 28 October 2019 14:39

Had hoped that some directive would do. Current source does not use stack and only modifies the software page table.

I'd guess this is one of those parts that will stay in assembly.

Excerpt:

Code:

.DLoadTLBMiss:
                mfspr        r2,HASH1                                #get first pointer
                li        r1,8                                          #load 8 for counter
                mfctr        r0                                          #save counter
                mfspr        r3,DCMP                                  #get first compare value
                addi        r2,r2,-8                                  #pre dec the pointer
dm0:
                mtctr        r1                                          #load counter
dm1:
                lwzu        r1,8(r2)                                #get next pte
                cmpw        r1,r3                                        #see if found pte
                bdnzf        eq,dm1                                        #dec count br if cmp ne and if count not zero
                bne        dataSecHash                                #if not found set up second hash or exit
                lwz        r1,4(r2)                                #load tlb entry lower-word
                mtctr        r0                                          #restore counter
                mfspr        r0,DMISS                                  #get the miss address for the tlbld
                mfsrr1        r3                                          #get the saved cr0 bits
                mtcrf        0x80,r3                                #restore CR0
                mtspr        RPA,r1                                  #set the pte
                ori        r1,r1,PTE_REFERENCED                          #set reference bit
                srwi        r1,r1,8                                  #get byte 7 of pte
                tlbld        r0                                            #load the dtlb
                stb        r1,6(r2)                                #update page table
                rfi                                              #return to executing program

dataSecHash:
                andi.        r1,r3,PTE_HASHID                        #see if we have done second hash
                bne        doDSI                                      #if so, go to DSI interrupt
                mfspr        r2,HASH2                                  #get the second pointer
                ori        r3,r3,PTE_HASHID                        #change the compare value
                li        r1,8                                          #load 8 for counter
                addi        r2,r2,-8                                  #pre dec for update on load
                b        dm0                                          #try second hash


phx 28 October 2019 15:47

No, you cannot restrict the code generator to use certain registers in a function. But to minimize assembler usage you could either use the __saveall attribute, which also saves/restores all (used) volatile registers, or insert inline-assembler to save them: __asm("\tstw\tr4,offset(r1)");
You might also want to use the __interrupt attribute, which makes sure that such a function ends with "rfi" instead of "blr".

Hedeon 28 October 2019 16:43

Thanks @phx. I learned that from you last time we spoke :-) (regarding __interrupt)

See https://github.com/Sakura-IT/PowerPC...aster/kernel.c

or

https://github.com/Sakura-IT/PowerPCAmiga

Nothing much there yet. I need to use assembly anyway to access the special registers, but want to keep it at minimum.

For these tlb exceptions I'll just use assembly also for speed reasons.

Hedeon 13 January 2020 23:17

Quote:

Originally Posted by phx (Post 1354767)
But to minimize assembler usage you could either use the __saveall attribute, which also saves/restores all (used) volatile registers, or insert inline-assembler to save them: __asm("\tstw\tr4,offset(r1)");

I have opted to make parts of the exception code in asm. Can you confirm i can use any volatile register I want (r3-r12) inside the asm code without trashing stuff? And non-volatile (r13-r31) are saved on the stack before jumping to a(n asm) function? @phx.

phx 14 January 2020 12:17

Quote:

Originally Posted by Hedeon (Post 1371387)
Can you confirm i can use any volatile register I want (r3-r12) inside the asm code without trashing stuff?

Using __asm() inside a C function? No. The compiler does not know what happens inside such a block.

Or did you think about calling an assembler function or inline routine? Then yes, the compiler always treats the ABI-defined volatile registers accordingly and may save them before a call. If your function does not use all volatile registers you may specify that with __regsused().

Quote:

And non-volatile (r13-r31) are saved on the stack before jumping to a(n asm) function?
Saving and restoring non-volatile registers is always the duty of the callee, not of the caller.

Hedeon 14 January 2020 12:44

I meant a C function calling an asm function. I use r3-r12 in those asm functions and was hoping that that would be possible without saving them all and then restoring them again inside the asm function. You say they are saved in the caller function so that is great.

Regarding r13-r31, that was a duh moment as I should have known that :-)

WarpOS has a small exception context that directly uses all the registers from the original interrupted task. However, the main exception itself (which is mostly C) of course changes a lot of these registers before it jumps to the custom exception handler (wosdb uses large exception context in which the interrupted task registers are used indirectly from the context, iFusion uses small context. Only two programs I know that uses them).

I had hoped that I just needed to reload the volatile registers before jumping to the small context custom exception, but I just better reload all the registers (they were saved in a stack frame when the main exception happened).

Thanks for your help. Now to finish this project quickly :-)

Hedeon 06 April 2020 23:54

I have another registers related question and this is more for the 68K backend but it is WarpOS related:

I see in the original powerpc.library that all registers are saved on the stack except d0. I now also see that some WarpOS programs from the WarpOS distribution itself rely on this (e.g. CyberPi). They use but don't save d1 before calling RunPPC and use it again after. This works with the original powerpc.library, but in C d1 (and a0 and a1) are not saved as they are volatile.

For optimal compatibility I also need to save those registers and I want it to do without asm. I looked through the vbcc manual but cannot find anything on this. Is this possible?

robinsonb5 07 April 2020 01:14

Quote:

Originally Posted by Hedeon (Post 1390066)
For optimal compatibility I also need to save those registers and I want it to do without asm. I looked through the vbcc manual but cannot find anything on this. Is this possible?


Possible, yes - but very ugly.
The only way I can think of (without modifying vbcc) would be to use an inline assembly function to save the registers. It would look something like this:

Code:

int d1wrapper() = "\tmovem.l a0-a1/d1,-(a7)\n\tjsr _func_inner\n\tmovem.l (a7)+,a0-a1/d1\n";

int func(int param1,int param2)
{
    return(d1wrapper());
}

int func_inner(int dummy1, int dummy2, int dummy3, int dummy4, int param1, int param2)
{
    return(param1+param2);
}

(The extra dummy parameters are to account for the extra entries on the stack - one extra return address and three registers.)

Edit: if you don't need to return a value to the caller, you can simply declare your function with the __interrupt attribute. Unfortunately, because it saves and restores not just d1/a0/a1, but d0 as well, you lose the ability to return a value!

Edit 2: If your function uses register parameters, rather than stack based parameters, (and I guess it does if it's a library function) then the hideous hack with dummy parameters shouldn't be necessary - you should be able to just omit those.)

Hedeon 07 April 2020 21:03

Yes. It is a library function. I do have some asm already in there (especially at the ppc side due to special registers), so I could just add to the asm part something like push, bsr/jsr function and pop, but maybe there was something like __interrupt but then ending with rts (and not rte) and not restoring d0 (didn't know it did that, actually).

Thanks for the suggestions. I guess I have to add a little bit of asm.

EDIT:

There is also something like __amigainterrupt which reads like it will end with rts. Have to see.

EDIT2:

Not sure if __saveall would help here?

robinsonb5 07 April 2020 23:41

Quote:

Originally Posted by Hedeon (Post 1390280)
There is also something like __amigainterrupt which reads like it will end with rts. Have to see.


Ah yes, I missed that __interrupt causes the function to end with rte.


A quick test suggests __amigainterrupt doesn't seem to save the scratch registers either.


Quote:

Not sure if __saveall would help here?

It would help a lot if it were implemented for m68k. Sadly, it's PPC only.

Hedeon 01 July 2020 12:42

Ended up with a library of some sorts. :-) Bit slower (5% or so) than the original asm one.

Thanks for all the support.


All times are GMT +2. The time now is 05:58.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.

Page generated in 0.07197 seconds with 11 queries