Article View: gnu.gcc.help
Article #577__asm__ changing memory - no way to tell gcc?
From: Linus Torvalds
Date: Fri, 06 Nov 1992 17:10
Date: Fri, 06 Nov 1992 17:10
76 lines
3202 bytes
3202 bytes
I just spent a couple of days looking for a bug in the linux sources
that was "less than obvious", and due to a gcc __asm__ statement
changing memory - without gcc knowing about it, and optimizing away (or
moving) some needed instructions.
The problem essentially looked like this:
void clear_inode(struct inode * inode)
{
struct wait_queue * tmp;
tmp = inode->i_wait;
memset(inode,0,sizeof(*inode));
inode->i_wait = tmp;
}
Now, this looks like it simply couldn't fail, so I was searching for the
bug elsewhere for days, until I in desperation looked at the assembly
output of the above:
_clear_inode:
pushl %edi
movl 8(%esp),%edx
xorb %al,%al
movl %edx,%edi
movl $164,%ecx
/APP
cld
rep
stosb
/NO_APP
movl 52(%edx),%eax <<<<<<
movl %eax,52(%edx) <<<<<<
popl %edi
ret
Well, the problem was obvious: I was using inline assembly for the
memset routines (as at least gcc-2.2.2 doesn't yet do it by
__builtin_memset). The exact definition I used for the above code is
(yes, I know - I should re-write it to use long-word clearing):
extern inline void * memset(void * s,char c,size_t count)
{
__asm__ __volatile__("cld\n\t"
"rep\n\t"
"stosb"
::"a" (c),"D" (s),"c" (count)
:"cx","di");
return s;
}
Note that the gcc optimizer hasn't noticed that the inline assembly has
changed the inode data, so it thinks that it can just move the first
assignment to *after* 'memset()'. Even the __volatile__ doesn't help:
it seems __volatile__ stops only major re-organizations.
Ok, it may not be a bug - the docs do state that you have to be careful
about __asm__ statements with side-effects, but it's a bad feature:
especially as I couldn't find a good workaround. I tried adding a
output field of the form '"=m" (*s)' to tell gcc that the memory pointed
to by 's' got changed, but this (not surprisingly) only resulted in a
bad type error due to the dereferencing of a void pointer.
The '"=m" (*s)' trick does work when the type of the pointer is known,
but not with a void pointer (and I can't just cast the void pointer to
something else: it would have to be cast to something of "infinite size"
to be safe). The optimal solution would be to allow '*s' in this kind
of context, but I don't know how well that meshes with the rest of the
compiler...
I solved the problem by changing the temporary variable assignments to
be volatile (ie "tmp = ((volatile inode *) inode)->i_wait" etc), so I
got the bug corrected, but I was left wondering if there was nothing
cleaner I could do.. (the only manual I have is a pre-version for 2.0).
Linus
Technical Details
Message-ID:
<1992Nov6.171035.3689@klaava.Helsinki.FI>Path:
arc1-ca.pugleaf.net!sparky!uunet!charon.amdahl.com!pacbell.com!sgiblab!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!emory!swrinde!cs.utexas.edu!sun-barr!news2me.EBay.Sun.COM!cronkite.Central.Sun.COM!texsun!exucom.exu.ericsson.se!ericom!sunic!news.funet.fi!hydra!klaava!torvaldsShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!charon.amdahl.com!pacbell.com!sgiblab!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!emory!swrinde!cs.utexas.edu!sun-barr!news2me.EBay.Sun.COM!cronkite.Central.Sun.COM!texsun!exucom.exu.ericsson.se!ericom!sunic!news.funet.fi!hydra!klaava!torvalds From: torvalds@klaava.Helsinki.FI (Linus Torvalds) Newsgroups: gnu.gcc.help Subject: __asm__ changing memory - no way to tell gcc? Message-ID: <1992Nov6.171035.3689@klaava.Helsinki.FI> Date: 6 Nov 92 17:10:35 GMT Organization: University of Helsinki Lines: 76