diff mbox

Handle CFA adjust notes in combine stack adjustments

Message ID 543C366E.4060504@redhat.com
State New
Headers show

Commit Message

Richard Henderson Oct. 13, 2014, 8:30 p.m. UTC
I was hitting this during a i386 bootstrap, wherein csa managed to combine two
insns that both had REG_CFA_ADJUST_CFA notes, but didn't modify the notes.
This lead to an abort later in dwarf2cfi, when the inconsistency was noticed.

I'm not quite sure why this was happening on my branch but not mainline, but
I'm applying it here anyway.

Tested on i686 and x86_64.


r~
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.

	* g++.dg/torture/20141013.C: New.
diff mbox

Patch

diff --git a/gcc/combine-stack-adj.c b/gcc/combine-stack-adj.c
index aebdf87..844873c 100644
--- a/gcc/combine-stack-adj.c
+++ b/gcc/combine-stack-adj.c
@@ -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
diff --git a/gcc/testsuite/g++.dg/torture/20141013.C b/gcc/testsuite/g++.dg/torture/20141013.C
new file mode 100644
index 0000000..529ef09
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/20141013.C
@@ -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;
+	    }
+	  }
+      }
+    }
+}