/* Subroutines for loongarch-specific option handling. Copyright (C) 2021-2023 Free Software Foundation, Inc. Contributed by Loongson Ltd. 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 "tm.h" #include "obstack.h" #include "diagnostic-core.h" #include "loongarch-cpu.h" #include "loongarch-opts.h" #include "loongarch-str.h" struct loongarch_target la_target; /* ABI-related configuration. */ #define ABI_COUNT (sizeof(abi_priority_list)/sizeof(struct loongarch_abi)) static const struct loongarch_abi abi_priority_list[] = { {ABI_BASE_LP64D, ABI_EXT_BASE}, {ABI_BASE_LP64F, ABI_EXT_BASE}, {ABI_BASE_LP64S, ABI_EXT_BASE}, }; /* Initialize enabled_abi_types from TM_MULTILIB_LIST. */ #ifdef LA_DISABLE_MULTILIB #define MULTILIB_LIST_LEN 1 #else #define MULTILIB_LIST_LEN (sizeof (tm_multilib_list) / sizeof (int) / 2) static const int tm_multilib_list[] = { TM_MULTILIB_LIST }; #endif static int enabled_abi_types[N_ABI_BASE_TYPES][N_ABI_EXT_TYPES] = { 0 }; #define isa_required(ABI) (abi_minimal_isa[(ABI).base][(ABI).ext]) extern "C" const struct loongarch_isa abi_minimal_isa[N_ABI_BASE_TYPES][N_ABI_EXT_TYPES]; static inline int is_multilib_enabled (struct loongarch_abi abi) { return enabled_abi_types[abi.base][abi.ext]; } static void init_enabled_abi_types () { #ifdef LA_DISABLE_MULTILIB enabled_abi_types[DEFAULT_ABI_BASE][DEFAULT_ABI_EXT] = 1; #else int abi_base, abi_ext; for (unsigned int i = 0; i < MULTILIB_LIST_LEN; i++) { abi_base = tm_multilib_list[i << 1]; abi_ext = tm_multilib_list[(i << 1) + 1]; enabled_abi_types[abi_base][abi_ext] = 1; } #endif } /* Switch masks. */ #undef M #define M(NAME) OPTION_MASK_##NAME const int loongarch_switch_mask[N_SWITCH_TYPES] = { /* SW_SOFT_FLOAT */ M(FORCE_SOFTF), /* SW_SINGLE_FLOAT */ M(FORCE_F32), /* SW_DOUBLE_FLOAT */ M(FORCE_F64), }; #undef M /* String processing. */ static struct obstack msg_obstack; #define APPEND_STRING(STR) obstack_grow (&msg_obstack, STR, strlen(STR)); #define APPEND1(CH) obstack_1grow(&msg_obstack, CH); static const char* abi_str (struct loongarch_abi abi); static const char* isa_str (const struct loongarch_isa *isa, char separator); static const char* arch_str (const struct loongarch_target *target); static const char* multilib_enabled_abi_list (); /* Misc */ static struct loongarch_abi isa_default_abi (const struct loongarch_isa *isa); static int isa_base_compat_p (const struct loongarch_isa *set1, const struct loongarch_isa *set2); static int isa_fpu_compat_p (const struct loongarch_isa *set1, const struct loongarch_isa *set2); static int abi_compat_p (const struct loongarch_isa *isa, struct loongarch_abi abi); static int abi_default_cpu_arch (struct loongarch_abi abi); /* Checking configure-time defaults. */ #ifndef DEFAULT_ABI_BASE #error missing definition of DEFAULT_ABI_BASE in ${tm_defines}. #endif #ifndef DEFAULT_ABI_EXT #error missing definition of DEFAULT_ABI_EXT in ${tm_defines}. #endif #ifndef DEFAULT_CPU_ARCH #error missing definition of DEFAULT_CPU_ARCH in ${tm_defines}. #endif #ifndef DEFAULT_ISA_EXT_FPU #error missing definition of DEFAULT_ISA_EXT_FPU in ${tm_defines}. #endif /* Handle combinations of -m machine option values (see loongarch.opt and loongarch-opts.h). */ void loongarch_config_target (struct loongarch_target *target, HOST_WIDE_INT opt_switches, int opt_arch, int opt_tune, int opt_fpu, int opt_abi_base, int opt_abi_ext, int opt_cmodel, int follow_multilib_list) { struct loongarch_target t; if (!target) return; /* Initialization */ init_enabled_abi_types (); obstack_init (&msg_obstack); struct { int arch, tune, fpu, abi_base, abi_ext, cmodel; } constrained = { M_OPT_ABSENT(opt_arch) ? 0 : 1, M_OPT_ABSENT(opt_tune) ? 0 : 1, M_OPT_ABSENT(opt_fpu) ? 0 : 1, M_OPT_ABSENT(opt_abi_base) ? 0 : 1, M_OPT_ABSENT(opt_abi_ext) ? 0 : 1, M_OPT_ABSENT(opt_cmodel) ? 0 : 1, }; #define on(NAME) ((loongarch_switch_mask[(SW_##NAME)] & opt_switches) \ && (on_switch = (SW_##NAME), 1)) int on_switch; /* 1. Target ABI */ t.abi.base = constrained.abi_base ? opt_abi_base : DEFAULT_ABI_BASE; t.abi.ext = constrained.abi_ext ? opt_abi_ext : DEFAULT_ABI_EXT; /* Extra switch handling. */ if (on (SOFT_FLOAT) || on (SINGLE_FLOAT) || on (DOUBLE_FLOAT)) { switch (on_switch) { case SW_SOFT_FLOAT: opt_fpu = ISA_EXT_NOFPU; break; case SW_SINGLE_FLOAT: opt_fpu = ISA_EXT_FPU32; break; case SW_DOUBLE_FLOAT: opt_fpu = ISA_EXT_FPU64; break; default: gcc_unreachable(); } constrained.fpu = 1; /* The target ISA is not ready yet, but (isa_required (t.abi) + forced fpu) is enough for computing the forced base ABI. */ struct loongarch_isa default_isa = isa_required (t.abi); struct loongarch_isa force_isa = default_isa; struct loongarch_abi force_abi = t.abi; force_isa.fpu = opt_fpu; force_abi.base = isa_default_abi (&force_isa).base; if (constrained.abi_base && (t.abi.base != force_abi.base)) inform (UNKNOWN_LOCATION, "%<-m%s%> overrides %<-m%s=%s%>, adjusting ABI to %qs", loongarch_switch_strings[on_switch], OPTSTR_ABI_BASE, loongarch_abi_base_strings[t.abi.base], abi_str (force_abi)); t.abi.base = force_abi.base; } #ifdef LA_DISABLE_MULTILIB if (follow_multilib_list) if (t.abi.base != DEFAULT_ABI_BASE || t.abi.ext != DEFAULT_ABI_EXT) { static const struct loongarch_abi default_abi = {DEFAULT_ABI_BASE, DEFAULT_ABI_EXT}; warning (0, "ABI changed (%qs to %qs) while multilib is disabled", abi_str (default_abi), abi_str (t.abi)); } #endif /* 2. Target CPU */ t.cpu_arch = constrained.arch ? opt_arch : DEFAULT_CPU_ARCH; t.cpu_tune = constrained.tune ? opt_tune : (constrained.arch ? DEFAULT_CPU_ARCH : DEFAULT_CPU_TUNE); #ifdef __loongarch__ /* For native compilers, gather local CPU information and fill the "CPU_NATIVE" index of arrays defined in loongarch-cpu.c. */ t.cpu_native = fill_native_cpu_config (t.cpu_arch == CPU_NATIVE, t.cpu_tune == CPU_NATIVE); #else if (t.cpu_arch == CPU_NATIVE) fatal_error (UNKNOWN_LOCATION, "%qs does not work on a cross compiler", "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE); else if (t.cpu_tune == CPU_NATIVE) fatal_error (UNKNOWN_LOCATION, "%qs does not work on a cross compiler", "-m" OPTSTR_TUNE "=" STR_CPU_NATIVE); #endif /* 3. Target ISA */ config_target_isa: /* Get default ISA from "-march" or its default value. */ t.isa = loongarch_cpu_default_isa[LARCH_ACTUAL_ARCH]; /* Apply incremental changes. */ /* "-march=native" overrides the default FPU type. */ t.isa.fpu = constrained.fpu ? opt_fpu : ((t.cpu_arch == CPU_NATIVE && constrained.arch) ? t.isa.fpu : DEFAULT_ISA_EXT_FPU); /* 4. ABI-ISA compatibility */ /* Note: - There IS a unique default -march value for each ABI type (config.gcc: triplet -> abi -> default arch). - If the base ABI is incompatible with the default arch, try using the default -march it implies (and mark it as "constrained" this time), then re-apply step 3. */ struct loongarch_abi abi_tmp; const struct loongarch_isa* isa_min; abi_tmp = t.abi; isa_min = &isa_required (abi_tmp); if (isa_base_compat_p (&t.isa, isa_min)); /* OK. */ else if (!constrained.arch) { /* Base architecture can only be implied by -march, so we adjust that first if it is not constrained. */ int fallback_arch = abi_default_cpu_arch (t.abi); if (t.cpu_arch == CPU_NATIVE) warning (0, "your native CPU architecture (%qs) " "does not support %qs ABI, falling back to %<-m%s=%s%>", arch_str (&t), abi_str (t.abi), OPTSTR_ARCH, loongarch_cpu_strings[fallback_arch]); else warning (0, "default CPU architecture (%qs) " "does not support %qs ABI, falling back to %<-m%s=%s%>", arch_str (&t), abi_str (t.abi), OPTSTR_ARCH, loongarch_cpu_strings[fallback_arch]); t.cpu_arch = fallback_arch; constrained.arch = 1; goto config_target_isa; } else if (!constrained.abi_base) { /* If -march is given while -mabi is not, try selecting another base ABI type. */ abi_tmp.base = isa_default_abi (&t.isa).base; } else goto fatal; if (isa_fpu_compat_p (&t.isa, isa_min)); /* OK. */ else if (!constrained.fpu) t.isa.fpu = isa_min->fpu; else if (!constrained.abi_base) /* If -march is compatible with the default ABI while -mfpu is not. */ abi_tmp.base = isa_default_abi (&t.isa).base; else goto fatal; if (0) fatal: fatal_error (UNKNOWN_LOCATION, "unable to implement ABI %qs with instruction set %qs", abi_str (t.abi), isa_str (&t.isa, '/')); /* Using the fallback ABI. */ if (abi_tmp.base != t.abi.base || abi_tmp.ext != t.abi.ext) { /* This flag is only set in the GCC driver. */ if (follow_multilib_list) { /* Continue falling back until we find a feasible ABI type enabled by TM_MULTILIB_LIST. */ if (!is_multilib_enabled (abi_tmp)) { for (unsigned int i = 0; i < ABI_COUNT; i++) { if (is_multilib_enabled (abi_priority_list[i]) && abi_compat_p (&t.isa, abi_priority_list[i])) { abi_tmp = abi_priority_list[i]; warning (0, "ABI %qs cannot be implemented due to " "limited instruction set %qs, " "falling back to %qs", abi_str (t.abi), isa_str (&t.isa, '/'), abi_str (abi_tmp)); goto fallback; } } /* Otherwise, keep using abi_tmp with a warning. */ #ifdef LA_DISABLE_MULTILIB warning (0, "instruction set %qs cannot implement " "default ABI %qs, falling back to %qs", isa_str (&t.isa, '/'), abi_str (t.abi), abi_str (abi_tmp)); #else warning (0, "no multilib-enabled ABI (%qs) can be implemented " "with instruction set %qs, falling back to %qs", multilib_enabled_abi_list (), isa_str (&t.isa, '/'), abi_str (abi_tmp)); #endif } } fallback: t.abi = abi_tmp; } else if (follow_multilib_list) { if (!is_multilib_enabled (t.abi)) { inform (UNKNOWN_LOCATION, "ABI %qs is not enabled at configure-time, " "the linker might report an error", abi_str (t.abi)); inform (UNKNOWN_LOCATION, "ABI with startfiles: %s", multilib_enabled_abi_list ()); } } /* 5. Target code model */ t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL; switch (t.cmodel) { case CMODEL_TINY: case CMODEL_TINY_STATIC: case CMODEL_LARGE: warning (0, "%qs is not supported, now cmodel is set to %qs", loongarch_cmodel_strings[t.cmodel], "normal"); t.cmodel = CMODEL_NORMAL; break; case CMODEL_NORMAL: case CMODEL_MEDIUM: case CMODEL_EXTREME: break; default: gcc_unreachable (); } /* Cleanup and return. */ obstack_free (&msg_obstack, NULL); *target = t; } /* Returns the default ABI for the given instruction set. */ static inline struct loongarch_abi isa_default_abi (const struct loongarch_isa *isa) { struct loongarch_abi abi; switch (isa->fpu) { case ISA_EXT_FPU64: if (isa->base == ISA_BASE_LA64V100) abi.base = ABI_BASE_LP64D; break; case ISA_EXT_FPU32: if (isa->base == ISA_BASE_LA64V100) abi.base = ABI_BASE_LP64F; break; case ISA_EXT_NOFPU: if (isa->base == ISA_BASE_LA64V100) abi.base = ABI_BASE_LP64S; break; default: gcc_unreachable (); } abi.ext = ABI_EXT_BASE; return abi; } /* Check if set2 is a subset of set1. */ static inline int isa_base_compat_p (const struct loongarch_isa *set1, const struct loongarch_isa *set2) { switch (set2->base) { case ISA_BASE_LA64V100: return (set1->base == ISA_BASE_LA64V100); default: gcc_unreachable (); } } static inline int isa_fpu_compat_p (const struct loongarch_isa *set1, const struct loongarch_isa *set2) { switch (set2->fpu) { case ISA_EXT_FPU64: return set1->fpu == ISA_EXT_FPU64; case ISA_EXT_FPU32: return set1->fpu == ISA_EXT_FPU32 || set1->fpu == ISA_EXT_FPU64; case ISA_EXT_NOFPU: return 1; default: gcc_unreachable (); } } static inline int abi_compat_p (const struct loongarch_isa *isa, struct loongarch_abi abi) { int compatible = 1; const struct loongarch_isa *isa2 = &isa_required (abi); /* Append conditionals for new ISA components below. */ compatible = compatible && isa_base_compat_p (isa, isa2); compatible = compatible && isa_fpu_compat_p (isa, isa2); return compatible; } /* The behavior of this function should be consistent with config.gcc. */ static inline int abi_default_cpu_arch (struct loongarch_abi abi) { switch (abi.base) { case ABI_BASE_LP64D: case ABI_BASE_LP64F: case ABI_BASE_LP64S: if (abi.ext == ABI_EXT_BASE) return CPU_LOONGARCH64; } gcc_unreachable (); } static const char* abi_str (struct loongarch_abi abi) { /* "/base" can be omitted. */ if (abi.ext == ABI_EXT_BASE) return (const char*) obstack_copy0 (&msg_obstack, loongarch_abi_base_strings[abi.base], strlen (loongarch_abi_base_strings[abi.base])); else { APPEND_STRING (loongarch_abi_base_strings[abi.base]) APPEND1 ('/') APPEND_STRING (loongarch_abi_ext_strings[abi.ext]) APPEND1 ('\0') return XOBFINISH (&msg_obstack, const char *); } } static const char* isa_str (const struct loongarch_isa *isa, char separator) { APPEND_STRING (loongarch_isa_base_strings[isa->base]) APPEND1 (separator) if (isa->fpu == ISA_EXT_NOFPU) { APPEND_STRING ("no" OPTSTR_ISA_EXT_FPU) } else { APPEND_STRING (OPTSTR_ISA_EXT_FPU) APPEND_STRING (loongarch_isa_ext_strings[isa->fpu]) } APPEND1 ('\0') /* Add more here. */ return XOBFINISH (&msg_obstack, const char *); } static const char* arch_str (const struct loongarch_target *target) { if (target->cpu_arch == CPU_NATIVE) { if (target->cpu_native == CPU_NATIVE) { /* Describe a native CPU with unknown PRID. */ const char* isa_string = isa_str (&target->isa, ','); APPEND_STRING ("PRID: 0x") APPEND_STRING (get_native_prid_str ()) APPEND_STRING (", ISA features: ") APPEND_STRING (isa_string) APPEND1 ('\0') } else APPEND_STRING (loongarch_cpu_strings[target->cpu_native]); } else APPEND_STRING (loongarch_cpu_strings[target->cpu_arch]); APPEND1 ('\0') return XOBFINISH (&msg_obstack, const char *); } static const char* multilib_enabled_abi_list () { int enabled_abi_idx[MULTILIB_LIST_LEN] = { 0 }; const char* enabled_abi_str[MULTILIB_LIST_LEN] = { NULL }; unsigned int j = 0; for (unsigned int i = 0; i < ABI_COUNT && j < MULTILIB_LIST_LEN; i++) { if (enabled_abi_types[abi_priority_list[i].base] [abi_priority_list[i].ext]) { enabled_abi_idx[j++] = i; } } for (unsigned int k = 0; k < j; k++) { enabled_abi_str[k] = abi_str (abi_priority_list[enabled_abi_idx[k]]); } for (unsigned int k = 0; k < j - 1; k++) { APPEND_STRING (enabled_abi_str[k]) APPEND1 (',') APPEND1 (' ') } APPEND_STRING (enabled_abi_str[j - 1]) APPEND1 ('\0') return XOBFINISH (&msg_obstack, const char *); }