home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / gnu / gcc / help / 2468 < prev    next >
Encoding:
Text File  |  1992-11-08  |  3.0 KB  |  86 lines

  1. Path: 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
  2. From: torvalds@klaava.Helsinki.FI (Linus Torvalds)
  3. Newsgroups: gnu.gcc.help
  4. Subject: __asm__ changing memory - no way to tell gcc?
  5. Message-ID: <1992Nov6.171035.3689@klaava.Helsinki.FI>
  6. Date: 6 Nov 92 17:10:35 GMT
  7. Organization: University of Helsinki
  8. Lines: 76
  9.  
  10. I just spent a couple of days looking for a bug in the linux sources
  11. that was "less than obvious", and due to a gcc __asm__ statement
  12. changing memory - without gcc knowing about it, and optimizing away (or
  13. moving) some needed instructions. 
  14.  
  15. The problem essentially looked like this:
  16.  
  17.   void clear_inode(struct inode * inode)
  18.   {
  19.     struct wait_queue * tmp;
  20.  
  21.     tmp = inode->i_wait;
  22.     memset(inode,0,sizeof(*inode));
  23.     inode->i_wait = tmp;
  24.   }
  25.  
  26. Now, this looks like it simply couldn't fail, so I was searching for the
  27. bug elsewhere for days, until I in desperation looked at the assembly
  28. output of the above:
  29.  
  30.   _clear_inode:
  31.     pushl %edi
  32.     movl 8(%esp),%edx
  33.     xorb %al,%al
  34.     movl %edx,%edi
  35.     movl $164,%ecx
  36.   /APP
  37.     cld
  38.     rep
  39.     stosb
  40.   /NO_APP
  41.     movl 52(%edx),%eax        <<<<<<
  42.     movl %eax,52(%edx)        <<<<<<
  43.     popl %edi
  44.     ret
  45.  
  46. Well, the problem was obvious: I was using inline assembly for the
  47. memset routines (as at least gcc-2.2.2 doesn't yet do it by
  48. __builtin_memset).  The exact definition I used for the above code is
  49. (yes, I know - I should re-write it to use long-word clearing):
  50.  
  51.   extern inline void * memset(void * s,char c,size_t count)
  52.   {
  53.   __asm__ __volatile__("cld\n\t"
  54.     "rep\n\t"
  55.     "stosb"
  56.     ::"a" (c),"D" (s),"c" (count)
  57.     :"cx","di");
  58.   return s;
  59.   }
  60.  
  61. Note that the gcc optimizer hasn't noticed that the inline assembly has
  62. changed the inode data, so it thinks that it can just move the first
  63. assignment to *after* 'memset()'.  Even the __volatile__ doesn't help:
  64. it seems __volatile__ stops only major re-organizations.
  65.  
  66. Ok, it may not be a bug - the docs do state that you have to be careful
  67. about __asm__ statements with side-effects, but it's a bad feature:
  68. especially as I couldn't find a good workaround.  I tried adding a
  69. output field of the form '"=m" (*s)' to tell gcc that the memory pointed
  70. to by 's' got changed, but this (not surprisingly) only resulted in a
  71. bad type error due to the dereferencing of a void pointer. 
  72.  
  73. The '"=m" (*s)' trick does work when the type of the pointer is known,
  74. but not with a void pointer (and I can't just cast the void pointer to
  75. something else: it would have to be cast to something of "infinite size"
  76. to be safe).  The optimal solution would be to allow '*s' in this kind
  77. of context, but I don't know how well that meshes with the rest of the
  78. compiler... 
  79.  
  80. I solved the problem by changing the temporary variable assignments to
  81. be volatile (ie "tmp = ((volatile inode *) inode)->i_wait" etc), so I
  82. got the bug corrected, but I was left wondering if there was nothing
  83. cleaner I could do..  (the only manual I have is a pre-version for 2.0). 
  84.  
  85.         Linus
  86.