diff gcc/common/config/aarch64/aarch64-common.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line diff
--- a/gcc/common/config/aarch64/aarch64-common.c	Thu Oct 25 07:37:49 2018 +0900
+++ b/gcc/common/config/aarch64/aarch64-common.c	Thu Feb 13 11:34:05 2020 +0900
@@ -1,5 +1,5 @@
 /* Common hooks for AArch64.
-   Copyright (C) 2012-2018 Free Software Foundation, Inc.
+   Copyright (C) 2012-2020 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GCC.
@@ -30,7 +30,6 @@
 #include "opts.h"
 #include "flags.h"
 #include "diagnostic.h"
-#include "params.h"
 
 #ifdef  TARGET_BIG_ENDIAN_DEFAULT
 #undef  TARGET_DEFAULT_TARGET_FLAGS
@@ -42,10 +41,8 @@
 
 #undef	TARGET_OPTION_OPTIMIZATION_TABLE
 #define TARGET_OPTION_OPTIMIZATION_TABLE aarch_option_optimization_table
-#undef TARGET_OPTION_DEFAULT_PARAMS
-#define TARGET_OPTION_DEFAULT_PARAMS aarch64_option_default_params
-#undef TARGET_OPTION_VALIDATE_PARAM
-#define TARGET_OPTION_VALIDATE_PARAM aarch64_option_validate_param
+#undef TARGET_OPTION_INIT_STRUCT
+#define TARGET_OPTION_INIT_STRUCT aarch64_option_init_struct
 
 /* Set default optimization options.  */
 static const struct default_options aarch_option_optimization_table[] =
@@ -62,52 +59,12 @@
     { OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 },
     { OPT_LEVELS_ALL, OPT_funwind_tables, NULL, 1},
 #endif
+    { OPT_LEVELS_ALL, OPT__param_stack_clash_protection_guard_size_, NULL,
+      DEFAULT_STK_CLASH_GUARD_SIZE == 0 ? 16 : DEFAULT_STK_CLASH_GUARD_SIZE },
+
     { OPT_LEVELS_NONE, 0, NULL, 0 }
   };
 
-/* Implement target validation TARGET_OPTION_DEFAULT_PARAM.  */
-
-static bool
-aarch64_option_validate_param (const int value, const int param)
-{
-  /* Check that both parameters are the same.  */
-  if (param == (int) PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE)
-    {
-      if (value != 12 && value != 16)
-	{
-	  error ("only values 12 (4 KB) and 16 (64 KB) are supported for guard "
-		 "size.  Given value %d (%llu KB) is out of range",
-		 value, (1ULL << value) / 1024ULL);
-	  return false;
-	}
-    }
-
-  return true;
-}
-
-/* Implement TARGET_OPTION_DEFAULT_PARAMS.  */
-
-static void
-aarch64_option_default_params (void)
-{
-  /* We assume the guard page is 64k.  */
-  int index = (int) PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE;
-  set_default_param_value (PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE,
-			   DEFAULT_STK_CLASH_GUARD_SIZE == 0
-			     ? 16 : DEFAULT_STK_CLASH_GUARD_SIZE);
-
-  int guard_size
-    = default_param_value (PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE);
-
-  /* Set the interval parameter to be the same as the guard size.  This way the
-     mid-end code does the right thing for us.  */
-  set_default_param_value (PARAM_STACK_CLASH_PROTECTION_PROBE_INTERVAL,
-			   guard_size);
-
-  /* Validate the options.  */
-  aarch64_option_validate_param (guard_size, index);
-}
-
 /* Implement TARGET_HANDLE_OPTION.
    This function handles the target specific options for CPU/target selection.
 
@@ -164,38 +121,49 @@
     }
 }
 
-struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
-
 /* An ISA extension in the co-processor and main instruction set space.  */
 struct aarch64_option_extension
 {
   const char *const name;
-  const unsigned long flag_canonical;
-  const unsigned long flags_on;
-  const unsigned long flags_off;
+  const uint64_t flag_canonical;
+  const uint64_t flags_on;
+  const uint64_t flags_off;
+  const bool is_synthetic;
 };
 
 /* ISA extensions in AArch64.  */
 static const struct aarch64_option_extension all_extensions[] =
 {
-#define AARCH64_OPT_EXTENSION(NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, Z) \
-  {NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF},
+#define AARCH64_OPT_EXTENSION(NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, \
+			      SYNTHETIC, Z) \
+  {NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, SYNTHETIC},
 #include "config/aarch64/aarch64-option-extensions.def"
-  {NULL, 0, 0, 0}
+  {NULL, 0, 0, 0, false}
+};
+
+/* A copy of the ISA extensions list for AArch64 sorted by the popcount of
+   bits and extension turned on.  Cached for efficiency.  */
+static struct aarch64_option_extension all_extensions_by_on[] =
+{
+#define AARCH64_OPT_EXTENSION(NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, \
+			      SYNTHETIC, Z) \
+  {NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, SYNTHETIC},
+#include "config/aarch64/aarch64-option-extensions.def"
+  {NULL, 0, 0, 0, false}
 };
 
 struct processor_name_to_arch
 {
   const std::string processor_name;
   const enum aarch64_arch arch;
-  const unsigned long flags;
+  const uint64_t flags;
 };
 
 struct arch_to_arch_name
 {
   const enum aarch64_arch arch;
   const std::string arch_name;
-  const unsigned long flags;
+  const uint64_t flags;
 };
 
 /* Map processor names to the architecture revision they implement and
@@ -220,10 +188,13 @@
 
 /* Parse the architecture extension string STR and update ISA_FLAGS
    with the architecture features turned on or off.  Return a
-   aarch64_parse_opt_result describing the result.  */
+   aarch64_parse_opt_result describing the result.
+   When the STR string contains an invalid extension,
+   a copy of the string is created and stored to INVALID_EXTENSION.  */
 
 enum aarch64_parse_opt_result
-aarch64_parse_extension (const char *str, unsigned long *isa_flags)
+aarch64_parse_extension (const char *str, uint64_t *isa_flags,
+			 std::string *invalid_extension)
 {
   /* The extension string is parsed left to right.  */
   const struct aarch64_option_extension *opt = NULL;
@@ -274,6 +245,8 @@
       if (opt->name == NULL)
 	{
 	  /* Extension not found in list.  */
+	  if (invalid_extension)
+	    *invalid_extension = std::string (str, len);
 	  return AARCH64_PARSE_INVALID_FEATURE;
 	}
 
@@ -283,6 +256,89 @@
   return AARCH64_PARSE_OK;
 }
 
+/* Append all architecture extension candidates to the CANDIDATES vector.  */
+
+void
+aarch64_get_all_extension_candidates (auto_vec<const char *> *candidates)
+{
+  const struct aarch64_option_extension *opt;
+  for (opt = all_extensions; opt->name != NULL; opt++)
+    candidates->safe_push (opt->name);
+}
+
+/* Comparer to sort aarch64's feature extensions by population count. Largest
+   first.  */
+
+typedef const struct aarch64_option_extension opt_ext;
+
+int opt_ext_cmp (const void* a, const void* b)
+{
+  opt_ext *opt_a = (opt_ext *)a;
+  opt_ext *opt_b = (opt_ext *)b;
+
+  /* We consider the total set of bits an options turns on to be the union of
+     the singleton set containing the option itself and the set of options it
+     turns on as a dependency.  As an example +dotprod turns on FL_DOTPROD and
+     FL_SIMD.  As such the set of bits represented by this option is
+     {FL_DOTPROD, FL_SIMD}. */
+  uint64_t total_flags_a = opt_a->flag_canonical & opt_a->flags_on;
+  uint64_t total_flags_b = opt_b->flag_canonical & opt_b->flags_on;
+  int popcnt_a = popcount_hwi ((HOST_WIDE_INT)total_flags_a);
+  int popcnt_b = popcount_hwi ((HOST_WIDE_INT)total_flags_b);
+  int order = popcnt_b - popcnt_a;
+
+  /* If they have the same amount of bits set, give it a more
+     deterministic ordering by using the value of the bits themselves.  */
+  if (order != 0)
+    return order;
+
+  if (total_flags_a != total_flags_b)
+    return total_flags_a < total_flags_b ? 1 : -1;
+
+  return 0;
+}
+
+/* Implement TARGET_OPTION_INIT_STRUCT.  */
+
+static void
+aarch64_option_init_struct (struct gcc_options *opts ATTRIBUTE_UNUSED)
+{
+    /* Sort the extensions based on how many bits they set, order the larger
+       counts first.  We sort the list because this makes processing the
+       feature bits O(n) instead of O(n^2).  While n is small, the function
+       to calculate the feature strings is called on every options push,
+       pop and attribute change (arm_neon headers, lto etc all cause this to
+       happen quite frequently).  It is a trade-off between time and space and
+       so time won.  */
+    int n_extensions
+      = sizeof (all_extensions) / sizeof (struct aarch64_option_extension);
+    qsort (&all_extensions_by_on, n_extensions,
+	   sizeof (struct aarch64_option_extension), opt_ext_cmp);
+}
+
+/* Checks to see if enough bits from the option OPT are enabled in
+   ISA_FLAG_BITS to be able to replace the individual options with the
+   canonicalized version of the option.  This is done based on two rules:
+
+   1) Synthetic groups, such as +crypto we only care about the bits that are
+      turned on. e.g. +aes+sha2 can be replaced with +crypto.
+
+   2) Options that themselves have a bit, such as +rdma, in this case, all the
+      feature bits they turn on must be available and the bit for the option
+      itself must be.  In this case it's effectively a reduction rather than a
+      grouping. e.g. +fp+simd is not enough to turn on +rdma, for that you would
+      need +rdma+fp+simd which is reduced down to +rdma.
+*/
+
+static bool
+aarch64_contains_opt (uint64_t isa_flag_bits, opt_ext *opt)
+{
+  uint64_t flags_check
+    = opt->is_synthetic ? opt->flags_on : opt->flag_canonical;
+
+  return (isa_flag_bits & flags_check) == flags_check;
+}
+
 /* Return a string representation of ISA_FLAGS.  DEFAULT_ARCH_FLAGS
    gives the default set of flags which are implied by whatever -march
    we'd put out.  Our job is to figure out the minimal set of "+" and
@@ -290,32 +346,103 @@
    that all the "+" flags come before the "+no" flags.  */
 
 std::string
-aarch64_get_extension_string_for_isa_flags (unsigned long isa_flags,
-					    unsigned long default_arch_flags)
+aarch64_get_extension_string_for_isa_flags (uint64_t isa_flags,
+					    uint64_t default_arch_flags)
 {
   const struct aarch64_option_extension *opt = NULL;
   std::string outstr = "";
 
-  /* Pass one: Find all the things we need to turn on.  As a special case,
-     we always want to put out +crc if it is enabled.  */
-  for (opt = all_extensions; opt->name != NULL; opt++)
-    if ((isa_flags & opt->flag_canonical
-	 && !(default_arch_flags & opt->flag_canonical))
-	|| (default_arch_flags & opt->flag_canonical
-            && opt->flag_canonical == AARCH64_ISA_CRC))
+  uint64_t isa_flag_bits = isa_flags;
+
+  /* Pass one: Minimize the search space by reducing the set of options
+     to the smallest set that still turns on the same features as before in
+     conjunction with the bits that are turned on by default for the selected
+     architecture.  */
+  for (opt = all_extensions_by_on; opt->name != NULL; opt++)
+    {
+      /* If the bit is on by default, then all the options it turns on are also
+	 on by default due to the transitive dependencies.
+
+         If the option is enabled explicitly in the set then we need to emit
+	 an option for it.  Since this list is sorted by extensions setting the
+	 largest number of featers first, we can be sure that nothing else will
+	 ever need to set the bits we already set.  Consider the following
+	 situation:
+
+	  Feat1 = A + B + C
+	  Feat2 = A + B
+	  Feat3 = A + D
+	  Feat4 = B + C
+	  Feat5 = C
+
+	The following results are expected:
+
+	  A + C = A + Feat5
+	  B + C = Feat4
+	  Feat4 + A = Feat1
+	  Feat2 + Feat5 = Feat1
+	  Feat1 + C = Feat1
+          Feat3 + Feat4 = Feat1 + D
+
+	This search assumes that all invidual feature bits are use visible,
+	in other words the user must be able to do +A, +B, +C and +D.  */
+      if (aarch64_contains_opt (isa_flag_bits | default_arch_flags, opt))
       {
-	outstr += "+";
-	outstr += opt->name;
+	/* We remove all the dependent bits, to prevent them from being turned
+	   on twice.  This only works because we assume that all there are
+	   individual options to set all bits standalone.  */
+	isa_flag_bits &= ~opt->flags_on;
+	isa_flag_bits |= opt->flag_canonical;
       }
+    }
+
+   /* By toggling bits on and off, we may have set bits on that are already
+      enabled by default.  So we mask the default set out so we don't emit an
+      option for them.  Instead of checking for this each time during Pass One
+      we just mask all default bits away at the end.  */
+   isa_flag_bits &= ~default_arch_flags;
+
+   /* We now have the smallest set of features we need to process.  A subsequent
+      linear scan of the bits in isa_flag_bits will allow us to print the ext
+      names.  However as a special case if CRC was enabled before, always print
+      it.  This is required because some CPUs have an incorrect specification
+      in older assemblers.  Even though CRC should be the default for these
+      cases the -mcpu values won't turn it on.  */
+  if (isa_flags & AARCH64_ISA_CRC)
+    isa_flag_bits |= AARCH64_ISA_CRC;
 
-  /* Pass two: Find all the things we need to turn off.  */
-  for (opt = all_extensions; opt->name != NULL; opt++)
-    if ((~isa_flags) & opt->flag_canonical
-	&& !((~default_arch_flags) & opt->flag_canonical))
-      {
-	outstr += "+no";
-	outstr += opt->name;
-      }
+  /* Pass Two:
+     Print the option names that we're sure we must turn on.  These are only
+     optional extension names.  Mandatory ones have already been removed and
+     ones we explicitly want off have been too.  */
+  for (opt = all_extensions_by_on; opt->name != NULL; opt++)
+    {
+      if (isa_flag_bits & opt->flag_canonical)
+	{
+	  outstr += "+";
+	  outstr += opt->name;
+	}
+    }
+
+  /* Pass Three:
+     Print out a +no for any mandatory extension that we are
+     turning off.  By this point aarch64_parse_extension would have ensured
+     that any optional extensions are turned off.  The only things left are
+     things that can't be turned off usually, e.g. something that is on by
+     default because it's mandatory and we want it off.  For turning off bits
+     we don't guarantee the smallest set of flags, but instead just emit all
+     options the user has specified.
+
+     The assembler requires all +<opts> to be printed before +no<opts>.  */
+  for (opt = all_extensions_by_on; opt->name != NULL; opt++)
+    {
+      if ((~isa_flags) & opt->flag_canonical
+		&& !((~default_arch_flags) & opt->flag_canonical))
+	{
+	  outstr += "+no";
+	  outstr += opt->name;
+	}
+    }
 
   return outstr;
 }
@@ -367,10 +494,10 @@
      found does not map to an architecture we understand.  */
   if (p_to_a->arch == aarch64_no_arch
       || a_to_an->arch == aarch64_no_arch)
-    fatal_error (input_location, "unknown value %qs for -mcpu", name);
+    fatal_error (input_location, "unknown value %qs for %<-mcpu%>", name);
 
-  unsigned long extensions = p_to_a->flags;
-  aarch64_parse_extension (extension_str.c_str (), &extensions);
+  uint64_t extensions = p_to_a->flags;
+  aarch64_parse_extension (extension_str.c_str (), &extensions, NULL);
 
   std::string outstr = a_to_an->arch_name
 	+ aarch64_get_extension_string_for_isa_flags (extensions,
@@ -396,5 +523,7 @@
   return aarch64_rewrite_selected_cpu (argv[argc - 1]);
 }
 
+struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
+
 #undef AARCH64_CPU_NAME_LENGTH