Re: volatile attribute
From: Kai Ruottu (karuottu_miaauu_mbnet_point_fi_at_spam.tome.no)
Date: 12/08/04
- Next message: Vadim Borshchev: "Re: "Emulate" USB Host"
- Previous message: Roland Zitzke: "Re: Circuit that produces a tingling sensation in the fingers."
- In reply to: Hans-Bernhard Broeker: "Re: volatile attribute"
- Next in thread: Hans-Bernhard Broeker: "Re: volatile attribute"
- Reply: Hans-Bernhard Broeker: "Re: volatile attribute"
- Reply: Guy Macon: "Re: volatile attribute"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 08 Dec 2004 14:48:44 +0200
On 7 Dec 2004 12:32:27 GMT, Hans-Bernhard Broeker
<broeker@physik.rwth-aachen.de> wrote:
>Kai Ruottu <karuottu_miaauu_mbnet_point_fi@spam.tome.no> wrote:
>
>> What the GNU C compiler does in the 'volatile' case :
>
>> 1. load the data from the memory place into a CPU register
>> 2. modify it
>> 3. write it back
>
>> instead of doing the same thing as with a non-volatile variable:
>
>> a. modify the data directly in the memory place
>
>Does it? On what architecture, in what version of GCC?
H8/300, H8/500, m68k, v850,... all these have opcodes to "directly
set/clear a bit in a memory place whose address is given in the opcode
or in a CPU register". And GCC really handles these operations with
these expected opcodes when handling non-volatile objects... How
these opcodes work internally, maybe copying the data into some
invisible register, then modifying it and writing back, isn't
important, only that none of the visible CPU registers are used.
All GCCs since 1995 or so behave this way... Maybe also earlier ones
but in 1995 RMS & Co. seem to have last done something with the
'volatile_ok' variable in the GCC code... Quite many have reported,
at least unofficially, this bug but some kind of MS-like attitude, "it
is not a bug, it is a feature" has been the normal reaction. Or
people start to talk about the C/C++ standards and that 'volatile'
should somehow trigger the memory handling from direct to
load-store...
> I'd like to see an actual example of this behaviour.
Anyone can get an example with a code doing bit set/clear in a
memory place... But here is one from the H8/300 world showing
how the produced code should be :
------------------- clip -----------------------------
#define SFR __attribute__((eightbit_data))
extern volatile unsigned char SFR SCI_SSR0;
extern volatile unsigned char SFR SCI_TDR0;
/*
#define SCI_TDR0 (*(volatile unsigned char *) (0xffffdb))
#define SCI_SSR0 (*(volatile unsigned char *) (0xffffdc))
*/
/* Single char out to the serial port */
void put_ser(unsigned char c)
{
while ((SCI_SSR0 & 0x80) != 0x80)
;
SCI_TDR0 = c;
SCI_SSR0 &= 0x7F;
}
------------------- clip -----------------------------
; GCC For the Hitachi H8/300[HS]
; By Hitachi America Ltd and Cygnus Support
; Modified by Kai Ruottu
; release F-1.0.1
; -O2
.file "put_ser.c"
.section .text
.align 1
.global _put_ser
_put_ser:
.L5:
mov.b @_SCI_SSR0:8,r2l
and #128,r2l
beq .L5
mov.b r0l,@_SCI_TDR0:8
bclr #7,@_SCI_SSR0:8
rts
.end
------------------- clip -----------------------------
and here are some clips from a diff between the outputs of a
non-modified GCC and a by-me modified gcc-3.3.5 for m68k/cpu32 :
------------------- clip -----------------------------
*** quicc_335-1.s 2004-10-27 18:46:34.000000000 +0300
--- quicc_335-2.s 2004-10-27 14:33:46.000000000 +0300
***************
*** 6,13 ****
init_channel0:
move.l quicc,%a1
lea (3072,%a1),%a0
! lea (5632,%a1),%a1
! clr.l (%a1)
clr.w (%a0)
move.w #400,2(%a0)
move.b #24,4(%a0)
--- 6,12 ----
init_channel0:
move.l quicc,%a1
lea (3072,%a1),%a0
! clr.l 5632(%a1)
clr.w (%a0)
move.w #400,2(%a0)
move.b #24,4(%a0)
***************
*** 132,141 ****
move.w #2570,8(%a0)
move.l #384,4(%a0)
move.l #277348364,(%a0)
! move.l (%a0),%d0
! moveq.l #48,%d1
! or.l %d1,%d0
! move.l %d0,(%a0)
rts
.size ethernet_up, .-ethernet_up
.align 2
--- 130,137 ----
move.w #2570,8(%a0)
move.l #384,4(%a0)
move.l #277348364,(%a0)
! moveq.l #48,%d0
! or.l %d0,(%a0)
rts
.size ethernet_up, .-ethernet_up
.align 2
***************
*** 265,276 ****
extb.l %d0
moveq.l #32,%d1
and.l %d1,%d0
! add.l quicc,%d0
! move.l %d0,%a0
! lea (5632,%a0),%a0
! move.w 20(%a0),%d0
! or.w #8,%d0
! move.w %d0,20(%a0)
rts
.size quicc_finish, .-quicc_finish
.align 2
--- 261,269 ----
extb.l %d0
moveq.l #32,%d1
and.l %d1,%d0
! move.l quicc,%a0
! add.l %d0,%a0
! or.w #8,5652(%a0)
rts
.size quicc_finish, .-quicc_finish
.align 2
***************
*** 529,548 ****
lea RQB+6,%a0
move.w %a6,%d0
move.b %d0,(%a0,%a1.l)
! move.w (%a2),%d0
! and.w #8192,%d0
! move.w %d0,(%a2)
tst.b %d7
jbne .L42
! move.w (%a2),%d0
! or.w #-32768,%d0
! move.w %d0,(%a2)
jbra .L43
.align 2
.L42:
move.l %a2,%a4
clr.b %d7
.L43:
move.w (%a2),%d0
and.l #8192,%d0
addq.l #1,%d5
--- 525,541 ----
lea RQB+6,%a0
move.w %a6,%d0
move.b %d0,(%a0,%a1.l)
! and.w #8192,(%a2)
tst.b %d7
jbne .L42
! or.w #-32768,(%a2)
jbra .L43
.align 2
.L42:
move.l %a2,%a4
clr.b %d7
.L43:
+ clr.l %d0
move.w (%a2),%d0
and.l #8192,%d0
addq.l #1,%d5
***************
*** 739,748 ****
addq.l #6,%a3
addq.l #8,%a2
jbpl .L67
! subq.l #8,%a2
! move.w (%a2),%d0
! or.w #8192,%d0
! move.w %d0,(%a2)
movm.l (%sp)+,#0xc0c
rts
.size create_tx_pool, .-create_tx_pool
--- 726,732 ----
addq.l #6,%a3
addq.l #8,%a2
jbpl .L67
! or.w #8192,-8(%a2)
movm.l (%sp)+,#0xc0c
rts
.size create_tx_pool, .-create_tx_pool
------------------- clip -----------------------------
In the H8/300 case compiling the:
SCI_SSR0 &= 0x7F;
to be:
bclr #7,@_SCI_SSR0:8 ('Clear bit #7 in address SCI_SSR0')
is expected but replacing it with 'mov.b' / 'and' / 'mov.b' opcodes
would be very unexpected.... As the example shows, the C code
translated to machine code is a quite direct 1:1 translation...
As the m68k clips show, replacing a "clear the data in the given
memory address" is converted to "put the memory address into a
register and use this register as a pointer when clearing the data",
is another bug type besides that load-store seen also in the last
clip....
My local H8/300, H8/500, m68k etc. tools use a hack which seems to fix
this bug but at least one bug caused by the fix, as the example shows
.L43:
+ clr.l %d0
move.w (%a2),%d0
appears then... Clearing the target register before moving something
from memory into it, is vain and without the hack this doesn't happen.
Using a hack without fully knowing what it could cause somewhere else,
has however been the only way this far... Of course I have tried to
understand how GCC works but it could be much simpler to get someone
of the GCC-gurus to try to fix this.
Quite many 'current' CPU archs are now RISC-like, even those 8/16
bit ones (like AVR) and don't know this kind of opcodes, then there
aren't any differences between non-volatiles and volatiles with
them...
>> looks being simply wrong.
>
>Not necessarily. You're mixing up two things here: volatility and
>atomicity.
Maybe, I'm far from being a specialist in the C or C++ language or
its standards... My 'atomicity' simply meaned something which cannot
be interrupted when started... In the good old Z80 architecture
there were those LDIR and LDDR 'block move' opcodes, seemingly
'atomic' but these could be interrupted during their execution, but I
would be quite sure that the 'bclr', 'bset' etc. in the H8/300
architecture cannot be interrupted...
>It may go contrary to common wisdom, but since C has no concept of
>multithreading in its language definition, flagging an object as
>volatile does *not* guarantee thread-safety through atomic accesses.
>That's not its intended usage, nor is the above difference actually
>the one 'volatile' is supposed to cause.
Not changing the way GCC handles I/O ports in memory places
in the non-volatile and volatile cases is what ordinary people would
expect, not that the handling will switch to those RISC-like
load-store operations because the object is volatile....
> What volatile is supposed to
>do, in this particular context, is forbid the compiler from *keeping*
>the value in a register across any sequence point. I.e. for normal
>objects, in the above nomenclature, a sequence of
>
> 1. 2. 2. 2. 2. 2. 3. 2. 2. 2. 3.
>
>would be allowed, but for volatiles, that's expressly forbidden.
Yes, I will understand this, but cannot understand why toggling a
bit in a memory place in the H8/300 example case cannot happen as :
bset #7,@_SCI_SSR0:8 ('Set bit#7 in SCI_SSR0')
bclr #7,@_SCI_SSR0:8 ('Clear bit#7 in SCI_SSR0')
but it would maybe require 6 opcodes to do the same thing.... If this
would be allowed, someone of the GCC gurus could fix this current
feature. So there must be something which disables handling memory
places just like the CPU registers, directly, or via a pointer without
first moving the data into a CPU register and then back again after
modifying it.
I became aware of this feature more than 5 years ago and have
nagged since that, always when there is some kind of chance...
Haven't yet met an embedded person who hasn't thought this
"feature" in GCC being a bug... The GCC developers must have
some better knowledge.
People usually nag about GCC producing much bigger code than
those old commercial C compilers and in the m68k case this
"volatile bug" feature is one reason for that. The old commercial
C compilers didn't switch from direct handling to load-store with
volatile objects...
If enough embedded people will nag about this, maybe some day
we will see it being fixed... Before all the people will stop using
the H8/300, m68k, v850 etc. old (partially) CISC-type architectures,
and the fixing then being too late... With RISC-type CPUs the
"load-store" is the only and the standard way to do these things...
- Next message: Vadim Borshchev: "Re: "Emulate" USB Host"
- Previous message: Roland Zitzke: "Re: Circuit that produces a tingling sensation in the fingers."
- In reply to: Hans-Bernhard Broeker: "Re: volatile attribute"
- Next in thread: Hans-Bernhard Broeker: "Re: volatile attribute"
- Reply: Hans-Bernhard Broeker: "Re: volatile attribute"
- Reply: Guy Macon: "Re: volatile attribute"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|
|