1 The semantic descriptions in this International Standard
describe the behavior of an abstract machine in which issues
of optimization are irrelevant.
2 Accessing a volatile object, modifying an object, modifying
a file, or calling a function that does any of those operations
are all side effects,11) which are changes in the state of the
execution environment. Evaluation of an expression may
produce side effects. At certain specified points in the
execution sequence called sequence points, all side effects
of previous evaluations shall be complete and no side effects
of subsequent evaluations shall have taken place. (A
summary of the sequence points is given in annex C.)
3 In the abstract machine, all expressions are evaluated as
specified by the semantics. An actual implementation need
not evaluate part of an expression if it can deduce that its
value is not used and that no needed side effects are
produced (including any caused by calling a function or
accessing a volatile object).
6.7.3 Type qualifiers
6 An object that has volatile-qualified type may be modified in
ways unknown to the implementation or have other unknown
side effects. Therefore any expressionreferring to such an
object shall be evaluated strictly according to the rules of the
abstract machine, as described in 5.1.2.3. Furthermore, at
every sequence point the value last stored in the object shall
agree with that prescribed by the abstract machine, except
as modified by the unknown factors mentioned
previously.114) What constitutes an access to an object that
has volatile-qualified type is implementation-defined.
In fact GCC does comply with this. GCC 2.95.3 generates
the following
*correct* code:
If newer versions of GCC behave otherwise, then they are
broken.
Further on, the proposed solution of using "dummy=x"
doesn't actually change anything. If "dummy" is a non-volatile
local variable, then it can be optimized out, bringing us back
to the same problem. If "dummy" is global variable, then we
are changing the desired semantics of the program (which in
this case are to read from "x" and ignore the value), which is
unacceptable.
This bug may be difficult to fix, but there is no other standard
way to access memory-mapped hardware registers. I believe
this is very important for an embedded compiler.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
You are right, this is a bug. Sorry about my premature
conclusion.
The problem starts in ast2iCode where it is NULLOP. This
can be handled by inserting a dummy assignment to an
iTemp, however a volatile iTemp is misinterpreted in the
optimizer and if handled correctly there, later on in the code
generator. The following code shows that clearly:
sfr at 0x80 MYREG;
void oops (void) {
MYREG=MYREG;
}
The aopPut() and aopGet() methods in the code generator
have no knowledge about the volatile-ness of the symbol. This
requires a major redesign.
So I raised the bug with three points above average.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In this context it is an interresting question whether sfr-s
should be volatile by default. I think that ideally it should be
decided on a case by case basis - for example SBUF should
be declared volatile, but "pure" registers like DPTR, ACC, B,
etc should not be.
Volatile has always been a pain in the a** :-) Note that the
Standard actually allows an implementation to
ignore "volatile", as long as this is a documented and
deliberate choice ("What constitutes an access to an object
that has volatile-qualified type is implementation-defined").
Unfortunately this is not very useful in practice...
I am thinking that for now a possibly easy workaround might
be to ignore "volatile" altogether but add several inline
intrinsic functions like __volatile_read_char(),
__volatile_write_char(), etc, with precisely defined semantics.
This however still could be defeated by a peephole
optimization like this seemingly safe one:
replace {
mov a,%1
mov a,%1
} by {
mov a,%1
}
so a predicate "operandsNotVolatile" may have to be added.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I see genDummyRead in avr, ds390, hc08, mcs51, pic,
pic16, xa51 and z80. Does this mean every port has this bug
fixed and this report can be closed? Has anyone any idea for
a regression test for this bug.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
How about this: If I'm not mistaken the simulator ends a
simulation when a certain specific address is accessed. By
placing the volatile variable at that specific address using
__at we can end the simulation: a side-effect we can check.
Otherwise the test must hang and generate a timeout.
volatile xdata at 0x7654 char x;
void test (void)
{
x;
while (1); //let the "watchdog" bite
}
Unfortunately this seems only true for mcs51 alike mcu's.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Logged In: YES
user_id=63512
GCC doesn't do that either, so I think this is correct. Use
dummy=x instead.
Logged In: YES
user_id=675109
SDCC is not correct.
Relevant abstracts from the standard:
5.1.2.3 Program execution
1 The semantic descriptions in this International Standard
describe the behavior of an abstract machine in which issues
of optimization are irrelevant.
2 Accessing a volatile object, modifying an object, modifying
a file, or calling a function that does any of those operations
are all side effects,11) which are changes in the state of the
execution environment. Evaluation of an expression may
produce side effects. At certain specified points in the
execution sequence called sequence points, all side effects
of previous evaluations shall be complete and no side effects
of subsequent evaluations shall have taken place. (A
summary of the sequence points is given in annex C.)
3 In the abstract machine, all expressions are evaluated as
specified by the semantics. An actual implementation need
not evaluate part of an expression if it can deduce that its
value is not used and that no needed side effects are
produced (including any caused by calling a function or
accessing a volatile object).
6.7.3 Type qualifiers
6 An object that has volatile-qualified type may be modified in
ways unknown to the implementation or have other unknown
side effects. Therefore any expressionreferring to such an
object shall be evaluated strictly according to the rules of the
abstract machine, as described in 5.1.2.3. Furthermore, at
every sequence point the value last stored in the object shall
agree with that prescribed by the abstract machine, except
as modified by the unknown factors mentioned
previously.114) What constitutes an access to an object that
has volatile-qualified type is implementation-defined.
In fact GCC does comply with this. GCC 2.95.3 generates
the following
*correct* code:
volatile int x;
void test ( void )
{
x;
}
_test:
pushl %ebp
movl %esp,%ebp
movl _x,%eax
movl %ebp,%esp
popl %ebp
ret
If newer versions of GCC behave otherwise, then they are
broken.
Further on, the proposed solution of using "dummy=x"
doesn't actually change anything. If "dummy" is a non-volatile
local variable, then it can be optimized out, bringing us back
to the same problem. If "dummy" is global variable, then we
are changing the desired semantics of the program (which in
this case are to read from "x" and ignore the value), which is
unacceptable.
This bug may be difficult to fix, but there is no other standard
way to access memory-mapped hardware registers. I believe
this is very important for an embedded compiler.
Logged In: YES
user_id=63512
You are right, this is a bug. Sorry about my premature
conclusion.
The problem starts in ast2iCode where it is NULLOP. This
can be handled by inserting a dummy assignment to an
iTemp, however a volatile iTemp is misinterpreted in the
optimizer and if handled correctly there, later on in the code
generator. The following code shows that clearly:
sfr at 0x80 MYREG;
void oops (void) {
MYREG=MYREG;
}
The aopPut() and aopGet() methods in the code generator
have no knowledge about the volatile-ness of the symbol. This
requires a major redesign.
So I raised the bug with three points above average.
Logged In: YES
user_id=675109
In this context it is an interresting question whether sfr-s
should be volatile by default. I think that ideally it should be
decided on a case by case basis - for example SBUF should
be declared volatile, but "pure" registers like DPTR, ACC, B,
etc should not be.
Volatile has always been a pain in the a** :-) Note that the
Standard actually allows an implementation to
ignore "volatile", as long as this is a documented and
deliberate choice ("What constitutes an access to an object
that has volatile-qualified type is implementation-defined").
Unfortunately this is not very useful in practice...
I am thinking that for now a possibly easy workaround might
be to ignore "volatile" altogether but add several inline
intrinsic functions like __volatile_read_char(),
__volatile_write_char(), etc, with precisely defined semantics.
This however still could be defeated by a peephole
optimization like this seemingly safe one:
replace {
mov a,%1
mov a,%1
} by {
mov a,%1
}
so a predicate "operandsNotVolatile" may have to be added.
Logged In: YES
user_id=635249
Fixed for mcs51, ds390, z80, & their associated targets. See
ChangeLog 1.444
pic & pic16 still need a real genDummyRead and support for
dummy iTemps.
Logged In: YES
user_id=888171
I see genDummyRead in avr, ds390, hc08, mcs51, pic,
pic16, xa51 and z80. Does this mean every port has this bug
fixed and this report can be closed? Has anyone any idea for
a regression test for this bug.
Logged In: YES
user_id=589052
> Has anyone any idea for a regression test for this bug.
This one is a rude check whether there's any code generated:
volatile xdata char x;
volatile data char check_me;
void test ( void )
{
_asm
MY_UNIQUE_1 = .
_endasm;
x;
_asm
MY_UNIQUE_2 = .
mov _check_me,#MY_UNIQUE_2-MY_UNIQUE_1
_endasm;
}
Logged In: YES
user_id=888171
How about this: If I'm not mistaken the simulator ends a
simulation when a certain specific address is accessed. By
placing the volatile variable at that specific address using
__at we can end the simulation: a side-effect we can check.
Otherwise the test must hang and generate a timeout.
volatile xdata at 0x7654 char x;
void test (void)
{
x;
while (1); //let the "watchdog" bite
}
Unfortunately this seems only true for mcs51 alike mcu's.