https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128575
Bug ID: 128575
Summary: dse_optimize_redundant_stores omits
gimple_has_volatile_ops(use_stmt) and deletes
subsequent volatile zeroing stores (*(volatile T *)p =
0) when preceded by a zero store to the same address
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: 220245569 at seu dot edu.cn
CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
Target Milestone: ---
Target: *-*-*
Reproducer
----------
$ cat trigger.c
void scrub_int(int *p) {
*p = 0;
*(volatile int *)p = 0;
}
Compile command / flags:
$ x86_64-linux-gnu-gcc -O2 -fdump-tree-dse1-details -S trigger.c -o trigger.s
$ grep "Deleted redundant" trigger.c.*dse1*
Observed output (GCC 16.2.0):
Assembly:
scrub_int:
movl $0, (%rdi)
ret
dse1 dump:
Deleted redundant store: MEM[(volatile int *)p_2(D)] ={v} 0;
Expected behavior:
A volatile store (MEM[(volatile T *)p] ={v} 0) has observable side effects
required by the C/C++ abstract machine (C23 5.1.2.3 / 6.7.3) and must never be
deleted as redundant by DSE.
Root cause
----------
Source location: gcc/tree-ssa-dse.cc:903-946 (dse_optimize_redundant_stores) vs
1488 (dse_optimize_stmt) and gcc/tree-ssa-sccvn.cc:7377
In pass_dse (gcc/tree-ssa-dse.cc), the main dead-store elimination driver
dse_optimize_stmt (line 1488) explicitly guards against touching volatile
statements: 'if (gimple_has_volatile_ops (stmt)) return;' (and SCCVN's
eliminate_dom_walker::eliminate_stmt at tree-ssa-sccvn.cc:7377 likewise checks
'!gimple_has_volatile_ops (stmt)'). However, when a non-volatile zeroing store
(e.g. *p = 0 or memset(p, 0, n) or = {}) is visited in dse_optimize_stmt, it
calls dse_optimize_redundant_stores (stmt) (tree-ssa-dse.cc:903-946), which
walks forward over dominated virtual-def stores (use_stmt) that also store zero
to the same bytes and deletes use_stmt via delete_dead_or_redundant_assignment
(&gsi, "redundant", ...). Crucially, dse_optimize_redundant_stores never checks
'if (gimple_has_volatile_ops (use_stmt)) continue;'! As a result, when a
non-volatile zero write is followed by an explicit volatile scrub write
(*(volatile int *)p = 0), dse1 deletes the volatile store!
Impact:
Eliminates explicit volatile memory/MMIO/cryptographic zeroing stores (such as
volatile-pointer zeroing idioms used to scrub secrets or trigger twice-written
hardware MMIO registers) whenever preceded by an ordinary zero store to the
same address.
Suggested Fix
-------------
In gcc/tree-ssa-dse.cc:918 (dse_optimize_redundant_stores), add 'if
(gimple_has_volatile_ops (use_stmt)) break;' (or continue) before inspecting or
deleting use_stmt.