]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tree-optimization/101373 - avoid PRE across externally throwing call
authorRichard Biener <rguenther@suse.de>
Thu, 8 Jul 2021 07:52:49 +0000 (09:52 +0200)
committerRichard Biener <rguenther@suse.de>
Mon, 12 Jul 2021 14:47:45 +0000 (16:47 +0200)
PRE already tries to avoid hoisting possibly trapping expressions
across calls that might not return normally but fails to consider
const calls that throw externally.  The following fixes that and
also plugs the hole of trapping references not pruned in case
they are not catched by the actuall call clobbering it.

At -Os we hit the same issue in RTL PRE and postreload-gcse has
even more incomplete checks so the patch adjusts both of those
as well.

2021-07-08  Richard Biener  <rguenther@suse.de>

PR tree-optimization/101373
* tree-ssa-pre.c (prune_clobbered_mems): Also prune trapping
references when the BB may not return.
(compute_avail): Pass in the function we're working on and
replace cfun references with it.  Externally throwing
const calls also possibly terminate the function.
(pass_pre::execute): Pass down the function we're working on.
* gcse.c (compute_hash_table_work): Externally throwing
const/pure calls also need record_last_mem_set_info.
* postreload-gcse.c (record_opr_changes): Looping or externally
throwing const/pure calls also need record_last_mem_set_info.

* g++.dg/torture/pr101373.C: New testcase, XFAILed.
* gnat.dg/opt95.adb: Likewise.

gcc/gcse.c
gcc/postreload-gcse.c
gcc/testsuite/g++.dg/torture/pr101373.C [new file with mode: 0644]
gcc/testsuite/gnat.dg/opt95.adb [new file with mode: 0644]
gcc/tree-ssa-pre.c

index ecf7e51aac535634834a973a8e8379e006310bae..ccd33664af535b36c8a331b0678f9bd49c98692b 100644 (file)
@@ -1537,7 +1537,8 @@ compute_hash_table_work (struct gcse_hash_table_d *table)
                record_last_reg_set_info (insn, regno);
 
              if (! RTL_CONST_OR_PURE_CALL_P (insn)
-                 || RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))
+                 || RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
+                 || can_throw_external (insn))
                record_last_mem_set_info (insn);
            }
 
index 0b28247e299eb25d7a272046f44666da41c48526..6c95d09a1e5af400125e91969a4ff385beb32a94 100644 (file)
@@ -779,7 +779,9 @@ record_opr_changes (rtx_insn *insn)
       EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi)
        record_last_reg_set_info_regno (insn, regno);
 
-      if (! RTL_CONST_OR_PURE_CALL_P (insn))
+      if (! RTL_CONST_OR_PURE_CALL_P (insn)
+         || RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
+         || can_throw_external (insn))
        record_last_mem_set_info (insn);
     }
 }
diff --git a/gcc/testsuite/g++.dg/torture/pr101373.C b/gcc/testsuite/g++.dg/torture/pr101373.C
new file mode 100644 (file)
index 0000000..f8c8097
--- /dev/null
@@ -0,0 +1,33 @@
+// { dg-do run }
+// { dg-xfail-run-if "PR100409" { *-*-* } }
+
+int __attribute__((const,noipa)) foo (int j)
+{
+  if (j != 0)
+    throw 1;
+  return 0;
+}
+
+int __attribute__((noipa)) bar (int *p, int n)
+{
+  int ret = 0;
+  if (n)
+    {
+       foo (n);
+       ret = *p;
+    }
+  ret += *p;
+  return ret;
+}
+
+int main()
+{
+  try
+    {
+      return bar (nullptr, 1);
+    }
+  catch (...)
+    {
+      return 0;
+    }
+}
diff --git a/gcc/testsuite/gnat.dg/opt95.adb b/gcc/testsuite/gnat.dg/opt95.adb
new file mode 100644 (file)
index 0000000..2c72582
--- /dev/null
@@ -0,0 +1,40 @@
+-- { dg-do run }
+-- { dg-options "-O2 -gnatp" }
+
+procedure Opt95 is
+
+  function Foo (J : Integer) return Integer;
+  pragma Pure_Function (Foo);
+  pragma Machine_Attribute (Foo, "noipa");
+
+  function Foo (J : Integer) return Integer is
+  begin
+    if J /= 0 then
+      raise Constraint_Error;
+    end if;
+    return 0;
+  end;
+
+  function Bar (A : access Integer; N : Integer) return Integer;
+  pragma Machine_Attribute (Bar, "noipa");
+
+  function Bar (A : access Integer; N : Integer) return Integer is
+    Ret : Integer := 0;
+    Ret2 : Integer := 0;
+  begin
+    if N /= 0 then
+      Ret2 := Foo (N);
+      Ret := A.all;
+    end if;
+    Ret := Ret + A.all;
+    return Ret + Ret2;
+  end;
+
+  V : Integer;
+  pragma Volatile (V);
+
+begin
+  V := Bar (null, 1);
+exception
+  when Constraint_Error => null;
+end;
index 69141c2f0c9287d3cb49e5f67b2657fb324d67a8..aa5244e678cd0f1414924026d07f78f796e67089 100644 (file)
@@ -2071,6 +2071,13 @@ prune_clobbered_mems (bitmap_set_t set, basic_block block)
                          && value_dies_in_block_x (expr, block))))
                to_remove = i;
            }
+         /* If the REFERENCE may trap make sure the block does not contain
+            a possible exit point.
+            ???  This is overly conservative if we translate AVAIL_OUT
+            as the available expression might be after the exit point.  */
+         if (BB_MAY_NOTRETURN (block)
+             && vn_reference_may_trap (ref))
+           to_remove = i;
        }
       else if (expr->kind == NARY)
        {
@@ -3860,7 +3867,7 @@ insert (void)
    AVAIL_OUT[BLOCK] = AVAIL_IN[BLOCK] U PHI_GEN[BLOCK] U TMP_GEN[BLOCK].  */
 
 static void
-compute_avail (void)
+compute_avail (function *fun)
 {
 
   basic_block block, son;
@@ -3871,7 +3878,7 @@ compute_avail (void)
 
   /* We pretend that default definitions are defined in the entry block.
      This includes function arguments and the static chain decl.  */
-  FOR_EACH_SSA_NAME (i, name, cfun)
+  FOR_EACH_SSA_NAME (i, name, fun)
     {
       pre_expr e;
       if (!SSA_NAME_IS_DEFAULT_DEF (name)
@@ -3881,31 +3888,31 @@ compute_avail (void)
 
       e = get_or_alloc_expr_for_name (name);
       add_to_value (get_expr_value_id (e), e);
-      bitmap_insert_into_set (TMP_GEN (ENTRY_BLOCK_PTR_FOR_FN (cfun)), e);
-      bitmap_value_insert_into_set (AVAIL_OUT (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
+      bitmap_insert_into_set (TMP_GEN (ENTRY_BLOCK_PTR_FOR_FN (fun)), e);
+      bitmap_value_insert_into_set (AVAIL_OUT (ENTRY_BLOCK_PTR_FOR_FN (fun)),
                                    e);
     }
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
-      print_bitmap_set (dump_file, TMP_GEN (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
+      print_bitmap_set (dump_file, TMP_GEN (ENTRY_BLOCK_PTR_FOR_FN (fun)),
                        "tmp_gen", ENTRY_BLOCK);
-      print_bitmap_set (dump_file, AVAIL_OUT (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
+      print_bitmap_set (dump_file, AVAIL_OUT (ENTRY_BLOCK_PTR_FOR_FN (fun)),
                        "avail_out", ENTRY_BLOCK);
     }
 
   /* Allocate the worklist.  */
-  worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (cfun));
+  worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (fun));
 
   /* Seed the algorithm by putting the dominator children of the entry
      block on the worklist.  */
-  for (son = first_dom_son (CDI_DOMINATORS, ENTRY_BLOCK_PTR_FOR_FN (cfun));
+  for (son = first_dom_son (CDI_DOMINATORS, ENTRY_BLOCK_PTR_FOR_FN (fun));
        son;
        son = next_dom_son (CDI_DOMINATORS, son))
     worklist[sp++] = son;
 
-  BB_LIVE_VOP_ON_EXIT (ENTRY_BLOCK_PTR_FOR_FN (cfun))
-    = ssa_default_def (cfun, gimple_vop (cfun));
+  BB_LIVE_VOP_ON_EXIT (ENTRY_BLOCK_PTR_FOR_FN (fun))
+    = ssa_default_def (fun, gimple_vop (fun));
 
   /* Loop until the worklist is empty.  */
   while (sp)
@@ -3970,7 +3977,8 @@ compute_avail (void)
                 before it.  */
              int flags = gimple_call_flags (stmt);
              if (!(flags & ECF_CONST)
-                 || (flags & ECF_LOOPING_CONST_OR_PURE))
+                 || (flags & ECF_LOOPING_CONST_OR_PURE)
+                 || stmt_can_throw_external (fun, stmt))
                BB_MAY_NOTRETURN (block) = 1;
            }
 
@@ -3987,7 +3995,7 @@ compute_avail (void)
            BB_LIVE_VOP_ON_EXIT (block) = gimple_vdef (stmt);
 
          if (gimple_has_side_effects (stmt)
-             || stmt_could_throw_p (cfun, stmt)
+             || stmt_could_throw_p (fun, stmt)
              || is_gimple_debug (stmt))
            continue;
 
@@ -4384,7 +4392,7 @@ pass_pre::execute (function *fun)
      we require AVAIL.  */
   if (n_basic_blocks_for_fn (fun) < 4000)
     {
-      compute_avail ();
+      compute_avail (fun);
       compute_antic ();
       insert ();
     }