https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67443
--- Comment #13 from wmi at google dot com --- Use the extracted testcase vogt contributed. Here is some digging about why rtx_refs_may_alias_p returns noalias for the load and store: (gdb) c Continuing. Breakpoint 3, rtx_refs_may_alias_p (x=0x7ffff57fe768, mem=0x7ffff57fe708, tbaa_p=true) at ../../src/gcc/alias.c:385 385 if (!ao_ref_from_mem (&ref1, x) ****** (gdb) p print_rtl_single(stderr, x) (mem/j:SI (reg/v/f:DI 1 %r1 [orig:64 ps ] [64]) [5 ps_8->f2+-1 S4 A32]) $1 = 1 (gdb) p print_rtl_single(stderr, mem) (mem/j:QI (reg/v/f:DI 2 %r2 [orig:64 ps ] [64]) [5 ps_8->f1+0 S1 A32]) ****** rtx_refs_may_alias_p(x, mem, true) returns no_alias for "x" and "mem". >From RTL representation, x's starting address is ps_8->f2+-1, size is 4 //See [5 ps_8->f2+-1 S4 A32] mem's starting address is ps_8->f1+0, size is 1 // see [5 ps_8->f1+0 S1 A32] So x and mem are aliased with each other. ****** (gdb) p ref1 $3 = {ref = 0x7ffff568d9f0, base = 0x7ffff57d7c30, offset = 8, size = 24, max_size = 24, ref_alias_set = 5, base_alias_set = -1, volatile_p = false} (gdb) p ref2 $4 = {ref = 0x7ffff568d9c0, base = 0x7ffff57d7f50, offset = 0, size = 8, max_size = 8, ref_alias_set = 5, base_alias_set = -1, volatile_p = false} (gdb) p debug_generic_expr(ref1.base) *ps_8 $6 = void (gdb) p debug_generic_expr(ref2.base) *ps_8 $7 = void ****** rtx_refs_may_alias_p(x, mem, true) calls refs_may_alias_p_1(&ref1, &ref2, ...) as its helper func. For ref1 and ref2, they have the same base -- *ps_8, but they have non-overlapping accessing ranges -- ref1 from 8 to 8+24, ref2 from 0 to 8, so ref1 has no-alias with ref2. The major difference is between ref1 and x. ref1 is initialized using MEM_EXPR(x) (MEM_EXPR(x) is ps_8->f2). So ref1 has its offset to be 8 and its size to be 24. However, x has starting address to be ps_8->f2-1 and size to be 32 bits. Usually ref1's offset and size will be adjusted according to MEM_SIZE(x) and MEM_OFFSET(x). However, because of the if (...) clause below, ao_ref_from_mem returns true without adjusting ref1->offset and ref1->size. (gdb) p debug_generic_expr(MEM_EXPR(x)) ps_8->f2 (gdb) p MEM_OFFSET(x) $11 = -1 (gdb) p MEM_SIZE(x) $12 = 4 ao_ref_from_mem (ao_ref *ref, const_rtx mem) { tree expr = MEM_EXPR (mem); ... ao_ref_init (ref, expr); base = ao_ref_base (ref); ... /* If the base decl is a parameter we can have negative MEM_OFFSET in case of promoted subregs on bigendian targets. Trust the MEM_EXPR here. */ if (MEM_OFFSET (mem) < 0 && (MEM_SIZE (mem) + MEM_OFFSET (mem)) * BITS_PER_UNIT == ref->size) return true; ref->offset += MEM_OFFSET (mem) * BITS_PER_UNIT; ref->size = MEM_SIZE (mem) * BITS_PER_UNIT; ... } I don't understand the code above well -- why can we trust MEM_EXPR instead of relying on MEM_OFFSET and MEM_SIZE? It seems not the case for the testcase here.