Home | History | Annotate | Line # | Download | only in riscv
      1 /* Subroutines used for parsing target attribute for RISC-V.
      2    Copyright (C) 2023-2024 Free Software Foundation, Inc.
      3 
      4 This file is part of GCC.
      5 
      6 GCC is free software; you can redistribute it and/or modify
      7 it under the terms of the GNU General Public License as published by
      8 the Free Software Foundation; either version 3, or (at your option)
      9 any later version.
     10 
     11 GCC is distributed in the hope that it will be useful,
     12 but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 GNU General Public License for more details.
     15 
     16 You should have received a copy of the GNU General Public License
     17 along with GCC; see the file COPYING3.  If not see
     18 <http://www.gnu.org/licenses/>.  */
     19 
     20 #define IN_TARGET_CODE 1
     21 
     22 #define INCLUDE_MEMORY
     23 #define INCLUDE_STRING
     24 #include "config.h"
     25 #include "system.h"
     26 #include "coretypes.h"
     27 #include "target.h"
     28 #include "tree.h"
     29 #include "tm_p.h"
     30 #include "diagnostic.h"
     31 #include "opts.h"
     32 #include "riscv-subset.h"
     33 
     34 namespace {
     35 class riscv_target_attr_parser
     36 {
     37 public:
     38   riscv_target_attr_parser (location_t loc)
     39     : m_found_arch_p (false)
     40     , m_found_tune_p (false)
     41     , m_found_cpu_p (false)
     42     , m_subset_list (nullptr)
     43     , m_loc (loc)
     44     , m_cpu_info (nullptr)
     45     , m_tune (nullptr)
     46   {
     47   }
     48 
     49   bool handle_arch (const char *);
     50   bool handle_cpu (const char *);
     51   bool handle_tune (const char *);
     52 
     53   void update_settings (struct gcc_options *opts) const;
     54 private:
     55   const char *m_raw_attr_str;
     56   bool parse_arch (const char *);
     57 
     58   bool m_found_arch_p;
     59   bool m_found_tune_p;
     60   bool m_found_cpu_p;
     61   riscv_subset_list *m_subset_list;
     62   location_t m_loc;
     63   const  riscv_cpu_info *m_cpu_info;
     64   const char *m_tune;
     65 };
     66 }
     67 
     68 /* All the information needed to handle a target attribute.
     69    NAME is the name of the attribute.
     70    HANDLER is the function that takes the attribute string as an argument.  */
     71 
     72 struct riscv_attribute_info
     73 {
     74   const char *name;
     75   bool (riscv_target_attr_parser::*handler) (const char *);
     76 };
     77 
     78 /* The target attributes that we support.  */
     79 
     80 static const struct riscv_attribute_info riscv_attributes[]
     81   = {{"arch", &riscv_target_attr_parser::handle_arch},
     82      {"cpu", &riscv_target_attr_parser::handle_cpu},
     83      {"tune", &riscv_target_attr_parser::handle_tune}};
     84 
     85 bool
     86 riscv_target_attr_parser::parse_arch (const char *str)
     87 {
     88   if (m_subset_list)
     89     delete m_subset_list;
     90   /* Check if it's setting full arch string.  */
     91   if (strncmp ("rv", str, strlen ("rv")) == 0)
     92     {
     93       m_subset_list = riscv_subset_list::parse (str, m_loc);
     94 
     95       if (m_subset_list == nullptr)
     96 	goto fail;
     97 
     98       return true;
     99     }
    100   else
    101     {
    102       /* Parsing the extension list like "+<ext>[,+<ext>]*".  */
    103       size_t len = strlen (str);
    104       std::unique_ptr<char[]> buf (new char[len+1]);
    105       char *str_to_check = buf.get ();
    106       strcpy (str_to_check, str);
    107       const char *token = strtok_r (str_to_check, ",", &str_to_check);
    108       const char *local_arch_str = global_options.x_riscv_arch_string;
    109       m_subset_list = local_arch_str
    110 		      ? riscv_subset_list::parse (local_arch_str, m_loc)
    111 		      : riscv_cmdline_subset_list ()->clone ();
    112       m_subset_list->set_loc (m_loc);
    113       m_subset_list->set_allow_adding_dup (true);
    114 
    115       while (token)
    116 	{
    117 	  if (token[0] != '+')
    118 	    {
    119 	      error_at (
    120 		m_loc,
    121 		"unexpected arch for %<target()%> attribute: must start "
    122 		"with + or rv");
    123 	      goto fail;
    124 	    }
    125 
    126 	  const char *result = m_subset_list->parse_single_ext (token + 1);
    127 	  /* Check parse_single_ext has consume all string.  */
    128 	  if (*result != '\0')
    129 	    {
    130 	      error_at (
    131 		m_loc,
    132 		"unexpected arch for %<target()%> attribute: bad "
    133 		"string found %<%s%>", token);
    134 	      goto fail;
    135 	    }
    136 
    137 	  token = strtok_r (NULL, ",", &str_to_check);
    138 	}
    139 
    140       m_subset_list->set_allow_adding_dup (false);
    141       m_subset_list->finalize ();
    142       return true;
    143     }
    144 fail:
    145   if (m_subset_list != nullptr)
    146     {
    147       delete m_subset_list;
    148       m_subset_list = nullptr;
    149     }
    150   return false;
    151 }
    152 
    153 /* Handle the ARCH_STR argument to the arch= target attribute.  */
    154 
    155 bool
    156 riscv_target_attr_parser::handle_arch (const char *str)
    157 {
    158   if (m_found_arch_p)
    159     error_at (m_loc, "%<target()%> attribute: arch appears more than once");
    160   m_found_arch_p = true;
    161   return parse_arch (str);
    162 }
    163 
    164 /* Handle the CPU_STR argument to the cpu= target attribute.  */
    165 
    166 bool
    167 riscv_target_attr_parser::handle_cpu (const char *str)
    168 {
    169   if (m_found_cpu_p)
    170     error_at (m_loc, "%<target()%> attribute: cpu appears more than once");
    171 
    172   m_found_cpu_p = true;
    173   const riscv_cpu_info *cpu_info = riscv_find_cpu (str);
    174 
    175   if (!cpu_info)
    176     {
    177       error_at (m_loc, "%<target()%> attribute: unknown CPU %qs", str);
    178       return false;
    179     }
    180 
    181   if (m_subset_list == nullptr)
    182     {
    183       const char *arch_str = cpu_info->arch;
    184       m_subset_list = riscv_subset_list::parse (arch_str, m_loc);
    185       gcc_assert (m_subset_list);
    186     }
    187 
    188   m_cpu_info = cpu_info;
    189   return true;
    190 }
    191 
    192 /* Handle the TUNE_STR argument to the tune= target attribute.  */
    193 
    194 bool
    195 riscv_target_attr_parser::handle_tune (const char *str)
    196 {
    197   if (m_found_tune_p)
    198     error_at (m_loc, "%<target()%> attribute: tune appears more than once");
    199   m_found_tune_p = true;
    200   const struct riscv_tune_info *tune = riscv_parse_tune (str, true);
    201 
    202   if (tune == nullptr)
    203     {
    204       error_at (m_loc, "%<target()%> attribute: unknown TUNE %qs", str);
    205       return false;
    206     }
    207 
    208   m_tune = tune->name;
    209 
    210   return true;
    211 }
    212 
    213 void
    214 riscv_target_attr_parser::update_settings (struct gcc_options *opts) const
    215 {
    216   if (m_subset_list)
    217     {
    218       std::string local_arch = m_subset_list->to_string (true);
    219       const char* local_arch_str = local_arch.c_str ();
    220       struct cl_target_option *default_opts
    221 	= TREE_TARGET_OPTION (target_option_default_node);
    222       if (opts->x_riscv_arch_string != default_opts->x_riscv_arch_string)
    223 	free (CONST_CAST (void *, (const void *) opts->x_riscv_arch_string));
    224       opts->x_riscv_arch_string = xstrdup (local_arch_str);
    225 
    226       riscv_set_arch_by_subset_list (m_subset_list, opts);
    227     }
    228 
    229   if (m_cpu_info)
    230     opts->x_riscv_cpu_string = m_cpu_info->name;
    231 
    232   if (m_tune)
    233     opts->x_riscv_tune_string = m_tune;
    234   else
    235     {
    236       if (m_cpu_info)
    237 	opts->x_riscv_tune_string = m_cpu_info->tune;
    238     }
    239 }
    240 
    241 /* Parse ARG_STR which contains the definition of one target attribute.
    242    Show appropriate errors if any or return true if the attribute is valid.  */
    243 
    244 static bool
    245 riscv_process_one_target_attr (char *arg_str,
    246 			       location_t loc,
    247 			       riscv_target_attr_parser &attr_parser)
    248 {
    249   size_t len = strlen (arg_str);
    250 
    251   if (len == 0)
    252     {
    253       error_at (loc, "malformed %<target()%> attribute");
    254       return false;
    255     }
    256 
    257   std::unique_ptr<char[]> buf (new char[len+1]);
    258   char *str_to_check = buf.get();
    259   strcpy (str_to_check, arg_str);
    260 
    261   char *arg = strchr (str_to_check, '=');
    262 
    263   if (!arg)
    264     {
    265       error_at (
    266 	loc,
    267 	"attribute %<target(\"%s\")%> does not accept an argument",
    268 	str_to_check);
    269       return false;
    270     }
    271 
    272   arg[0] = '\0';
    273   ++arg;
    274   for (const auto &attr : riscv_attributes)
    275     {
    276       /* If the names don't match up, or the user has given an argument
    277 	 to an attribute that doesn't accept one, or didn't give an argument
    278 	 to an attribute that expects one, fail to match.  */
    279       if (strncmp (str_to_check, attr.name, strlen (attr.name)) != 0)
    280 	continue;
    281 
    282       return (&attr_parser->*attr.handler) (arg);
    283     }
    284 
    285   error_at (loc, "Got unknown attribute %<target(\"%s\")%>", str_to_check);
    286   return false;
    287 }
    288 
    289 /* Count how many times the character C appears in
    290    NULL-terminated string STR.  */
    291 
    292 static unsigned int
    293 num_occurences_in_str (char c, char *str)
    294 {
    295   unsigned int res = 0;
    296   while (*str != '\0')
    297     {
    298       if (*str == c)
    299 	res++;
    300 
    301       str++;
    302     }
    303 
    304   return res;
    305 }
    306 
    307 /* Parse the tree in ARGS that contains the target attribute information
    308    and update the global target options space.  */
    309 
    310 static bool
    311 riscv_process_target_attr (tree args, location_t loc)
    312 {
    313   if (TREE_CODE (args) == TREE_LIST)
    314     {
    315       do
    316 	{
    317 	  tree head = TREE_VALUE (args);
    318 	  if (head)
    319 	    {
    320 	      if (!riscv_process_target_attr (head, loc))
    321 		return false;
    322 	    }
    323 	  args = TREE_CHAIN (args);
    324       } while (args);
    325 
    326       return true;
    327     }
    328 
    329   if (TREE_CODE (args) != STRING_CST)
    330     {
    331       error_at (loc, "attribute %<target%> argument not a string");
    332       return false;
    333     }
    334 
    335   size_t len = strlen (TREE_STRING_POINTER (args));
    336 
    337   /* No need to emit warning or error on empty string here, generic code already
    338      handle this case.  */
    339   if (len == 0)
    340     {
    341       return false;
    342     }
    343 
    344   std::unique_ptr<char[]> buf (new char[len+1]);
    345   char *str_to_check = buf.get ();
    346   strcpy (str_to_check, TREE_STRING_POINTER (args));
    347 
    348   /* Used to catch empty spaces between semi-colons i.e.
    349      attribute ((target ("attr1;;attr2"))).  */
    350   unsigned int num_semicolons = num_occurences_in_str (';', str_to_check);
    351 
    352   /* Handle multiple target attributes separated by ';'.  */
    353   char *token = strtok_r (str_to_check, ";", &str_to_check);
    354 
    355   riscv_target_attr_parser attr_parser (loc);
    356   unsigned int num_attrs = 0;
    357   while (token)
    358     {
    359       num_attrs++;
    360       if (!riscv_process_one_target_attr (token, loc, attr_parser))
    361 	return false;
    362 
    363       token = strtok_r (NULL, ";", &str_to_check);
    364     }
    365 
    366   if (num_attrs != num_semicolons + 1)
    367     {
    368       error_at (loc, "malformed %<target(\"%s\")%> attribute",
    369 		TREE_STRING_POINTER (args));
    370       return false;
    371     }
    372 
    373   /* Apply settings from target attribute.  */
    374   attr_parser.update_settings (&global_options);
    375 
    376   return true;
    377 }
    378 
    379 /* Implement TARGET_OPTION_VALID_ATTRIBUTE_P.
    380    This is used to process attribute ((target ("..."))).
    381    Note, that riscv_set_current_function() has not been called before,
    382    so we need must not mess with the current global_options, which
    383    likely belong to another function.  */
    384 
    385 bool
    386 riscv_option_valid_attribute_p (tree fndecl, tree, tree args, int)
    387 {
    388   struct cl_target_option cur_target;
    389   bool ret;
    390   tree new_target;
    391   tree existing_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
    392   location_t loc = DECL_SOURCE_LOCATION (fndecl);
    393 
    394   /* Save the current target options to restore at the end.  */
    395   cl_target_option_save (&cur_target, &global_options, &global_options_set);
    396 
    397   /* If fndecl already has some target attributes applied to it, unpack
    398      them so that we add this attribute on top of them, rather than
    399      overwriting them.  */
    400   if (existing_target)
    401     {
    402       struct cl_target_option *existing_options
    403 	= TREE_TARGET_OPTION (existing_target);
    404 
    405       if (existing_options)
    406 	cl_target_option_restore (&global_options, &global_options_set,
    407 				  existing_options);
    408     }
    409   else
    410     cl_target_option_restore (&global_options, &global_options_set,
    411 			      TREE_TARGET_OPTION (target_option_default_node));
    412 
    413   /* Now we can parse the attributes and set &global_options accordingly.  */
    414   ret = riscv_process_target_attr (args, loc);
    415   if (ret)
    416     {
    417       riscv_override_options_internal (&global_options);
    418       new_target = build_target_option_node (&global_options,
    419 					     &global_options_set);
    420       DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
    421     }
    422 
    423   /* Restore current target options to original state.  */
    424   cl_target_option_restore (&global_options, &global_options_set, &cur_target);
    425   return ret;
    426 }
    427