+#if S390_USE_TARGET_ATTRIBUTE
+/* Inner function to process the attribute((target(...))), take an argument and
+ set the current options from the argument. If we have a list, recursively go
+ over the list. */
+
+static bool
+s390_valid_target_attribute_inner_p (tree args,
+ struct gcc_options *opts,
+ struct gcc_options *new_opts_set,
+ bool force_pragma)
+{
+ char *next_optstr;
+ bool ret = true;
+
+#define S390_ATTRIB(S,O,A) { S, sizeof (S)-1, O, A, 0 }
+#define S390_PRAGMA(S,O,A) { S, sizeof (S)-1, O, A, 1 }
+ static const struct
+ {
+ const char *string;
+ size_t len;
+ int opt;
+ int has_arg;
+ int only_as_pragma;
+ } attrs[] = {
+ /* enum options */
+ S390_ATTRIB ("arch=", OPT_march_, 1),
+ S390_ATTRIB ("tune=", OPT_mtune_, 1),
+ /* uinteger options */
+ S390_ATTRIB ("stack-guard=", OPT_mstack_guard_, 1),
+ S390_ATTRIB ("stack-size=", OPT_mstack_size_, 1),
+ S390_ATTRIB ("branch-cost=", OPT_mbranch_cost_, 1),
+ S390_ATTRIB ("warn-framesize=", OPT_mwarn_framesize_, 1),
+ /* flag options */
+ S390_ATTRIB ("backchain", OPT_mbackchain, 0),
+ S390_ATTRIB ("hard-dfp", OPT_mhard_dfp, 0),
+ S390_ATTRIB ("hard-float", OPT_mhard_float, 0),
+ S390_ATTRIB ("htm", OPT_mhtm, 0),
+ S390_ATTRIB ("vx", OPT_mvx, 0),
+ S390_ATTRIB ("packed-stack", OPT_mpacked_stack, 0),
+ S390_ATTRIB ("small-exec", OPT_msmall_exec, 0),
+ S390_ATTRIB ("soft-float", OPT_msoft_float, 0),
+ S390_ATTRIB ("mvcle", OPT_mmvcle, 0),
+ S390_PRAGMA ("zvector", OPT_mzvector, 0),
+ /* boolean options */
+ S390_ATTRIB ("warn-dynamicstack", OPT_mwarn_dynamicstack, 0),
+ };
+#undef S390_ATTRIB
+#undef S390_PRAGMA
+
+ /* If this is a list, recurse to get the options. */
+ if (TREE_CODE (args) == TREE_LIST)
+ {
+ bool ret = true;
+ int num_pragma_values;
+ int i;
+
+ /* Note: attribs.c:decl_attributes prepends the values from
+ current_target_pragma to the list of target attributes. To determine
+ whether we're looking at a value of the attribute or the pragma we
+ assume that the first [list_length (current_target_pragma)] values in
+ the list are the values from the pragma. */
+ num_pragma_values = (!force_pragma && current_target_pragma != NULL)
+ ? list_length (current_target_pragma) : 0;
+ for (i = 0; args; args = TREE_CHAIN (args), i++)
+ {
+ bool is_pragma;
+
+ is_pragma = (force_pragma || i < num_pragma_values);
+ if (TREE_VALUE (args)
+ && !s390_valid_target_attribute_inner_p (TREE_VALUE (args),
+ opts, new_opts_set,
+ is_pragma))
+ {
+ ret = false;
+ }
+ }
+ return ret;
+ }
+
+ else if (TREE_CODE (args) != STRING_CST)
+ {
+ error ("attribute %<target%> argument not a string");
+ return false;
+ }
+
+ /* Handle multiple arguments separated by commas. */
+ next_optstr = ASTRDUP (TREE_STRING_POINTER (args));
+
+ while (next_optstr && *next_optstr != '\0')
+ {
+ char *p = next_optstr;
+ char *orig_p = p;
+ char *comma = strchr (next_optstr, ',');
+ size_t len, opt_len;
+ int opt;
+ bool opt_set_p;
+ char ch;
+ unsigned i;
+ int mask = 0;
+ enum cl_var_type var_type;
+ bool found;
+
+ if (comma)
+ {
+ *comma = '\0';
+ len = comma - next_optstr;
+ next_optstr = comma + 1;
+ }
+ else
+ {
+ len = strlen (p);
+ next_optstr = NULL;
+ }
+
+ /* Recognize no-xxx. */
+ if (len > 3 && p[0] == 'n' && p[1] == 'o' && p[2] == '-')
+ {
+ opt_set_p = false;
+ p += 3;
+ len -= 3;
+ }
+ else
+ opt_set_p = true;
+
+ /* Find the option. */
+ ch = *p;
+ found = false;
+ for (i = 0; i < ARRAY_SIZE (attrs); i++)
+ {
+ opt_len = attrs[i].len;
+ if (ch == attrs[i].string[0]
+ && ((attrs[i].has_arg) ? len > opt_len : len == opt_len)
+ && memcmp (p, attrs[i].string, opt_len) == 0)
+ {
+ opt = attrs[i].opt;
+ if (!opt_set_p && cl_options[opt].cl_reject_negative)
+ continue;
+ mask = cl_options[opt].var_value;
+ var_type = cl_options[opt].var_type;
+ found = true;
+ break;
+ }
+ }
+
+ /* Process the option. */
+ if (!found)
+ {
+ error ("attribute(target(\"%s\")) is unknown", orig_p);
+ return false;
+ }
+ else if (attrs[i].only_as_pragma && !force_pragma)
+ {
+ /* Value is not allowed for the target attribute. */
+ error ("Value %qs is not supported by attribute %<target%>",
+ attrs[i].string);
+ return false;
+ }
+
+ else if (var_type == CLVC_BIT_SET || var_type == CLVC_BIT_CLEAR)
+ {
+ if (var_type == CLVC_BIT_CLEAR)
+ opt_set_p = !opt_set_p;
+
+ if (opt_set_p)
+ opts->x_target_flags |= mask;
+ else
+ opts->x_target_flags &= ~mask;
+ new_opts_set->x_target_flags |= mask;
+ }
+
+ else if (cl_options[opt].var_type == CLVC_BOOLEAN)
+ {
+ int value;
+
+ if (cl_options[opt].cl_uinteger)
+ {
+ /* Unsigned integer argument. Code based on the function
+ decode_cmdline_option () in opts-common.c. */
+ value = integral_argument (p + opt_len);
+ }
+ else
+ value = (opt_set_p) ? 1 : 0;
+
+ if (value != -1)
+ {
+ struct cl_decoded_option decoded;
+
+ /* Value range check; only implemented for numeric and boolean
+ options at the moment. */
+ generate_option (opt, NULL, value, CL_TARGET, &decoded);
+ s390_handle_option (opts, new_opts_set, &decoded, input_location);
+ set_option (opts, new_opts_set, opt, value,
+ p + opt_len, DK_UNSPECIFIED, input_location,
+ global_dc);
+ }
+ else
+ {
+ error ("attribute(target(\"%s\")) is unknown", orig_p);
+ ret = false;
+ }
+ }
+
+ else if (cl_options[opt].var_type == CLVC_ENUM)
+ {
+ bool arg_ok;
+ int value;
+
+ arg_ok = opt_enum_arg_to_value (opt, p + opt_len, &value, CL_TARGET);
+ if (arg_ok)
+ set_option (opts, new_opts_set, opt, value,
+ p + opt_len, DK_UNSPECIFIED, input_location,
+ global_dc);
+ else
+ {
+ error ("attribute(target(\"%s\")) is unknown", orig_p);
+ ret = false;
+ }
+ }
+
+ else
+ gcc_unreachable ();
+ }
+ return ret;
+}
+
+/* Return a TARGET_OPTION_NODE tree of the target options listed or NULL. */
+
+tree
+s390_valid_target_attribute_tree (tree args,
+ struct gcc_options *opts,
+ const struct gcc_options *opts_set,
+ bool force_pragma)
+{
+ tree t = NULL_TREE;
+ struct gcc_options new_opts_set;
+
+ memset (&new_opts_set, 0, sizeof (new_opts_set));
+
+ /* Process each of the options on the chain. */
+ if (! s390_valid_target_attribute_inner_p (args, opts, &new_opts_set,
+ force_pragma))
+ return error_mark_node;
+
+ /* If some option was set (even if it has not changed), rerun
+ s390_option_override_internal, and then save the options away. */
+ if (new_opts_set.x_target_flags
+ || new_opts_set.x_s390_arch
+ || new_opts_set.x_s390_tune
+ || new_opts_set.x_s390_stack_guard
+ || new_opts_set.x_s390_stack_size
+ || new_opts_set.x_s390_branch_cost
+ || new_opts_set.x_s390_warn_framesize
+ || new_opts_set.x_s390_warn_dynamicstack_p)
+ {
+ const unsigned char *src = (const unsigned char *)opts_set;
+ unsigned char *dest = (unsigned char *)&new_opts_set;
+ unsigned int i;
+
+ /* Merge the original option flags into the new ones. */
+ for (i = 0; i < sizeof(*opts_set); i++)
+ dest[i] |= src[i];
+
+ /* Do any overrides, such as arch=xxx, or tune=xxx support. */
+ s390_option_override_internal (false, opts, &new_opts_set);
+ /* Save the current options unless we are validating options for
+ #pragma. */
+ t = build_target_option_node (opts);
+ }
+ return t;
+}
+
+/* Hook to validate attribute((target("string"))). */
+
+static bool
+s390_valid_target_attribute_p (tree fndecl,
+ tree ARG_UNUSED (name),
+ tree args,
+ int ARG_UNUSED (flags))
+{
+ struct gcc_options func_options;
+ tree new_target, new_optimize;
+ bool ret = true;
+
+ /* attribute((target("default"))) does nothing, beyond
+ affecting multi-versioning. */
+ if (TREE_VALUE (args)
+ && TREE_CODE (TREE_VALUE (args)) == STRING_CST
+ && TREE_CHAIN (args) == NULL_TREE
+ && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "default") == 0)
+ return true;
+
+ tree old_optimize = build_optimization_node (&global_options);
+
+ /* Get the optimization options of the current function. */
+ tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+ if (!func_optimize)
+ func_optimize = old_optimize;
+
+ /* Init func_options. */
+ memset (&func_options, 0, sizeof (func_options));
+ init_options_struct (&func_options, NULL);
+ lang_hooks.init_options_struct (&func_options);
+
+ cl_optimization_restore (&func_options, TREE_OPTIMIZATION (func_optimize));
+
+ /* Initialize func_options to the default before its target options can
+ be set. */
+ cl_target_option_restore (&func_options,
+ TREE_TARGET_OPTION (target_option_default_node));
+
+ new_target = s390_valid_target_attribute_tree (args, &func_options,
+ &global_options_set,
+ (args ==
+ current_target_pragma));
+ new_optimize = build_optimization_node (&func_options);
+ if (new_target == error_mark_node)
+ ret = false;
+ else if (fndecl && new_target)
+ {
+ DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
+ if (old_optimize != new_optimize)
+ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize;
+ }
+ return ret;
+}
+
+/* Restore targets globals from NEW_TREE and invalidate s390_previous_fndecl
+ cache. */
+
+void
+s390_activate_target_options (tree new_tree)
+{
+ cl_target_option_restore (&global_options, TREE_TARGET_OPTION (new_tree));
+ if (TREE_TARGET_GLOBALS (new_tree))
+ restore_target_globals (TREE_TARGET_GLOBALS (new_tree));
+ else if (new_tree == target_option_default_node)
+ restore_target_globals (&default_target_globals);
+ else
+ TREE_TARGET_GLOBALS (new_tree) = save_target_globals_default_opts ();
+ s390_previous_fndecl = NULL_TREE;
+}
+
+/* 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
+s390_set_current_function (tree fndecl)
+{
+ /* 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 == s390_previous_fndecl)
+ return;
+
+ tree old_tree;
+ if (s390_previous_fndecl == NULL_TREE)
+ old_tree = target_option_current_node;
+ else if (DECL_FUNCTION_SPECIFIC_TARGET (s390_previous_fndecl))
+ old_tree = DECL_FUNCTION_SPECIFIC_TARGET (s390_previous_fndecl);
+ else
+ old_tree = target_option_default_node;
+
+ if (fndecl == NULL_TREE)
+ {
+ if (old_tree != target_option_current_node)
+ s390_activate_target_options (target_option_current_node);
+ return;
+ }
+
+ tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
+ if (new_tree == NULL_TREE)
+ new_tree = target_option_default_node;
+
+ if (old_tree != new_tree)
+ s390_activate_target_options (new_tree);
+ s390_previous_fndecl = fndecl;
+}
+#endif
+