+/* Mask options that we want to support inside of attribute((target)) and
+ #pragma GCC target operations. Note, we do not include things like
+ 64/32-bit, endianess, hard/soft floating point, etc. that would have
+ different calling sequences. */
+
+struct rs6000_opt_mask {
+ const char *name; /* option name */
+ int mask; /* mask to set */
+ bool invert; /* invert sense of mask */
+ bool valid_target; /* option is a target option */
+};
+
+static struct rs6000_opt_mask const rs6000_opt_masks[] =
+{
+ { "altivec", MASK_ALTIVEC, false, true },
+ { "cmpb", MASK_CMPB, false, true },
+ { "dlmzb", MASK_DLMZB, false, true },
+ { "fprnd", MASK_FPRND, false, true },
+ { "hard-dfp", MASK_DFP, false, true },
+ { "isel", MASK_ISEL, false, true },
+ { "mfcrf", MASK_MFCRF, false, true },
+ { "mfpgpr", MASK_MFPGPR, false, true },
+ { "mulhw", MASK_MULHW, false, true },
+ { "multiple", MASK_MULTIPLE, false, true },
+ { "update", MASK_NO_UPDATE, true , true },
+ { "popcntb", MASK_POPCNTB, false, true },
+ { "popcntd", MASK_POPCNTD, false, true },
+ { "powerpc-gfxopt", MASK_PPC_GFXOPT, false, true },
+ { "powerpc-gpopt", MASK_PPC_GPOPT, false, true },
+ { "recip-precision", MASK_RECIP_PRECISION, false, true },
+ { "string", MASK_STRING, false, true },
+ { "vsx", MASK_VSX, false, true },
+#ifdef MASK_64BIT
+#if TARGET_AIX_OS
+ { "aix64", MASK_64BIT, false, false },
+ { "aix32", MASK_64BIT, true, false },
+#else
+ { "64", MASK_64BIT, false, false },
+ { "32", MASK_64BIT, true, false },
+#endif
+#endif
+#ifdef MASK_EABI
+ { "eabi", MASK_EABI, false, false },
+#endif
+#ifdef MASK_LITTLE_ENDIAN
+ { "little", MASK_LITTLE_ENDIAN, false, false },
+ { "big", MASK_LITTLE_ENDIAN, true, false },
+#endif
+#ifdef MASK_RELOCATABLE
+ { "relocatable", MASK_RELOCATABLE, false, false },
+#endif
+#ifdef MASK_STRICT_ALIGN
+ { "strict-align", MASK_STRICT_ALIGN, false, false },
+#endif
+ { "power", MASK_POWER, false, false },
+ { "power2", MASK_POWER2, false, false },
+ { "powerpc", MASK_POWERPC, false, false },
+ { "soft-float", MASK_SOFT_FLOAT, false, false },
+ { "string", MASK_STRING, false, false },
+};
+
+/* Option variables that we want to support inside attribute((target)) and
+ #pragma GCC target operations. */
+
+struct rs6000_opt_var {
+ const char *name; /* option name */
+ size_t global_offset; /* offset of the option in global_options. */
+ size_t target_offset; /* offset of the option in target optiosn. */
+};
+
+static struct rs6000_opt_var const rs6000_opt_vars[] =
+{
+ { "friz",
+ offsetof (struct gcc_options, x_TARGET_FRIZ),
+ offsetof (struct cl_target_option, x_TARGET_FRIZ), },
+ { "avoid-indexed-addresses",
+ offsetof (struct gcc_options, x_TARGET_AVOID_XFORM),
+ offsetof (struct cl_target_option, x_TARGET_AVOID_XFORM) },
+ { "paired",
+ offsetof (struct gcc_options, x_rs6000_paired_float),
+ offsetof (struct cl_target_option, x_rs6000_paired_float), },
+ { "longcall",
+ offsetof (struct gcc_options, x_rs6000_default_long_calls),
+ offsetof (struct cl_target_option, x_rs6000_default_long_calls), },
+};
+
+/* Inner function to handle attribute((target("..."))) and #pragma GCC target
+ parsing. Return true if there were no errors. */
+
+static bool
+rs6000_inner_target_options (tree args, bool attr_p)
+{
+ bool ret = true;
+
+ if (args == NULL_TREE)
+ ;
+
+ else if (TREE_CODE (args) == STRING_CST)
+ {
+ char *p = ASTRDUP (TREE_STRING_POINTER (args));
+ char *q;
+
+ while ((q = strtok (p, ",")) != NULL)
+ {
+ bool error_p = false;
+ bool not_valid_p = false;
+ const char *cpu_opt = NULL;
+
+ p = NULL;
+ if (strncmp (q, "cpu=", 4) == 0)
+ {
+ int cpu_index = rs6000_cpu_name_lookup (q+4);
+ if (cpu_index >= 0)
+ rs6000_cpu_index = cpu_index;
+ else
+ {
+ error_p = true;
+ cpu_opt = q+4;
+ }
+ }
+ else if (strncmp (q, "tune=", 5) == 0)
+ {
+ int tune_index = rs6000_cpu_name_lookup (q+5);
+ if (tune_index >= 0)
+ rs6000_tune_index = tune_index;
+ else
+ {
+ error_p = true;
+ cpu_opt = q+5;
+ }
+ }
+ else
+ {
+ size_t i;
+ bool invert = false;
+ char *r = q;
+
+ error_p = true;
+ if (strncmp (r, "no-", 3) == 0)
+ {
+ invert = true;
+ r += 3;
+ }
+
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_masks); i++)
+ if (strcmp (r, rs6000_opt_masks[i].name) == 0)
+ {
+ int mask = rs6000_opt_masks[i].mask;
+
+ if (!rs6000_opt_masks[i].valid_target)
+ not_valid_p = true;
+ else
+ {
+ error_p = false;
+ target_flags_explicit |= mask;
+
+ if (rs6000_opt_masks[i].invert)
+ invert = !invert;
+
+ if (invert)
+ target_flags &= ~mask;
+ else
+ target_flags |= mask;
+ }
+ break;
+ }
+
+ if (error_p && !not_valid_p)
+ {
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_vars); i++)
+ if (strcmp (r, rs6000_opt_vars[i].name) == 0)
+ {
+ size_t j = rs6000_opt_vars[i].global_offset;
+ ((int *) &global_options)[j] = !invert;
+ error_p = false;
+ break;
+ }
+ }
+ }
+
+ if (error_p)
+ {
+ const char *eprefix, *esuffix;
+
+ ret = false;
+ if (attr_p)
+ {
+ eprefix = "__attribute__((__target__(";
+ esuffix = ")))";
+ }
+ else
+ {
+ eprefix = "#pragma GCC target ";
+ esuffix = "";
+ }
+
+ if (cpu_opt)
+ error ("invalid cpu \"%s\" for %s\"%s\"%s", cpu_opt, eprefix,
+ q, esuffix);
+ else if (not_valid_p)
+ error ("%s\"%s\"%s is not allowed", eprefix, q, esuffix);
+ else
+ error ("%s\"%s\"%s is invalid", eprefix, q, esuffix);
+ }
+ }
+ }
+
+ else if (TREE_CODE (args) == TREE_LIST)
+ {
+ do
+ {
+ tree value = TREE_VALUE (args);
+ if (value)
+ {
+ bool ret2 = rs6000_inner_target_options (value, attr_p);
+ if (!ret2)
+ ret = false;
+ }
+ args = TREE_CHAIN (args);
+ }
+ while (args != NULL_TREE);
+ }
+
+ else
+ gcc_unreachable ();
+
+ return ret;
+}
+
+/* Print out the target options as a list for -mdebug=target. */
+
+static void
+rs6000_debug_target_options (tree args, const char *prefix)
+{
+ if (args == NULL_TREE)
+ fprintf (stderr, "%s<NULL>", prefix);
+
+ else if (TREE_CODE (args) == STRING_CST)
+ {
+ char *p = ASTRDUP (TREE_STRING_POINTER (args));
+ char *q;
+
+ while ((q = strtok (p, ",")) != NULL)
+ {
+ p = NULL;
+ fprintf (stderr, "%s\"%s\"", prefix, q);
+ prefix = ", ";
+ }
+ }
+
+ else if (TREE_CODE (args) == TREE_LIST)
+ {
+ do
+ {
+ tree value = TREE_VALUE (args);
+ if (value)
+ {
+ rs6000_debug_target_options (value, prefix);
+ prefix = ", ";
+ }
+ args = TREE_CHAIN (args);
+ }
+ while (args != NULL_TREE);
+ }
+
+ else
+ gcc_unreachable ();
+
+ return;
+}
+
+\f
+/* Hook to validate attribute((target("..."))). */
+
+static bool
+rs6000_valid_attribute_p (tree fndecl,
+ tree ARG_UNUSED (name),
+ tree args,
+ int flags)
+{
+ struct cl_target_option cur_target;
+ bool ret;
+ tree old_optimize = build_optimization_node ();
+ tree new_target, new_optimize;
+ tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+ gcc_assert ((fndecl != NULL_TREE) && (args != NULL_TREE));
+
+ if (TARGET_DEBUG_TARGET)
+ {
+ tree tname = DECL_NAME (fndecl);
+ fprintf (stderr, "\n==================== rs6000_valid_attribute_p:\n");
+ if (tname)
+ fprintf (stderr, "function: %.*s\n",
+ (int) IDENTIFIER_LENGTH (tname),
+ IDENTIFIER_POINTER (tname));
+ else
+ fprintf (stderr, "function: unknown\n");
+
+ fprintf (stderr, "args:");
+ rs6000_debug_target_options (args, " ");
+ fprintf (stderr, "\n");
+
+ if (flags)
+ fprintf (stderr, "flags: 0x%x\n", flags);
+
+ fprintf (stderr, "--------------------\n");
+ }
+
+ old_optimize = build_optimization_node ();
+ func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+ /* If the function changed the optimization levels as well as setting target
+ options, start with the optimizations specified. */
+ if (func_optimize && func_optimize != old_optimize)
+ cl_optimization_restore (&global_options,
+ TREE_OPTIMIZATION (func_optimize));
+
+ /* The target attributes may also change some optimization flags, so update
+ the optimization options if necessary. */
+ cl_target_option_save (&cur_target, &global_options);
+ rs6000_cpu_index = rs6000_tune_index = -1;
+ ret = rs6000_inner_target_options (args, true);
+
+ /* Set up any additional state. */
+ if (ret)
+ {
+ ret = rs6000_option_override_internal (false);
+ new_target = build_target_option_node ();
+ }
+ else
+ new_target = NULL;
+
+ new_optimize = build_optimization_node ();
+
+ if (!new_target)
+ ret = false;
+
+ else if (fndecl)
+ {
+ DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
+
+ if (old_optimize != new_optimize)
+ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize;
+ }
+
+ cl_target_option_restore (&global_options, &cur_target);
+
+ if (old_optimize != new_optimize)
+ cl_optimization_restore (&global_options,
+ TREE_OPTIMIZATION (old_optimize));
+
+ return ret;
+}
+
+\f
+/* Hook to validate the current #pragma GCC target and set the state, and
+ update the macros based on what was changed. If ARGS is NULL, then
+ POP_TARGET is used to reset the options. */
+
+bool
+rs6000_pragma_target_parse (tree args, tree pop_target)
+{
+ tree cur_tree;
+ bool ret;
+
+ if (TARGET_DEBUG_TARGET)
+ {
+ fprintf (stderr, "\n==================== rs6000_pragma_target_parse\n");
+ fprintf (stderr, "args:");
+ rs6000_debug_target_options (args, " ");
+ fprintf (stderr, "\n");
+
+ if (pop_target)
+ {
+ fprintf (stderr, "pop_target:\n");
+ debug_tree (pop_target);
+ }
+ else
+ fprintf (stderr, "pop_target: <NULL>\n");
+
+ fprintf (stderr, "--------------------\n");
+ }
+
+ if (! args)
+ {
+ ret = true;
+ cur_tree = ((pop_target)
+ ? pop_target
+ : target_option_default_node);
+ cl_target_option_restore (&global_options,
+ TREE_TARGET_OPTION (cur_tree));
+ }
+ else
+ {
+ rs6000_cpu_index = rs6000_tune_index = -1;
+ ret = rs6000_inner_target_options (args, false);
+ cur_tree = build_target_option_node ();
+
+ if (!cur_tree)
+ ret = false;
+ }
+
+ if (cur_tree)
+ target_option_current_node = cur_tree;
+
+ return ret;
+}
+
+\f
+/* Remember the last target of rs6000_set_current_function. */
+static GTY(()) tree rs6000_previous_fndecl;
+
+/* Establish appropriate back-end context for processing the function
+ FNDECL. The argument might be NULL to indicate processing at top
+ level, outside of any function scope. */
+static void
+rs6000_set_current_function (tree fndecl)
+{
+ tree old_tree = (rs6000_previous_fndecl
+ ? DECL_FUNCTION_SPECIFIC_TARGET (rs6000_previous_fndecl)
+ : NULL_TREE);
+
+ tree new_tree = (fndecl
+ ? DECL_FUNCTION_SPECIFIC_TARGET (fndecl)
+ : NULL_TREE);
+
+ if (TARGET_DEBUG_TARGET)
+ {
+ bool print_final = false;
+ fprintf (stderr, "\n==================== rs6000_set_current_function");
+
+ if (fndecl)
+ fprintf (stderr, ", fndecl %s (%p)",
+ (DECL_NAME (fndecl)
+ ? IDENTIFIER_POINTER (DECL_NAME (fndecl))
+ : "<unknown>"), (void *)fndecl);
+
+ if (rs6000_previous_fndecl)
+ fprintf (stderr, ", prev_fndecl (%p)", (void *)rs6000_previous_fndecl);
+
+ fprintf (stderr, "\n");
+ if (new_tree)
+ {
+ fprintf (stderr, "\nnew fndecl target specific options:\n");
+ debug_tree (new_tree);
+ print_final = true;
+ }
+
+ if (old_tree)
+ {
+ fprintf (stderr, "\nold fndecl target specific options:\n");
+ debug_tree (old_tree);
+ print_final = true;
+ }
+
+ if (print_final)
+ fprintf (stderr, "--------------------\n");
+ }
+
+ /* Only change the context if the function changes. This hook is called
+ several times in the course of compiling a function, and we don't want to
+ slow things down too much or call target_reinit when it isn't safe. */
+ if (fndecl && fndecl != rs6000_previous_fndecl)
+ {
+ rs6000_previous_fndecl = fndecl;
+ if (old_tree == new_tree)
+ ;
+
+ else if (new_tree)
+ {
+ cl_target_option_restore (&global_options,
+ TREE_TARGET_OPTION (new_tree));
+ target_reinit ();
+ }
+
+ else if (old_tree)
+ {
+ struct cl_target_option *def
+ = TREE_TARGET_OPTION (target_option_current_node);
+
+ cl_target_option_restore (&global_options, def);
+ target_reinit ();
+ }
+ }
+}
+
+\f
+/* Save the current options */
+
+static void
+rs6000_function_specific_save (struct cl_target_option *ptr)
+{
+ ptr->rs6000_target_flags_explicit = target_flags_explicit;
+}
+
+/* Restore the current options */
+
+static void
+rs6000_function_specific_restore (struct cl_target_option *ptr)
+{
+ target_flags_explicit = ptr->rs6000_target_flags_explicit;
+ (void) rs6000_option_override_internal (false);
+}
+
+/* Print the current options */
+
+static void
+rs6000_function_specific_print (FILE *file, int indent,
+ struct cl_target_option *ptr)
+{
+ size_t i;
+ int flags = ptr->x_target_flags;
+
+ /* Print the various mask options. */
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_masks); i++)
+ if ((flags & rs6000_opt_masks[i].mask) != 0)
+ {
+ flags &= ~ rs6000_opt_masks[i].mask;
+ fprintf (file, "%*s-m%s%s\n", indent, "",
+ rs6000_opt_masks[i].invert ? "no-" : "",
+ rs6000_opt_masks[i].name);
+ }
+
+ /* Print the various options that are variables. */
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_vars); i++)
+ {
+ size_t j = rs6000_opt_vars[i].target_offset;
+ if (((signed char *) ptr)[j])
+ fprintf (file, "%*s-m%s\n", indent, "",
+ rs6000_opt_vars[i].name);
+ }
+}
+
+\f
+/* Hook to determine if one function can safely inline another. */
+
+static bool
+rs6000_can_inline_p (tree caller, tree callee)
+{
+ bool ret = false;
+ tree caller_tree = DECL_FUNCTION_SPECIFIC_TARGET (caller);
+ tree callee_tree = DECL_FUNCTION_SPECIFIC_TARGET (callee);
+
+ /* If callee has no option attributes, then it is ok to inline. */
+ if (!callee_tree)
+ ret = true;
+
+ /* If caller has no option attributes, but callee does then it is not ok to
+ inline. */
+ else if (!caller_tree)
+ ret = false;
+
+ else
+ {
+ struct cl_target_option *caller_opts = TREE_TARGET_OPTION (caller_tree);
+ struct cl_target_option *callee_opts = TREE_TARGET_OPTION (callee_tree);
+
+ /* Callee's options should a subset of the caller's, i.e. a vsx function
+ can inline an altivec function but a non-vsx function can't inline a
+ vsx function. */
+ if ((caller_opts->x_target_flags & callee_opts->x_target_flags)
+ == callee_opts->x_target_flags)
+ ret = true;
+ }
+
+ if (TARGET_DEBUG_TARGET)
+ fprintf (stderr, "rs6000_can_inline_p:, caller %s, callee %s, %s inline\n",
+ (DECL_NAME (caller)
+ ? IDENTIFIER_POINTER (DECL_NAME (caller))
+ : "<unknown>"),
+ (DECL_NAME (callee)
+ ? IDENTIFIER_POINTER (DECL_NAME (callee))
+ : "<unknown>"),
+ (ret ? "can" : "cannot"));
+
+ return ret;
+}
+\f