/* RISC-V-specific code for C family languages. Copyright (C) 2011-2025 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). 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 #define INCLUDE_STRING #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "c-family/c-common.h" #include "cpplib.h" #include "c-family/c-pragma.h" #include "target.h" #include "tm_p.h" #include "riscv-subset.h" #define builtin_define(TXT) cpp_define (pfile, TXT) struct pragma_intrinsic_flags { int intrinsic_target_flags; int intrinsic_riscv_vector_elen_flags; int intrinsic_riscv_zvl_flags; int intrinsic_riscv_zvb_subext; int intrinsic_riscv_zvk_subext; }; static void riscv_pragma_intrinsic_flags_pollute (struct pragma_intrinsic_flags *flags) { flags->intrinsic_target_flags = target_flags; flags->intrinsic_riscv_vector_elen_flags = riscv_vector_elen_flags; flags->intrinsic_riscv_zvl_flags = riscv_zvl_flags; flags->intrinsic_riscv_zvb_subext = riscv_zvb_subext; flags->intrinsic_riscv_zvk_subext = riscv_zvk_subext; target_flags = target_flags | MASK_VECTOR; riscv_zvl_flags = riscv_zvl_flags | MASK_ZVL32B | MASK_ZVL64B | MASK_ZVL128B | MASK_ZVL256B | MASK_ZVL512B | MASK_ZVL1024B | MASK_ZVL2048B | MASK_ZVL4096B; riscv_vector_elen_flags = riscv_vector_elen_flags | MASK_VECTOR_ELEN_32 | MASK_VECTOR_ELEN_64 | MASK_VECTOR_ELEN_FP_16 | MASK_VECTOR_ELEN_FP_32 | MASK_VECTOR_ELEN_FP_64; riscv_zvb_subext = riscv_zvb_subext | MASK_ZVBB | MASK_ZVBC | MASK_ZVKB; riscv_zvk_subext = riscv_zvk_subext | MASK_ZVKG | MASK_ZVKNED | MASK_ZVKNHA | MASK_ZVKNHB | MASK_ZVKSED | MASK_ZVKSH | MASK_ZVKN | MASK_ZVKNC | MASK_ZVKNG | MASK_ZVKS | MASK_ZVKSC | MASK_ZVKSG | MASK_ZVKT; } static void riscv_pragma_intrinsic_flags_restore (struct pragma_intrinsic_flags *flags) { target_flags = flags->intrinsic_target_flags; riscv_vector_elen_flags = flags->intrinsic_riscv_vector_elen_flags; riscv_zvl_flags = flags->intrinsic_riscv_zvl_flags; riscv_zvb_subext = flags->intrinsic_riscv_zvb_subext; riscv_zvk_subext = flags->intrinsic_riscv_zvk_subext; } static int riscv_ext_version_value (unsigned major, unsigned minor) { return (major * RISCV_MAJOR_VERSION_BASE) + (minor * RISCV_MINOR_VERSION_BASE); } /* Implement TARGET_CPU_CPP_BUILTINS. */ void riscv_cpu_cpp_builtins (cpp_reader *pfile) { builtin_define ("__riscv"); if (TARGET_RVC || TARGET_ZCA) builtin_define ("__riscv_compressed"); if (TARGET_RVE) builtin_define (TARGET_64BIT ? "__riscv_64e" : "__riscv_32e"); if (TARGET_ATOMIC) builtin_define ("__riscv_atomic"); if (TARGET_MUL) builtin_define ("__riscv_mul"); if (TARGET_DIV) builtin_define ("__riscv_div"); if (TARGET_DIV && TARGET_MUL) builtin_define ("__riscv_muldiv"); builtin_define_with_int_value ("__riscv_xlen", UNITS_PER_WORD * 8); if (TARGET_HARD_FLOAT) builtin_define_with_int_value ("__riscv_flen", UNITS_PER_FP_REG * 8); if ((TARGET_HARD_FLOAT || TARGET_ZFINX) && TARGET_FDIV) { builtin_define ("__riscv_fdiv"); builtin_define ("__riscv_fsqrt"); } switch (riscv_abi) { case ABI_ILP32E: case ABI_LP64E: builtin_define ("__riscv_abi_rve"); gcc_fallthrough (); case ABI_ILP32: case ABI_LP64: builtin_define ("__riscv_float_abi_soft"); break; case ABI_ILP32F: case ABI_LP64F: builtin_define ("__riscv_float_abi_single"); break; case ABI_ILP32D: case ABI_LP64D: builtin_define ("__riscv_float_abi_double"); break; } switch (riscv_cmodel) { case CM_MEDLOW: builtin_define ("__riscv_cmodel_medlow"); break; case CM_LARGE: builtin_define ("__riscv_cmodel_large"); break; case CM_PIC: case CM_MEDANY: builtin_define ("__riscv_cmodel_medany"); break; } if (riscv_user_wants_strict_align) builtin_define_with_int_value ("__riscv_misaligned_avoid", 1); else if (riscv_slow_unaligned_access_p) builtin_define_with_int_value ("__riscv_misaligned_slow", 1); else builtin_define_with_int_value ("__riscv_misaligned_fast", 1); if (TARGET_MIN_VLEN != 0) builtin_define_with_int_value ("__riscv_v_min_vlen", TARGET_MIN_VLEN); if (TARGET_VECTOR_ELEN_64) builtin_define_with_int_value ("__riscv_v_elen", 64); else if (TARGET_VECTOR_ELEN_32) builtin_define_with_int_value ("__riscv_v_elen", 32); if (TARGET_VECTOR_ELEN_FP_64) builtin_define_with_int_value ("__riscv_v_elen_fp", 64); else if (TARGET_VECTOR_ELEN_FP_32) builtin_define_with_int_value ("__riscv_v_elen_fp", 32); else if (TARGET_MIN_VLEN != 0) builtin_define_with_int_value ("__riscv_v_elen_fp", 0); if (TARGET_MIN_VLEN) { builtin_define ("__riscv_vector"); builtin_define_with_int_value ("__riscv_v_intrinsic", riscv_ext_version_value (0, 12)); if (rvv_vector_bits == RVV_VECTOR_BITS_ZVL) builtin_define_with_int_value ("__riscv_v_fixed_vlen", TARGET_MIN_VLEN); } if (TARGET_XTHEADVECTOR) builtin_define_with_int_value ("__riscv_th_v_intrinsic", riscv_ext_version_value (0, 11)); /* Define architecture extension test macros. */ builtin_define_with_int_value ("__riscv_arch_test", 1); const riscv_subset_list *subset_list = riscv_cmdline_subset_list (); if (!subset_list) return; size_t max_ext_len = 0; /* Figure out the max length of extension name for reserving buffer. */ for (const riscv_subset_t *subset = subset_list->begin (); subset != subset_list->end (); subset = subset->next) max_ext_len = MAX (max_ext_len, subset->name.length ()); char *buf = (char *)alloca (max_ext_len + 10 /* For __riscv_ and '\0'. */); for (const riscv_subset_t *subset = subset_list->begin (); subset != subset_list->end (); subset = subset->next) { int version_value = riscv_ext_version_value (subset->major_version, subset->minor_version); /* Special rule for zicsr and zifencei, it's used for ISA spec 2.2 or earlier. */ if ((subset->name == "zicsr" || subset->name == "zifencei") && version_value == 0) version_value = riscv_ext_version_value (2, 0); sprintf (buf, "__riscv_%s", subset->name.c_str ()); builtin_define_with_int_value (buf, version_value); } } /* Implement "#pragma riscv intrinsic". */ static void riscv_pragma_intrinsic (cpp_reader *) { tree x; if (pragma_lex (&x) != CPP_STRING) { error ("%<#pragma riscv intrinsic%> requires a string parameter"); return; } const char *name = TREE_STRING_POINTER (x); if (strcmp (name, "vector") == 0 || strcmp (name, "xtheadvector") == 0) { struct pragma_intrinsic_flags backup_flags; riscv_pragma_intrinsic_flags_pollute (&backup_flags); riscv_option_override (); init_adjust_machine_modes (); riscv_vector::reinit_builtins (); riscv_vector::handle_pragma_vector (); riscv_pragma_intrinsic_flags_restore (&backup_flags); /* Re-initialize after the flags are restored. */ riscv_option_override (); init_adjust_machine_modes (); } else error ("unknown %<#pragma riscv intrinsic%> option %qs", name); } /* Implement TARGET_CHECK_BUILTIN_CALL. */ static bool riscv_check_builtin_call (location_t loc, vec arg_loc, tree fndecl, tree, unsigned int nargs, tree *args, bool) { unsigned int code = DECL_MD_FUNCTION_CODE (fndecl); unsigned int subcode = code >> RISCV_BUILTIN_SHIFT; switch (code & RISCV_BUILTIN_CLASS) { case RISCV_BUILTIN_GENERAL: return true; case RISCV_BUILTIN_VECTOR: return riscv_vector::check_builtin_call (loc, arg_loc, subcode, fndecl, nargs, args); } gcc_unreachable (); } /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN. */ static tree riscv_resolve_overloaded_builtin (location_t loc, tree fndecl, void *uncast_arglist, bool) { vec empty = {}; vec *arglist = (vec *) uncast_arglist; unsigned int code = DECL_MD_FUNCTION_CODE (fndecl); unsigned int subcode = code >> RISCV_BUILTIN_SHIFT; tree new_fndecl = NULL_TREE; if (!arglist) arglist = ∅ switch (code & RISCV_BUILTIN_CLASS) { case RISCV_BUILTIN_GENERAL: break; case RISCV_BUILTIN_VECTOR: new_fndecl = riscv_vector::resolve_overloaded_builtin (loc, subcode, fndecl, arglist); break; default: gcc_unreachable (); } if (new_fndecl == NULL_TREE) return new_fndecl; return build_function_call_vec (loc, vNULL, new_fndecl, arglist, NULL, fndecl); } /* Implement REGISTER_TARGET_PRAGMAS. */ void riscv_register_pragmas (void) { targetm.resolve_overloaded_builtin = riscv_resolve_overloaded_builtin; targetm.check_builtin_call = riscv_check_builtin_call; c_register_pragma ("riscv", "intrinsic", riscv_pragma_intrinsic); }