Handle cfa adjustments in csa pass
authorRichard Henderson <rth@redhat.com>
Mon, 13 Oct 2014 20:20:44 +0000 (13:20 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Mon, 13 Oct 2014 20:20:44 +0000 (13:20 -0700)
* combine-stack-adj.c (no_unhandled_cfa): New.
(maybe_merge_cfa_adjust): New.
(combine_stack_adjustments_for_block): Use them.

From-SVN: r216161

gcc/ChangeLog
gcc/combine-stack-adj.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/torture/20141013.C [new file with mode: 0644]

index a60aae0c3525d63a55888c5b650752becdb689e7..8829dfeec2003ec621cb4ec398ccc4d74bd4e1c0 100644 (file)
@@ -1,3 +1,9 @@
+2014-10-13  Richard Henderson  <rth@redhat.com>
+
+       * combine-stack-adj.c (no_unhandled_cfa): New.
+       (maybe_merge_cfa_adjust): New.
+       (combine_stack_adjustments_for_block): Use them.
+
 2014-10-13  Aldy Hernandez  <aldyh@redhat.com>
 
        * Makefile.in (TAGS): Tag ../include files.
index aebdf87d80752e927e457da1a88eac3afa09cf77..844873c574dce27eaf8d9d1f4467d92e95192644 100644 (file)
@@ -190,6 +190,44 @@ record_one_stack_ref (rtx_insn *insn, rtx *ref, struct csa_reflist *next_reflist
   return ml;
 }
 
+/* We only know how to adjust the CFA; no other frame-related changes
+   may appear in any insn to be deleted.  */
+
+static bool
+no_unhandled_cfa (rtx_insn *insn)
+{
+  if (!RTX_FRAME_RELATED_P (insn))
+    return true;
+
+  /* No CFA notes at all is a legacy interpretation like
+     FRAME_RELATED_EXPR, and is context sensitive within
+     the prologue state machine.  We can't handle that here.  */
+  bool has_cfa_adjust = false;
+
+  for (rtx link = REG_NOTES (insn); link; link = XEXP (link, 1))
+    switch (REG_NOTE_KIND (link))
+      {
+      default:
+        break;
+      case REG_CFA_ADJUST_CFA:
+       has_cfa_adjust = true;
+       break;
+
+      case REG_FRAME_RELATED_EXPR:
+      case REG_CFA_DEF_CFA:
+      case REG_CFA_OFFSET:
+      case REG_CFA_REGISTER:
+      case REG_CFA_EXPRESSION:
+      case REG_CFA_RESTORE:
+      case REG_CFA_SET_VDRAP:
+      case REG_CFA_WINDOW_SAVE:
+      case REG_CFA_FLUSH_QUEUE:
+       return false;
+      }
+
+  return has_cfa_adjust;
+}
+
 /* Attempt to apply ADJUST to the stack adjusting insn INSN, as well
    as each of the memories and stack references in REFLIST.  Return true
    on success.  */
@@ -320,6 +358,44 @@ maybe_move_args_size_note (rtx_insn *last, rtx_insn *insn, bool after)
     add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0));
 }
 
+/* Merge any REG_CFA_ADJUST_CFA note from SRC into DST.
+   AFTER is true iff DST follows SRC in the instruction stream.  */
+
+static void
+maybe_merge_cfa_adjust (rtx_insn *dst, rtx_insn *src, bool after)
+{
+  rtx snote = NULL, dnote = NULL;
+  rtx sexp, dexp;
+  rtx exp1, exp2;
+
+  if (RTX_FRAME_RELATED_P (src))
+    snote = find_reg_note (src, REG_CFA_ADJUST_CFA, NULL_RTX);
+  if (snote == NULL)
+    return;
+  sexp = XEXP (snote, 0);
+
+  if (RTX_FRAME_RELATED_P (dst))
+    dnote = find_reg_note (dst, REG_CFA_ADJUST_CFA, NULL_RTX);
+  if (dnote == NULL)
+    {
+      add_reg_note (dst, REG_CFA_ADJUST_CFA, sexp);
+      return;
+    }
+  dexp = XEXP (dnote, 0);
+
+  gcc_assert (GET_CODE (sexp) == SET);
+  gcc_assert (GET_CODE (dexp) == SET);
+
+  if (after)
+    exp1 = dexp, exp2 = sexp;
+  else
+    exp1 = sexp, exp2 = dexp;
+
+  SET_SRC (exp1) = simplify_replace_rtx (SET_SRC (exp1), SET_DEST (exp2),
+                                        SET_SRC (exp2));
+  XEXP (dnote, 0) = exp1;
+}
+
 /* Return the next (or previous) active insn within BB.  */
 
 static rtx_insn *
@@ -491,12 +567,15 @@ combine_stack_adjustments_for_block (basic_block bb)
              /* Combine an allocation into the first instruction.  */
              if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0)
                {
-                 if (try_apply_stack_adjustment (last_sp_set, reflist,
-                                                 last_sp_adjust + this_adjust,
-                                                 this_adjust))
+                 if (no_unhandled_cfa (insn)
+                     && try_apply_stack_adjustment (last_sp_set, reflist,
+                                                    last_sp_adjust
+                                                    + this_adjust,
+                                                    this_adjust))
                    {
                      /* It worked!  */
                      maybe_move_args_size_note (last_sp_set, insn, false);
+                     maybe_merge_cfa_adjust (last_sp_set, insn, false);
                      delete_insn (insn);
                      last_sp_adjust += this_adjust;
                      continue;
@@ -508,12 +587,15 @@ combine_stack_adjustments_for_block (basic_block bb)
              else if (STACK_GROWS_DOWNWARD
                       ? last_sp_adjust >= 0 : last_sp_adjust <= 0)
                {
-                 if (try_apply_stack_adjustment (insn, reflist,
-                                                 last_sp_adjust + this_adjust,
-                                                 -last_sp_adjust))
+                 if (no_unhandled_cfa (last_sp_set)
+                     && try_apply_stack_adjustment (insn, reflist,
+                                                    last_sp_adjust
+                                                    + this_adjust,
+                                                    -last_sp_adjust))
                    {
                      /* It worked!  */
                      maybe_move_args_size_note (insn, last_sp_set, true);
+                     maybe_merge_cfa_adjust (insn, last_sp_set, true);
                      delete_insn (last_sp_set);
                      last_sp_set = insn;
                      last_sp_adjust += this_adjust;
@@ -528,9 +610,10 @@ combine_stack_adjustments_for_block (basic_block bb)
                 delete the old deallocation insn.  */
              if (last_sp_set)
                {
-                 if (last_sp_adjust == 0)
+                 if (last_sp_adjust == 0 && no_unhandled_cfa (last_sp_set))
                    {
                      maybe_move_args_size_note (insn, last_sp_set, true);
+                     maybe_merge_cfa_adjust (insn, last_sp_set, true);
                      delete_insn (last_sp_set);
                    }
                  else
index 139ea44dfcbf14012c74d496ea28abe4e94a7ec8..b052a7041065ddafa0aa8407c257be152c8aa3fc 100644 (file)
@@ -1,3 +1,7 @@
+2014-10-13  Richard Henderson  <rth@redhat.com>
+
+       * g++.dg/torture/20141013.C: New.
+
 2014-10-13  Evgeny Stupachenko  <evstupac@gmail.com>
 
        PR target/8340
diff --git a/gcc/testsuite/g++.dg/torture/20141013.C b/gcc/testsuite/g++.dg/torture/20141013.C
new file mode 100644 (file)
index 0000000..529ef09
--- /dev/null
@@ -0,0 +1,267 @@
+enum
+{
+  _sch_isdigit = 0x0004,
+  _sch_ispunct = 0x0020,
+  _sch_isxdigit = 0x0100,
+  _sch_isidst = 0x0200,
+  _sch_isvsp = 0x0400,
+  _sch_isnvsp = 0x0800,
+  _sch_isalnum = _sch_isidst | _sch_isdigit,
+  _sch_iscppsp = _sch_isvsp | _sch_isnvsp,
+};
+extern const unsigned short _sch_istable[256];
+typedef union tree_node *tree;
+typedef const union tree_node *const_tree;
+enum opt_code
+{
+  OPT_Warray_bounds = 240,
+  OPT_Wformat_ = 245,
+  OPT_Wintf_annotation = 368,
+  OPT_std_gnu__14 = 1311,
+};
+enum tree_code
+{
+  TREE_LIST,
+  CONST_DECL,
+  ADDR_EXPR,
+  MAX_TREE_CODES
+};
+enum tree_code_class
+{
+  tcc_type,
+};
+enum tree_node_structure_enum
+{
+  TS_TYPED,
+  TS_COMMON,
+};
+enum integer_type_kind
+{
+  itk_char,
+  itk_none
+};
+struct tree_base
+{
+  enum tree_code code:16;
+};
+struct tree_typed
+{
+  tree type;
+};
+struct tree_common
+{
+  tree chain;
+};
+struct tree_list
+{
+  tree purpose;
+};
+struct tree_type_common
+{
+  tree main_variant;
+};
+union tree_node
+{
+  struct tree_base base;
+  struct tree_typed typed;
+  struct tree_common common;
+  struct tree_type_common type_common;
+  struct tree_list list;
+};
+extern unsigned char tree_contains_struct[MAX_TREE_CODES][64];
+extern tree integer_types[itk_none];
+extern void tree_contains_struct_check_failed (const_tree,
+                                              tree_node_structure_enum,
+                                              const char *, int,
+                                              const char *)
+  __attribute__ ((__noreturn__));
+inline tree
+tree_check (tree __t, const char *__f, int __l, const char *__g,
+           tree_code __c)
+{
+}
+
+inline const_tree
+contains_struct_check (const_tree __t,
+                      const enum tree_node_structure_enum __s,
+                      const char *__f, int __l, const char *__g)
+{
+  if (tree_contains_struct[((enum tree_code) (__t)->base.code)][__s] != 1)
+    tree_contains_struct_check_failed (__t, __s, __f, __l, __g);
+}
+
+inline const_tree
+tree_class_check (const_tree __t, const enum tree_code_class __class,
+                 const char *__f, int __l, const char *__g)
+{
+}
+
+static inline bool
+is_attribute_p (const char *attr_name, const_tree ident)
+{
+}
+
+extern int integer_zerop (const_tree);
+extern bool warning (int, const char *, ...)
+  __attribute__ ((__nonnull__ (2)));
+extern void
+check_function_arguments_recurse (void (*)(void *, tree, unsigned long long),
+                                 void *, tree, unsigned long long);
+extern bool objc_string_ref_type_p (tree);
+enum
+{
+  FMT_FLAG_SCANF_A_KLUDGE = 2,
+  FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL = 256
+};
+typedef struct
+{
+}
+format_flag_spec;
+typedef struct
+{
+  int flags;
+  tree *width_type;
+}
+format_kind_info;
+typedef struct alloc_pool_list_def
+{
+}
+ *alloc_pool;
+struct gcc_targetcm
+{
+  bool (*string_object_ref_type_p) (const_tree stringref);
+}
+ ;
+extern struct gcc_targetcm targetcm;
+enum format_type
+{
+  gcc_objc_string_format_type,
+};
+typedef struct function_format_info
+{
+  int format_type;
+}
+function_format_info;
+static const format_kind_info format_types_orig[] = { };
+struct format_check_context { };
+
+static const format_kind_info *format_types = format_types_orig;
+static void check_format_info (function_format_info *, tree);
+void check_format_arg (void *, tree, unsigned long long);
+
+void
+check_function_format (tree attrs, int nargs, tree * argarray)
+{
+  tree a;
+  for (a = attrs;
+       a;
+       ((contains_struct_check
+        ((a), (TS_COMMON), "../../git-master/gcc/c-family/c-format.c", 1002,
+         __FUNCTION__))->common.chain))
+    {
+      if (is_attribute_p
+         ("format",
+          ((tree_check
+            ((a), "../../git-master/gcc/c-family/c-format.c", 1004,
+             __FUNCTION__, (TREE_LIST)))->list.purpose)))
+       {
+         function_format_info info;
+         {
+           tree params = (tree) __null;
+           check_format_info (&info, params);
+         }
+       }
+    }
+}
+
+static bool
+avoid_dollar_number (const char *format)
+{
+  while ((_sch_istable[(*format) & 0xff] & (unsigned short) (_sch_isdigit)))
+    format++;
+  if (*format == '$')
+    {
+      warning (OPT_Wformat_,
+              "$ operand number used after format without operand number");
+    }
+}
+
+static void
+check_format_info (function_format_info * info, tree params)
+{
+  format_check_context format_ctx;
+  unsigned long long arg_num;
+  tree format_tree;
+  check_function_arguments_recurse (check_format_arg, &format_ctx,
+                                   format_tree, arg_num);
+  const char *format_chars;
+  if (integer_zerop (format_tree))
+    {
+      {
+       ((contains_struct_check
+         ((params), (TS_COMMON),
+          "../../git-master/gcc/c-family/c-format.c", 1444,
+          __FUNCTION__))->common.chain);
+      }
+      return;
+    }
+  if (((enum tree_code) (format_tree)->base.code) != ADDR_EXPR)
+    {
+      return;
+    }
+  if (format_types[info->format_type].flags & (int)
+      FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL)
+    {
+      bool objc_str = (info->format_type == gcc_objc_string_format_type);
+      if (((enum tree_code) (format_tree)->base.code) != CONST_DECL
+         ||
+         !((objc_str
+            &&
+            objc_string_ref_type_p (((contains_struct_check
+                                      ((format_tree), (TS_TYPED),
+                                       "../../git-master/gcc/c-family/c-format.c",
+                                       1498, __FUNCTION__))->typed.type)))
+           ||
+           (*targetcm.string_object_ref_type_p) ((const_tree)
+                                                 ((contains_struct_check
+                                                   ((format_tree),
+                                                    (TS_TYPED),
+                                                    "../../git-master/gcc/c-family/c-format.c",
+                                                    1500,
+                                                    __FUNCTION__))->typed.
+                                                  type))))
+       {
+       }
+    }
+  {
+  }
+  if (((tree_class_check
+       ((((contains_struct_check
+           ((((contains_struct_check
+               ((format_tree), (TS_TYPED),
+                "../../git-master/gcc/c-family/c-format.c", 1549,
+                __FUNCTION__))->typed.type)), (TS_TYPED),
+            "../../git-master/gcc/c-family/c-format.c", 1549,
+            __FUNCTION__))->typed.type)), (tcc_type),
+        "../../git-master/gcc/c-family/c-format.c", 1549,
+        __FUNCTION__))->type_common.main_variant) != integer_types[itk_char])
+    {
+      return;
+    }
+  {
+  }
+  const format_kind_info *fki = &format_types[info->format_type];
+  while (*format_chars != 0)
+    {
+      {
+       if (fki->width_type != __null && *format_chars == '*')
+         {
+           {
+             if (avoid_dollar_number (format_chars))
+               if (avoid_dollar_number (format_chars))
+                 return;
+           }
+         }
+      }
+    }
+}