diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index dfe960e51293..c215484c287b 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -438,110 +438,13 @@ static const char *riscv_supported_std_ext (void);
 
 bool riscv_subset_list::parse_failed = false;
 
-static riscv_subset_list *current_subset_list = NULL;
-
 static riscv_subset_list *cmdline_subset_list = NULL;
 
-struct riscv_func_target_info
-{
-  tree fn_decl;
-  std::string fn_target_name;
-
-  riscv_func_target_info (const tree &decl, const std::string &target_name)
-    : fn_decl (decl), fn_target_name (target_name)
-  {
-  }
-};
-
-struct riscv_func_target_hasher : nofree_ptr_hash<struct riscv_func_target_info>
-{
-  typedef tree compare_type;
-
-  static hashval_t hash (value_type);
-  static bool equal (value_type, const compare_type &);
-};
-
-static hash_table<riscv_func_target_hasher> *func_target_table = NULL;
-
-static inline hashval_t riscv_func_decl_hash (tree fn_decl)
-{
-  inchash::hash h;
-
-  h.add_ptr (fn_decl);
-
-  return h.end ();
-}
-
-inline hashval_t
-riscv_func_target_hasher::hash (value_type value)
-{
-  return riscv_func_decl_hash (value->fn_decl);
-}
-
-inline bool
-riscv_func_target_hasher::equal (value_type value, const compare_type &key)
-{
-  return value->fn_decl == key;
-}
-
-const riscv_subset_list *riscv_current_subset_list ()
-{
-  return current_subset_list;
-}
-
-const riscv_subset_list * riscv_cmdline_subset_list ()
+const riscv_subset_list *riscv_cmdline_subset_list ()
 {
   return cmdline_subset_list;
 }
 
-static inline void riscv_func_target_table_lazy_init ()
-{
-  if (func_target_table != NULL)
-    return;
-
-  func_target_table = new hash_table<riscv_func_target_hasher> (1023);
-}
-
-std::string * riscv_func_target_get (tree fn_decl)
-{
-  riscv_func_target_table_lazy_init ();
-
-  hashval_t hash = riscv_func_decl_hash (fn_decl);
-  struct riscv_func_target_info *info
-    = func_target_table->find_with_hash (fn_decl, hash);
-
-  return info == NULL ? NULL : &info->fn_target_name;
-}
-
-void riscv_func_target_put (tree fn_decl, std::string fn_target_name)
-{
-  riscv_func_target_table_lazy_init ();
-
-  hashval_t hash = riscv_func_decl_hash (fn_decl);
-  struct riscv_func_target_info **target_info_slot
-    = func_target_table->find_slot_with_hash (fn_decl, hash, INSERT);
-
-  gcc_assert (!*target_info_slot);
-
-  struct riscv_func_target_info *info
-    = new riscv_func_target_info (fn_decl, fn_target_name);
-
-  *target_info_slot = info;
-}
-
-void riscv_func_target_remove_and_destory (tree fn_decl)
-{
-  hashval_t hash = riscv_func_decl_hash (fn_decl);
-  struct riscv_func_target_info *info
-    = func_target_table->find_with_hash (fn_decl, hash);
-
-  if (info)
-    {
-      func_target_table->remove_elt_with_hash (fn_decl, hash);
-      delete info;
-    }
-}
-
 /* struct for recording multi-lib info.  */
 struct riscv_multi_lib_info_t {
   std::string path;
@@ -1612,8 +1515,8 @@ riscv_subset_list::finalize ()
 std::string
 riscv_arch_str (bool version_p)
 {
-  if (current_subset_list)
-    return current_subset_list->to_string (version_p);
+  if (cmdline_subset_list)
+    return cmdline_subset_list->to_string (version_p);
   else
     return std::string();
 }
@@ -1798,8 +1701,7 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   {NULL, NULL, 0}
 };
 
-/* Apply SUBSET_LIST to OPTS if OPTS is not null, also set CURRENT_SUBSET_LIST
-   to SUBSET_LIST, just note this WON'T delete old CURRENT_SUBSET_LIST.  */
+/* Apply SUBSET_LIST to OPTS if OPTS is not null.  */
 
 void
 riscv_set_arch_by_subset_list (riscv_subset_list *subset_list,
@@ -1826,8 +1728,6 @@ riscv_set_arch_by_subset_list (riscv_subset_list *subset_list,
 	    opts->*arch_ext_flag_tab->var_ref |= arch_ext_flag_tab->mask;
 	}
     }
-
-  current_subset_list = subset_list;
 }
 
 /* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch
@@ -1843,15 +1743,10 @@ riscv_parse_arch_string (const char *isa,
   if (!subset_list)
     return;
 
-  /* Avoid double delete if current_subset_list equals cmdline_subset_list.  */
-  if (current_subset_list && current_subset_list != cmdline_subset_list)
-    delete current_subset_list;
-
   if (cmdline_subset_list)
     delete cmdline_subset_list;
 
   cmdline_subset_list = subset_list;
-  /* current_subset_list is set in the call below.  */
 
   riscv_set_arch_by_subset_list (subset_list, opts);
 }
diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc
index 43c8eecbb6ff..71112d9c66d7 100644
--- a/gcc/config/riscv/riscv-c.cc
+++ b/gcc/config/riscv/riscv-c.cc
@@ -218,7 +218,7 @@ riscv_cpu_cpp_builtins (cpp_reader *pfile)
   /* Define architecture extension test macros.  */
   builtin_define_with_int_value ("__riscv_arch_test", 1);
 
-  const riscv_subset_list *subset_list = riscv_current_subset_list ();
+  const riscv_subset_list *subset_list = riscv_cmdline_subset_list ();
   if (!subset_list)
     return;
 
diff --git a/gcc/config/riscv/riscv-subset.h b/gcc/config/riscv/riscv-subset.h
index fe7f54d8bc57..256d28657460 100644
--- a/gcc/config/riscv/riscv-subset.h
+++ b/gcc/config/riscv/riscv-subset.h
@@ -109,11 +109,7 @@ public:
   void finalize ();
 };
 
-extern const riscv_subset_list *riscv_current_subset_list (void);
 extern const riscv_subset_list *riscv_cmdline_subset_list (void);
-extern std::string * riscv_func_target_get (tree);
-extern void riscv_func_target_put (tree, std::string);
-extern void riscv_func_target_remove_and_destory (tree);
 extern void
 riscv_set_arch_by_subset_list (riscv_subset_list *, struct gcc_options *);
 
diff --git a/gcc/config/riscv/riscv-target-attr.cc b/gcc/config/riscv/riscv-target-attr.cc
index 3d7753f64574..317806143949 100644
--- a/gcc/config/riscv/riscv-target-attr.cc
+++ b/gcc/config/riscv/riscv-target-attr.cc
@@ -50,14 +50,6 @@ public:
   bool handle_cpu (const char *);
   bool handle_tune (const char *);
 
-  void set_loc (location_t loc) {
-    m_loc = loc;
-  }
-
-  riscv_subset_list* get_riscv_subset_list () {
-    return m_subset_list;
-  }
-
   void update_settings (struct gcc_options *opts) const;
 private:
   const char *m_raw_attr_str;
@@ -98,7 +90,7 @@ riscv_target_attr_parser::parse_arch (const char *str)
   /* Check if it's setting full arch string.  */
   if (strncmp ("rv", str, strlen ("rv")) == 0)
     {
-      m_subset_list = riscv_subset_list::parse (str, location_t ());
+      m_subset_list = riscv_subset_list::parse (str, m_loc);
 
       if (m_subset_list == nullptr)
 	goto fail;
@@ -112,7 +104,10 @@ riscv_target_attr_parser::parse_arch (const char *str)
       char *str_to_check = (char *) alloca (len + 1);
       strcpy (str_to_check, str);
       const char *token = strtok_r (str_to_check, ",", &str_to_check);
-      m_subset_list = riscv_cmdline_subset_list ()->clone ();
+      const char *local_arch_str = global_options.x_riscv_arch_string;
+      m_subset_list = local_arch_str
+		      ? riscv_subset_list::parse (local_arch_str, m_loc)
+		      : riscv_cmdline_subset_list ()->clone ();
       m_subset_list->set_loc (m_loc);
       while (token)
 	{
@@ -124,19 +119,18 @@ riscv_target_attr_parser::parse_arch (const char *str)
 		"with + or rv");
 	      goto fail;
 	    }
-	  else
+
+	  const char *result = m_subset_list->parse_single_ext (token + 1);
+	  /* Check parse_single_ext has consume all string.  */
+	  if (*result != '\0')
 	    {
-	      const char *result = m_subset_list->parse_single_ext (token + 1);
-	      /* Check parse_single_ext has consume all string.  */
-	      if (*result != '\0')
-		{
-		  error_at (
-		    m_loc,
-		    "unexpected arch for %<target()%> attribute: bad "
-		    "string found %<%s%>", token);
-		  goto fail;
-		}
+	      error_at (
+		m_loc,
+		"unexpected arch for %<target()%> attribute: bad "
+		"string found %<%s%>", token);
+	      goto fail;
 	    }
+
 	  token = strtok_r (NULL, ",", &str_to_check);
 	}
 
@@ -216,7 +210,17 @@ void
 riscv_target_attr_parser::update_settings (struct gcc_options *opts) const
 {
   if (m_subset_list)
-    riscv_set_arch_by_subset_list (m_subset_list, opts);
+    {
+      std::string local_arch = m_subset_list->to_string (true);
+      const char* local_arch_str = local_arch.c_str ();
+      struct cl_target_option *default_opts
+	= TREE_TARGET_OPTION (target_option_default_node);
+      if (opts->x_riscv_arch_string != default_opts->x_riscv_arch_string)
+	free (CONST_CAST (void *, (const void *) opts->x_riscv_arch_string));
+      opts->x_riscv_arch_string = xstrdup (local_arch_str);
+
+      riscv_set_arch_by_subset_list (m_subset_list, opts);
+    }
 
   if (m_cpu_info)
     opts->x_riscv_cpu_string = m_cpu_info->name;
@@ -272,8 +276,8 @@ riscv_process_one_target_attr (char *arg_str,
 
       return (&attr_parser->*attr.handler) (arg);
     }
-  error_at (loc, "Got unknown attribute %<target(\"%s\")%>", str_to_check);
 
+  error_at (loc, "Got unknown attribute %<target(\"%s\")%>", str_to_check);
   return false;
 }
 
@@ -299,8 +303,7 @@ num_occurences_in_str (char c, char *str)
    and update the global target options space.  */
 
 static bool
-riscv_process_target_attr (tree fndecl, tree args, location_t loc,
-			   struct gcc_options *opts)
+riscv_process_target_attr (tree args, location_t loc)
 {
   if (TREE_CODE (args) == TREE_LIST)
     {
@@ -309,7 +312,7 @@ riscv_process_target_attr (tree fndecl, tree args, location_t loc,
 	  tree head = TREE_VALUE (args);
 	  if (head)
 	    {
-	      if (!riscv_process_target_attr (fndecl, head, loc, opts))
+	      if (!riscv_process_target_attr (head, loc))
 		return false;
 	    }
 	  args = TREE_CHAIN (args);
@@ -323,6 +326,7 @@ riscv_process_target_attr (tree fndecl, tree args, location_t loc,
       error_at (loc, "attribute %<target%> argument not a string");
       return false;
     }
+
   size_t len = strlen (TREE_STRING_POINTER (args));
 
   /* No need to emit warning or error on empty string here, generic code already
@@ -347,7 +351,9 @@ riscv_process_target_attr (tree fndecl, tree args, location_t loc,
   while (token)
     {
       num_attrs++;
-      riscv_process_one_target_attr (token, loc, attr_parser);
+      if (!riscv_process_one_target_attr (token, loc, attr_parser))
+	return false;
+
       token = strtok_r (NULL, ";", &str_to_check);
     }
 
@@ -359,18 +365,16 @@ riscv_process_target_attr (tree fndecl, tree args, location_t loc,
     }
 
   /* Apply settings from target attribute.  */
-  attr_parser.update_settings (opts);
-
-  /* Add the string of the target attribute to the fndecl hash table.  */
-  riscv_subset_list *subset_list = attr_parser.get_riscv_subset_list ();
-  if (subset_list)
-    riscv_func_target_put (fndecl, subset_list->to_string (true));
+  attr_parser.update_settings (&global_options);
 
   return true;
 }
 
-/* Implement TARGET_OPTION_VALID_ATTRIBUTE_P.  This is used to
-   process attribute ((target ("..."))).  */
+/* Implement TARGET_OPTION_VALID_ATTRIBUTE_P.
+   This is used to process attribute ((target ("..."))).
+   Note, that riscv_set_current_function() has not been called before,
+   so we need must not mess with the current global_options, which
+   likely belong to another function.  */
 
 bool
 riscv_option_valid_attribute_p (tree fndecl, tree, tree args, int)
@@ -378,27 +382,39 @@ riscv_option_valid_attribute_p (tree fndecl, tree, tree args, int)
   struct cl_target_option cur_target;
   bool ret;
   tree new_target;
+  tree existing_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   location_t loc = DECL_SOURCE_LOCATION (fndecl);
 
   /* Save the current target options to restore at the end.  */
   cl_target_option_save (&cur_target, &global_options, &global_options_set);
 
-  ret = riscv_process_target_attr (fndecl, args, loc, &global_options);
-
-  if (ret)
+  /* If fndecl already has some target attributes applied to it, unpack
+     them so that we add this attribute on top of them, rather than
+     overwriting them.  */
+  if (existing_target)
     {
-      riscv_override_options_internal (&global_options);
-      new_target
-	= build_target_option_node (&global_options, &global_options_set);
+      struct cl_target_option *existing_options
+	= TREE_TARGET_OPTION (existing_target);
+
+      if (existing_options)
+	cl_target_option_restore (&global_options, &global_options_set,
+				  existing_options);
     }
   else
-    new_target = NULL;
+    cl_target_option_restore (&global_options, &global_options_set,
+			      TREE_TARGET_OPTION (target_option_default_node));
 
-  if (fndecl && ret)
+  /* Now we can parse the attributes and set &global_options accordingly.  */
+  ret = riscv_process_target_attr (args, loc);
+  if (ret)
     {
+      riscv_override_options_internal (&global_options);
+      new_target = build_target_option_node (&global_options,
+					     &global_options_set);
       DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
     }
 
+  /* Restore current target options to original state.  */
   cl_target_option_restore (&global_options, &global_options_set, &cur_target);
   return ret;
 }
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 38ed773c222d..d4766b6aeeee 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -719,6 +719,15 @@ riscv_min_arithmetic_precision (void)
   return 32;
 }
 
+/* Get the arch string from an options object.  */
+
+template <class T>
+static const char *
+get_arch_str (const T *opts)
+{
+  return opts->x_riscv_arch_string;
+}
+
 template <class T>
 static const char *
 get_tune_str (const T *opts)
@@ -9469,17 +9478,16 @@ riscv_declare_function_name (FILE *stream, const char *name, tree fndecl)
     {
       fprintf (stream, "\t.option push\n");
 
-      std::string *target_name = riscv_func_target_get (fndecl);
-      std::string isa = target_name != NULL
-	? *target_name
-	: riscv_cmdline_subset_list ()->to_string (true);
-      fprintf (stream, "\t.option arch, %s\n", isa.c_str ());
-      riscv_func_target_remove_and_destory (fndecl);
-
       struct cl_target_option *local_cl_target =
 	TREE_TARGET_OPTION (DECL_FUNCTION_SPECIFIC_TARGET (fndecl));
       struct cl_target_option *global_cl_target =
 	TREE_TARGET_OPTION (target_option_default_node);
+
+      const char *local_arch_str = get_arch_str (local_cl_target);
+      const char *arch_str = local_arch_str != NULL
+	? local_arch_str
+	: riscv_arch_str (true).c_str ();
+      fprintf (stream, "\t.option arch, %s\n", arch_str);
       const char *local_tune_str = get_tune_str (local_cl_target);
       const char *global_tune_str = get_tune_str (global_cl_target);
       if (strcmp (local_tune_str, global_tune_str) != 0)
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index 32a0dda58439..e882caa8b7f5 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -82,7 +82,7 @@ Target Mask(DIV)
 Use hardware instructions for integer division.
 
 march=
-Target RejectNegative Joined Negative(march=)
+Target RejectNegative Joined Negative(march=) Var(riscv_arch_string) Save
 -march=	Generate code for given RISC-V ISA (e.g. RV64IM).  ISA strings must be
 lower-case.
 
diff --git a/gcc/testsuite/gcc.target/riscv/pr115554.c b/gcc/testsuite/gcc.target/riscv/pr115554.c
new file mode 100644
index 000000000000..e7dcde6276fa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr115554.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+extern
+__attribute__((target("arch=+zba")))
+__attribute__((target("arch=+zbb")))
+void foo(void);
+
+extern
+__attribute__((target("arch=+zbb")))
+__attribute__((target("arch=+zbb")))
+void bar(void);
+
+/* { dg-error "extension 'zbb' appear more than one time" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/gcc.target/riscv/pr115562.c b/gcc/testsuite/gcc.target/riscv/pr115562.c
new file mode 100644
index 000000000000..b20f69d8dec6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr115562.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+
+void foo (void);
+
+__attribute__((target("arch=+zbb")))
+void*
+memcpy (void *d, const void *s, unsigned long n)
+{
+  (void) s;
+  (void) n;
+  return d;
+}
+__attribute__((target("arch=+zbb"))) void fun0(void) {}
+__attribute__((target("arch=+zbb"))) void fun1(void) {}
+__attribute__((target("arch=+zbb"))) void fun2(void) {}
+__attribute__((target("arch=+zbb"))) void fun3(void) {}
+__attribute__((target("arch=+zbb"))) void fun4(void) {}
+__attribute__((target("arch=+zbb"))) void fun5(void) {}
+__attribute__((target("arch=+zbb"))) void fun6(void) {}
+__attribute__((target("arch=+zbb"))) void fun7(void) {}
+__attribute__((target("arch=+zbb"))) void fun8(void) {}
+__attribute__((target("arch=+zbb"))) void fun9(void) {}
+__attribute__((target("arch=+zbb"))) void fun10(void) {}
+__attribute__((target("arch=+zbb"))) void fun11(void) {}
+__attribute__((target("arch=+zbb"))) void fun12(void) {}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-01.c b/gcc/testsuite/gcc.target/riscv/target-attr-01.c
index b3f3d65d543f..4748f6a08e72 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-01.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-01.c
@@ -1,19 +1,18 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
-
 /*
 ** foo:
 **   ...
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-
-
-long foo() __attribute__((target("arch=rv64gc_zba")));
-long foo(long a, long b){
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_zba1p0" } } */
+long foo () __attribute__((target("arch=rv64gc_zba")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -24,8 +23,7 @@ long foo(long a, long b){
 **   add\s*a0,a1,a0
 **   ...
 */
-
-
-long bar(long a, long b){
+long bar (long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-02.c b/gcc/testsuite/gcc.target/riscv/target-attr-02.c
index c010089a823b..9ebf16e3675e 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-02.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-02.c
@@ -1,19 +1,18 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
-
 /*
 ** foo:
 **   ...
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-
-
-long foo() __attribute__((target("arch=+zba")));
-long foo(long a, long b){
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_zba1p0" } } */
+long foo () __attribute__((target("arch=+zba")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -24,8 +23,7 @@ long foo(long a, long b){
 **   add\s*a0,a1,a0
 **   ...
 */
-
-
-long bar(long a, long b){
+long bar (long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-03.c b/gcc/testsuite/gcc.target/riscv/target-attr-03.c
index b4896cb2e277..44fabf68fd07 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-03.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-03.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc_zba -O2 -mabi=lp64" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
@@ -10,8 +10,10 @@
 **   add\s*a0,a1,a0
 **   ...
 */
-long foo() __attribute__((target("arch=rv64gc")));
-long foo(long a, long b){
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0" } } */
+long foo () __attribute__((target("arch=rv64gc")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -21,6 +23,7 @@ long foo(long a, long b){
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long bar(long a, long b){
+long bar (long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-04.c b/gcc/testsuite/gcc.target/riscv/target-attr-04.c
index 369d6514e5aa..258eaf4eb584 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-04.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-04.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc_zba -O2 -mabi=lp64 -mtune=rocket" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
@@ -12,8 +12,10 @@
 **   add\s*a0,a1,a0
 **   ...
 */
-long foo() __attribute__((target("cpu=sifive-u74")));
-long foo(long a, long b){
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zaamo1p0_zalrsc1p0" } } */
+long foo () __attribute__((target("cpu=sifive-u74")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -23,6 +25,7 @@ long foo(long a, long b){
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long bar(long a, long b){
+long bar (long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-05.c b/gcc/testsuite/gcc.target/riscv/target-attr-05.c
index c75368dcebf9..1474f319e414 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-05.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-05.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc_zba -O2 -mabi=lp64 -mtune=rocket" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
@@ -11,8 +11,9 @@
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long foo() __attribute__((target("cpu=sifive-u74;arch=rv64gc_zba")));
-long foo(long a, long b){
+long foo () __attribute__((target("cpu=sifive-u74;arch=rv64gc_zba")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -22,6 +23,7 @@ long foo(long a, long b){
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long bar(long a, long b){
+long bar (long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-06.c b/gcc/testsuite/gcc.target/riscv/target-attr-06.c
index 369c95eeb54b..32ecfaee319a 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-06.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-06.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc_zba -O2 -mabi=lp64 -mtune=rocket" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
@@ -11,8 +11,10 @@
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long foo() __attribute__((target("cpu=sifive-u74;tune=sifive-5-series;arch=rv64gc_zba")));
-long foo(long a, long b){
+long foo ()
+__attribute__((target("cpu=sifive-u74;tune=sifive-5-series;arch=rv64gc_zba")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -22,6 +24,7 @@ long foo(long a, long b){
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long bar(long a, long b){
+long bar(long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-07.c b/gcc/testsuite/gcc.target/riscv/target-attr-07.c
index 4ff81166a626..f3066f4fcfea 100644
--- a/gcc/testsuite/gcc.target/riscv/target-attr-07.c
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-07.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
 /* { dg-options "-march=rv64gc_zba -O2 -mabi=lp64 -mtune=rocket" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
@@ -9,8 +9,9 @@
 **   # tune = sifive-5-series
 **   ...
 */
-long foo() __attribute__((target("tune=sifive-5-series")));
-long foo(long a, long b){
+long foo () __attribute__((target("tune=sifive-5-series")));
+long foo (long a, long b)
+{
   return a + (b * 2);
 }
 
@@ -20,6 +21,7 @@ long foo(long a, long b){
 **   sh1add\s*a0,a1,a0
 **   ...
 */
-long bar(long a, long b){
+long bar (long a, long b)
+{
   return a + (b * 2);
 }
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-08.c b/gcc/testsuite/gcc.target/riscv/target-attr-08.c
new file mode 100644
index 000000000000..e7792c1598e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-08.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+long foo ()
+__attribute__((target("arch=rv64gc_zbb")))
+__attribute__((target("arch=rv64gc_zba")));
+
+/*
+** foo:
+**   ...
+**   sh1add\s*a0,a1,a0
+**   ...
+*/
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_zba1p0" } } */
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-09.c b/gcc/testsuite/gcc.target/riscv/target-attr-09.c
new file mode 100644
index 000000000000..ec43ce95998b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-09.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+long foo ()
+__attribute__((target("cpu=sifive-e20")))
+__attribute__((target("cpu=sifive-u74")));
+
+/*
+** foo:
+**   ...
+**   # tune = sifive-7-series
+**   ...
+*/
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-10.c b/gcc/testsuite/gcc.target/riscv/target-attr-10.c
new file mode 100644
index 000000000000..86a79c7d6ccf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-10.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+long foo ()
+__attribute__((target("tune=rocket")))
+__attribute__((target("tune=sifive-u74")));
+
+/*
+** foo:
+**   ...
+**   # tune = sifive-7-series
+**   ...
+*/
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-11.c b/gcc/testsuite/gcc.target/riscv/target-attr-11.c
new file mode 100644
index 000000000000..6e02a98f1206
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-11.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+long foo ()
+__attribute__((target("arch=rv64gc_zbb")));
+
+long foo ()
+__attribute__((target("arch=rv64gc_zba")));
+
+/*
+** foo:
+**   ...
+**   sh1add\s*a0,a1,a0
+**   ...
+*/
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_zba1p0" } } */
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-12.c b/gcc/testsuite/gcc.target/riscv/target-attr-12.c
new file mode 100644
index 000000000000..e0f8eafa040a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-12.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+long foo ()
+__attribute__((target("cpu=sifive-e20")));
+
+long foo ()
+__attribute__((target("cpu=sifive-u74")));
+
+/*
+** foo:
+**   ...
+**   # tune = sifive-7-series
+**   ...
+*/
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-13.c b/gcc/testsuite/gcc.target/riscv/target-attr-13.c
new file mode 100644
index 000000000000..0a26111956b9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-13.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+long foo ()
+__attribute__((target("tune=rocket")));
+
+long foo ()
+__attribute__((target("tune=sifive-u74")));
+
+/*
+** foo:
+**   ...
+**   # tune = sifive-7-series
+**   ...
+*/
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-14.c b/gcc/testsuite/gcc.target/riscv/target-attr-14.c
new file mode 100644
index 000000000000..59de060eec38
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-14.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/*
+** foo:
+**   ...
+**   sh1add\s*a0,a1,a0
+**   ...
+*/
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_zba1p0" } } */
+long foo () __attribute__((target("arch=rv64gc_zba")));
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
+
+/*
+** bar:
+**   ...
+**   slli\s*a1,a1,1
+**   add\s*a0,a1,a0
+**   ...
+*/
+long bar (long a, long b)
+{
+  return a + (b * 2);
+}
+
+/*
+** foo_th:
+**   ...
+**   th.addsl\s*a0,a0,a1,1
+**   ...
+*/
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_xtheadba1p0" } } */
+long foo_th () __attribute__((target("arch=rv64gc_xtheadba")));
+long foo_th (long a, long b)
+{
+  return a + (b * 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/target-attr-15.c b/gcc/testsuite/gcc.target/riscv/target-attr-15.c
new file mode 100644
index 000000000000..5120ad7b1d62
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/target-attr-15.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+/* { dg-options "-march=rv64gc -O2 -mabi=lp64" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/*
+** foo:
+**   ...
+**   sh1add\s*a0,a1,a0
+**   ...
+*/
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_zba1p0" } } */
+long foo () __attribute__((target("arch=rv64gc_zba")));
+long foo (long a, long b)
+{
+  return a + (b * 2);
+}
+
+/*
+** bar:
+**   ...
+**   slli\s*a1,a1,1
+**   add\s*a0,a1,a0
+**   ...
+*/
+long bar (long a, long b)
+{
+  return a + (b * 2);
+}
+
+/*
+** foo_th:
+**   ...
+**   th.addsl\s*a0,a0,a1,1
+**   ...
+*/
+/* { dg-final { scan-assembler ".option arch, rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zaamo1p0_zalrsc1p0_xtheadba1p0" } } */
+long foo_th () __attribute__((target("arch=+xtheadba")));
+long foo_th (long a, long b)
+{
+  return a + (b * 2);
+}
