/* Copyright (C) 1988-2022 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #define IN_TARGET_CODE 1 #include "config.h" #include "system.h" #include "coretypes.h" #include "backend.h" #include "rtl.h" #include "tree.h" #include "memmodel.h" #include "gimple.h" #include "cfghooks.h" #include "cfgloop.h" #include "df.h" #include "tm_p.h" #include "stringpool.h" #include "expmed.h" #include "optabs.h" #include "regs.h" #include "emit-rtl.h" #include "recog.h" #include "cgraph.h" #include "diagnostic.h" #include "cfgbuild.h" #include "alias.h" #include "fold-const.h" #include "attribs.h" #include "calls.h" #include "stor-layout.h" #include "varasm.h" #include "output.h" #include "insn-attr.h" #include "flags.h" #include "except.h" #include "explow.h" #include "expr.h" #include "cfgrtl.h" #include "common/common-target.h" #include "langhooks.h" #include "reload.h" #include "gimplify.h" #include "dwarf2.h" #include "tm-constrs.h" #include "cselib.h" #include "sched-int.h" #include "opts.h" #include "tree-pass.h" #include "context.h" #include "pass_manager.h" #include "target-globals.h" #include "gimple-iterator.h" #include "tree-vectorizer.h" #include "shrink-wrap.h" #include "builtins.h" #include "rtl-iter.h" #include "tree-iterator.h" #include "dbgcnt.h" #include "case-cfn-macros.h" #include "dojump.h" #include "fold-const-call.h" #include "tree-vrp.h" #include "tree-ssanames.h" #include "selftest.h" #include "selftest-rtl.h" #include "print-rtl.h" #include "intl.h" #include "ifcvt.h" #include "symbol-summary.h" #include "ipa-prop.h" #include "ipa-fnsummary.h" #include "wide-int-bitmask.h" #include "tree-vector-builder.h" #include "debug.h" #include "dwarf2out.h" #include "i386-options.h" #include "x86-tune-costs.h" #ifndef SUBTARGET32_DEFAULT_CPU #define SUBTARGET32_DEFAULT_CPU "i386" #endif /* Processor feature/optimization bitmasks. */ #define m_NONE HOST_WIDE_INT_0U #define m_ALL (~HOST_WIDE_INT_0U) #define m_386 (HOST_WIDE_INT_1U<\n\n", stderr); return; } /* Save the current options */ void ix86_function_specific_save (struct cl_target_option *ptr, struct gcc_options *opts, struct gcc_options */* opts_set */) { ptr->arch = ix86_arch; ptr->schedule = ix86_schedule; ptr->prefetch_sse = ix86_prefetch_sse; ptr->tune = ix86_tune; ptr->branch_cost = ix86_branch_cost; ptr->tune_defaulted = ix86_tune_defaulted; ptr->arch_specified = ix86_arch_specified; ptr->x_ix86_isa_flags_explicit = opts->x_ix86_isa_flags_explicit; ptr->x_ix86_isa_flags2_explicit = opts->x_ix86_isa_flags2_explicit; ptr->x_recip_mask_explicit = opts->x_recip_mask_explicit; ptr->x_ix86_arch_string = opts->x_ix86_arch_string; ptr->x_ix86_tune_string = opts->x_ix86_tune_string; ptr->x_ix86_asm_dialect = opts->x_ix86_asm_dialect; ptr->x_ix86_branch_cost = opts->x_ix86_branch_cost; ptr->x_ix86_dump_tunes = opts->x_ix86_dump_tunes; ptr->x_ix86_force_align_arg_pointer = opts->x_ix86_force_align_arg_pointer; ptr->x_ix86_force_drap = opts->x_ix86_force_drap; ptr->x_ix86_recip_name = opts->x_ix86_recip_name; ptr->x_ix86_section_threshold = opts->x_ix86_section_threshold; ptr->x_ix86_sse2avx = opts->x_ix86_sse2avx; ptr->x_ix86_stack_protector_guard = opts->x_ix86_stack_protector_guard; ptr->x_ix86_stringop_alg = opts->x_ix86_stringop_alg; ptr->x_ix86_tls_dialect = opts->x_ix86_tls_dialect; ptr->x_ix86_tune_ctrl_string = opts->x_ix86_tune_ctrl_string; ptr->x_ix86_tune_memcpy_strategy = opts->x_ix86_tune_memcpy_strategy; ptr->x_ix86_tune_memset_strategy = opts->x_ix86_tune_memset_strategy; ptr->x_ix86_tune_no_default = opts->x_ix86_tune_no_default; /* The fields are char but the variables are not; make sure the values fit in the fields. */ gcc_assert (ptr->arch == ix86_arch); gcc_assert (ptr->schedule == ix86_schedule); gcc_assert (ptr->tune == ix86_tune); gcc_assert (ptr->branch_cost == ix86_branch_cost); } /* Feature tests against the various architecture variations, used to create ix86_arch_features based on the processor mask. */ static unsigned HOST_WIDE_INT initial_ix86_arch_features[X86_ARCH_LAST] = { /* X86_ARCH_CMOV: Conditional move was added for pentiumpro. */ ~(m_386 | m_486 | m_PENT | m_LAKEMONT | m_K6), /* X86_ARCH_CMPXCHG: Compare and exchange was added for 80486. */ ~m_386, /* X86_ARCH_CMPXCHG8B: Compare and exchange 8 bytes was added for pentium. */ ~(m_386 | m_486), /* X86_ARCH_XADD: Exchange and add was added for 80486. */ ~m_386, /* X86_ARCH_BSWAP: Byteswap was added for 80486. */ ~m_386, }; /* This table must be in sync with enum processor_type in i386.h. */ static const struct processor_costs *processor_cost_table[] = { &generic_cost, &i386_cost, &i486_cost, &pentium_cost, &lakemont_cost, &pentiumpro_cost, &pentium4_cost, &nocona_cost, &core_cost, &core_cost, &core_cost, &core_cost, &atom_cost, &slm_cost, &slm_cost, &slm_cost, &tremont_cost, &slm_cost, &slm_cost, &skylake_cost, &skylake_cost, &icelake_cost, &icelake_cost, &icelake_cost, &skylake_cost, &icelake_cost, &skylake_cost, &icelake_cost, &alderlake_cost, &icelake_cost, &intel_cost, &geode_cost, &k6_cost, &athlon_cost, &k8_cost, &amdfam10_cost, &bdver_cost, &bdver_cost, &bdver_cost, &bdver_cost, &btver1_cost, &btver2_cost, &znver1_cost, &znver2_cost, &znver3_cost, &znver4_cost }; /* Guarantee that the array is aligned with enum processor_type. */ STATIC_ASSERT (ARRAY_SIZE (processor_cost_table) == PROCESSOR_max); static bool ix86_option_override_internal (bool main_args_p, struct gcc_options *opts, struct gcc_options *opts_set); static void set_ix86_tune_features (struct gcc_options *opts, enum processor_type ix86_tune, bool dump); /* Restore the current options */ void ix86_function_specific_restore (struct gcc_options *opts, struct gcc_options */* opts_set */, struct cl_target_option *ptr) { enum processor_type old_tune = ix86_tune; enum processor_type old_arch = ix86_arch; unsigned HOST_WIDE_INT ix86_arch_mask; int i; /* We don't change -fPIC. */ opts->x_flag_pic = flag_pic; ix86_arch = (enum processor_type) ptr->arch; ix86_schedule = (enum attr_cpu) ptr->schedule; ix86_tune = (enum processor_type) ptr->tune; ix86_prefetch_sse = ptr->prefetch_sse; ix86_tune_defaulted = ptr->tune_defaulted; ix86_arch_specified = ptr->arch_specified; opts->x_ix86_isa_flags_explicit = ptr->x_ix86_isa_flags_explicit; opts->x_ix86_isa_flags2_explicit = ptr->x_ix86_isa_flags2_explicit; opts->x_recip_mask_explicit = ptr->x_recip_mask_explicit; opts->x_ix86_arch_string = ptr->x_ix86_arch_string; opts->x_ix86_tune_string = ptr->x_ix86_tune_string; opts->x_ix86_asm_dialect = ptr->x_ix86_asm_dialect; opts->x_ix86_branch_cost = ptr->x_ix86_branch_cost; opts->x_ix86_dump_tunes = ptr->x_ix86_dump_tunes; opts->x_ix86_force_align_arg_pointer = ptr->x_ix86_force_align_arg_pointer; opts->x_ix86_force_drap = ptr->x_ix86_force_drap; opts->x_ix86_recip_name = ptr->x_ix86_recip_name; opts->x_ix86_section_threshold = ptr->x_ix86_section_threshold; opts->x_ix86_sse2avx = ptr->x_ix86_sse2avx; opts->x_ix86_stack_protector_guard = ptr->x_ix86_stack_protector_guard; opts->x_ix86_stringop_alg = ptr->x_ix86_stringop_alg; opts->x_ix86_tls_dialect = ptr->x_ix86_tls_dialect; opts->x_ix86_tune_ctrl_string = ptr->x_ix86_tune_ctrl_string; opts->x_ix86_tune_memcpy_strategy = ptr->x_ix86_tune_memcpy_strategy; opts->x_ix86_tune_memset_strategy = ptr->x_ix86_tune_memset_strategy; opts->x_ix86_tune_no_default = ptr->x_ix86_tune_no_default; ix86_tune_cost = processor_cost_table[ix86_tune]; /* TODO: ix86_cost should be chosen at instruction or function granuality so for cold code we use size_cost even in !optimize_size compilation. */ if (opts->x_optimize_size) ix86_cost = &ix86_size_cost; else ix86_cost = ix86_tune_cost; /* Recreate the arch feature tests if the arch changed */ if (old_arch != ix86_arch) { ix86_arch_mask = HOST_WIDE_INT_1U << ix86_arch; for (i = 0; i < X86_ARCH_LAST; ++i) ix86_arch_features[i] = !!(initial_ix86_arch_features[i] & ix86_arch_mask); } /* Recreate the tune optimization tests */ if (old_tune != ix86_tune) set_ix86_tune_features (opts, ix86_tune, false); } /* Adjust target options after streaming them in. This is mainly about reconciling them with global options. */ void ix86_function_specific_post_stream_in (struct cl_target_option *ptr) { /* flag_pic is a global option, but ix86_cmodel is target saved option partly computed from flag_pic. If flag_pic is on, adjust x_ix86_cmodel for PIC, or error out. */ if (flag_pic) switch (ptr->x_ix86_cmodel) { case CM_SMALL: ptr->x_ix86_cmodel = CM_SMALL_PIC; break; case CM_MEDIUM: ptr->x_ix86_cmodel = CM_MEDIUM_PIC; break; case CM_LARGE: ptr->x_ix86_cmodel = CM_LARGE_PIC; break; case CM_KERNEL: error ("code model %s does not support PIC mode", "kernel"); break; default: break; } else switch (ptr->x_ix86_cmodel) { case CM_SMALL_PIC: ptr->x_ix86_cmodel = CM_SMALL; break; case CM_MEDIUM_PIC: ptr->x_ix86_cmodel = CM_MEDIUM; break; case CM_LARGE_PIC: ptr->x_ix86_cmodel = CM_LARGE; break; default: break; } } /* Print the current options */ void ix86_function_specific_print (FILE *file, int indent, struct cl_target_option *ptr) { char *target_string = ix86_target_string (ptr->x_ix86_isa_flags, ptr->x_ix86_isa_flags2, ptr->x_target_flags, ptr->x_ix86_target_flags, NULL, NULL, ptr->x_ix86_fpmath, ptr->x_prefer_vector_width_type, ptr->x_ix86_move_max, ptr->x_ix86_store_max, false, true); gcc_assert (ptr->arch < PROCESSOR_max); fprintf (file, "%*sarch = %d (%s)\n", indent, "", ptr->arch, processor_names[ptr->arch]); gcc_assert (ptr->tune < PROCESSOR_max); fprintf (file, "%*stune = %d (%s)\n", indent, "", ptr->tune, processor_names[ptr->tune]); fprintf (file, "%*sbranch_cost = %d\n", indent, "", ptr->branch_cost); if (target_string) { fprintf (file, "%*s%s\n", indent, "", target_string); free (target_string); } } /* 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 ix86_valid_target_attribute_inner_p (tree fndecl, tree args, char *p_strings[], struct gcc_options *opts, struct gcc_options *opts_set, struct gcc_options *enum_opts_set, bool target_clone_attr) { char *next_optstr; bool ret = true; #define IX86_ATTR_ISA(S,O) { S, sizeof (S)-1, ix86_opt_isa, O, 0 } #define IX86_ATTR_STR(S,O) { S, sizeof (S)-1, ix86_opt_str, O, 0 } #define IX86_ATTR_ENUM(S,O) { S, sizeof (S)-1, ix86_opt_enum, O, 0 } #define IX86_ATTR_YES(S,O,M) { S, sizeof (S)-1, ix86_opt_yes, O, M } #define IX86_ATTR_NO(S,O,M) { S, sizeof (S)-1, ix86_opt_no, O, M } #define IX86_ATTR_IX86_YES(S,O,M) \ { S, sizeof (S)-1, ix86_opt_ix86_yes, O, M } #define IX86_ATTR_IX86_NO(S,O,M) \ { S, sizeof (S)-1, ix86_opt_ix86_no, O, M } enum ix86_opt_type { ix86_opt_unknown, ix86_opt_yes, ix86_opt_no, ix86_opt_ix86_yes, ix86_opt_ix86_no, ix86_opt_str, ix86_opt_enum, ix86_opt_isa }; static const struct { const char *string; size_t len; enum ix86_opt_type type; int opt; int mask; } attrs[] = { /* isa options */ IX86_ATTR_ISA ("pconfig", OPT_mpconfig), IX86_ATTR_ISA ("wbnoinvd", OPT_mwbnoinvd), IX86_ATTR_ISA ("sgx", OPT_msgx), IX86_ATTR_ISA ("avx5124fmaps", OPT_mavx5124fmaps), IX86_ATTR_ISA ("avx5124vnniw", OPT_mavx5124vnniw), IX86_ATTR_ISA ("avx512vpopcntdq", OPT_mavx512vpopcntdq), IX86_ATTR_ISA ("avx512vbmi2", OPT_mavx512vbmi2), IX86_ATTR_ISA ("avx512vnni", OPT_mavx512vnni), IX86_ATTR_ISA ("avx512bitalg", OPT_mavx512bitalg), IX86_ATTR_ISA ("avx512vp2intersect", OPT_mavx512vp2intersect), IX86_ATTR_ISA ("avx512vbmi", OPT_mavx512vbmi), IX86_ATTR_ISA ("avx512ifma", OPT_mavx512ifma), IX86_ATTR_ISA ("avx512vl", OPT_mavx512vl), IX86_ATTR_ISA ("avx512bw", OPT_mavx512bw), IX86_ATTR_ISA ("avx512dq", OPT_mavx512dq), IX86_ATTR_ISA ("avx512er", OPT_mavx512er), IX86_ATTR_ISA ("avx512pf", OPT_mavx512pf), IX86_ATTR_ISA ("avx512cd", OPT_mavx512cd), IX86_ATTR_ISA ("avx512f", OPT_mavx512f), IX86_ATTR_ISA ("avx2", OPT_mavx2), IX86_ATTR_ISA ("fma", OPT_mfma), IX86_ATTR_ISA ("xop", OPT_mxop), IX86_ATTR_ISA ("fma4", OPT_mfma4), IX86_ATTR_ISA ("f16c", OPT_mf16c), IX86_ATTR_ISA ("avx", OPT_mavx), IX86_ATTR_ISA ("sse4", OPT_msse4), IX86_ATTR_ISA ("sse4.2", OPT_msse4_2), IX86_ATTR_ISA ("sse4.1", OPT_msse4_1), IX86_ATTR_ISA ("sse4a", OPT_msse4a), IX86_ATTR_ISA ("ssse3", OPT_mssse3), IX86_ATTR_ISA ("sse3", OPT_msse3), IX86_ATTR_ISA ("aes", OPT_maes), IX86_ATTR_ISA ("sha", OPT_msha), IX86_ATTR_ISA ("pclmul", OPT_mpclmul), IX86_ATTR_ISA ("sse2", OPT_msse2), IX86_ATTR_ISA ("sse", OPT_msse), IX86_ATTR_ISA ("3dnowa", OPT_m3dnowa), IX86_ATTR_ISA ("3dnow", OPT_m3dnow), IX86_ATTR_ISA ("mmx", OPT_mmmx), IX86_ATTR_ISA ("rtm", OPT_mrtm), IX86_ATTR_ISA ("prfchw", OPT_mprfchw), IX86_ATTR_ISA ("rdseed", OPT_mrdseed), IX86_ATTR_ISA ("adx", OPT_madx), IX86_ATTR_ISA ("prefetchwt1", OPT_mprefetchwt1), IX86_ATTR_ISA ("clflushopt", OPT_mclflushopt), IX86_ATTR_ISA ("xsaves", OPT_mxsaves), IX86_ATTR_ISA ("xsavec", OPT_mxsavec), IX86_ATTR_ISA ("xsaveopt", OPT_mxsaveopt), IX86_ATTR_ISA ("xsave", OPT_mxsave), IX86_ATTR_ISA ("abm", OPT_mabm), IX86_ATTR_ISA ("bmi", OPT_mbmi), IX86_ATTR_ISA ("bmi2", OPT_mbmi2), IX86_ATTR_ISA ("lzcnt", OPT_mlzcnt), IX86_ATTR_ISA ("tbm", OPT_mtbm), IX86_ATTR_ISA ("popcnt", OPT_mpopcnt), IX86_ATTR_ISA ("cx16", OPT_mcx16), IX86_ATTR_ISA ("sahf", OPT_msahf), IX86_ATTR_ISA ("movbe", OPT_mmovbe), IX86_ATTR_ISA ("crc32", OPT_mcrc32), IX86_ATTR_ISA ("fsgsbase", OPT_mfsgsbase), IX86_ATTR_ISA ("rdrnd", OPT_mrdrnd), IX86_ATTR_ISA ("mwaitx", OPT_mmwaitx), IX86_ATTR_ISA ("mwait", OPT_mmwait), IX86_ATTR_ISA ("clzero", OPT_mclzero), IX86_ATTR_ISA ("pku", OPT_mpku), IX86_ATTR_ISA ("lwp", OPT_mlwp), IX86_ATTR_ISA ("hle", OPT_mhle), IX86_ATTR_ISA ("fxsr", OPT_mfxsr), IX86_ATTR_ISA ("clwb", OPT_mclwb), IX86_ATTR_ISA ("rdpid", OPT_mrdpid), IX86_ATTR_ISA ("gfni", OPT_mgfni), IX86_ATTR_ISA ("shstk", OPT_mshstk), IX86_ATTR_ISA ("vaes", OPT_mvaes), IX86_ATTR_ISA ("vpclmulqdq", OPT_mvpclmulqdq), IX86_ATTR_ISA ("movdiri", OPT_mmovdiri), IX86_ATTR_ISA ("movdir64b", OPT_mmovdir64b), IX86_ATTR_ISA ("waitpkg", OPT_mwaitpkg), IX86_ATTR_ISA ("cldemote", OPT_mcldemote), IX86_ATTR_ISA ("uintr", OPT_muintr), IX86_ATTR_ISA ("ptwrite", OPT_mptwrite), IX86_ATTR_ISA ("kl", OPT_mkl), IX86_ATTR_ISA ("widekl", OPT_mwidekl), IX86_ATTR_ISA ("avx512bf16", OPT_mavx512bf16), IX86_ATTR_ISA ("enqcmd", OPT_menqcmd), IX86_ATTR_ISA ("serialize", OPT_mserialize), IX86_ATTR_ISA ("tsxldtrk", OPT_mtsxldtrk), IX86_ATTR_ISA ("amx-tile", OPT_mamx_tile), IX86_ATTR_ISA ("amx-int8", OPT_mamx_int8), IX86_ATTR_ISA ("amx-bf16", OPT_mamx_bf16), IX86_ATTR_ISA ("hreset", OPT_mhreset), IX86_ATTR_ISA ("avxvnni", OPT_mavxvnni), IX86_ATTR_ISA ("avx512fp16", OPT_mavx512fp16), /* enum options */ IX86_ATTR_ENUM ("fpmath=", OPT_mfpmath_), IX86_ATTR_ENUM ("prefer-vector-width=", OPT_mprefer_vector_width_), /* string options */ IX86_ATTR_STR ("arch=", IX86_FUNCTION_SPECIFIC_ARCH), IX86_ATTR_STR ("tune=", IX86_FUNCTION_SPECIFIC_TUNE), /* flag options */ IX86_ATTR_YES ("cld", OPT_mcld, MASK_CLD), IX86_ATTR_NO ("fancy-math-387", OPT_mfancy_math_387, MASK_NO_FANCY_MATH_387), IX86_ATTR_YES ("ieee-fp", OPT_mieee_fp, MASK_IEEE_FP), IX86_ATTR_YES ("inline-all-stringops", OPT_minline_all_stringops, MASK_INLINE_ALL_STRINGOPS), IX86_ATTR_YES ("inline-stringops-dynamically", OPT_minline_stringops_dynamically, MASK_INLINE_STRINGOPS_DYNAMICALLY), IX86_ATTR_NO ("align-stringops", OPT_mno_align_stringops, MASK_NO_ALIGN_STRINGOPS), IX86_ATTR_YES ("recip", OPT_mrecip, MASK_RECIP), IX86_ATTR_IX86_YES ("general-regs-only", OPT_mgeneral_regs_only, OPTION_MASK_GENERAL_REGS_ONLY), IX86_ATTR_YES ("relax-cmpxchg-loop", OPT_mrelax_cmpxchg_loop, MASK_RELAX_CMPXCHG_LOOP), }; location_t loc = fndecl == NULL ? UNKNOWN_LOCATION : DECL_SOURCE_LOCATION (fndecl); const char *attr_name = target_clone_attr ? "target_clone" : "target"; /* If this is a list, recurse to get the options. */ if (TREE_CODE (args) == TREE_LIST) { for (; args; args = TREE_CHAIN (args)) if (TREE_VALUE (args) && !ix86_valid_target_attribute_inner_p (fndecl, TREE_VALUE (args), p_strings, opts, opts_set, enum_opts_set, target_clone_attr)) ret = false; return ret; } else if (TREE_CODE (args) != STRING_CST) { error_at (loc, "attribute %qs argument is not a string", attr_name); 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; enum ix86_opt_type type = ix86_opt_unknown; int mask = 0; 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; opt = N_OPTS; for (i = 0; i < ARRAY_SIZE (attrs); i++) { type = attrs[i].type; opt_len = attrs[i].len; if (ch == attrs[i].string[0] && ((type != ix86_opt_str && type != ix86_opt_enum) ? len == opt_len : len > opt_len) && memcmp (p, attrs[i].string, opt_len) == 0) { opt = attrs[i].opt; mask = attrs[i].mask; break; } } /* Process the option. */ if (opt == N_OPTS) { error_at (loc, "attribute %qs argument %qs is unknown", attr_name, orig_p); ret = false; } else if (type == ix86_opt_isa) { struct cl_decoded_option decoded; generate_option (opt, NULL, opt_set_p, CL_TARGET, &decoded); ix86_handle_option (opts, opts_set, &decoded, input_location); } else if (type == ix86_opt_yes || type == ix86_opt_no) { if (type == ix86_opt_no) opt_set_p = !opt_set_p; if (opt_set_p) opts->x_target_flags |= mask; else opts->x_target_flags &= ~mask; } else if (type == ix86_opt_ix86_yes || type == ix86_opt_ix86_no) { if (mask == OPTION_MASK_GENERAL_REGS_ONLY) { if (!opt_set_p) { error_at (loc, "pragma or attribute % " "does not allow a negated form", p); return false; } if (type != ix86_opt_ix86_yes) gcc_unreachable (); opts->x_ix86_target_flags |= mask; struct cl_decoded_option decoded; generate_option (opt, NULL, opt_set_p, CL_TARGET, &decoded); ix86_handle_option (opts, opts_set, &decoded, input_location); } else { if (type == ix86_opt_ix86_no) opt_set_p = !opt_set_p; if (opt_set_p) opts->x_ix86_target_flags |= mask; else opts->x_ix86_target_flags &= ~mask; } } else if (type == ix86_opt_str) { if (p_strings[opt]) { error_at (loc, "attribute value %qs was already specified " "in %qs attribute", orig_p, attr_name); ret = false; } else { p_strings[opt] = xstrdup (p + opt_len); if (opt == IX86_FUNCTION_SPECIFIC_ARCH) { /* If arch= is set, clear all bits in x_ix86_isa_flags, except for ISA_64BIT, ABI_64, ABI_X32, and CODE16 and all bits in x_ix86_isa_flags2. */ opts->x_ix86_isa_flags &= (OPTION_MASK_ISA_64BIT | OPTION_MASK_ABI_64 | OPTION_MASK_ABI_X32 | OPTION_MASK_CODE16); opts->x_ix86_isa_flags_explicit &= (OPTION_MASK_ISA_64BIT | OPTION_MASK_ABI_64 | OPTION_MASK_ABI_X32 | OPTION_MASK_CODE16); opts->x_ix86_isa_flags2 = 0; opts->x_ix86_isa_flags2_explicit = 0; } } } else if (type == ix86_opt_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, enum_opts_set, opt, value, p + opt_len, DK_UNSPECIFIED, input_location, global_dc); else { error_at (loc, "attribute value %qs is unknown in %qs attribute", orig_p, attr_name); ret = false; } } else gcc_unreachable (); } return ret; } /* Release allocated strings. */ static void release_options_strings (char **option_strings) { /* Free up memory allocated to hold the strings */ for (unsigned i = 0; i < IX86_FUNCTION_SPECIFIC_MAX; i++) free (option_strings[i]); } /* Return a TARGET_OPTION_NODE tree of the target options listed or NULL. */ tree ix86_valid_target_attribute_tree (tree fndecl, tree args, struct gcc_options *opts, struct gcc_options *opts_set, bool target_clone_attr) { const char *orig_arch_string = opts->x_ix86_arch_string; const char *orig_tune_string = opts->x_ix86_tune_string; enum fpmath_unit orig_fpmath_set = opts_set->x_ix86_fpmath; enum prefer_vector_width orig_pvw_set = opts_set->x_prefer_vector_width_type; enum prefer_vector_width orig_ix86_move_max_set = opts_set->x_ix86_move_max; enum prefer_vector_width orig_ix86_store_max_set = opts_set->x_ix86_store_max; int orig_tune_defaulted = ix86_tune_defaulted; int orig_arch_specified = ix86_arch_specified; char *option_strings[IX86_FUNCTION_SPECIFIC_MAX] = { NULL, NULL }; tree t = NULL_TREE; struct cl_target_option *def = TREE_TARGET_OPTION (target_option_default_node); struct gcc_options enum_opts_set; memset (&enum_opts_set, 0, sizeof (enum_opts_set)); /* Process each of the options on the chain. */ if (!ix86_valid_target_attribute_inner_p (fndecl, args, option_strings, opts, opts_set, &enum_opts_set, target_clone_attr)) return error_mark_node; /* If the changed options are different from the default, rerun ix86_option_override_internal, and then save the options away. The string options are attribute options, and will be undone when we copy the save structure. */ if (opts->x_ix86_isa_flags != def->x_ix86_isa_flags || opts->x_ix86_isa_flags2 != def->x_ix86_isa_flags2 || opts->x_target_flags != def->x_target_flags || option_strings[IX86_FUNCTION_SPECIFIC_ARCH] || option_strings[IX86_FUNCTION_SPECIFIC_TUNE] || enum_opts_set.x_ix86_fpmath || enum_opts_set.x_prefer_vector_width_type) { /* If we are using the default tune= or arch=, undo the string assigned, and use the default. */ if (option_strings[IX86_FUNCTION_SPECIFIC_ARCH]) opts->x_ix86_arch_string = ggc_strdup (option_strings[IX86_FUNCTION_SPECIFIC_ARCH]); else if (!orig_arch_specified) opts->x_ix86_arch_string = NULL; if (option_strings[IX86_FUNCTION_SPECIFIC_TUNE]) opts->x_ix86_tune_string = ggc_strdup (option_strings[IX86_FUNCTION_SPECIFIC_TUNE]); else if (orig_tune_defaulted) opts->x_ix86_tune_string = NULL; /* If fpmath= is not set, and we now have sse2 on 32-bit, use it. */ if (enum_opts_set.x_ix86_fpmath) opts_set->x_ix86_fpmath = (enum fpmath_unit) 1; if (enum_opts_set.x_prefer_vector_width_type) opts_set->x_prefer_vector_width_type = (enum prefer_vector_width) 1; /* Do any overrides, such as arch=xxx, or tune=xxx support. */ bool r = ix86_option_override_internal (false, opts, opts_set); if (!r) { release_options_strings (option_strings); return error_mark_node; } /* Add any builtin functions with the new isa if any. */ ix86_add_new_builtins (opts->x_ix86_isa_flags, opts->x_ix86_isa_flags2); enum excess_precision orig_ix86_excess_precision = opts->x_ix86_excess_precision; bool orig_ix86_unsafe_math_optimizations = opts->x_ix86_unsafe_math_optimizations; opts->x_ix86_excess_precision = opts->x_flag_excess_precision; opts->x_ix86_unsafe_math_optimizations = opts->x_flag_unsafe_math_optimizations; /* Save the current options unless we are validating options for #pragma. */ t = build_target_option_node (opts, opts_set); opts->x_ix86_arch_string = orig_arch_string; opts->x_ix86_tune_string = orig_tune_string; opts_set->x_ix86_fpmath = orig_fpmath_set; opts_set->x_prefer_vector_width_type = orig_pvw_set; opts_set->x_ix86_move_max = orig_ix86_move_max_set; opts_set->x_ix86_store_max = orig_ix86_store_max_set; opts->x_ix86_excess_precision = orig_ix86_excess_precision; opts->x_ix86_unsafe_math_optimizations = orig_ix86_unsafe_math_optimizations; release_options_strings (option_strings); } return t; } static GTY(()) tree target_attribute_cache[3]; /* Hook to validate attribute((target("string"))). */ bool ix86_valid_target_attribute_p (tree fndecl, tree ARG_UNUSED (name), tree args, int flags) { struct gcc_options func_options, func_options_set; 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; if ((DECL_FUNCTION_SPECIFIC_TARGET (fndecl) == target_attribute_cache[1] || DECL_FUNCTION_SPECIFIC_TARGET (fndecl) == NULL_TREE) && (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) == target_attribute_cache[2] || DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) == NULL_TREE) && simple_cst_list_equal (args, target_attribute_cache[0])) { DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = target_attribute_cache[1]; DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = target_attribute_cache[2]; return true; } tree old_optimize = build_optimization_node (&global_options, &global_options_set); /* 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); memset (&func_options_set, 0, sizeof (func_options_set)); cl_optimization_restore (&func_options, &func_options_set, TREE_OPTIMIZATION (func_optimize)); /* Initialize func_options to the default before its target options can be set. */ tree old_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); if (old_target == NULL_TREE) old_target = target_option_default_node; cl_target_option_restore (&func_options, &func_options_set, TREE_TARGET_OPTION (old_target)); /* FLAGS == 1 is used for target_clones attribute. */ new_target = ix86_valid_target_attribute_tree (fndecl, args, &func_options, &func_options_set, flags == 1); new_optimize = build_optimization_node (&func_options, &func_options_set); if (new_target == error_mark_node) ret = false; else if (new_target) { if (DECL_FUNCTION_SPECIFIC_TARGET (fndecl) == NULL_TREE && DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) == NULL_TREE) { target_attribute_cache[0] = copy_list (args); target_attribute_cache[1] = new_target; target_attribute_cache[2] = old_optimize != new_optimize ? new_optimize : NULL_TREE; } DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target; if (old_optimize != new_optimize) DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize; } return ret; } const char *stringop_alg_names[] = { #define DEF_ALG(alg, name) #name, #include "stringop.def" #undef DEF_ALG }; /* Parse parameter string passed to -mmemcpy-strategy= or -mmemset-strategy=. The string is of the following form (or comma separated list of it): strategy_alg:max_size:[align|noalign] where the full size range for the strategy is either [0, max_size] or [min_size, max_size], in which min_size is the max_size + 1 of the preceding range. The last size range must have max_size == -1. Examples: 1. -mmemcpy-strategy=libcall:-1:noalign this is equivalent to (for known size memcpy) -mstringop-strategy=libcall 2. -mmemset-strategy=rep_8byte:16:noalign,vector_loop:2048:align,libcall:-1:noalign This is to tell the compiler to use the following strategy for memset 1) when the expected size is between [1, 16], use rep_8byte strategy; 2) when the size is between [17, 2048], use vector_loop; 3) when the size is > 2048, use libcall. */ struct stringop_size_range { int max; stringop_alg alg; bool noalign; }; static void ix86_parse_stringop_strategy_string (char *strategy_str, bool is_memset) { const struct stringop_algs *default_algs; stringop_size_range input_ranges[MAX_STRINGOP_ALGS]; char *curr_range_str, *next_range_str; const char *opt = is_memset ? "-mmemset_strategy=" : "-mmemcpy_strategy="; int i = 0, n = 0; if (is_memset) default_algs = &ix86_cost->memset[TARGET_64BIT != 0]; else default_algs = &ix86_cost->memcpy[TARGET_64BIT != 0]; curr_range_str = strategy_str; do { int maxs; char alg_name[128]; char align[16]; next_range_str = strchr (curr_range_str, ','); if (next_range_str) *next_range_str++ = '\0'; if (sscanf (curr_range_str, "%20[^:]:%d:%10s", alg_name, &maxs, align) != 3) { error ("wrong argument %qs to option %qs", curr_range_str, opt); return; } if (n > 0 && (maxs < (input_ranges[n - 1].max + 1) && maxs != -1)) { error ("size ranges of option %qs should be increasing", opt); return; } for (i = 0; i < last_alg; i++) if (!strcmp (alg_name, stringop_alg_names[i])) break; if (i == last_alg) { error ("wrong strategy name %qs specified for option %qs", alg_name, opt); auto_vec candidates; for (i = 0; i < last_alg; i++) if ((stringop_alg) i != rep_prefix_8_byte || TARGET_64BIT) candidates.safe_push (stringop_alg_names[i]); char *s; const char *hint = candidates_list_and_hint (alg_name, s, candidates); if (hint) inform (input_location, "valid arguments to %qs are: %s; did you mean %qs?", opt, s, hint); else inform (input_location, "valid arguments to %qs are: %s", opt, s); XDELETEVEC (s); return; } if ((stringop_alg) i == rep_prefix_8_byte && !TARGET_64BIT) { /* rep; movq isn't available in 32-bit code. */ error ("strategy name %qs specified for option %qs " "not supported for 32-bit code", alg_name, opt); return; } input_ranges[n].max = maxs; input_ranges[n].alg = (stringop_alg) i; if (!strcmp (align, "align")) input_ranges[n].noalign = false; else if (!strcmp (align, "noalign")) input_ranges[n].noalign = true; else { error ("unknown alignment %qs specified for option %qs", align, opt); return; } n++; curr_range_str = next_range_str; } while (curr_range_str); if (input_ranges[n - 1].max != -1) { error ("the max value for the last size range should be -1" " for option %qs", opt); return; } if (n > MAX_STRINGOP_ALGS) { error ("too many size ranges specified in option %qs", opt); return; } /* Now override the default algs array. */ for (i = 0; i < n; i++) { *const_cast(&default_algs->size[i].max) = input_ranges[i].max; *const_cast(&default_algs->size[i].alg) = input_ranges[i].alg; *const_cast(&default_algs->size[i].noalign) = input_ranges[i].noalign; } } /* parse -mtune-ctrl= option. When DUMP is true, print the features that are explicitly set. */ static void parse_mtune_ctrl_str (struct gcc_options *opts, bool dump) { if (!opts->x_ix86_tune_ctrl_string) return; char *next_feature_string = NULL; char *curr_feature_string = xstrdup (opts->x_ix86_tune_ctrl_string); char *orig = curr_feature_string; int i; do { bool clear = false; next_feature_string = strchr (curr_feature_string, ','); if (next_feature_string) *next_feature_string++ = '\0'; if (*curr_feature_string == '^') { curr_feature_string++; clear = true; } for (i = 0; i < X86_TUNE_LAST; i++) { if (!strcmp (curr_feature_string, ix86_tune_feature_names[i])) { ix86_tune_features[i] = !clear; if (dump) fprintf (stderr, "Explicitly %s feature %s\n", clear ? "clear" : "set", ix86_tune_feature_names[i]); break; } } if (i == X86_TUNE_LAST) error ("unknown parameter to option %<-mtune-ctrl%>: %s", clear ? curr_feature_string - 1 : curr_feature_string); curr_feature_string = next_feature_string; } while (curr_feature_string); free (orig); } /* Helper function to set ix86_tune_features. IX86_TUNE is the processor type. */ static void set_ix86_tune_features (struct gcc_options *opts, enum processor_type ix86_tune, bool dump) { unsigned HOST_WIDE_INT ix86_tune_mask = HOST_WIDE_INT_1U << ix86_tune; int i; for (i = 0; i < X86_TUNE_LAST; ++i) { if (ix86_tune_no_default) ix86_tune_features[i] = 0; else ix86_tune_features[i] = !!(initial_ix86_tune_features[i] & ix86_tune_mask); } if (dump) { fprintf (stderr, "List of x86 specific tuning parameter names:\n"); for (i = 0; i < X86_TUNE_LAST; i++) fprintf (stderr, "%s : %s\n", ix86_tune_feature_names[i], ix86_tune_features[i] ? "on" : "off"); } parse_mtune_ctrl_str (opts, dump); } /* Default align_* from the processor table. */ static void ix86_default_align (struct gcc_options *opts) { /* -falign-foo without argument: supply one. */ if (opts->x_flag_align_loops && !opts->x_str_align_loops) opts->x_str_align_loops = processor_cost_table[ix86_tune]->align_loop; if (opts->x_flag_align_jumps && !opts->x_str_align_jumps) opts->x_str_align_jumps = processor_cost_table[ix86_tune]->align_jump; if (opts->x_flag_align_labels && !opts->x_str_align_labels) opts->x_str_align_labels = processor_cost_table[ix86_tune]->align_label; if (opts->x_flag_align_functions && !opts->x_str_align_functions) opts->x_str_align_functions = processor_cost_table[ix86_tune]->align_func; } #ifndef USE_IX86_FRAME_POINTER #define USE_IX86_FRAME_POINTER 0 #endif /* (Re)compute option overrides affected by optimization levels in target-specific ways. */ static void ix86_recompute_optlev_based_flags (struct gcc_options *opts, struct gcc_options *opts_set) { /* Set the default values for switches whose default depends on TARGET_64BIT in case they weren't overwritten by command line options. */ if (TARGET_64BIT_P (opts->x_ix86_isa_flags)) { if (opts->x_optimize >= 1) SET_OPTION_IF_UNSET (opts, opts_set, flag_omit_frame_pointer, !USE_IX86_FRAME_POINTER); if (opts->x_flag_asynchronous_unwind_tables && TARGET_64BIT_MS_ABI) SET_OPTION_IF_UNSET (opts, opts_set, flag_unwind_tables, 1); if (opts->x_flag_asynchronous_unwind_tables == 2) opts->x_flag_unwind_tables = opts->x_flag_asynchronous_unwind_tables = 1; if (opts->x_flag_pcc_struct_return == 2) opts->x_flag_pcc_struct_return = 0; } else { if (opts->x_optimize >= 1) SET_OPTION_IF_UNSET (opts, opts_set, flag_omit_frame_pointer, !(USE_IX86_FRAME_POINTER || opts->x_optimize_size)); if (opts->x_flag_asynchronous_unwind_tables == 2) opts->x_flag_asynchronous_unwind_tables = !USE_IX86_FRAME_POINTER; if (opts->x_flag_pcc_struct_return == 2) { /* Intel MCU psABI specifies that -freg-struct-return should be on. Instead of setting DEFAULT_PCC_STRUCT_RETURN to 0, we check -miamcu so that -freg-struct-return is always turned on if -miamcu is used. */ if (TARGET_IAMCU_P (opts->x_target_flags)) opts->x_flag_pcc_struct_return = 0; else opts->x_flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN; } } } /* Implement TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE hook. */ void ix86_override_options_after_change (void) { ix86_default_align (&global_options); ix86_recompute_optlev_based_flags (&global_options, &global_options_set); } /* Clear stack slot assignments remembered from previous functions. This is called from INIT_EXPANDERS once before RTL is emitted for each function. */ static struct machine_function * ix86_init_machine_status (void) { struct machine_function *f; f = ggc_cleared_alloc (); f->call_abi = ix86_abi; f->stack_frame_required = true; f->silent_p = true; return f; } /* Override various settings based on options. If MAIN_ARGS_P, the options are from the command line, otherwise they are from attributes. Return true if there's an error related to march option. */ static bool ix86_option_override_internal (bool main_args_p, struct gcc_options *opts, struct gcc_options *opts_set) { unsigned int i; unsigned HOST_WIDE_INT ix86_arch_mask; const bool ix86_tune_specified = (opts->x_ix86_tune_string != NULL); /* -mrecip options. */ static struct { const char *string; /* option name */ unsigned int mask; /* mask bits to set */ } const recip_options[] = { { "all", RECIP_MASK_ALL }, { "none", RECIP_MASK_NONE }, { "div", RECIP_MASK_DIV }, { "sqrt", RECIP_MASK_SQRT }, { "vec-div", RECIP_MASK_VEC_DIV }, { "vec-sqrt", RECIP_MASK_VEC_SQRT }, }; /* Turn off both OPTION_MASK_ABI_64 and OPTION_MASK_ABI_X32 if TARGET_64BIT_DEFAULT is true and TARGET_64BIT is false. */ if (TARGET_64BIT_DEFAULT && !TARGET_64BIT_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags &= ~(OPTION_MASK_ABI_64 | OPTION_MASK_ABI_X32); #ifdef TARGET_BI_ARCH else { #if TARGET_BI_ARCH == 1 /* When TARGET_BI_ARCH == 1, by default, OPTION_MASK_ABI_64 is on and OPTION_MASK_ABI_X32 is off. We turn off OPTION_MASK_ABI_64 if OPTION_MASK_ABI_X32 is turned on by -mx32. */ if (TARGET_X32_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags &= ~OPTION_MASK_ABI_64; #else /* When TARGET_BI_ARCH == 2, by default, OPTION_MASK_ABI_X32 is on and OPTION_MASK_ABI_64 is off. We turn off OPTION_MASK_ABI_X32 if OPTION_MASK_ABI_64 is turned on by -m64 or OPTION_MASK_CODE16 is turned on by -m16. */ if (TARGET_LP64_P (opts->x_ix86_isa_flags) || TARGET_16BIT_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags &= ~OPTION_MASK_ABI_X32; #endif if (TARGET_64BIT_P (opts->x_ix86_isa_flags) && TARGET_IAMCU_P (opts->x_target_flags)) sorry ("Intel MCU psABI isn%'t supported in %s mode", TARGET_X32_P (opts->x_ix86_isa_flags) ? "x32" : "64-bit"); } #endif if (TARGET_X32_P (opts->x_ix86_isa_flags)) { /* Always turn on OPTION_MASK_ISA_64BIT and turn off OPTION_MASK_ABI_64 for TARGET_X32. */ opts->x_ix86_isa_flags |= OPTION_MASK_ISA_64BIT; opts->x_ix86_isa_flags &= ~OPTION_MASK_ABI_64; } else if (TARGET_16BIT_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags &= ~(OPTION_MASK_ISA_64BIT | OPTION_MASK_ABI_X32 | OPTION_MASK_ABI_64); else if (TARGET_LP64_P (opts->x_ix86_isa_flags)) { /* Always turn on OPTION_MASK_ISA_64BIT and turn off OPTION_MASK_ABI_X32 for TARGET_LP64. */ opts->x_ix86_isa_flags |= OPTION_MASK_ISA_64BIT; opts->x_ix86_isa_flags &= ~OPTION_MASK_ABI_X32; } #ifdef SUBTARGET_OVERRIDE_OPTIONS SUBTARGET_OVERRIDE_OPTIONS; #endif #ifdef SUBSUBTARGET_OVERRIDE_OPTIONS SUBSUBTARGET_OVERRIDE_OPTIONS; #endif #ifdef HAVE_LD_BROKEN_PE_DWARF5 /* If the PE linker has broken DWARF 5 support, make DWARF 4 the default. */ if (TARGET_PECOFF) SET_OPTION_IF_UNSET (opts, opts_set, dwarf_version, 4); #endif /* -fPIC is the default for x86_64. */ if (TARGET_MACHO && TARGET_64BIT_P (opts->x_ix86_isa_flags)) opts->x_flag_pic = 2; /* Need to check -mtune=generic first. */ if (opts->x_ix86_tune_string) { /* As special support for cross compilers we read -mtune=native as -mtune=generic. With native compilers we won't see the -mtune=native, as it was changed by the driver. */ if (!strcmp (opts->x_ix86_tune_string, "native")) opts->x_ix86_tune_string = "generic"; else if (!strcmp (opts->x_ix86_tune_string, "x86-64")) warning (OPT_Wdeprecated, main_args_p ? G_("%<-mtune=x86-64%> is deprecated; use %<-mtune=k8%> " "or %<-mtune=generic%> instead as appropriate") : G_("% is deprecated; use " "% or %" " instead as appropriate")); } else { if (opts->x_ix86_arch_string) opts->x_ix86_tune_string = opts->x_ix86_arch_string; if (!opts->x_ix86_tune_string) { opts->x_ix86_tune_string = processor_names[TARGET_CPU_DEFAULT]; ix86_tune_defaulted = 1; } /* opts->x_ix86_tune_string is set to opts->x_ix86_arch_string or defaulted. We need to use a sensible tune option. */ if (startswith (opts->x_ix86_tune_string, "x86-64") && (opts->x_ix86_tune_string[6] == '\0' || (!strcmp (opts->x_ix86_tune_string + 6, "-v2") || !strcmp (opts->x_ix86_tune_string + 6, "-v3") || !strcmp (opts->x_ix86_tune_string + 6, "-v4")))) opts->x_ix86_tune_string = "generic"; } if (opts->x_ix86_stringop_alg == rep_prefix_8_byte && !TARGET_64BIT_P (opts->x_ix86_isa_flags)) { /* rep; movq isn't available in 32-bit code. */ error ("%<-mstringop-strategy=rep_8byte%> not supported for 32-bit code"); opts->x_ix86_stringop_alg = no_stringop; } if (TARGET_UINTR && !TARGET_64BIT) error ("%<-muintr%> not supported for 32-bit code"); if (!opts->x_ix86_arch_string) opts->x_ix86_arch_string = TARGET_64BIT_P (opts->x_ix86_isa_flags) ? "x86-64" : SUBTARGET32_DEFAULT_CPU; else ix86_arch_specified = 1; if (opts_set->x_ix86_pmode) { if ((TARGET_LP64_P (opts->x_ix86_isa_flags) && opts->x_ix86_pmode == PMODE_SI) || (!TARGET_64BIT_P (opts->x_ix86_isa_flags) && opts->x_ix86_pmode == PMODE_DI)) error ("address mode %qs not supported in the %s bit mode", TARGET_64BIT_P (opts->x_ix86_isa_flags) ? "short" : "long", TARGET_64BIT_P (opts->x_ix86_isa_flags) ? "64" : "32"); } else opts->x_ix86_pmode = TARGET_LP64_P (opts->x_ix86_isa_flags) ? PMODE_DI : PMODE_SI; SET_OPTION_IF_UNSET (opts, opts_set, ix86_abi, DEFAULT_ABI); if (opts->x_ix86_abi == MS_ABI && TARGET_X32_P (opts->x_ix86_isa_flags)) error ("%<-mabi=ms%> not supported with X32 ABI"); gcc_assert (opts->x_ix86_abi == SYSV_ABI || opts->x_ix86_abi == MS_ABI); const char *abi_name = opts->x_ix86_abi == MS_ABI ? "ms" : "sysv"; if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS) && opts->x_ix86_abi != DEFAULT_ABI) error ("%<-mabi=%s%> not supported with %<-fsanitize=address%>", abi_name); if ((opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS) && opts->x_ix86_abi != DEFAULT_ABI) error ("%<-mabi=%s%> not supported with %<-fsanitize=kernel-address%>", abi_name); if ((opts->x_flag_sanitize & SANITIZE_THREAD) && opts->x_ix86_abi != DEFAULT_ABI) error ("%<-mabi=%s%> not supported with %<-fsanitize=thread%>", abi_name); /* For targets using ms ABI enable ms-extensions, if not explicit turned off. For non-ms ABI we turn off this option. */ SET_OPTION_IF_UNSET (opts, opts_set, flag_ms_extensions, (MS_ABI == DEFAULT_ABI)); if (opts_set->x_ix86_cmodel) { switch (opts->x_ix86_cmodel) { case CM_SMALL: case CM_SMALL_PIC: if (opts->x_flag_pic) opts->x_ix86_cmodel = CM_SMALL_PIC; if (!TARGET_64BIT_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in the %s bit mode", "small", "32"); break; case CM_MEDIUM: case CM_MEDIUM_PIC: if (opts->x_flag_pic) opts->x_ix86_cmodel = CM_MEDIUM_PIC; if (!TARGET_64BIT_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in the %s bit mode", "medium", "32"); else if (TARGET_X32_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in x32 mode", "medium"); break; case CM_LARGE: case CM_LARGE_PIC: if (opts->x_flag_pic) opts->x_ix86_cmodel = CM_LARGE_PIC; if (!TARGET_64BIT_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in the %s bit mode", "large", "32"); else if (TARGET_X32_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in x32 mode", "large"); break; case CM_32: if (opts->x_flag_pic) error ("code model %s does not support PIC mode", "32"); if (TARGET_64BIT_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in the %s bit mode", "32", "64"); break; case CM_KERNEL: if (opts->x_flag_pic) { error ("code model %s does not support PIC mode", "kernel"); opts->x_ix86_cmodel = CM_32; } if (!TARGET_64BIT_P (opts->x_ix86_isa_flags)) error ("code model %qs not supported in the %s bit mode", "kernel", "32"); break; default: gcc_unreachable (); } } else { /* For TARGET_64BIT and MS_ABI, force pic on, in order to enable the use of rip-relative addressing. This eliminates fixups that would otherwise be needed if this object is to be placed in a DLL, and is essentially just as efficient as direct addressing. */ if (TARGET_64BIT_P (opts->x_ix86_isa_flags) && (TARGET_RDOS || TARGET_PECOFF)) opts->x_ix86_cmodel = CM_MEDIUM_PIC, opts->x_flag_pic = 1; else if (TARGET_64BIT_P (opts->x_ix86_isa_flags)) opts->x_ix86_cmodel = opts->x_flag_pic ? CM_SMALL_PIC : CM_SMALL; else opts->x_ix86_cmodel = CM_32; } if (TARGET_MACHO && opts->x_ix86_asm_dialect == ASM_INTEL) { error ("%<-masm=intel%> not supported in this configuration"); opts->x_ix86_asm_dialect = ASM_ATT; } if ((TARGET_64BIT_P (opts->x_ix86_isa_flags) != 0) != ((opts->x_ix86_isa_flags & OPTION_MASK_ISA_64BIT) != 0)) sorry ("%i-bit mode not compiled in", (opts->x_ix86_isa_flags & OPTION_MASK_ISA_64BIT) ? 64 : 32); /* Last processor_alias_table must point to "generic" entry. */ gcc_checking_assert (strcmp (processor_alias_table[pta_size - 1].name, "generic") == 0); for (i = 0; i < pta_size; i++) if (! strcmp (opts->x_ix86_arch_string, processor_alias_table[i].name)) { if (!strcmp (opts->x_ix86_arch_string, "generic")) { error (main_args_p ? G_("% CPU can be used only for %<-mtune=%> " "switch") : G_("% CPU can be used only for " "% attribute")); return false; } else if (!strcmp (opts->x_ix86_arch_string, "intel")) { error (main_args_p ? G_("% CPU can be used only for %<-mtune=%> " "switch") : G_("% CPU can be used only for " "% attribute")); return false; } if (TARGET_64BIT_P (opts->x_ix86_isa_flags) && !((processor_alias_table[i].flags & PTA_64BIT) != 0)) { error ("CPU you selected does not support x86-64 " "instruction set"); return false; } ix86_schedule = processor_alias_table[i].schedule; ix86_arch = processor_alias_table[i].processor; /* Default cpu tuning to the architecture, unless the table entry requests not to do this. Used by the x86-64 psABI micro-architecture levels. */ if ((processor_alias_table[i].flags & PTA_NO_TUNE) == 0) ix86_tune = ix86_arch; else ix86_tune = PROCESSOR_GENERIC; /* Enable PTA flags that are enabled by default by a -march option. */ #define TARGET_EXPLICIT_NO_SAHF_P(opts) (false) #define SET_TARGET_NO_SAHF(opts) {} #define TARGET_EXPLICIT_PREFETCH_SSE_P(opts) (false) #define SET_TARGET_PREFETCH_SSE(opts) {} #define TARGET_EXPLICIT_NO_TUNE_P(opts) (false) #define SET_TARGET_NO_TUNE(opts) {} #define TARGET_EXPLICIT_NO_80387_P(opts) (false) #define SET_TARGET_NO_80387(opts) {} #define DEF_PTA(NAME) \ if (((processor_alias_table[i].flags & PTA_ ## NAME) != 0) \ && PTA_ ## NAME != PTA_64BIT \ && (TARGET_64BIT || PTA_ ## NAME != PTA_UINTR) \ && !TARGET_EXPLICIT_ ## NAME ## _P (opts)) \ SET_TARGET_ ## NAME (opts); #include "i386-isa.def" #undef DEF_PTA if (!(TARGET_64BIT_P (opts->x_ix86_isa_flags) && ((processor_alias_table[i].flags & PTA_NO_SAHF) != 0)) && !TARGET_EXPLICIT_SAHF_P (opts)) SET_TARGET_SAHF (opts); if (((processor_alias_table[i].flags & PTA_ABM) != 0) && !TARGET_EXPLICIT_ABM_P (opts)) { if (!TARGET_EXPLICIT_LZCNT_P (opts)) SET_TARGET_LZCNT (opts); if (!TARGET_EXPLICIT_POPCNT_P (opts)) SET_TARGET_POPCNT (opts); } if ((processor_alias_table[i].flags & (PTA_PREFETCH_SSE | PTA_SSE)) != 0) ix86_prefetch_sse = true; /* Don't enable x87 instructions if only general registers are allowed by target("general-regs-only") function attribute or -mgeneral-regs-only. */ if (!(opts->x_ix86_target_flags & OPTION_MASK_GENERAL_REGS_ONLY) && !(opts_set->x_target_flags & MASK_80387)) { if (((processor_alias_table[i].flags & PTA_NO_80387) != 0)) opts->x_target_flags &= ~MASK_80387; else opts->x_target_flags |= MASK_80387; } break; } if (i == pta_size) { error (main_args_p ? G_("bad value %qs for %<-march=%> switch") : G_("bad value %qs for % attribute"), opts->x_ix86_arch_string); auto_vec candidates; for (i = 0; i < pta_size; i++) if (strcmp (processor_alias_table[i].name, "generic") && strcmp (processor_alias_table[i].name, "intel") && (!TARGET_64BIT_P (opts->x_ix86_isa_flags) || ((processor_alias_table[i].flags & PTA_64BIT) != 0))) candidates.safe_push (processor_alias_table[i].name); #ifdef HAVE_LOCAL_CPU_DETECT /* Add also "native" as possible value. */ candidates.safe_push ("native"); #endif char *s; const char *hint = candidates_list_and_hint (opts->x_ix86_arch_string, s, candidates); if (hint) inform (input_location, main_args_p ? G_("valid arguments to %<-march=%> switch are: " "%s; did you mean %qs?") : G_("valid arguments to % attribute are: " "%s; did you mean %qs?"), s, hint); else inform (input_location, main_args_p ? G_("valid arguments to %<-march=%> switch are: %s") : G_("valid arguments to % attribute " "are: %s"), s); XDELETEVEC (s); } ix86_arch_mask = HOST_WIDE_INT_1U << ix86_arch; for (i = 0; i < X86_ARCH_LAST; ++i) ix86_arch_features[i] = !!(initial_ix86_arch_features[i] & ix86_arch_mask); for (i = 0; i < pta_size; i++) if (! strcmp (opts->x_ix86_tune_string, processor_alias_table[i].name) && (processor_alias_table[i].flags & PTA_NO_TUNE) == 0) { ix86_schedule = processor_alias_table[i].schedule; ix86_tune = processor_alias_table[i].processor; if (TARGET_64BIT_P (opts->x_ix86_isa_flags)) { if (!((processor_alias_table[i].flags & PTA_64BIT) != 0)) { if (ix86_tune_defaulted) { opts->x_ix86_tune_string = "x86-64"; for (i = 0; i < pta_size; i++) if (! strcmp (opts->x_ix86_tune_string, processor_alias_table[i].name)) break; ix86_schedule = processor_alias_table[i].schedule; ix86_tune = processor_alias_table[i].processor; } else error ("CPU you selected does not support x86-64 " "instruction set"); } } /* Intel CPUs have always interpreted SSE prefetch instructions as NOPs; so, we can enable SSE prefetch instructions even when -mtune (rather than -march) points us to a processor that has them. However, the VIA C3 gives a SIGILL, so we only do that for i686 and higher processors. */ if (TARGET_CMOV && ((processor_alias_table[i].flags & (PTA_PREFETCH_SSE | PTA_SSE)) != 0)) ix86_prefetch_sse = true; break; } if (ix86_tune_specified && i == pta_size) { error (main_args_p ? G_("bad value %qs for %<-mtune=%> switch") : G_("bad value %qs for % attribute"), opts->x_ix86_tune_string); auto_vec candidates; for (i = 0; i < pta_size; i++) if ((!TARGET_64BIT_P (opts->x_ix86_isa_flags) || ((processor_alias_table[i].flags & PTA_64BIT) != 0)) && (processor_alias_table[i].flags & PTA_NO_TUNE) == 0) candidates.safe_push (processor_alias_table[i].name); #ifdef HAVE_LOCAL_CPU_DETECT /* Add also "native" as possible value. */ candidates.safe_push ("native"); #endif char *s; const char *hint = candidates_list_and_hint (opts->x_ix86_tune_string, s, candidates); if (hint) inform (input_location, main_args_p ? G_("valid arguments to %<-mtune=%> switch are: " "%s; did you mean %qs?") : G_("valid arguments to % attribute are: " "%s; did you mean %qs?"), s, hint); else inform (input_location, main_args_p ? G_("valid arguments to %<-mtune=%> switch are: %s") : G_("valid arguments to % attribute " "are: %s"), s); XDELETEVEC (s); } set_ix86_tune_features (opts, ix86_tune, opts->x_ix86_dump_tunes); ix86_recompute_optlev_based_flags (opts, opts_set); ix86_tune_cost = processor_cost_table[ix86_tune]; /* TODO: ix86_cost should be chosen at instruction or function granuality so for cold code we use size_cost even in !optimize_size compilation. */ if (opts->x_optimize_size) ix86_cost = &ix86_size_cost; else ix86_cost = ix86_tune_cost; /* Arrange to set up i386_stack_locals for all functions. */ init_machine_status = ix86_init_machine_status; /* Validate -mregparm= value. */ if (opts_set->x_ix86_regparm) { if (TARGET_64BIT_P (opts->x_ix86_isa_flags)) warning (0, "%<-mregparm%> is ignored in 64-bit mode"); else if (TARGET_IAMCU_P (opts->x_target_flags)) warning (0, "%<-mregparm%> is ignored for Intel MCU psABI"); if (opts->x_ix86_regparm > REGPARM_MAX) { error ("%<-mregparm=%d%> is not between 0 and %d", opts->x_ix86_regparm, REGPARM_MAX); opts->x_ix86_regparm = 0; } } if (TARGET_IAMCU_P (opts->x_target_flags) || TARGET_64BIT_P (opts->x_ix86_isa_flags)) opts->x_ix86_regparm = REGPARM_MAX; /* Default align_* from the processor table. */ ix86_default_align (opts); /* Provide default for -mbranch-cost= value. */ SET_OPTION_IF_UNSET (opts, opts_set, ix86_branch_cost, ix86_tune_cost->branch_cost); if (TARGET_64BIT_P (opts->x_ix86_isa_flags)) { opts->x_target_flags |= TARGET_SUBTARGET64_DEFAULT & ~opts_set->x_target_flags; if (!ix86_arch_specified) opts->x_ix86_isa_flags |= TARGET_SUBTARGET64_ISA_DEFAULT & ~opts->x_ix86_isa_flags_explicit; if (!TARGET_128BIT_LONG_DOUBLE_P (opts->x_target_flags)) error ("%<-m96bit-long-double%> is not compatible with this target"); if (TARGET_RTD_P (opts->x_target_flags)) warning (0, main_args_p ? G_("%<-mrtd%> is ignored in 64bit mode") : G_("% is ignored in 64bit mode")); } else { opts->x_target_flags |= TARGET_SUBTARGET32_DEFAULT & ~opts_set->x_target_flags; if (!ix86_arch_specified) opts->x_ix86_isa_flags |= TARGET_SUBTARGET32_ISA_DEFAULT & ~opts->x_ix86_isa_flags_explicit; /* i386 ABI does not specify red zone. It still makes sense to use it when programmer takes care to stack from being destroyed. */ if (!(opts_set->x_target_flags & MASK_NO_RED_ZONE)) opts->x_target_flags |= MASK_NO_RED_ZONE; } /* Keep nonleaf frame pointers. */ if (opts->x_flag_omit_frame_pointer) opts->x_target_flags &= ~MASK_OMIT_LEAF_FRAME_POINTER; else if (TARGET_OMIT_LEAF_FRAME_POINTER_P (opts->x_target_flags)) opts->x_flag_omit_frame_pointer = 1; /* If we're doing fast math, we don't care about comparison order wrt NaNs. This lets us use a shorter comparison sequence. */ if (opts->x_flag_finite_math_only) opts->x_target_flags &= ~MASK_IEEE_FP; /* If the architecture always has an FPU, turn off NO_FANCY_MATH_387, since the insns won't need emulation. */ if (ix86_tune_features [X86_TUNE_ALWAYS_FANCY_MATH_387]) opts->x_target_flags &= ~MASK_NO_FANCY_MATH_387; /* Likewise, if the target doesn't have a 387, or we've specified software floating point, don't use 387 inline intrinsics. */ if (!TARGET_80387_P (opts->x_target_flags)) opts->x_target_flags |= MASK_NO_FANCY_MATH_387; /* Turn on MMX builtins for -msse. */ if (TARGET_SSE_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags |= OPTION_MASK_ISA_MMX & ~opts->x_ix86_isa_flags_explicit; /* Enable SSE prefetch. */ if (TARGET_SSE_P (opts->x_ix86_isa_flags) || (TARGET_PRFCHW_P (opts->x_ix86_isa_flags) && !TARGET_3DNOW_P (opts->x_ix86_isa_flags)) || TARGET_PREFETCHWT1_P (opts->x_ix86_isa_flags)) ix86_prefetch_sse = true; /* Enable mwait/monitor instructions for -msse3. */ if (TARGET_SSE3_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags2 |= OPTION_MASK_ISA2_MWAIT & ~opts->x_ix86_isa_flags2_explicit; /* Enable popcnt instruction for -msse4.2 or -mabm. */ if (TARGET_SSE4_2_P (opts->x_ix86_isa_flags) || TARGET_ABM_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags |= OPTION_MASK_ISA_POPCNT & ~opts->x_ix86_isa_flags_explicit; /* Enable crc32 instruction for -msse4.2. */ if (TARGET_SSE4_2_P (opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags |= OPTION_MASK_ISA_CRC32 & ~opts->x_ix86_isa_flags_explicit; /* Enable lzcnt instruction for -mabm. */ if (TARGET_ABM_P(opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags |= OPTION_MASK_ISA_LZCNT & ~opts->x_ix86_isa_flags_explicit; /* Disable BMI, BMI2 and TBM instructions for -m16. */ if (TARGET_16BIT_P(opts->x_ix86_isa_flags)) opts->x_ix86_isa_flags &= ~((OPTION_MASK_ISA_BMI | OPTION_MASK_ISA_BMI2 | OPTION_MASK_ISA_TBM) & ~opts->x_ix86_isa_flags_explicit); /* Validate -mpreferred-stack-boundary= value or default it to PREFERRED_STACK_BOUNDARY_DEFAULT. */ ix86_preferred_stack_boundary = PREFERRED_STACK_BOUNDARY_DEFAULT; if (opts_set->x_ix86_preferred_stack_boundary_arg) { int min = TARGET_64BIT_P (opts->x_ix86_isa_flags)? 3 : 2; int max = TARGET_SEH ? 4 : 12; if (opts->x_ix86_preferred_stack_boundary_arg < min || opts->x_ix86_preferred_stack_boundary_arg > max) { if (min == max) error ("%<-mpreferred-stack-boundary%> is not supported " "for this target"); else error ("%<-mpreferred-stack-boundary=%d%> is not between %d and %d", opts->x_ix86_preferred_stack_boundary_arg, min, max); } else ix86_preferred_stack_boundary = (1 << opts->x_ix86_preferred_stack_boundary_arg) * BITS_PER_UNIT; } /* Set the default value for -mstackrealign. */ SET_OPTION_IF_UNSET (opts, opts_set, ix86_force_align_arg_pointer, STACK_REALIGN_DEFAULT); ix86_default_incoming_stack_boundary = PREFERRED_STACK_BOUNDARY; /* Validate -mincoming-stack-boundary= value or default it to MIN_STACK_BOUNDARY/PREFERRED_STACK_BOUNDARY. */ ix86_incoming_stack_boundary = ix86_default_incoming_stack_boundary; if (opts_set->x_ix86_incoming_stack_boundary_arg) { int min = TARGET_64BIT_P (opts->x_ix86_isa_flags) ? 3 : 2; if (opts->x_ix86_incoming_stack_boundary_arg < min || opts->x_ix86_incoming_stack_boundary_arg > 12) error ("%<-mincoming-stack-boundary=%d%> is not between %d and 12", opts->x_ix86_incoming_stack_boundary_arg, min); else { ix86_user_incoming_stack_boundary = (1 << opts->x_ix86_incoming_stack_boundary_arg) * BITS_PER_UNIT; ix86_incoming_stack_boundary = ix86_user_incoming_stack_boundary; } } #ifndef NO_PROFILE_COUNTERS if (flag_nop_mcount) error ("%<-mnop-mcount%> is not compatible with this target"); #endif if (flag_nop_mcount && flag_pic) error ("%<-mnop-mcount%> is not implemented for %<-fPIC%>"); /* Accept -msseregparm only if at least SSE support is enabled. */ if (TARGET_SSEREGPARM_P (opts->x_target_flags) && ! TARGET_SSE_P (opts->x_ix86_isa_flags)) error (main_args_p ? G_("%<-msseregparm%> used without SSE enabled") : G_("% used without SSE enabled")); if (opts_set->x_ix86_fpmath) { if (opts->x_ix86_fpmath & FPMATH_SSE) { if (!TARGET_SSE_P (opts->x_ix86_isa_flags)) { if (TARGET_80387_P (opts->x_target_flags)) { warning (0, "SSE instruction set disabled, using 387 arithmetics"); opts->x_ix86_fpmath = FPMATH_387; } } else if ((opts->x_ix86_fpmath & FPMATH_387) && !TARGET_80387_P (opts->x_target_flags)) { warning (0, "387 instruction set disabled, using SSE arithmetics"); opts->x_ix86_fpmath = FPMATH_SSE; } } } /* For all chips supporting SSE2, -mfpmath=sse performs better than fpmath=387. The second is however default at many targets since the extra 80bit precision of temporaries is considered to be part of ABI. Overwrite the default at least for -ffast-math. TODO: -mfpmath=both seems to produce same performing code with bit smaller binaries. It is however not clear if register allocation is ready for this setting. Also -mfpmath=387 is overall a lot more compact (bout 4-5%) than SSE codegen. We may switch to 387 with -ffast-math for size optimized functions. */ else if (fast_math_flags_set_p (&global_options) && TARGET_SSE2_P (opts->x_ix86_isa_flags)) opts->x_ix86_fpmath = FPMATH_SSE; else opts->x_ix86_fpmath = TARGET_FPMATH_DEFAULT_P (opts->x_ix86_isa_flags); /* Use external vectorized library in vectorizing intrinsics. */ if (opts_set->x_ix86_veclibabi_type) switch (opts->x_ix86_veclibabi_type) { case ix86_veclibabi_type_svml: ix86_veclib_handler = &ix86_veclibabi_svml; break; case ix86_veclibabi_type_acml: ix86_veclib_handler = &ix86_veclibabi_acml; break; default: gcc_unreachable (); } if (ix86_tune_features [X86_TUNE_ACCUMULATE_OUTGOING_ARGS] && !(opts_set->x_target_flags & MASK_ACCUMULATE_OUTGOING_ARGS)) opts->x_target_flags |= MASK_ACCUMULATE_OUTGOING_ARGS; /* If stack probes are required, the space used for large function arguments on the stack must also be probed, so enable -maccumulate-outgoing-args so this happens in the prologue. */ if (TARGET_STACK_PROBE_P (opts->x_target_flags) && !(opts->x_target_flags & MASK_ACCUMULATE_OUTGOING_ARGS)) { if (opts_set->x_target_flags & MASK_ACCUMULATE_OUTGOING_ARGS) warning (0, main_args_p ? G_("stack probing requires %<-maccumulate-outgoing-args%> " "for correctness") : G_("stack probing requires " "% for " "correctness")); opts->x_target_flags |= MASK_ACCUMULATE_OUTGOING_ARGS; } /* Stack realignment without -maccumulate-outgoing-args requires %ebp, so enable -maccumulate-outgoing-args when %ebp is fixed. */ if (fixed_regs[BP_REG] && !(opts->x_target_flags & MASK_ACCUMULATE_OUTGOING_ARGS)) { if (opts_set->x_target_flags & MASK_ACCUMULATE_OUTGOING_ARGS) warning (0, main_args_p ? G_("fixed ebp register requires " "%<-maccumulate-outgoing-args%>") : G_("fixed ebp register requires " "%")); opts->x_target_flags |= MASK_ACCUMULATE_OUTGOING_ARGS; } /* Figure out what ASM_GENERATE_INTERNAL_LABEL builds as a prefix. */ { char *p; ASM_GENERATE_INTERNAL_LABEL (internal_label_prefix, "LX", 0); p = strchr (internal_label_prefix, 'X'); internal_label_prefix_len = p - internal_label_prefix; *p = '\0'; } /* When scheduling description is not available, disable scheduler pass so it won't slow down the compilation and make x87 code slower. */ if (!TARGET_SCHEDULE) opts->x_flag_schedule_insns_after_reload = opts->x_flag_schedule_insns = 0; SET_OPTION_IF_UNSET (opts, opts_set, param_simultaneous_prefetches, ix86_tune_cost->simultaneous_prefetches); SET_OPTION_IF_UNSET (opts, opts_set, param_l1_cache_line_size, ix86_tune_cost->prefetch_block); SET_OPTION_IF_UNSET (opts, opts_set, param_l1_cache_size, ix86_tune_cost->l1_cache_size); SET_OPTION_IF_UNSET (opts, opts_set, param_l2_cache_size, ix86_tune_cost->l2_cache_size); /* 64B is the accepted value for these for all x86. */ SET_OPTION_IF_UNSET (&global_options, &global_options_set, param_destruct_interfere_size, 64); SET_OPTION_IF_UNSET (&global_options, &global_options_set, param_construct_interfere_size, 64); /* Enable sw prefetching at -O3 for CPUS that prefetching is helpful. */ if (opts->x_flag_prefetch_loop_arrays < 0 && HAVE_prefetch && (opts->x_optimize >= 3 || opts->x_flag_profile_use) && !opts->x_optimize_size && TARGET_SOFTWARE_PREFETCHING_BENEFICIAL) opts->x_flag_prefetch_loop_arrays = 1; /* If using typedef char *va_list, signal that __builtin_va_start (&ap, 0) can be opts->x_optimized to ap = __builtin_next_arg (0). */ if (!TARGET_64BIT_P (opts->x_ix86_isa_flags) && !opts->x_flag_split_stack) targetm.expand_builtin_va_start = NULL; #ifdef USE_IX86_CLD /* Use -mcld by default for 32-bit code if configured with --enable-cld. */ if (!TARGET_64BIT_P (opts->x_ix86_isa_flags)) opts->x_target_flags |= MASK_CLD & ~opts_set->x_target_flags; #endif /* Set the default value for -mfentry. */ if (!opts_set->x_flag_fentry) opts->x_flag_fentry = TARGET_SEH; else { if (!TARGET_64BIT_P (opts->x_ix86_isa_flags) && opts->x_flag_pic && opts->x_flag_fentry) sorry ("%<-mfentry%> isn%'t supported for 32-bit in combination " "with %<-fpic%>"); else if (TARGET_SEH && !opts->x_flag_fentry) sorry ("%<-mno-fentry%> isn%'t compatible with SEH"); } if (TARGET_SEH && TARGET_CALL_MS2SYSV_XLOGUES) sorry ("%<-mcall-ms2sysv-xlogues%> isn%'t currently supported with SEH"); if (!(opts_set->x_target_flags & MASK_VZEROUPPER) && TARGET_EMIT_VZEROUPPER) opts->x_target_flags |= MASK_VZEROUPPER; if (!(opts_set->x_target_flags & MASK_STV)) opts->x_target_flags |= MASK_STV; /* Disable STV if -mpreferred-stack-boundary={2,3} or -mincoming-stack-boundary={2,3} or -mstackrealign - the needed stack realignment will be extra cost the pass doesn't take into account and the pass can't realign the stack. */ if (ix86_preferred_stack_boundary < 128 || ix86_incoming_stack_boundary < 128 || opts->x_ix86_force_align_arg_pointer) opts->x_target_flags &= ~MASK_STV; if (!ix86_tune_features[X86_TUNE_AVX256_UNALIGNED_LOAD_OPTIMAL] && !(opts_set->x_target_flags & MASK_AVX256_SPLIT_UNALIGNED_LOAD)) opts->x_target_flags |= MASK_AVX256_SPLIT_UNALIGNED_LOAD; else if (!main_args_p && ix86_tune_features[X86_TUNE_AVX256_UNALIGNED_LOAD_OPTIMAL]) opts->x_target_flags &= ~MASK_AVX256_SPLIT_UNALIGNED_LOAD; if (!ix86_tune_features[X86_TUNE_AVX256_UNALIGNED_STORE_OPTIMAL] && !(opts_set->x_target_flags & MASK_AVX256_SPLIT_UNALIGNED_STORE)) opts->x_target_flags |= MASK_AVX256_SPLIT_UNALIGNED_STORE; else if (!main_args_p && ix86_tune_features[X86_TUNE_AVX256_UNALIGNED_STORE_OPTIMAL]) opts->x_target_flags &= ~MASK_AVX256_SPLIT_UNALIGNED_STORE; /* Enable 128-bit AVX instruction generation for the auto-vectorizer. */ if (ix86_tune_features[X86_TUNE_AVX128_OPTIMAL] && (opts_set->x_prefer_vector_width_type == PVW_NONE)) opts->x_prefer_vector_width_type = PVW_AVX128; /* Use 256-bit AVX instruction generation in the auto-vectorizer. */ if (ix86_tune_features[X86_TUNE_AVX256_OPTIMAL] && (opts_set->x_prefer_vector_width_type == PVW_NONE)) opts->x_prefer_vector_width_type = PVW_AVX256; if (opts_set->x_ix86_move_max == PVW_NONE) { /* Set the maximum number of bits can be moved from memory to memory efficiently. */ if (ix86_tune_features[X86_TUNE_AVX512_MOVE_BY_PIECES]) opts->x_ix86_move_max = PVW_AVX512; else if (ix86_tune_features[X86_TUNE_AVX256_MOVE_BY_PIECES]) opts->x_ix86_move_max = PVW_AVX256; else { opts->x_ix86_move_max = opts->x_prefer_vector_width_type; if (opts_set->x_ix86_move_max == PVW_NONE) { if (TARGET_AVX512F_P (opts->x_ix86_isa_flags)) opts->x_ix86_move_max = PVW_AVX512; else opts->x_ix86_move_max = PVW_AVX128; } } } if (opts_set->x_ix86_store_max == PVW_NONE) { /* Set the maximum number of bits can be stored to memory efficiently. */ if (ix86_tune_features[X86_TUNE_AVX512_STORE_BY_PIECES]) opts->x_ix86_store_max = PVW_AVX512; else if (ix86_tune_features[X86_TUNE_AVX256_STORE_BY_PIECES]) opts->x_ix86_store_max = PVW_AVX256; else { opts->x_ix86_store_max = opts->x_prefer_vector_width_type; if (opts_set->x_ix86_store_max == PVW_NONE) { if (TARGET_AVX512F_P (opts->x_ix86_isa_flags)) opts->x_ix86_store_max = PVW_AVX512; else opts->x_ix86_store_max = PVW_AVX128; } } } if (opts->x_ix86_recip_name) { char *p = ASTRDUP (opts->x_ix86_recip_name); char *q; unsigned int mask; bool invert; while ((q = strtok (p, ",")) != NULL) { p = NULL; if (*q == '!') { invert = true; q++; } else invert = false; if (!strcmp (q, "default")) mask = RECIP_MASK_ALL; else { for (i = 0; i < ARRAY_SIZE (recip_options); i++) if (!strcmp (q, recip_options[i].string)) { mask = recip_options[i].mask; break; } if (i == ARRAY_SIZE (recip_options)) { error ("unknown option for %<-mrecip=%s%>", q); invert = false; mask = RECIP_MASK_NONE; } } opts->x_recip_mask_explicit |= mask; if (invert) opts->x_recip_mask &= ~mask; else opts->x_recip_mask |= mask; } } if (TARGET_RECIP_P (opts->x_target_flags)) opts->x_recip_mask |= RECIP_MASK_ALL & ~opts->x_recip_mask_explicit; else if (opts_set->x_target_flags & MASK_RECIP) opts->x_recip_mask &= ~(RECIP_MASK_ALL & ~opts->x_recip_mask_explicit); /* Default long double to 64-bit for 32-bit Bionic and to __float128 for 64-bit Bionic. Also default long double to 64-bit for Intel MCU psABI. */ if ((TARGET_HAS_BIONIC || TARGET_IAMCU) && !(opts_set->x_target_flags & (MASK_LONG_DOUBLE_64 | MASK_LONG_DOUBLE_128))) opts->x_target_flags |= (TARGET_64BIT ? MASK_LONG_DOUBLE_128 : MASK_LONG_DOUBLE_64); /* Only one of them can be active. */ gcc_assert ((opts->x_target_flags & MASK_LONG_DOUBLE_64) == 0 || (opts->x_target_flags & MASK_LONG_DOUBLE_128) == 0); /* Handle stack protector */ if (!opts_set->x_ix86_stack_protector_guard) { #ifdef TARGET_THREAD_SSP_OFFSET if (!TARGET_HAS_BIONIC) opts->x_ix86_stack_protector_guard = SSP_TLS; else #endif opts->x_ix86_stack_protector_guard = SSP_GLOBAL; } if (opts_set->x_ix86_stack_protector_guard_offset_str) { char *endp; const char *str = opts->x_ix86_stack_protector_guard_offset_str; errno = 0; int64_t offset; #if defined(INT64_T_IS_LONG) offset = strtol (str, &endp, 0); #else offset = strtoll (str, &endp, 0); #endif if (!*str || *endp || errno) error ("%qs is not a valid number " "in %<-mstack-protector-guard-offset=%>", str); if (!IN_RANGE (offset, HOST_WIDE_INT_C (-0x80000000), HOST_WIDE_INT_C (0x7fffffff))) error ("%qs is not a valid offset " "in %<-mstack-protector-guard-offset=%>", str); opts->x_ix86_stack_protector_guard_offset = offset; } #ifdef TARGET_THREAD_SSP_OFFSET else opts->x_ix86_stack_protector_guard_offset = TARGET_THREAD_SSP_OFFSET; #endif if (opts_set->x_ix86_stack_protector_guard_reg_str) { const char *str = opts->x_ix86_stack_protector_guard_reg_str; addr_space_t seg = ADDR_SPACE_GENERIC; /* Discard optional register prefix. */ if (str[0] == '%') str++; if (strlen (str) == 2 && str[1] == 's') { if (str[0] == 'f') seg = ADDR_SPACE_SEG_FS; else if (str[0] == 'g') seg = ADDR_SPACE_SEG_GS; } if (seg == ADDR_SPACE_GENERIC) error ("%qs is not a valid base register " "in %<-mstack-protector-guard-reg=%>", opts->x_ix86_stack_protector_guard_reg_str); opts->x_ix86_stack_protector_guard_reg = seg; } else { opts->x_ix86_stack_protector_guard_reg = DEFAULT_TLS_SEG_REG; /* The kernel uses a different segment register for performance reasons; a system call would not have to trash the userspace segment register, which would be expensive. */ if (opts->x_ix86_cmodel == CM_KERNEL) opts->x_ix86_stack_protector_guard_reg = ADDR_SPACE_SEG_GS; } /* Handle -mmemcpy-strategy= and -mmemset-strategy= */ if (opts->x_ix86_tune_memcpy_strategy) { char *str = xstrdup (opts->x_ix86_tune_memcpy_strategy); ix86_parse_stringop_strategy_string (str, false); free (str); } if (opts->x_ix86_tune_memset_strategy) { char *str = xstrdup (opts->x_ix86_tune_memset_strategy); ix86_parse_stringop_strategy_string (str, true); free (str); } /* Save the initial options in case the user does function specific options. */ if (main_args_p) { opts->x_ix86_excess_precision = opts->x_flag_excess_precision; opts->x_ix86_unsafe_math_optimizations = opts->x_flag_unsafe_math_optimizations; target_option_default_node = target_option_current_node = build_target_option_node (opts, opts_set); } if (opts->x_flag_cf_protection != CF_NONE) { if ((opts->x_flag_cf_protection & CF_BRANCH) == CF_BRANCH && !TARGET_64BIT && !TARGET_CMOV) error ("%<-fcf-protection%> is not compatible with this target"); opts->x_flag_cf_protection = (cf_protection_level) (opts->x_flag_cf_protection | CF_SET); } if (ix86_tune_features [X86_TUNE_AVOID_256FMA_CHAINS]) SET_OPTION_IF_UNSET (opts, opts_set, param_avoid_fma_max_bits, 512); else if (ix86_tune_features [X86_TUNE_AVOID_256FMA_CHAINS]) SET_OPTION_IF_UNSET (opts, opts_set, param_avoid_fma_max_bits, 256); else if (ix86_tune_features [X86_TUNE_AVOID_128FMA_CHAINS]) SET_OPTION_IF_UNSET (opts, opts_set, param_avoid_fma_max_bits, 128); /* PR86952: jump table usage with retpolines is slow. The PR provides some numbers about the slowness. */ if (ix86_indirect_branch != indirect_branch_keep) SET_OPTION_IF_UNSET (opts, opts_set, flag_jump_tables, 0); SET_OPTION_IF_UNSET (opts, opts_set, param_ira_consider_dup_in_all_alts, 0); /* Fully masking the main or the epilogue vectorized loop is not profitable generally so leave it disabled until we get more fine grained control & costing. */ SET_OPTION_IF_UNSET (opts, opts_set, param_vect_partial_vector_usage, 0); return true; } /* Implement the TARGET_OPTION_OVERRIDE hook. */ void ix86_option_override (void) { ix86_option_override_internal (true, &global_options, &global_options_set); } /* Remember the last target of ix86_set_current_function. */ static GTY(()) tree ix86_previous_fndecl; /* Set targets globals to the default (or current #pragma GCC target if active). Invalidate ix86_previous_fndecl cache. */ void ix86_reset_previous_fndecl (void) { tree new_tree = target_option_current_node; cl_target_option_restore (&global_options, &global_options_set, 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 (); ix86_previous_fndecl = NULL_TREE; } /* Add target attribute to SIMD clone NODE if needed. */ void ix86_simd_clone_adjust (struct cgraph_node *node) { const char *str = NULL; /* Attributes need to be adjusted for definitions, not declarations. */ if (!node->definition) return; gcc_assert (node->decl == cfun->decl); switch (node->simdclone->vecsize_mangle) { case 'b': if (!TARGET_SSE2) str = "sse2"; break; case 'c': if (TARGET_PREFER_AVX128) { if (!TARGET_AVX) str = "avx,prefer-vector-width=256"; else str = "prefer-vector-width=256"; } else if (!TARGET_AVX) str = "avx"; break; case 'd': if (TARGET_PREFER_AVX128) { if (!TARGET_AVX2) str = "avx2,prefer-vector-width=256"; else str = "prefer-vector-width=256"; } else if (!TARGET_AVX2) str = "avx2"; break; case 'e': if (TARGET_PREFER_AVX256) { if (!TARGET_AVX512F) str = "avx512f,prefer-vector-width=512"; else str = "prefer-vector-width=512"; } else if (!TARGET_AVX512F) str = "avx512f"; break; default: gcc_unreachable (); } if (str == NULL) return; push_cfun (NULL); tree args = build_tree_list (NULL_TREE, build_string (strlen (str), str)); bool ok = ix86_valid_target_attribute_p (node->decl, NULL, args, 0); gcc_assert (ok); pop_cfun (); ix86_reset_previous_fndecl (); ix86_set_current_function (node->decl); } /* Set the func_type field from the function FNDECL. */ static void ix86_set_func_type (tree fndecl) { if (cfun->machine->func_type == TYPE_UNKNOWN) { if (lookup_attribute ("interrupt", TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) { if (ix86_function_naked (fndecl)) error_at (DECL_SOURCE_LOCATION (fndecl), "interrupt and naked attributes are not compatible"); int nargs = 0; for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg)) nargs++; cfun->machine->no_caller_saved_registers = true; cfun->machine->func_type = nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT; ix86_optimize_mode_switching[X86_DIRFLAG] = 1; /* Only dwarf2out.cc can handle -WORD(AP) as a pointer argument. */ if (write_symbols != NO_DEBUG && write_symbols != DWARF2_DEBUG) sorry ("only DWARF debug format is supported for interrupt " "service routine"); } else { cfun->machine->func_type = TYPE_NORMAL; if (lookup_attribute ("no_caller_saved_registers", TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) cfun->machine->no_caller_saved_registers = true; } } } /* Set the indirect_branch_type field from the function FNDECL. */ static void ix86_set_indirect_branch_type (tree fndecl) { if (cfun->machine->indirect_branch_type == indirect_branch_unset) { tree attr = lookup_attribute ("indirect_branch", DECL_ATTRIBUTES (fndecl)); if (attr != NULL) { tree args = TREE_VALUE (attr); if (args == NULL) gcc_unreachable (); tree cst = TREE_VALUE (args); if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0) cfun->machine->indirect_branch_type = indirect_branch_keep; else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0) cfun->machine->indirect_branch_type = indirect_branch_thunk; else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0) cfun->machine->indirect_branch_type = indirect_branch_thunk_inline; else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0) cfun->machine->indirect_branch_type = indirect_branch_thunk_extern; else gcc_unreachable (); } else cfun->machine->indirect_branch_type = ix86_indirect_branch; /* -mcmodel=large is not compatible with -mindirect-branch=thunk nor -mindirect-branch=thunk-extern. */ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC) && ((cfun->machine->indirect_branch_type == indirect_branch_thunk_extern) || (cfun->machine->indirect_branch_type == indirect_branch_thunk))) error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not " "compatible", ((cfun->machine->indirect_branch_type == indirect_branch_thunk_extern) ? "thunk-extern" : "thunk")); if (cfun->machine->indirect_branch_type != indirect_branch_keep && (cfun->machine->indirect_branch_type != indirect_branch_thunk_extern) && (flag_cf_protection & CF_RETURN)) error ("%<-mindirect-branch%> and %<-fcf-protection%> are not " "compatible"); } if (cfun->machine->function_return_type == indirect_branch_unset) { tree attr = lookup_attribute ("function_return", DECL_ATTRIBUTES (fndecl)); if (attr != NULL) { tree args = TREE_VALUE (attr); if (args == NULL) gcc_unreachable (); tree cst = TREE_VALUE (args); if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0) cfun->machine->function_return_type = indirect_branch_keep; else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0) cfun->machine->function_return_type = indirect_branch_thunk; else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0) cfun->machine->function_return_type = indirect_branch_thunk_inline; else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0) cfun->machine->function_return_type = indirect_branch_thunk_extern; else gcc_unreachable (); } else cfun->machine->function_return_type = ix86_function_return; /* -mcmodel=large is not compatible with -mfunction-return=thunk nor -mfunction-return=thunk-extern. */ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC) && ((cfun->machine->function_return_type == indirect_branch_thunk_extern) || (cfun->machine->function_return_type == indirect_branch_thunk))) error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not " "compatible", ((cfun->machine->function_return_type == indirect_branch_thunk_extern) ? "thunk-extern" : "thunk")); if (cfun->machine->function_return_type != indirect_branch_keep && (cfun->machine->function_return_type != indirect_branch_thunk_extern) && (flag_cf_protection & CF_RETURN)) error ("%<-mfunction-return%> and %<-fcf-protection%> are not " "compatible"); } } /* 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. */ void ix86_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 == ix86_previous_fndecl) { /* There may be 2 function bodies for the same function FNDECL, one is extern inline and one isn't. Call ix86_set_func_type to set the func_type field. */ if (fndecl != NULL_TREE) { ix86_set_func_type (fndecl); ix86_set_indirect_branch_type (fndecl); } return; } tree old_tree; if (ix86_previous_fndecl == NULL_TREE) old_tree = target_option_current_node; else if (DECL_FUNCTION_SPECIFIC_TARGET (ix86_previous_fndecl)) old_tree = DECL_FUNCTION_SPECIFIC_TARGET (ix86_previous_fndecl); else old_tree = target_option_default_node; if (fndecl == NULL_TREE) { if (old_tree != target_option_current_node) ix86_reset_previous_fndecl (); return; } ix86_set_func_type (fndecl); ix86_set_indirect_branch_type (fndecl); tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); if (new_tree == NULL_TREE) new_tree = target_option_default_node; bool fp_flag_change = (flag_unsafe_math_optimizations != TREE_TARGET_OPTION (new_tree)->x_ix86_unsafe_math_optimizations || (flag_excess_precision != TREE_TARGET_OPTION (new_tree)->x_ix86_excess_precision)); if (old_tree != new_tree || fp_flag_change) { cl_target_option_restore (&global_options, &global_options_set, TREE_TARGET_OPTION (new_tree)); if (fp_flag_change) { ix86_excess_precision = flag_excess_precision; ix86_unsafe_math_optimizations = flag_unsafe_math_optimizations; DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_tree = build_target_option_node (&global_options, &global_options_set); } 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 (); } ix86_previous_fndecl = fndecl; static bool prev_no_caller_saved_registers; /* 64-bit MS and SYSV ABI have different set of call used registers. Avoid expensive re-initialization of init_regs each time we switch function context. */ if (TARGET_64BIT && (call_used_or_fixed_reg_p (SI_REG) == (cfun->machine->call_abi == MS_ABI))) reinit_regs (); /* Need to re-initialize init_regs if caller-saved registers are changed. */ else if (prev_no_caller_saved_registers != cfun->machine->no_caller_saved_registers) reinit_regs (); if (cfun->machine->func_type != TYPE_NORMAL || cfun->machine->no_caller_saved_registers) { /* Don't allow SSE, MMX nor x87 instructions since they may change processor state. */ const char *isa; if (TARGET_SSE) isa = "SSE"; else if (TARGET_MMX) isa = "MMX/3Dnow"; else if (TARGET_80387) isa = "80387"; else isa = NULL; if (isa != NULL) { if (cfun->machine->func_type != TYPE_NORMAL) sorry (cfun->machine->func_type == TYPE_EXCEPTION ? G_("%s instructions aren%'t allowed in an" " exception service routine") : G_("%s instructions aren%'t allowed in an" " interrupt service routine"), isa); else sorry ("%s instructions aren%'t allowed in a function with " "the % attribute", isa); /* Don't issue the same error twice. */ cfun->machine->func_type = TYPE_NORMAL; cfun->machine->no_caller_saved_registers = false; } } prev_no_caller_saved_registers = cfun->machine->no_caller_saved_registers; } /* Implement the TARGET_OFFLOAD_OPTIONS hook. */ char * ix86_offload_options (void) { if (TARGET_LP64) return xstrdup ("-foffload-abi=lp64"); return xstrdup ("-foffload-abi=ilp32"); } /* Handle "cdecl", "stdcall", "fastcall", "regparm", "thiscall", and "sseregparm" calling convention attributes; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_cconv_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning (OPT_Wattributes, "%qE attribute only applies to functions", name); *no_add_attrs = true; return NULL_TREE; } /* Can combine regparm with all attributes but fastcall, and thiscall. */ if (is_attribute_p ("regparm", name)) { tree cst; if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and regparm attributes are not compatible"); } if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node))) { error ("regparam and thiscall attributes are not compatible"); } cst = TREE_VALUE (args); if (TREE_CODE (cst) != INTEGER_CST) { warning (OPT_Wattributes, "%qE attribute requires an integer constant argument", name); *no_add_attrs = true; } else if (compare_tree_int (cst, REGPARM_MAX) > 0) { warning (OPT_Wattributes, "argument to %qE attribute larger than %d", name, REGPARM_MAX); *no_add_attrs = true; } return NULL_TREE; } if (TARGET_64BIT) { /* Do not warn when emulating the MS ABI. */ if ((TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE) || ix86_function_type_abi (*node) != MS_ABI) warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; return NULL_TREE; } /* Can combine fastcall with stdcall (redundant) and sseregparm. */ if (is_attribute_p ("fastcall", name)) { if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node))) { error ("fastcall and cdecl attributes are not compatible"); } if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and stdcall attributes are not compatible"); } if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (*node))) { error ("fastcall and regparm attributes are not compatible"); } if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and thiscall attributes are not compatible"); } } /* Can combine stdcall with fastcall (redundant), regparm and sseregparm. */ else if (is_attribute_p ("stdcall", name)) { if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node))) { error ("stdcall and cdecl attributes are not compatible"); } if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) { error ("stdcall and fastcall attributes are not compatible"); } if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node))) { error ("stdcall and thiscall attributes are not compatible"); } } /* Can combine cdecl with regparm and sseregparm. */ else if (is_attribute_p ("cdecl", name)) { if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node))) { error ("stdcall and cdecl attributes are not compatible"); } if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and cdecl attributes are not compatible"); } if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node))) { error ("cdecl and thiscall attributes are not compatible"); } } else if (is_attribute_p ("thiscall", name)) { if (TREE_CODE (*node) != METHOD_TYPE && pedantic) warning (OPT_Wattributes, "%qE attribute is used for non-class method", name); if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node))) { error ("stdcall and thiscall attributes are not compatible"); } if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and thiscall attributes are not compatible"); } if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node))) { error ("cdecl and thiscall attributes are not compatible"); } } /* Can combine sseregparm with all attributes. */ return NULL_TREE; } #ifndef CHECK_STACK_LIMIT #define CHECK_STACK_LIMIT (-1) #endif /* The transactional memory builtins are implicitly regparm or fastcall depending on the ABI. Override the generic do-nothing attribute that these builtins were declared with, and replace it with one of the two attributes that we expect elsewhere. */ static tree ix86_handle_tm_regparm_attribute (tree *node, tree, tree, int flags, bool *no_add_attrs) { tree alt; /* In no case do we want to add the placeholder attribute. */ *no_add_attrs = true; /* The 64-bit ABI is unchanged for transactional memory. */ if (TARGET_64BIT) return NULL_TREE; /* ??? Is there a better way to validate 32-bit windows? We have cfun->machine->call_abi, but that seems to be set only for 64-bit. */ if (CHECK_STACK_LIMIT > 0) alt = tree_cons (get_identifier ("fastcall"), NULL, NULL); else { alt = tree_cons (NULL, build_int_cst (NULL, 2), NULL); alt = tree_cons (get_identifier ("regparm"), alt, NULL); } decl_attributes (node, alt, flags); return NULL_TREE; } /* Handle a "force_align_arg_pointer" attribute. */ static tree ix86_handle_force_align_arg_pointer_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning (OPT_Wattributes, "%qE attribute only applies to functions", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "ms_struct" or "gcc_struct" attribute; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_struct_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { tree *type = NULL; if (DECL_P (*node)) { if (TREE_CODE (*node) == TYPE_DECL) type = &TREE_TYPE (*node); } else type = node; if (!(type && RECORD_OR_UNION_TYPE_P (*type))) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } else if ((is_attribute_p ("ms_struct", name) && lookup_attribute ("gcc_struct", TYPE_ATTRIBUTES (*type))) || ((is_attribute_p ("gcc_struct", name) && lookup_attribute ("ms_struct", TYPE_ATTRIBUTES (*type))))) { warning (OPT_Wattributes, "%qE incompatible attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "callee_pop_aggregate_return" attribute; arguments as in struct attribute_spec handler. */ static tree ix86_handle_callee_pop_aggregate_return (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning (OPT_Wattributes, "%qE attribute only applies to functions", name); *no_add_attrs = true; return NULL_TREE; } if (TARGET_64BIT) { warning (OPT_Wattributes, "%qE attribute only available for 32-bit", name); *no_add_attrs = true; return NULL_TREE; } if (is_attribute_p ("callee_pop_aggregate_return", name)) { tree cst; cst = TREE_VALUE (args); if (TREE_CODE (cst) != INTEGER_CST) { warning (OPT_Wattributes, "%qE attribute requires an integer constant argument", name); *no_add_attrs = true; } else if (compare_tree_int (cst, 0) != 0 && compare_tree_int (cst, 1) != 0) { warning (OPT_Wattributes, "argument to %qE attribute is neither zero, nor one", name); *no_add_attrs = true; } return NULL_TREE; } return NULL_TREE; } /* Handle a "ms_abi" or "sysv" attribute; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_abi_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning (OPT_Wattributes, "%qE attribute only applies to functions", name); *no_add_attrs = true; return NULL_TREE; } /* Can combine regparm with all attributes but fastcall. */ if (is_attribute_p ("ms_abi", name)) { if (lookup_attribute ("sysv_abi", TYPE_ATTRIBUTES (*node))) { error ("%qs and %qs attributes are not compatible", "ms_abi", "sysv_abi"); } return NULL_TREE; } else if (is_attribute_p ("sysv_abi", name)) { if (lookup_attribute ("ms_abi", TYPE_ATTRIBUTES (*node))) { error ("%qs and %qs attributes are not compatible", "ms_abi", "sysv_abi"); } return NULL_TREE; } return NULL_TREE; } static tree ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute only applies to functions", name); *no_add_attrs = true; } if (is_attribute_p ("indirect_branch", name)) { tree cst = TREE_VALUE (args); if (TREE_CODE (cst) != STRING_CST) { warning (OPT_Wattributes, "%qE attribute requires a string constant argument", name); *no_add_attrs = true; } else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0 && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0 && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) { warning (OPT_Wattributes, "argument to %qE attribute is not " "(keep|thunk|thunk-inline|thunk-extern)", name); *no_add_attrs = true; } } if (is_attribute_p ("function_return", name)) { tree cst = TREE_VALUE (args); if (TREE_CODE (cst) != STRING_CST) { warning (OPT_Wattributes, "%qE attribute requires a string constant argument", name); *no_add_attrs = true; } else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0 && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0 && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) { warning (OPT_Wattributes, "argument to %qE attribute is not " "(keep|thunk|thunk-inline|thunk-extern)", name); *no_add_attrs = true; } } return NULL_TREE; } static tree ix86_handle_no_caller_saved_registers_attribute (tree *, tree, tree, int, bool *) { return NULL_TREE; } static tree ix86_handle_interrupt_attribute (tree *node, tree, tree, int, bool *) { /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet, but the function type contains args and return type data. */ tree func_type = *node; tree return_type = TREE_TYPE (func_type); int nargs = 0; tree current_arg_type = TYPE_ARG_TYPES (func_type); while (current_arg_type && ! VOID_TYPE_P (TREE_VALUE (current_arg_type))) { if (nargs == 0) { if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type))) error ("interrupt service routine should have a pointer " "as the first argument"); } else if (nargs == 1) { if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode) error ("interrupt service routine should have %qs " "as the second argument", TARGET_64BIT ? (TARGET_X32 ? "unsigned long long int" : "unsigned long int") : "unsigned int"); } nargs++; current_arg_type = TREE_CHAIN (current_arg_type); } if (!nargs || nargs > 2) error ("interrupt service routine can only have a pointer argument " "and an optional integer argument"); if (! VOID_TYPE_P (return_type)) error ("interrupt service routine must return %"); return NULL_TREE; } /* Handle fentry_name / fentry_section attribute. */ static tree ix86_handle_fentry_name (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL && TREE_CODE (TREE_VALUE (args)) == STRING_CST) /* Do nothing else, just set the attribute. We'll get at it later with lookup_attribute. */ ; else { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "nodirect_extern_access" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_nodirect_extern_access_attribute (tree *pnode, tree name, tree ARG_UNUSED (args), int ARG_UNUSED (flags), bool *no_add_attrs) { tree node = *pnode; if (VAR_OR_FUNCTION_DECL_P (node)) { if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL && !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node)) { warning (OPT_Wattributes, "%qE attribute have effect only on public objects", name); *no_add_attrs = true; } } else { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Table of valid machine attributes. */ const struct attribute_spec ix86_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, affects_type_identity, handler, exclude } */ /* Stdcall attribute says callee is responsible for popping arguments if they are not variable. */ { "stdcall", 0, 0, false, true, true, true, ix86_handle_cconv_attribute, NULL }, /* Fastcall attribute says callee is responsible for popping arguments if they are not variable. */ { "fastcall", 0, 0, false, true, true, true, ix86_handle_cconv_attribute, NULL }, /* Thiscall attribute says callee is responsible for popping arguments if they are not variable. */ { "thiscall", 0, 0, false, true, true, true, ix86_handle_cconv_attribute, NULL }, /* Cdecl attribute says the callee is a normal C declaration */ { "cdecl", 0, 0, false, true, true, true, ix86_handle_cconv_attribute, NULL }, /* Regparm attribute specifies how many integer arguments are to be passed in registers. */ { "regparm", 1, 1, false, true, true, true, ix86_handle_cconv_attribute, NULL }, /* Sseregparm attribute says we are using x86_64 calling conventions for FP arguments. */ { "sseregparm", 0, 0, false, true, true, true, ix86_handle_cconv_attribute, NULL }, /* The transactional memory builtins are implicitly regparm or fastcall depending on the ABI. Override the generic do-nothing attribute that these builtins were declared with. */ { "*tm regparm", 0, 0, false, true, true, true, ix86_handle_tm_regparm_attribute, NULL }, /* force_align_arg_pointer says this function realigns the stack at entry. */ { "force_align_arg_pointer", 0, 0, false, true, true, false, ix86_handle_force_align_arg_pointer_attribute, NULL }, #if TARGET_DLLIMPORT_DECL_ATTRIBUTES { "dllimport", 0, 0, false, false, false, false, handle_dll_attribute, NULL }, { "dllexport", 0, 0, false, false, false, false, handle_dll_attribute, NULL }, { "shared", 0, 0, true, false, false, false, ix86_handle_shared_attribute, NULL }, #endif { "ms_struct", 0, 0, false, false, false, false, ix86_handle_struct_attribute, NULL }, { "gcc_struct", 0, 0, false, false, false, false, ix86_handle_struct_attribute, NULL }, #ifdef SUBTARGET_ATTRIBUTE_TABLE SUBTARGET_ATTRIBUTE_TABLE, #endif /* ms_abi and sysv_abi calling convention function attributes. */ { "ms_abi", 0, 0, false, true, true, true, ix86_handle_abi_attribute, NULL }, { "sysv_abi", 0, 0, false, true, true, true, ix86_handle_abi_attribute, NULL }, { "ms_abi va_list", 0, 0, false, false, false, false, NULL, NULL }, { "sysv_abi va_list", 0, 0, false, false, false, false, NULL, NULL }, { "ms_hook_prologue", 0, 0, true, false, false, false, ix86_handle_fndecl_attribute, NULL }, { "callee_pop_aggregate_return", 1, 1, false, true, true, true, ix86_handle_callee_pop_aggregate_return, NULL }, { "interrupt", 0, 0, false, true, true, false, ix86_handle_interrupt_attribute, NULL }, { "no_caller_saved_registers", 0, 0, false, true, true, false, ix86_handle_no_caller_saved_registers_attribute, NULL }, { "naked", 0, 0, true, false, false, false, ix86_handle_fndecl_attribute, NULL }, { "indirect_branch", 1, 1, true, false, false, false, ix86_handle_fndecl_attribute, NULL }, { "function_return", 1, 1, true, false, false, false, ix86_handle_fndecl_attribute, NULL }, { "indirect_return", 0, 0, false, true, true, false, NULL, NULL }, { "fentry_name", 1, 1, true, false, false, false, ix86_handle_fentry_name, NULL }, { "fentry_section", 1, 1, true, false, false, false, ix86_handle_fentry_name, NULL }, { "cf_check", 0, 0, true, false, false, false, ix86_handle_fndecl_attribute, NULL }, { "nodirect_extern_access", 0, 0, true, false, false, false, handle_nodirect_extern_access_attribute, NULL }, /* End element. */ { NULL, 0, 0, false, false, false, false, NULL, NULL } }; #include "gt-i386-options.h"