From 68f800b3093c6a5bf9fff86ec362af766ad5288b Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Fri, 6 Jan 2017 16:01:23 +0100
Subject: [PATCH] Ignore Debug options for ICF equality.

gcc/testsuite/ChangeLog:

2017-01-09  Martin Liska  <mliska@suse.cz>

	* gcc.dg/ipa/ipa-icf-38.c: New test.
	* gcc.dg/ipa/ipa-icf-39.c: New test.

gcc/ChangeLog:

2017-01-09  Martin Liska  <mliska@suse.cz>

	* common.opt: Add Debug attribute for debug
	optimization flags.
	* ipa-icf.c (sem_function::get_hash): Ignore CL_DEBUG options.
	(sem_item::ignore_attr_p): New function.
	(sem_item::compare_attributes): Use the function.
	(sem_function::equals_wpa): Fix typo.
	* ipa-icf.h (ignore_attr_p): Declare new function.
	* ipa-inline.c (can_inline_edge_p): Remove comparison
	of optimization flags.
	* opt-functions.awk (switch_opts_type_flags): New function.
	* optc-save-gen.awk: Add new assert.
	(cl_optimization_hash): Add new argument.
	(cl_optimization_eq): New function.
	* opth-gen.awk: Update declaration.
	* opts.h (CL_DEBUG): Define new macro.
---
 gcc/common.opt                        |  8 ++--
 gcc/ipa-icf.c                         | 25 +++++++----
 gcc/ipa-icf.h                         |  3 ++
 gcc/ipa-inline.c                      |  7 ----
 gcc/opt-functions.awk                 | 12 +++++-
 gcc/optc-save-gen.awk                 | 79 ++++++++++++++++++++---------------
 gcc/opth-gen.awk                      | 16 +++----
 gcc/opts.h                            |  1 +
 gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c | 23 ++++++++++
 gcc/testsuite/gcc.dg/ipa/ipa-icf-39.c | 13 ++++++
 10 files changed, 123 insertions(+), 64 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-icf-39.c

diff --git a/gcc/common.opt b/gcc/common.opt
index 9e751bda6be..d4a5e2461af 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2644,7 +2644,7 @@ Common Undocumented Var(flag_use_linker_plugin)
 ; will be set according to optimize, debug_info_level and debug_hooks
 ; in process_options ().
 fvar-tracking
-Common Report Var(flag_var_tracking) Init(2) Optimization
+Common Report Var(flag_var_tracking) Init(2) Optimization Debug
 Perform variable tracking.
 
 ; Positive if we should track variables at assignments, negative if
@@ -2652,13 +2652,13 @@ Perform variable tracking.
 ; annotations.  When flag_var_tracking_assignments ==
 ; AUTODETECT_VALUE it will be set according to flag_var_tracking.
 fvar-tracking-assignments
-Common Report Var(flag_var_tracking_assignments) Init(2) Optimization
+Common Report Var(flag_var_tracking_assignments) Init(2) Optimization Debug
 Perform variable tracking by annotating assignments.
 
 ; Nonzero if we should toggle flag_var_tracking_assignments after
 ; processing options and computing its default.  */
 fvar-tracking-assignments-toggle
-Common Report Var(flag_var_tracking_assignments_toggle) Optimization
+Common Report Var(flag_var_tracking_assignments_toggle) Optimization Debug
 Toggle -fvar-tracking-assignments.
 
 ; Positive if we should track uninitialized variables, negative if
@@ -2666,7 +2666,7 @@ Toggle -fvar-tracking-assignments.
 ; annotations.  When flag_var_tracking_uninit == AUTODETECT_VALUE it
 ; will be set according to flag_var_tracking.
 fvar-tracking-uninit
-Common Report Var(flag_var_tracking_uninit) Optimization
+Common Report Var(flag_var_tracking_uninit) Optimization Debug
 Perform variable tracking and also tag variables that are uninitialized.
 
 ftree-vectorize
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index 4c835c39e3d..94e6a9ed5a0 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ipa-icf.h"
 #include "stor-layout.h"
 #include "dbgcnt.h"
+#include "opts.h"
 
 using namespace ipa_icf_gimple;
 
@@ -289,10 +290,7 @@ sem_function::get_hash (void)
         hstate.add_wide_int
 	 (cl_target_option_hash
 	   (TREE_TARGET_OPTION (DECL_FUNCTION_SPECIFIC_TARGET (decl))));
-      if (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))
-	hstate.add_wide_int
-	 (cl_optimization_hash
-	   (TREE_OPTIMIZATION (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))));
+      hstate.add_wide_int (cl_optimization_hash (opts_for_fn (decl), CL_DEBUG));
       hstate.add_flag (DECL_CXX_CONSTRUCTOR_P (decl));
       hstate.add_flag (DECL_CXX_DESTRUCTOR_P (decl));
 
@@ -302,6 +300,17 @@ sem_function::get_hash (void)
   return m_hash;
 }
 
+bool
+sem_item::ignore_attr_p (const attribute_spec *as)
+{
+  /* Do not allow optimization or (and) target options.  */
+  if (strcmp (as->name, "optimize") == 0
+      || strcmp (as->name, "target") == 0)
+    return true;
+
+  return false;
+}
+
 /* Return ture if A1 and A2 represent equivalent function attribute lists.
    Based on comp_type_attributes.  */
 
@@ -324,7 +333,7 @@ sem_item::compare_attributes (const_tree a1, const_tree a2)
 	 For example returns_nonnull affects only references, while
 	 optimize attribute can be ignored because it is already lowered
 	 into flags representation and compared separately.  */
-      if (!as)
+      if (!as || ignore_attr_p (as))
         continue;
 
       attr = lookup_attribute (as->name, CONST_CAST_TREE (a2));
@@ -338,7 +347,7 @@ sem_item::compare_attributes (const_tree a1, const_tree a2)
 	  const struct attribute_spec *as;
 
 	  as = lookup_attribute_spec (get_attribute_name (a));
-	  if (!as)
+	  if (!as || ignore_attr_p (as))
 	    continue;
 
 	  if (!lookup_attribute (as->name, CONST_CAST_TREE (a1)))
@@ -652,13 +661,13 @@ sem_function::equals_wpa (sem_item *item,
 	  cl_target_option_print_diff (dump_file, 2, tar1, tar2);
 	}
 
-      return return_false_with_msg ("Target flags are different");
+      return return_false_with_msg ("target flags are different");
     }
 
   cl_optimization *opt1 = opts_for_fn (decl);
   cl_optimization *opt2 = opts_for_fn (item->decl);
 
-  if (opt1 != opt2 && memcmp (opt1, opt2, sizeof(cl_optimization)))
+  if (opt1 != opt2 && !cl_optimization_eq (opt1, opt2, CL_DEBUG))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
index c57224c1517..9a2166ce9c9 100644
--- a/gcc/ipa-icf.h
+++ b/gcc/ipa-icf.h
@@ -253,6 +253,9 @@ protected:
 					            symtab_node *n2,
 					            bool address);
 
+  /* Return true when attribute specification AC should be ignored.  */
+  static bool ignore_attr_p (const attribute_spec *as);
+
   /* Compare two attribute lists.  */
   static bool compare_attributes (const_tree list1, const_tree list2);
 
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index d7ea38f364f..0e7cb809cc6 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -448,13 +448,6 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
       /* gcc.dg/pr43564.c.  Apply user-forced inline even at -O0.  */
       else if (always_inline)
 	;
-      /* When user added an attribute to the callee honor it.  */
-      else if (lookup_attribute ("optimize", DECL_ATTRIBUTES (callee->decl))
-	       && opts_for_fn (caller->decl) != opts_for_fn (callee->decl))
-	{
-	  e->inline_failed = CIF_OPTIMIZATION_MISMATCH;
-	  inlinable = false;
-	}
       /* If explicit optimize attribute are not used, the mismatch is caused
 	 by different command line options used to build different units.
 	 Do not care about COMDAT functions - those are intended to be
diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
index 540980ef54e..b2b5b66ea56 100644
--- a/gcc/opt-functions.awk
+++ b/gcc/opt-functions.awk
@@ -105,7 +105,17 @@ function switch_flags (flags)
 	  test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
 	  test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
 	  test_flag("Warning", flags,  " | CL_WARNING") \
-	  test_flag("Optimization", flags,  " | CL_OPTIMIZATION")
+	  test_flag("Optimization", flags,  " | CL_OPTIMIZATION") \
+	  test_flag("Debug", flags,  " | CL_DEBUG")
+	sub( "^0 \\| ", "", result )
+	return result
+}
+
+function switch_opts_type_flags (flags)
+{
+	result = "0"
+	result = result \
+	  test_flag("Debug", flags,  " | CL_DEBUG")
 	sub( "^0 \\| ", "", result )
 	return result
 }
diff --git a/gcc/optc-save-gen.awk b/gcc/optc-save-gen.awk
index fb517fe034b..1d224672357 100644
--- a/gcc/optc-save-gen.awk
+++ b/gcc/optc-save-gen.awk
@@ -127,7 +127,7 @@ for (i = 0; i < n_opts; i++) {
 				var_opt_range[name] = "-128, 127"
 		}
 		else
-			var_opt_other[n_opt_other++] = name;
+			n_opt_other++;
 	}
 }
 
@@ -137,10 +137,8 @@ for (i = 0; i < n_opt_char; i++) {
 		print "  gcc_assert (IN_RANGE (opts->x_" name ", " var_opt_range[name] "));";
 }
 
-print "";
-for (i = 0; i < n_opt_other; i++) {
-	print "  ptr->x_" var_opt_other[i] " = opts->x_" var_opt_other[i] ";";
-}
+if (n_opt_other > 0)
+  print "#error Unsupported type of optimization node";
 
 for (i = 0; i < n_opt_int; i++) {
 	print "  ptr->x_" var_opt_int[i] " = opts->x_" var_opt_int[i] ";";
@@ -166,10 +164,6 @@ print "void";
 print "cl_optimization_restore (struct gcc_options *opts, struct cl_optimization *ptr)";
 print "{";
 
-for (i = 0; i < n_opt_other; i++) {
-	print "  opts->x_" var_opt_other[i] " = ptr->x_" var_opt_other[i] ";";
-}
-
 for (i = 0; i < n_opt_int; i++) {
 	print "  opts->x_" var_opt_int[i] " = ptr->x_" var_opt_int[i] ";";
 }
@@ -198,15 +192,6 @@ print "                       struct cl_optimization *ptr)";
 print "{";
 
 print "  fputs (\"\\n\", file);";
-for (i = 0; i < n_opt_other; i++) {
-	print "  if (ptr->x_" var_opt_other[i] ")";
-	print "    fprintf (file, \"%*s%s (%#lx)\\n\",";
-	print "             indent_to, \"\",";
-	print "             \"" var_opt_other[i] "\",";
-	print "             (unsigned long)ptr->x_" var_opt_other[i] ");";
-	print "";
-}
-
 for (i = 0; i < n_opt_int; i++) {
 	print "  if (ptr->x_" var_opt_int[i] ")";
 	print "    fprintf (file, \"%*s%s (%#x)\\n\",";
@@ -254,16 +239,6 @@ print "                            struct cl_optimization *ptr2)";
 print "{";
 
 print "  fputs (\"\\n\", file);";
-for (i = 0; i < n_opt_other; i++) {
-	print "  if (ptr1->x_" var_opt_other[i] " != ptr2->x_" var_opt_other[i] ")";
-	print "    fprintf (file, \"%*s%s (%#lx/%#lx)\\n\",";
-	print "             indent_to, \"\",";
-	print "             \"" var_opt_other[i] "\",";
-	print "             (unsigned long)ptr1->x_" var_opt_other[i] ",";
-	print "             (unsigned long)ptr2->x_" var_opt_other[i] ");";
-	print "";
-}
-
 for (i = 0; i < n_opt_int; i++) {
 	print "  if (ptr1->x_" var_opt_int[i] " != ptr2->x_" var_opt_int[i] ")";
 	print "    fprintf (file, \"%*s%s (%#x/%#x)\\n\",";
@@ -306,7 +281,6 @@ for (i = 0; i < n_opt_char; i++) {
 
 print "}";
 
-
 print "";
 print "/* Save selected option variables into a structure.  */"
 print "void";
@@ -738,10 +712,13 @@ print "}";
 n_opt_val = 3;
 var_opt_val[0] = "x_optimize"
 var_opt_val_type[0] = "char "
+var_opt_type_flags[0] = "0"
 var_opt_val[1] = "x_optimize_size"
-var_opt_val[2] = "x_optimize_debug"
 var_opt_val_type[1] = "char "
+var_opt_type_flags[1] = "0"
+var_opt_val[2] = "x_optimize_debug"
 var_opt_val_type[2] = "char "
+var_opt_type_flags[2] = 0""
 for (i = 0; i < n_opts; i++) {
 	if (flag_set_p("Optimization", flags[i])) {
 		name = var_name(flags[i])
@@ -755,18 +732,29 @@ for (i = 0; i < n_opts; i++) {
 
 		otype = var_type_struct(flags[i])
 		var_opt_val_type[n_opt_val] = otype;
-		var_opt_val[n_opt_val++] = "x_" name;
+		var_opt_val[n_opt_val] = "x_" name;
+		var_opt_type_flags[n_opt_val++] = switch_opts_type_flags(flags[i])
 	}
 }
+
+print "";
+print "/* Hash optimization options, ignore flags having a flag in IGNORED flags.  */";
 print "";
-print "/* Hash optimization options  */";
 print "hashval_t";
-print "cl_optimization_hash (struct cl_optimization const *ptr ATTRIBUTE_UNUSED)";
+print "cl_optimization_hash (struct cl_optimization const *ptr ATTRIBUTE_UNUSED,";
+print "                      unsigned int ignored_flags ATTRIBUTE_UNUSED)";
 print "{";
 print "  inchash::hash hstate;";
 for (i = 0; i < n_opt_val; i++) {
 	name = var_opt_val[i]
-	print "  hstate.add_wide_int (ptr->" name");";
+	f = var_opt_type_flags[i];
+	padding = "";
+	if (f != "0") {
+	  print "  if (!(ignored_flags & (" f ")))"
+	  padding = "  "
+	}
+
+	print padding "  hstate.add_wide_int (ptr->" name");";
 }
 print "  return hstate.end ();";
 print "}";
@@ -794,4 +782,27 @@ for (i = 0; i < n_opt_val; i++) {
 	print "  ptr->" name" = (" var_opt_val_type[i] ") bp_unpack_value (bp, 64);";
 }
 print "}";
+
+print "";
+print "/* Compare two target options  */";
+print "bool";
+print "cl_optimization_eq (struct cl_optimization const *ptr1 ATTRIBUTE_UNUSED,";
+print "                    struct cl_optimization const *ptr2 ATTRIBUTE_UNUSED,";
+print "                    unsigned int ignored_flags ATTRIBUTE_UNUSED)";
+print "{";
+
+for (i = 0; i < n_opt_val; i++) {
+	name = var_opt_val[i]
+	f = var_opt_type_flags[i];
+	printf "  if (ptr1->" name" != ptr2->" name;
+	if (f != "0")
+		print "\n      && !(ignored_flags & (" f ")))";
+	else
+		print(")")
+	print "    return false;";
+}
+
+print "  return true;";
+print "}";
+
 }
diff --git a/gcc/opth-gen.awk b/gcc/opth-gen.awk
index f6b3812b8be..1868b5d2e8d 100644
--- a/gcc/opth-gen.awk
+++ b/gcc/opth-gen.awk
@@ -136,7 +136,6 @@ n_opt_char = 3;
 n_opt_short = 0;
 n_opt_int = 0;
 n_opt_enum = 0;
-n_opt_other = 0;
 var_opt_char[0] = "unsigned char x_optimize";
 var_opt_char[1] = "unsigned char x_optimize_size";
 var_opt_char[2] = "unsigned char x_optimize_debug";
@@ -164,15 +163,9 @@ for (i = 0; i < n_opts; i++) {
 		else if (otype ~ ("^enum +[_" alnum "]+ *$"))
 			var_opt_enum[n_opt_enum++] = otype "x_" name;
 
-		else
-			var_opt_other[n_opt_other++] = otype "x_" name;
 	}
 }
 
-for (i = 0; i < n_opt_other; i++) {
-	print "  " var_opt_other[i] ";";
-}
-
 for (i = 0; i < n_opt_int; i++) {
 	print "  " var_opt_int[i] ";";
 }
@@ -306,7 +299,10 @@ print "/* Hash option variables from a structure.  */";
 print "extern hashval_t cl_target_option_hash (const struct cl_target_option *);";
 print "";
 print "/* Hash optimization from a structure.  */";
-print "extern hashval_t cl_optimization_hash (const struct cl_optimization *);";
+print "extern hashval_t cl_optimization_hash (const struct cl_optimization *, unsigned int ignored_flags = 0);";
+print "";
+print "/* Compare two optimization option variables from a structure.  */";
+print "extern bool cl_optimization_eq (const struct cl_optimization *, const struct cl_optimization *, unsigned int ignored_flags = 0);";
 print "";
 print "/* Generator files may not have access to location_t, and don't need these.  */"
 print "#if defined(UNKNOWN_LOCATION)"
@@ -332,7 +328,7 @@ for (i = 0; i < n_langs; i++) {
 print "void cpp_handle_option_auto (const struct gcc_options * opts, size_t scode,"
 print "                             struct cpp_options * cpp_opts);"
 print "void init_global_opts_from_cpp(struct gcc_options * opts,      "
-print "                               const struct cpp_options * cpp_opts);"    
+print "                               const struct cpp_options * cpp_opts);"
 print "#endif";
 print "#endif";
 print "";
@@ -436,7 +432,7 @@ print "#define CL_LANG_ALL   ((1U << " n_langs ") - 1)"
 print ""
 print "enum opt_code"
 print "{"
-	
+
 for (i = 0; i < n_opts; i++)
 	back_chain[i] = "N_OPTS";
 
diff --git a/gcc/opts.h b/gcc/opts.h
index 56ba9101c64..65ad93d2930 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -145,6 +145,7 @@ extern const unsigned int cl_lang_count;
 #define CL_UNDOCUMENTED		(1U << 24) /* Do not output with --help.  */
 #define CL_NO_DWARF_RECORD	(1U << 25) /* Do not add to producer string.  */
 #define CL_PCH_IGNORE		(1U << 26) /* Do compare state for pch.  */
+#define CL_DEBUG		(1U << 27) /* Drives debug info generation.  */
 
 /* Flags for an enumerated option argument.  */
 #define CL_ENUM_CANONICAL	(1 << 0) /* Canonical for this value.  */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
new file mode 100644
index 00000000000..12f9453f5ad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+int
+__attribute__((optimize(("split-loops"))))
+foo(void) { return 0; }
+
+int
+__attribute__((optimize(("split-loops"))))
+bar (void) { return 0; }
+
+
+int
+__attribute__ ((hot))
+foo2(void) { return 0; }
+
+int
+__attribute__ ((hot))
+bar2(void) { return 0; }
+
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo->bar" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo2->bar2" "icf"  } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-39.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-39.c
new file mode 100644
index 00000000000..890961929f8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-39.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+int
+__attribute__((hot, no_reorder))
+foo(void) { return 0; }
+
+int
+__attribute__((no_reorder, hot))
+bar (void) { return 0; }
+
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo->bar" "icf"  } } */
-- 
2.11.0

