In the following C code, the order of loads and stores is messed up, leading to wrong code: extern void abort (void) __attribute__((noreturn)); union setconflict { short a[20]; int b[10]; }; int main () { int sum = 0; { union setconflict a; short *c; c = a.a; asm ("": "=r" (c):"0" (c)); *c = 0; asm ("": "=r" (c):"0" (c)); sum += *c; } { union setconflict a; int *c; c = a.b; asm ("": "=r" (c):"0" (c)); *c = 1; asm ("": "=r" (c):"0" (c)); sum += *c; } printf ("%d\n",sum); if (sum != 1) abort(); return 0; } .file "t.c" # GNU C version 4.2.0 20060101 (experimental) (x86_64-unknown-linux-gnu) # compiled by GNU C version 4.0.2 20050901 (prerelease) (SUSE Linux). # GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096 # options passed: -iprefix -isystem -m32 -march=pentiumpro -auxbase -O2 # -fdump-tree-vars -fomit-frame-pointer -fverbose-asm # options enabled: -falign-loops -fargument-alias -fbranch-count-reg # -fcaller-saves -fcommon -fcprop-registers -fcrossjumping # -fcse-follow-jumps -fcse-skip-blocks -fdefer-pop # -fdelete-null-pointer-checks -fearly-inlining # -feliminate-unused-debug-types -fexpensive-optimizations -ffunction-cse # -fgcse -fgcse-lm -fguess-branch-probability -fident -fif-conversion # -fif-conversion2 -finline-functions-called-once -fipa-pure-const # -fipa-reference -fipa-type-escape -fivopts -fkeep-static-consts # -fleading-underscore -floop-optimize -floop-optimize2 -fmath-errno # -fmerge-constants -fomit-frame-pointer -foptimize-register-move # -foptimize-sibling-calls -fpcc-struct-return -fpeephole -fpeephole2 # -fregmove -freorder-blocks -freorder-functions -frerun-cse-after-loop # -frerun-loop-opt -fsched-interblock -fsched-spec # -fsched-stalled-insns-dep -fschedule-insns2 -fshow-column # -fsplit-ivs-in-unroller -fstrength-reduce -fstrict-aliasing # -fthread-jumps -ftrapping-math -ftree-ccp -ftree-ch -ftree-copy-prop # -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-fre # -ftree-loop-im -ftree-loop-ivcanon -ftree-loop-optimize -ftree-lrs # -ftree-pre -ftree-salias -ftree-sink -ftree-sra -ftree-store-ccp # -ftree-store-copy-prop -ftree-ter -ftree-vect-loop-version -ftree-vrp # -funit-at-a-time -fverbose-asm -fzero-initialized-in-bss -m32 -m80387 # -m96bit-long-double -maccumulate-outgoing-args -malign-stringops # -mfancy-math-387 -mfp-ret-in-387 -mieee-fp -mno-red-zone -mpush-args # -mtls-direct-seg-refs # Compiler executable checksum: 6d90f1c30ff8027bc6976ab2dbfe2320 .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "%d\n" .text .p2align 4,,15 .globl main .type main, @function main: leal 4(%esp), %ecx #, andl $-16, %esp #, pushl -4(%ecx) # subl $76, %esp #, leal 28(%esp), %eax #, tmp64 movl %ecx, 68(%esp) #, movl %eax, %edx # tmp64, c movl %ebx, 72(%esp) #, movw $0, (%edx) #,* c movl $1, (%eax) #,* c movswl (%edx),%ebx #* c, sum movl (%eax), %edx #* c, movl $.LC0, (%esp) #, addl %edx, %ebx #, sum movl %ebx, 4(%esp) # sum, call printf # decl %ebx # sum jne .L6 #, movl 68(%esp), %ecx #, xorl %eax, %eax # <result> movl 72(%esp), %ebx #, addl $76, %esp #, leal -4(%ecx), %esp #, ret .L6: call abort # .size main, .-main .ident "GCC: (GNU) 4.2.0 20060101 (experimental)" .section .note.GNU-stack,"",@progbits See also the thread on the gcc@ mailing list starting here: http://gcc.gnu.org/ml/gcc/2006-01/msg00008.html.
Confirmed.
I wonder if this problem can also be triggered without using two variables of the same union type. There is code in add_alias_set_conflicts to avoid the situation we're running into: static void add_alias_set_conflicts (void) { size_t i, j, n = stack_vars_num; for (i = 0; i < n; ++i) { tree type_i = TREE_TYPE (stack_vars[i].decl); bool aggr_i = AGGREGATE_TYPE_P (type_i); for (j = 0; j < i; ++j) { tree type_j = TREE_TYPE (stack_vars[j].decl); bool aggr_j = AGGREGATE_TYPE_P (type_j); if (aggr_i != aggr_j || !objects_must_conflict_p (type_i, type_j)) add_stack_var_conflict (i, j); } } } but if you have two stack variables of the same union type, aggr_i == aggr_j, and type_i == type_j so objects_must_conflict_p returns true. So perhaps if type_i and type_j are unions and type_i == type_j, we should also do an add_stack_var_conflict...?
I think we need to add a conflict if type_i and type_j are or contain a union of the same type.
Like consider the case of void foo(void) { struct A { union U u; } a; struct B { union U u; } b; } with a same-type union wrapped in different structs.
Hmm, not sure... Consider this modified test case: nion setconflict { short a[20]; int b[10]; }; int foo (void) { int sum = 0; { struct A { union setconflict u; } a; short *c; c = a.u.a; asm ("": "=r" (c):"0" (c)); *c = 2; asm ("": "=r" (c):"0" (c)); sum += *c; } { struct B { union setconflict u; } a; int *c; c = a.u.b; asm ("": "=r" (c):"0" (c)); *c = 1; asm ("": "=r" (c):"0" (c)); sum += *c; } return sum; } The two objects called a are put into different partitions because objects_must_conflict_p (type_i, type_j) now says the types A and B don't have to conflict, and we get: ;; Function foo (foo) Partition 0: size 40 align 4 a, offset 0 Partition 1: size 40 align 4 a, offset 0 This happens because the alias sets for A and B are different.
Created attachment 10651 [details] patch Testing the attached patch.
These two testcases seems to still fail for me even with the patch. (I use b.c -mpreferred-stack-boundary=2 -S -march=i686 -frename-registers) extern void abort (void) __attribute__((noreturn)); struct setconflict { char a[36]; int b; }; int main () { int sum = 0; { struct setconflict a; short *c; c = (void *)a.a; asm ("": "=r" (c):"0" (c)); *c = 0; asm ("": "=r" (c):"0" (c)); sum += *c; } { struct setconflict a; int *c; c = (void *)a.a; asm ("": "=r" (c):"0" (c)); *c = 1; asm ("": "=r" (c):"0" (c)); sum += *c; } printf ("%d\n",sum); if (sum != 1) abort(); return 0; } extern void abort (void) __attribute__((noreturn)); struct wrapper { union setconflict { short a[20]; int b[10]; } a; }; int main () { int sum = 0; { struct wrapper a; short *c; c = a.a.a; asm ("": "=r" (c):"0" (c)); *c = 0; asm ("": "=r" (c):"0" (c)); sum += *c; } { struct wrapper a; int *c; c = a.a.b; asm ("": "=r" (c):"0" (c)); *c = 1; asm ("": "=r" (c):"0" (c)); sum += *c; } printf ("%d\n",sum); if (sum != 1) abort(); return 0; } So looking for unions is IMO not strong enought test. Not sure what proper solution shall be :( Honza
Your char testcase is invalid - it violates type based aliasing rules as you access memory of type char as int and short. Is it solved with -fno-strict-aliasing?
It is solved (all are) with -fno-strict-aliasing. Whether the failure mode is that of an aliasing problem or not, is another question.
My understanding of C type based aliasing rules always was that char, as an exception, might alias with everything. Perhaps I lived in false belief for a while, but at least -Wstrict-aliasing seems to think so: ibm:~ # more t.c char a[10]; short b[10]; main() { *(int *)a=5; *(int *)b=5; } ibm:~ # gcc -O2 -Wstrict-aliasing t.c t.c: In function main: t.c:6: warning: dereferencing type-punned pointer will break strict-aliasing rules
(In reply to comment #10) > My understanding of C type based aliasing rules always was that char, as an > exception, might alias with everything. Perhaps I lived in false belief for a > while, but at least -Wstrict-aliasing seems to think so: The aliasing rules are asymmetrical. See http://gcc.gnu.org/ml/gcc-patches/2005-11/msg00980.html
Right, forgot about that... At the moment I can't think of testcase that would break the transitivity without use of unions... Honza
RTH's comments are here: http://gcc.gnu.org/ml/gcc-patches/2006-01/msg01390.html
Subject: Bug 25654 Author: rguenth Date: Mon Jan 23 09:47:01 2006 New Revision: 110109 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=110109 Log: 2006-01-23 Steven Bosscher <stevenb.gcc@gmail.com> Jan Hubicka <jh@suse.cz> Richard Guenther <rguenther@suse.de> PR rtl-optimization/25654 * cfgexpand.c (aggregate_contains_union_type): New function. (add_alias_set_conflicts): Call it. Make sure to add conflicts for structure variables that contain a union type. * gcc.dg/torture/pr25654.c: New testcase. * gcc.target/i386/pr25654.c: Likewise. Added: trunk/gcc/testsuite/gcc.dg/torture/pr25654.c trunk/gcc/testsuite/gcc.target/i386/pr25654.c Modified: trunk/gcc/ChangeLog trunk/gcc/cfgexpand.c trunk/gcc/testsuite/ChangeLog
Subject: Bug 25654 Author: rguenth Date: Mon Jan 23 09:50:07 2006 New Revision: 110110 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=110110 Log: 2006-01-23 Steven Bosscher <stevenb.gcc@gmail.com> Jan Hubicka <jh@suse.cz> Richard Guenther <rguenther@suse.de> PR rtl-optimization/25654 * cfgexpand.c (aggregate_contains_union_type): New function. (add_alias_set_conflicts): Call it. Make sure to add conflicts for structure variables that contain a union type. * gcc.dg/torture/pr25654.c: New testcase. * gcc.target/i386/pr25654.c: Likewise. Added: branches/gcc-4_1-branch/gcc/testsuite/gcc.dg/torture/pr25654.c branches/gcc-4_1-branch/gcc/testsuite/gcc.target/i386/pr25654.c Modified: branches/gcc-4_1-branch/gcc/ChangeLog branches/gcc-4_1-branch/gcc/cfgexpand.c branches/gcc-4_1-branch/gcc/testsuite/ChangeLog
Subject: Bug 25654 Author: rguenth Date: Mon Jan 23 09:54:12 2006 New Revision: 110111 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=110111 Log: 2006-01-23 Steven Bosscher <stevenb.gcc@gmail.com> Jan Hubicka <jh@suse.cz> Richard Guenther <rguenther@suse.de> PR rtl-optimization/25654 * cfgexpand.c (aggregate_contains_union_type): New function. (add_alias_set_conflicts): Call it. Make sure to add conflicts for structure variables that contain a union type. * gcc.dg/torture/pr25654.c: New testcase. * gcc.target/i386/pr25654.c: Likewise. Added: branches/gcc-4_0-branch/gcc/testsuite/gcc.dg/torture/pr25654.c branches/gcc-4_0-branch/gcc/testsuite/gcc.target/i386/pr25654.c Modified: branches/gcc-4_0-branch/gcc/ChangeLog branches/gcc-4_0-branch/gcc/cfgexpand.c branches/gcc-4_0-branch/gcc/testsuite/ChangeLog
Fixed.