[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MiNT] Missing clobbered register in gemlib



Helmut Karlowski wrote:
.globl _v_clswk
_v_clswk:
lea (-56,sp),sp
move.l d2,-(sp)		| d2 backuped
move.w 64(sp),d2		| d2  trashed
lea (26,sp),a0
move.l a0,6(sp)
clr.l 10(sp)
clr.l 14(sp)
move.l #_vdi_dummy,18(sp)
move.l #_vdi_dummy,22(sp)
lea (6,sp),a1
#APP				| begin inline assembly
movea.l a1,a0
move.l a0,d1
move.l (a0),a0
move.l #131072,(a0)+
eor.l d0,d0
move.l d0,(a0)+
move.l d0,(a0)+
move.w d2,(a0)		| d2 used
move.w #115,d0
trap #2
#NO_APP			| end  inline assembly
move.l (sp)+,d2		| d2 restored
lea (56,sp),sp
rts


Now I'm not very good at assembler, but I don't see where any registers
are saved or restored (except maybe d2.l)
You see the right things.

The only point you are missing is the function calling convention rule used by GCC. The calling convention is a kind of contract between the callers and callees. With GCC for MiNT, the rules for functions are:
- a function starts with a global label prepended with an underscore
- it ends with RTS
- it finds the arguments on the stack
- the function return value (if any) is put in d0.
- AND: changing the value of d0/d1/a0/a1 is allowed, but not the other registers (they can be used if backuped).
To ease reading the listing, GCC put the special comments #APP and #NO_APP 
to mark a block of inline assembly, manually written in the C source.
The registers d0/d1/a0/a1 are not backuped because it is legal to modify 
them. d2 is automatically backuped by GCC because it needs a free data 
register to do its work (this is unrelated to the trap). And a2 is not 
backuped because no one told GCC that it could be modified by the inline 
assembly block.
If a2 is actually modified by the VDI call, the contract of v_clswk() is not 
respected. The caller may have stored a variable in a2, it expects a2 to be 
left unmodified, but it is trashed, and a bug occurs.
Note that the GCC inline assembly is quite complicated. But the syntax of 
the clobber list is very simple. Any inline assembly block has to declare in 
the clobber list which registers it modifies. Then GCC adds automatically 
backup/restore code, when required.
Another case is the AES graf_handle() function. It is the one that caused 
the problem tracked down by Didier, Patrice and me, and was about the 
register a0. There is no doubt, it is proven that the VDI can modify a0. At 
the beginning of graf_handle(), GCC put important data into a0. Then it 
calls the trap #2. And after that, it uses a0 at the end of graf_handle() 
expecting the value has not changed. In this case, the bug occurs in 
graf_handle() itself, not in the caller.
The solution was easy: add a0 to the clobber list, so GCC uses another 
register, and backups it if necessary.
There is no mystery in GCC behaviour.

The problems appears only at high levels of optimization, when the registers are heavily used to store intermediate data across function calls.
--
Vincent Rivière