Bug 47409 - volatile struct member bug
Summary: volatile struct member bug
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 4.6.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 58409 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-01-22 03:46 UTC by John Regehr
Modified: 2016-10-24 18:02 UTC (History)
9 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description John Regehr 2011-01-22 03:46:07 UTC
The load/store shouldn't go away (especially at -O0).

regehr@home:~$ current-gcc -O0 vol.c -S -o - 
foo:
	pushl	%ebp
	movl	%esp, %ebp
	popl	%ebp
	ret

regehr@home:~$ cat small.c

const volatile int g_2 = 1;
int g_1 = 0;

void func_1 (void) {
   g_1 = g_2;
}

int main (void) {
  g_1 = g_2;
  return 0;
}

regehr@home:~$ current-gcc -v

Using built-in specs.
COLLECT_GCC=current-gcc
COLLECT_LTO_WRAPPER=/mnt/z/z/compiler-install/gcc-r169118-install/libexec/gcc/i686-pc-linux-gnu/4.6.0/lto-wrapper
Target: i686-pc-linux-gnu
Configured with: ../configure --with-libelf=/usr/local --enable-lto --prefix=/mnt/z/z/compiler-install/gcc-r169118-install --program-prefix=r169118- --enable-languages=c,c++
Thread model: posix
gcc version 4.6.0 20110122 (experimental) (GCC)
Comment 1 John Regehr 2011-01-22 03:57:20 UTC
Ack, sorry, wrong testcase!  This is it:

struct s2 {
  volatile int x;
};

struct s2 s;

void foo (void) {
  s = s;
}
Comment 2 Jakub Jelinek 2011-01-24 16:23:44 UTC
Not sure if this is a bug at all, structure assignment should be implementable using memcpy or memmove and thus the side effects that will happen on it are not very well defined.
Comment 3 John Regehr 2011-01-24 16:43:58 UTC
(In reply to comment #2)
> Not sure if this is a bug at all, structure assignment should be implementable
> using memcpy or memmove and thus the side effects that will happen on it are
> not very well defined.

Hi Jakub-  "volatile" isn't a very strong guarantee, but realistically people assume that if a volatile is on the RHS of an assignment, a load from that location occurs (and a store, if on the LHS).  My guess is that violating this contract will confuse embedded systems developers and break previously working code.

Also, if the rule for volatile becomes significantly different from what I said above, we won't be able to do volatile testing anymore since violations will be sort of meaningless.
Comment 4 Jakub Jelinek 2011-01-24 16:49:17 UTC
This is related to PR45472 and is solely about volatile fields in aggregates.
Comment 5 joseph@codesourcery.com 2011-01-25 00:00:37 UTC
I think we should respect volatile on fields, and not use memcpy/memmove 
for assignment of volatile structs or structs with volatile fields (at 
least not for the parts with those fields; it's probably OK, but not 
worthwhile, for the non-volatile bits of the structs).
Comment 6 Richard Biener 2011-01-25 11:03:10 UTC
We should at least make sure to use memcpy for the array part in

struct {
  volatile int i;
  int a[100000];
} a, b;
a = b;

do we really want to blow up code-size (and compile-time) for

struct {
  volatile int a[1000000];
} a, b;
a = b;

?  And what's the difference of the above to

volatile struct {
  int a[1000000];
} a, b;
a = b;

?

What do other compilers do for the above?  Is there a DR?
Comment 7 John Regehr 2011-01-25 15:41:58 UTC
(In reply to comment #6)
> struct {
>   volatile int a[1000000];
> } a, b;
> a = b;
> 
> ?  And what's the difference of the above to
> 
> volatile struct {
>   int a[1000000];
> } a, b;
> a = b;

There's no effective difference, I believe.
Comment 8 joseph@codesourcery.com 2011-01-25 17:04:24 UTC
On Tue, 25 Jan 2011, rguenth at gcc dot gnu.org wrote:

> do we really want to blow up code-size (and compile-time) for
> 
> struct {
>   volatile int a[1000000];
> } a, b;
> a = b;
> 
> ?  And what's the difference of the above to
> 
> volatile struct {
>   int a[1000000];
> } a, b;
> a = b;
> 
> ?

I think these are much the same - and of course an inline loop would be 
better than blowing up code size with separate instructions for each int.  
If in doubt, be conservative about the volatile values possibly being in 
some special kind of mapped memory that memcpy/memmove may not work with 
(and where certainly there should be exactly the correct number of reads 
and writes of each memory location).
Comment 9 John Regehr 2013-01-30 04:36:01 UTC
Just wanted to ping about this one; it's still there in tonight's GCC.

regehr@home:~$ cat vol.c
struct s2 {
  volatile int x;
};

struct s2 s;

void foo (void) {
  s = s;
}
regehr@home:~$ gcc -O0 vol.c -c
regehr@home:~$ objdump -d vol.o

vol.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <foo>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	5d                   	pop    %rbp
   5:	c3                   	retq   
regehr@home:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/home/regehr/z/compiler-install/gcc-r195565-install/libexec/gcc/x86_64-unknown-linux-gnu/4.8.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /home/regehr/z/compiler-source/gcc/configure --with-libelf=/usr/local --enable-lto --prefix=/home/regehr/z/compiler-install/gcc-r195565-install --enable-languages=c,c++
Thread model: posix
gcc version 4.8.0 20130129 (experimental) (GCC)
Comment 10 Richard Biener 2013-01-30 11:38:30 UTC
struct s2 {
  volatile int x;
};

struct s2 s;

void foo (void) {
  s = s;
}

As said previously I think that volatile struct members are ill-defined.
The only way for the middle-end to see the volatileness in the above
s = s copy is if the frontend would make 's' volatile.  That is,
effectively make it

struct s2 {
  volatile int x;
};

typedef volatile struct s2 S;

S s;

as there is otherwise no way to mark 's' in s = s with TREE_THIS_VOLATILE
(it's the plain decl, which in the original testcase is not volatile).
Comment 11 Jakub Jelinek 2013-01-30 11:43:38 UTC
Or the FE should expand the structure assignment in that case to some other stmts based on what the right semantics is (using loops for larger objects etc.) and only keep aggregate assignments in the IL for non-volatile (neither object itself, nor any of the fields) assignments.
Comment 12 John Regehr 2013-01-30 23:24:36 UTC
(In reply to comment #10)
> As said previously I think that volatile struct members are ill-defined.

As far as the C standard goes, I believe the situation is clear: a volatile struct member is a volatile-qualified variable and the rules for volatile variables apply to it.

Clang, for example, turns foo() into a load + store at all optimization levels. I believe the Intel compiler does as well but I don't have it available right now.
Comment 13 Jakub Jelinek 2013-02-06 11:47:05 UTC
I guess we could e.g. handle this in c_gimplify_expr, but the question is what the exact semantics should it have.  Testcase:
struct S { int a; volatile int b; long c; volatile char d[10]; char e[10]; };
struct S a, b;
volatile struct S c, d;
union U { int a; volatile char b[10]; volatile long c[5]; };
union U e, f;
volatile union U g, h;
struct T { int a; volatile int b; union U c; volatile union U d; };
struct T i, j;
volatile struct T k, l;
struct V { int a : 5; volatile int b : 7; volatile int c : 1; int d; volatile long e : 5; long f : 6; volatile long g : 1; long h : 1; };
struct V m, n;
volatile struct V o, p;
void f1 () { a = b; }
void f2 () { c = b; }
void f3 () { a = d; }
void f4 () { c = d; }
void f5 () { e = f; }
void f6 () { g = f; }
void f7 () { e = h; }
void f8 () { g = h; }
void f9 () { i = j; }
void f10 () { k = j; }
void f11 () { i = l; }
void f12 () { k = l; }
void f13 () { m = n; }
void f14 () { o = n; }
void f15 () { m = p; }
void f16 () { o = p; }

I guess for struct S, it could gimplify it for f1 to:
a.a = b.a;
a.b = b.b;
a.c = b.c;
for (temp = 0; temp < 10; temp++)
a.d[i] = b.d[i];
a.e = b.e; // aggregate assignment
and for f2, f3 and f4 the same, except that instead of the aggregate assignment
at the end it would emit a loop similar to d field.
But, what to do about unions?  The standard says that only one union member is active, but which one it is?  I think the compiler generally can't know.  So, do we just ignore unions and expand them always as we used to?  Pick up the first union member (or randomly or preferrably one with volatile)?
What about bitfields?  Does it have to be per bitfield assignment, or can we e.g. assign the whole representative field at a time?

What are other compilers doing here?

I've tried clang 3.1, and don't see it would consider any of the volatile keywords here in any way.
Comment 14 Jason Merrill 2013-02-06 14:59:57 UTC
(In reply to comment #13)
> But, what to do about unions?  The standard says that only one union member is
> active, but which one it is?  I think the compiler generally can't know.  So,
> do we just ignore unions and expand them always as we used to?  Pick up the
> first union member (or randomly or preferably one with volatile)?

C++ defines copy of a (trivially copyable) union to copy the object representation, which is not volatile unless the whole union is volatile.  I can't find anything relevant in C11.

There is also C++ DR 496:
http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_toc.html#496

It seems the effect of this change will be to make assignment of a union with a volatile field ill-formed in C++ unless the union has a user-provided assignment operator.

I think we just ignore unions, at least in the middle end.

> What about bitfields?  Does it have to be per bitfield assignment, or can we
> e.g. assign the whole representative field at a time?

I think we should follow the memory model data race rules here; volatile accesses are done per memory location, rather than per field.
Comment 15 joseph@codesourcery.com 2013-02-07 01:42:21 UTC
I think the most obvious way to handle volatile and unions for C would be 
to follow the handling of const (set C_TYPE_FIELDS_VOLATILE in the same 
way as C_TYPE_FIELDS_READONLY - that is, checking for fields whose types 
have C_TYPE_FIELDS_VOLATILE rather than just fields that are directly 
volatile - and use it to determine whether the struct or union is at 
least in part volatile for assignment).  Though for unions the best you 
can do might be a copying loop; without knowing the active union member 
you can hardly respect access sizes for individual members, even if you 
wanted to.
Comment 16 Francesco Zappa Nardelli 2013-07-09 08:48:11 UTC
Dear all

a possibly related issue.  Consider

struct S1 {
  long f;
};
volatile struct S1 g;

struct S1 func_1 () {
  return g;
}

void main () {
  func_1 ();
}

This program, if compiled with a recent gcc svn:

$ gcc -v
Target: x86_64-unknown-linux-gnu
gcc version 4.9.0 20130625 (experimental) (GCC) 

correctly loads the long at g.f at -O0.  However the assembly generated at -O2:

func_1:
      movq    g(%rip), %rax
      ret
main:
      rep; ret 

does not perform the volatile load access, which, as far as I understand, is incorrect.

-francesco
Comment 17 Richard Biener 2013-09-13 09:38:45 UTC
*** Bug 58409 has been marked as a duplicate of this bug. ***
Comment 18 Richard Biener 2013-09-13 09:52:06 UTC
(In reply to Francesco Zappa Nardelli from comment #16)
> Dear all
> 
> a possibly related issue.  Consider
> 
> struct S1 {
>   long f;
> };
> volatile struct S1 g;
> 
> struct S1 func_1 () {
>   return g;
> }
> 
> void main () {
>   func_1 ();
> }
> 
> This program, if compiled with a recent gcc svn:
> 
> $ gcc -v
> Target: x86_64-unknown-linux-gnu
> gcc version 4.9.0 20130625 (experimental) (GCC) 
> 
> correctly loads the long at g.f at -O0.  However the assembly generated at
> -O2:
> 
> func_1:
>       movq    g(%rip), %rax
>       ret
> main:
>       rep; ret 
> 
> does not perform the volatile load access, which, as far as I understand, is
> incorrect.

It does starting with GCC 4.8.2 and was a bug in older GCC versions.

Richard.

> -francesco
Comment 19 Francesco Zappa Nardelli 2013-09-13 10:38:24 UTC
>> does not perform the volatile load access.

> It does starting with GCC 4.8.2 and was a bug in older GCC versions.

I just tested my example (comment 16) against yesterday trunk

   gcc version 4.9.0 20130912 (experimental) (GCC) 

and indeed the volatile load access is no longer removed.  This is a good news.

However the code I reported in bug 58409, which has been marked duplicate of this bug, still exhibits the incorrect reordering of volatile accesses.  It thus seems to me that either bug 58409 is not a duplicate of this one, or the fix is incomplete.  

-francesco
Comment 20 Richard Biener 2013-09-13 11:51:38 UTC
(In reply to Francesco Zappa Nardelli from comment #19)
> >> does not perform the volatile load access.
> 
> > It does starting with GCC 4.8.2 and was a bug in older GCC versions.
> 
> I just tested my example (comment 16) against yesterday trunk
> 
>    gcc version 4.9.0 20130912 (experimental) (GCC) 
> 
> and indeed the volatile load access is no longer removed.  This is a good
> news.
> 
> However the code I reported in bug 58409, which has been marked duplicate of
> this bug, still exhibits the incorrect reordering of volatile accesses.  It
> thus seems to me that either bug 58409 is not a duplicate of this one, or
> the fix is incomplete.  

It is a duplicate of this one because it is about a volatile struct member
in a not volatile object g_3[1][1][1].  And it is about the aggregate
assignment to that struct.

> -francesco
Comment 21 Francesco Zappa Nardelli 2013-09-13 14:46:28 UTC
(In reply to Richard Biener from comment #20)

> > However the code I reported in bug 58409, which has been marked duplicate of
> > this bug, still exhibits the incorrect reordering of volatile accesses.  It
> > thus seems to me that either bug 58409 is not a duplicate of this one, or
> > the fix is incomplete.  
> 
> It is a duplicate of this one because it is about a volatile struct member
> in a not volatile object g_3[1][1][1].  And it is about the aggregate
> assignment to that struct.

Agreed.  What I don't understand is the fact that the commits that led to the recent gcc svn trunk

    gcc version 4.9.0 20130912 (experimental) (GCC) 

solve the problem with the code in comment 16, but do not prevent the reordering of volatile writes described in bug 58409.  As a consequence, it seems to me that gcc does not yet implement a correct semantics for accesses to volatile struct members in non volatile objects.  Am I missing something or another fix is to be expected?  Thanks.

-francesco
Comment 22 Jackie Rosen 2014-02-16 13:12:43 UTC Comment hidden (spam)