summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2021-11-11 18:14:45 +0100
committerJan Hubicka <jh@suse.cz>2021-11-11 18:14:45 +0100
commit494bdadf28d0fb3505ff8dce5afa587e0ff46544 (patch)
tree9924cba8140a7768bc2b1a4e4997f1d1eec9fa07
parentabdff441a07f55d16e3d0e5ced3123c83d210a0a (diff)
downloadgcc-494bdadf28d0fb3505ff8dce5afa587e0ff46544.tar.gz
Enable pure-const discovery in modref.
We newly can handle some extra cases, for example: struct a {int a,b,c;}; __attribute__ ((noinline)) int init (struct a *a) { a->a=1; a->b=2; a->c=3; } int const_fn () { struct a a; init (&a); return a.a + a.b + a.c; } Here pure/const stops on the fact that const_fn calls non-const init, while modref knows that the memory it initializes is local to const_fn. I ended up reordering passes so early modref is done after early pure-const mostly to avoid need to change testsuite which greps for const functions being detects in pure-const. Stil some testuiste compensation is needed. gcc/ChangeLog: 2021-11-11 Jan Hubicka <hubicka@ucw.cz> * ipa-modref.c (analyze_function): Do pure/const discovery, return true on success. (pass_modref::execute): If pure/const is discovered fixup cfg. (ignore_edge): Do not ignore pure/const edges. (modref_propagate_in_scc): Do pure/const discovery, return true if cdtor was promoted pure/const. (pass_ipa_modref::execute): If needed remove unreachable functions. * ipa-pure-const.c (warn_function_noreturn): Fix whitespace. (warn_function_cold): Likewise. (skip_function_for_local_pure_const): Move earlier. (ipa_make_function_const): Break out from ... (ipa_make_function_pure): Break out from ... (propagate_pure_const): ... here. (pass_local_pure_const::execute): Use it. * ipa-utils.h (ipa_make_function_const): Declare. (ipa_make_function_pure): Declare. * passes.def: Move early modref after pure-const. gcc/testsuite/ChangeLog: 2021-11-11 Jan Hubicka <hubicka@ucw.cz> * c-c++-common/tm/inline-asm.c: Disable pure-const. * g++.dg/ipa/modref-1.C: Update template. * gcc.dg/tree-ssa/modref-11.c: Disable pure-const. * gcc.dg/tree-ssa/modref-14.c: New test. * gcc.dg/tree-ssa/modref-8.c: Do not optimize sibling calls. * gfortran.dg/do_subscript_3.f90: Add -O0.
-rw-r--r--gcc/ipa-modref.c85
-rw-r--r--gcc/ipa-pure-const.c233
-rw-r--r--gcc/ipa-utils.h2
-rw-r--r--gcc/passes.def2
-rw-r--r--gcc/testsuite/c-c++-common/tm/inline-asm.c2
-rw-r--r--gcc/testsuite/g++.dg/ipa/modref-1.C4
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/modref-11.c2
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/modref-14.c16
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/modref-8.c2
-rw-r--r--gcc/testsuite/gfortran.dg/do_subscript_3.f901
10 files changed, 204 insertions, 145 deletions
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 45b391a565e..72006251f29 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -2603,11 +2603,13 @@ analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto,
}
/* Analyze function F. IPA indicates whether we're running in local mode
- (false) or the IPA mode (true). */
+ (false) or the IPA mode (true).
+ Return true if fixup cfg is needed after the pass. */
-static void
+static bool
analyze_function (function *f, bool ipa)
{
+ bool fixup_cfg = false;
if (dump_file)
fprintf (dump_file, "modref analyzing '%s' (ipa=%i)%s%s\n",
function_name (f), ipa,
@@ -2617,7 +2619,7 @@ analyze_function (function *f, bool ipa)
/* Don't analyze this function if it's compiled with -fno-strict-aliasing. */
if (!flag_ipa_modref
|| lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
- return;
+ return false;
/* Compute no-LTO summaries when local optimization is going to happen. */
bool nolto = (!ipa || ((!flag_lto || flag_fat_lto_objects) && !in_lto_p)
@@ -2774,12 +2776,32 @@ analyze_function (function *f, bool ipa)
if (!summary->useful_p (ecf_flags, false))
{
remove_summary (lto, nolto, ipa);
- return;
+ return false;
}
}
first = false;
}
}
+ if (summary && !summary->global_memory_written_p () && !summary->side_effects
+ && !finite_function_p ())
+ summary->side_effects = true;
+ if (summary_lto && !summary_lto->side_effects && !finite_function_p ())
+ summary_lto->side_effects = true;
+
+ if (!ipa && flag_ipa_pure_const)
+ {
+ if (!summary->stores->every_base && !summary->stores->bases)
+ {
+ if (!summary->loads->every_base && !summary->loads->bases)
+ fixup_cfg = ipa_make_function_const
+ (cgraph_node::get (current_function_decl),
+ summary->side_effects, true);
+ else
+ fixup_cfg = ipa_make_function_pure
+ (cgraph_node::get (current_function_decl),
+ summary->side_effects, true);
+ }
+ }
if (summary && !summary->useful_p (ecf_flags))
{
if (!ipa)
@@ -2793,11 +2815,6 @@ analyze_function (function *f, bool ipa)
summaries_lto->remove (fnode);
summary_lto = NULL;
}
- if (summary && !summary->global_memory_written_p () && !summary->side_effects
- && !finite_function_p ())
- summary->side_effects = true;
- if (summary_lto && !summary_lto->side_effects && !finite_function_p ())
- summary_lto->side_effects = true;
if (ipa && !summary && !summary_lto)
remove_modref_edge_summaries (fnode);
@@ -2907,6 +2924,7 @@ analyze_function (function *f, bool ipa)
}
}
}
+ return fixup_cfg;
}
/* Callback for generate_summary. */
@@ -3714,7 +3732,8 @@ public:
unsigned int pass_modref::execute (function *f)
{
- analyze_function (f, false);
+ if (analyze_function (f, false))
+ return execute_fixup_cfg ();
return 0;
}
@@ -3749,9 +3768,7 @@ ignore_edge (struct cgraph_edge *e)
return (avail <= AVAIL_INTERPOSABLE
|| ((!optimization_summaries || !optimization_summaries->get (callee))
- && (!summaries_lto || !summaries_lto->get (callee)))
- || flags_from_decl_or_type (e->callee->decl)
- & (ECF_CONST | ECF_NOVOPS));
+ && (!summaries_lto || !summaries_lto->get (callee))));
}
/* Compute parm_map for CALLEE_EDGE. */
@@ -4130,7 +4147,7 @@ remove_useless_summaries (cgraph_node *node,
/* Perform iterative dataflow on SCC component starting in COMPONENT_NODE
and propagate loads/stores. */
-static void
+static bool
modref_propagate_in_scc (cgraph_node *component_node)
{
bool changed = true;
@@ -4352,6 +4369,38 @@ modref_propagate_in_scc (cgraph_node *component_node)
if (dump_file)
fprintf (dump_file,
"Propagation finished in %i iterations\n", iteration);
+ bool pureconst = false;
+ for (struct cgraph_node *cur = component_node; cur;
+ cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
+ if (!cur->inlined_to && opt_for_fn (cur->decl, flag_ipa_pure_const))
+ {
+ modref_summary *summary = optimization_summaries
+ ? optimization_summaries->get (cur)
+ : NULL;
+ modref_summary_lto *summary_lto = summaries_lto
+ ? summaries_lto->get (cur)
+ : NULL;
+ if (summary && !summary->stores->every_base && !summary->stores->bases)
+ {
+ if (!summary->loads->every_base && !summary->loads->bases)
+ pureconst |= ipa_make_function_const
+ (cur, summary->side_effects, false);
+ else
+ pureconst |= ipa_make_function_pure
+ (cur, summary->side_effects, false);
+ }
+ if (summary_lto && !summary_lto->stores->every_base
+ && !summary_lto->stores->bases)
+ {
+ if (!summary_lto->loads->every_base && !summary_lto->loads->bases)
+ pureconst |= ipa_make_function_const
+ (cur, summary_lto->side_effects, false);
+ else
+ pureconst |= ipa_make_function_pure
+ (cur, summary_lto->side_effects, false);
+ }
+ }
+ return pureconst;
}
/* Dump results of propagation in SCC rooted in COMPONENT_NODE. */
@@ -4831,6 +4880,7 @@ pass_ipa_modref::execute (function *)
{
if (!summaries && !summaries_lto)
return 0;
+ bool pureconst = false;
if (optimization_summaries)
ggc_delete (optimization_summaries);
@@ -4853,7 +4903,7 @@ pass_ipa_modref::execute (function *)
if (dump_file)
fprintf (dump_file, "\n\nStart of SCC component\n");
- modref_propagate_in_scc (component_node);
+ pureconst |= modref_propagate_in_scc (component_node);
modref_propagate_flags_in_scc (component_node);
if (dump_file)
modref_propagate_dump_scc (component_node);
@@ -4869,7 +4919,10 @@ pass_ipa_modref::execute (function *)
fnspec_summaries = NULL;
delete escape_summaries;
escape_summaries = NULL;
- return 0;
+
+ /* If we posibly made constructors const/pure we may need to remove
+ them. */
+ return pureconst ? TODO_remove_functions : 0;
}
/* Summaries must stay alive until end of compilation. */
diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c
index 422b52fba4b..550bdeded16 100644
--- a/gcc/ipa-pure-const.c
+++ b/gcc/ipa-pure-const.c
@@ -275,7 +275,7 @@ warn_function_noreturn (tree decl)
static hash_set<tree> *warned_about;
if (!lang_hooks.missing_noreturn_ok_p (decl)
&& targetm.warn_func_return (decl))
- warned_about
+ warned_about
= suggest_attribute (OPT_Wsuggest_attribute_noreturn, original_decl,
true, warned_about, "noreturn");
}
@@ -286,7 +286,7 @@ warn_function_cold (tree decl)
tree original_decl = decl;
static hash_set<tree> *warned_about;
- warned_about
+ warned_about
= suggest_attribute (OPT_Wsuggest_attribute_cold, original_decl,
true, warned_about, "cold");
}
@@ -1428,6 +1428,105 @@ ignore_edge_for_pure_const (struct cgraph_edge *e)
flag_ipa_pure_const));
}
+/* Return true if function should be skipped for local pure const analysis. */
+
+static bool
+skip_function_for_local_pure_const (struct cgraph_node *node)
+{
+ /* Because we do not schedule pass_fixup_cfg over whole program after early
+ optimizations we must not promote functions that are called by already
+ processed functions. */
+
+ if (function_called_by_processed_nodes_p ())
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function called in recursive cycle; ignoring\n");
+ return true;
+ }
+ /* Save some work and do not analyze functions which are interposable and
+ do not have any non-interposable aliases. */
+ if (node->get_availability () <= AVAIL_INTERPOSABLE
+ && !flag_lto
+ && !node->has_aliases_p ())
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Function is interposable; not analyzing.\n");
+ return true;
+ }
+ return false;
+}
+
+/* Make function const and output warning. If LOCAL is true,
+ return true if anything changed. Otherwise return true if
+ we may have introduced removale ctors. */
+
+bool
+ipa_make_function_const (struct cgraph_node *node, bool looping, bool local)
+{
+ bool cdtor = false;
+
+ if (TREE_READONLY (node->decl)
+ && (looping || !DECL_LOOPING_CONST_OR_PURE_P (node->decl)))
+ return false;
+ warn_function_const (node->decl, !looping);
+ if (local && skip_function_for_local_pure_const (node))
+ return false;
+ if (dump_file)
+ fprintf (dump_file, "Function found to be %sconst: %s\n",
+ looping ? "looping " : "",
+ node->dump_name ());
+ if (!local)
+ cdtor = node->call_for_symbol_and_aliases (cdtor_p, NULL, true);
+ if (node->set_const_flag (true, looping))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Declaration updated to be %sconst: %s\n",
+ looping ? "looping " : "",
+ node->dump_name ());
+ if (local)
+ return true;
+ return cdtor;
+ }
+ return false;
+}
+
+/* Make function const and output warning. If LOCAL is true,
+ return true if anything changed. Otherwise return true if
+ we may have introduced removale ctors. */
+
+bool
+ipa_make_function_pure (struct cgraph_node *node, bool looping, bool local)
+{
+ bool cdtor = false;
+
+ if (DECL_PURE_P (node->decl)
+ && (looping || DECL_LOOPING_CONST_OR_PURE_P (node->decl)))
+ return false;
+ warn_function_pure (node->decl, !looping);
+ if (local && skip_function_for_local_pure_const (node))
+ return false;
+ if (dump_file)
+ fprintf (dump_file, "Function found to be %spure: %s\n",
+ looping ? "looping " : "",
+ node->dump_name ());
+ if (!local)
+ cdtor = node->call_for_symbol_and_aliases (cdtor_p, NULL, true);
+ if (node->set_pure_flag (true, looping))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Declaration updated to be %spure: %s\n",
+ looping ? "looping " : "",
+ node->dump_name ());
+ if (local)
+ return true;
+ return cdtor;
+ }
+ return false;
+}
+
/* Produce transitive closure over the callgraph and compute pure/const
attributes. */
@@ -1442,7 +1541,6 @@ propagate_pure_const (void)
int i;
struct ipa_dfs_info * w_info;
bool remove_p = false;
- bool has_cdtor;
order_pos = ipa_reduced_postorder (order, true,
ignore_edge_for_pure_const);
@@ -1516,6 +1614,9 @@ propagate_pure_const (void)
if (e->recursive_p ())
looping = true;
+ if (e->recursive_p ())
+ looping = true;
+
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " Call to %s",
@@ -1699,55 +1800,11 @@ propagate_pure_const (void)
switch (this_state)
{
case IPA_CONST:
- if (!TREE_READONLY (w->decl))
- {
- warn_function_const (w->decl, !this_looping);
- if (dump_file)
- fprintf (dump_file, "Function found to be %sconst: %s\n",
- this_looping ? "looping " : "",
- w->dump_name ());
- }
- /* Turning constructor or destructor to non-looping const/pure
- enables us to possibly remove the function completely. */
- if (this_looping)
- has_cdtor = false;
- else
- has_cdtor = w->call_for_symbol_and_aliases (cdtor_p,
- NULL, true);
- if (w->set_const_flag (true, this_looping))
- {
- if (dump_file)
- fprintf (dump_file,
- "Declaration updated to be %sconst: %s\n",
- this_looping ? "looping " : "",
- w->dump_name ());
- remove_p |= has_cdtor;
- }
+ remove_p |= ipa_make_function_const (node, looping, false);
break;
case IPA_PURE:
- if (!DECL_PURE_P (w->decl))
- {
- warn_function_pure (w->decl, !this_looping);
- if (dump_file)
- fprintf (dump_file, "Function found to be %spure: %s\n",
- this_looping ? "looping " : "",
- w->dump_name ());
- }
- if (this_looping)
- has_cdtor = false;
- else
- has_cdtor = w->call_for_symbol_and_aliases (cdtor_p,
- NULL, true);
- if (w->set_pure_flag (true, this_looping))
- {
- if (dump_file)
- fprintf (dump_file,
- "Declaration updated to be %spure: %s\n",
- this_looping ? "looping " : "",
- w->dump_name ());
- remove_p |= has_cdtor;
- }
+ remove_p |= ipa_make_function_pure (node, looping, false);
break;
default:
@@ -2046,34 +2103,6 @@ make_pass_ipa_pure_const (gcc::context *ctxt)
return new pass_ipa_pure_const (ctxt);
}
-/* Return true if function should be skipped for local pure const analysis. */
-
-static bool
-skip_function_for_local_pure_const (struct cgraph_node *node)
-{
- /* Because we do not schedule pass_fixup_cfg over whole program after early
- optimizations we must not promote functions that are called by already
- processed functions. */
-
- if (function_called_by_processed_nodes_p ())
- {
- if (dump_file)
- fprintf (dump_file, "Function called in recursive cycle; ignoring\n");
- return true;
- }
- /* Save some work and do not analyze functions which are interposable and
- do not have any non-interposable aliases. */
- if (node->get_availability () <= AVAIL_INTERPOSABLE
- && !node->has_aliases_p ())
- {
- if (dump_file)
- fprintf (dump_file,
- "Function is interposable; not analyzing.\n");
- return true;
- }
- return false;
-}
-
/* Simple local pass for pure const discovery reusing the analysis from
ipa_pure_const. This pass is effective when executed together with
other optimization passes in early optimization pass queue. */
@@ -2144,55 +2173,13 @@ pass_local_pure_const::execute (function *fun)
switch (l->pure_const_state)
{
case IPA_CONST:
- if (!TREE_READONLY (current_function_decl))
- {
- warn_function_const (current_function_decl, !l->looping);
- if (dump_file)
- fprintf (dump_file, "Function found to be %sconst: %s\n",
- l->looping ? "looping " : "",
- current_function_name ());
- }
- else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl)
- && !l->looping)
- {
- if (dump_file)
- fprintf (dump_file, "Function found to be non-looping: %s\n",
- current_function_name ());
- }
- if (!skip && node->set_const_flag (true, l->looping))
- {
- if (dump_file)
- fprintf (dump_file, "Declaration updated to be %sconst: %s\n",
- l->looping ? "looping " : "",
- current_function_name ());
- changed = true;
- }
+ changed |= ipa_make_function_const
+ (cgraph_node::get (current_function_decl), l->looping, true);
break;
case IPA_PURE:
- if (!DECL_PURE_P (current_function_decl))
- {
- warn_function_pure (current_function_decl, !l->looping);
- if (dump_file)
- fprintf (dump_file, "Function found to be %spure: %s\n",
- l->looping ? "looping " : "",
- current_function_name ());
- }
- else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl)
- && !l->looping)
- {
- if (dump_file)
- fprintf (dump_file, "Function found to be non-looping: %s\n",
- current_function_name ());
- }
- if (!skip && node->set_pure_flag (true, l->looping))
- {
- if (dump_file)
- fprintf (dump_file, "Declaration updated to be %spure: %s\n",
- l->looping ? "looping " : "",
- current_function_name ());
- changed = true;
- }
+ changed |= ipa_make_function_pure
+ (cgraph_node::get (current_function_decl), l->looping, true);
break;
default:
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 824780f562a..e2440a1f893 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -50,6 +50,8 @@ bool recursive_call_p (tree, tree);
/* In ipa-pure-const.c */
bool finite_function_p ();
bool builtin_safe_for_const_function_p (bool *, tree);
+bool ipa_make_function_const (cgraph_node *, bool, bool);
+bool ipa_make_function_pure (cgraph_node *, bool, bool);
/* In ipa-profile.c */
bool ipa_propagate_frequency (struct cgraph_node *node);
diff --git a/gcc/passes.def b/gcc/passes.def
index 56dab80a029..b583d17c86f 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -92,13 +92,13 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_dse);
NEXT_PASS (pass_cd_dce, false /* update_address_taken_p */);
NEXT_PASS (pass_phiopt, true /* early_p */);
- NEXT_PASS (pass_modref);
NEXT_PASS (pass_tail_recursion);
NEXT_PASS (pass_if_to_switch);
NEXT_PASS (pass_convert_switch);
NEXT_PASS (pass_cleanup_eh);
NEXT_PASS (pass_profile);
NEXT_PASS (pass_local_pure_const);
+ NEXT_PASS (pass_modref);
/* Split functions creates parts that are not run through
early optimizations again. It is thus good idea to do this
late. */
diff --git a/gcc/testsuite/c-c++-common/tm/inline-asm.c b/gcc/testsuite/c-c++-common/tm/inline-asm.c
index 73892601897..176266893e9 100644
--- a/gcc/testsuite/c-c++-common/tm/inline-asm.c
+++ b/gcc/testsuite/c-c++-common/tm/inline-asm.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-fgnu-tm -O1" } */
+/* { dg-options "-fgnu-tm -O1 -fno-ipa-modref -fno-ipa-pure-const" } */
static inline void
inline_death ()
diff --git a/gcc/testsuite/g++.dg/ipa/modref-1.C b/gcc/testsuite/g++.dg/ipa/modref-1.C
index c57aaca0230..b49238162fe 100644
--- a/gcc/testsuite/g++.dg/ipa/modref-1.C
+++ b/gcc/testsuite/g++.dg/ipa/modref-1.C
@@ -30,6 +30,6 @@ int main()
linker_error ();
return 0;
}
-/* { dg-final { scan-tree-dump "Function found to be const: {anonymous}::B::genB" "local-pure-const1" } } */
-/* { dg-final { scan-tree-dump "Retslot flags: no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly not_returned_indirectly no_direct_read no_indirect_read" "modref1" } } */
+/* { dg-final { scan-tree-dump "Function found to be const: static {anonymous}::B {anonymous}::B::genB" "local-pure-const1" } } */
+/* { dg-final { scan-tree-dump "Retslot flags: not_returned_directly" "modref1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c
index cafb4f34894..10ebe1ff474 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-modref1" } */
+/* { dg-options "-O2 -fdump-tree-modref1 -fno-ipa-pure-const" } */
struct linkedlist {
struct linkedlist *next;
};
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-14.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-14.c
new file mode 100644
index 00000000000..986248f37a2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-14.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wsuggest-attribute=const" } */
+struct a {int a,b,c;};
+__attribute__ ((noinline))
+int init (struct a *a)
+{
+ a->a=1;
+ a->b=2;
+ a->c=3;
+}
+int const_fn () /* { dg-warning "function might be candidate for attribute 'const" } */
+{
+ struct a a;
+ init (&a);
+ return a.a + a.b + a.c;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c
index 15ae4acc03f..4a18e34cd16 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O2 --param modref-max-adjustments=8 -fdump-tree-modref1" } */
+/* { dg-options "-O2 --param modref-max-adjustments=8 -fdump-tree-modref1 -fno-optimize-sibling-calls" } */
/* { dg-do compile } */
void
set (char *p)
diff --git a/gcc/testsuite/gfortran.dg/do_subscript_3.f90 b/gcc/testsuite/gfortran.dg/do_subscript_3.f90
index 2f62f58142b..8f7183eb9b6 100644
--- a/gcc/testsuite/gfortran.dg/do_subscript_3.f90
+++ b/gcc/testsuite/gfortran.dg/do_subscript_3.f90
@@ -1,4 +1,5 @@
! { dg-do compile }
+! { dg-options "-O0" }
! PR fortran/91424
! Check that only one warning is issued inside blocks, and that
! warnings are also issued for contained subroutines.