Hi!

The following patch fixes ICEs when the new inline asm syntax
to use C++26 static_assert-like constant expressions in place
of string literals is used in templates.
As finish_asm_stmt doesn't do any checking for
processing_template_decl, this patch also just defers handling
those strings in templates rather than say trying fold_non_dependent_expr
and if the result is non-dependent and usable, try to extract.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

There is one case I didn't handle and I'd like to discuss.
The initial commit to enable this new extension also changed
cp_parser_asm_specification_opt to use cp_parser_asm_string_expression.
That function doesn't have anything to do with asm statements though,
it is about asm redirection of declarations.
I couldn't find any testcase coverage for that nor documentation.
It is basically whether one can use something like
void foo () asm ((std::string_view ("bar")));
or not.  GCC 14 didn't support that obviously, now it works
when not used in templates (both before and after this patch), but
will crash when used on templates.  Say
template <int N>
void baz () asm ((std::string_view ("qux")));
(or even worse when it is dependent).
Do we want to revert the cp_parser_asm_specification_opt
changes and only support void foo () asm ("bar");, or was it intentional
to add support for constant expressions even for those?
The problem with supporting those in templates is that
cp_finish_decl/grokfield really assume asmspec_tree is NULL,
error_mark_node or STRING_CST, they perform e.g. pragma renaming on those
and there is no obvious place to store the not yet finalized
constant expression for the asmspec, those are really stored as const char *
later transformed into INDENTIFIER_NODEs.  Or shall we sorry if it is
used in templates for now?  Or say temporarily turn into some internal
attribute?

2025-01-07  Jakub Jelinek  <ja...@redhat.com>

        PR c++/118277
        * cp-tree.h (finish_asm_string_expression): Declare.
        * semantics.cc (finish_asm_string_expression): New function.
        (finish_asm_stmt): Use it.
        * parser.cc (cp_parser_asm_string_expression): Likewise.

        * g++.dg/cpp1z/constexpr-asm-4.C: New test.

--- gcc/cp/cp-tree.h.jj 2025-01-03 17:54:12.405906056 +0100
+++ gcc/cp/cp-tree.h    2025-01-07 11:25:58.138829264 +0100
@@ -7946,6 +7946,7 @@ enum {
 extern tree begin_compound_stmt                        (unsigned int);
 
 extern void finish_compound_stmt               (tree);
+extern tree finish_asm_string_expression       (location_t, tree);
 extern tree finish_asm_stmt                    (location_t, int, tree, tree,
                                                 tree, tree, tree, bool, bool);
 extern tree finish_label_stmt                  (tree);
--- gcc/cp/semantics.cc.jj      2025-01-03 11:05:47.908986058 +0100
+++ gcc/cp/semantics.cc 2025-01-07 12:02:47.372041244 +0100
@@ -2133,6 +2133,26 @@ finish_compound_stmt (tree stmt)
   add_stmt (stmt);
 }
 
+/* Finish an asm string literal, which can be a string literal
+   or parenthesized constant expression.  Extract the string literal
+   from the latter.  */
+
+tree
+finish_asm_string_expression (location_t loc, tree string)
+{
+  if (string == error_mark_node
+      || TREE_CODE (string) == STRING_CST
+      || processing_template_decl)
+    return string;
+  string = cxx_constant_value (string, tf_error);
+  cexpr_str cstr (string);
+  if (!cstr.type_check (loc))
+    return error_mark_node;
+  if (!cstr.extract (loc, string))
+    string = error_mark_node;
+  return string;
+}
+
 /* Finish an asm-statement, whose components are a STRING, some
    OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some
    LABELS.  Also note whether the asm-statement should be
@@ -2159,6 +2179,26 @@ finish_asm_stmt (location_t loc, int vol
 
       oconstraints = XALLOCAVEC (const char *, noutputs);
 
+      string = finish_asm_string_expression (cp_expr_loc_or_loc (string, loc),
+                                            string);
+      if (string == error_mark_node)
+       return error_mark_node;
+      for (int i = 0; i < 2; ++i)
+       for (t = i ? input_operands : output_operands; t; t = TREE_CHAIN (t))
+         {
+           tree s = TREE_VALUE (TREE_PURPOSE (t));
+           s = finish_asm_string_expression (cp_expr_loc_or_loc (s, loc), s);
+           if (s == error_mark_node)
+             return error_mark_node;
+           TREE_VALUE (TREE_PURPOSE (t)) = s;
+         }
+      for (t = clobbers; t; t = TREE_CHAIN (t))
+       {
+         tree s = TREE_VALUE (t);
+         s = finish_asm_string_expression (cp_expr_loc_or_loc (s, loc), s);
+         TREE_VALUE (t) = s;
+       }
+
       string = resolve_asm_operand_names (string, output_operands,
                                          input_operands, labels);
 
--- gcc/cp/parser.cc.jj 2025-01-03 11:05:47.878986483 +0100
+++ gcc/cp/parser.cc    2025-01-07 11:27:11.384808357 +0100
@@ -23107,15 +23107,8 @@ cp_parser_asm_string_expression (cp_pars
       matching_parens parens;
       parens.consume_open (parser);
       tree string = cp_parser_constant_expression (parser);
-      if (string != error_mark_node)
-       string = cxx_constant_value (string, tf_error);
-      cexpr_str cstr (string);
-      if (!cstr.type_check (tok->location))
-       return error_mark_node;
-      if (!cstr.extract (tok->location, string))
-       string = error_mark_node;
       parens.require_close (parser);
-      return string;
+      return finish_asm_string_expression (tok->location, string);
     }
   else if (!cp_parser_is_string_literal (tok))
     {
--- gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C.jj     2025-01-07 
12:19:34.472033295 +0100
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C        2025-01-07 
12:28:59.178178486 +0100
@@ -0,0 +1,71 @@
+// PR c++/118277
+// { dg-do compile }
+// { dg-options "-std=gnu++17" }
+
+using size_t = decltype (sizeof (0));
+struct string_view {
+  size_t s;
+  const char *d;
+  constexpr string_view () : s (0), d (nullptr) {}
+  constexpr string_view (const char *p) : s (__builtin_strlen (p)), d (p) {}
+  constexpr string_view (size_t l, const char *p) : s (l), d (p) {}
+  constexpr size_t size () const noexcept { return s; }
+  constexpr const char *data () const noexcept { return d; }
+};
+
+template <typename T>
+constexpr T
+gen (int n)
+{
+  switch (n)
+    {
+    case 0: return "foo %3,%2,%1,%0";
+    case 1: return "=r";
+    case 2: return "r";
+    case 3: return "memory";
+    case 4: return "cc";
+    case 5: return "goo %3,%2,%1,%0";
+    case 6: return "hoo %3,%2,%1,%0";
+    }
+}
+
+void
+bar ()
+{
+  int a, b;
+  asm ((gen <string_view> (0))
+       : (gen <string_view> (1)) (a), (gen <string_view> (1)) (b)
+       : (gen <string_view> (2)) (1), (gen <string_view> (2)) (2)
+       : (gen <string_view> (3)), (gen <string_view> (4)));
+}
+
+template <typename T, typename U>
+void
+baz ()
+{
+  U a, b;
+  asm ((gen <T> (5))
+       : (gen <T> (1)) (a), (gen <T> (1)) (b)
+       : (gen <T> (2)) (U(1)), (gen <T> (2)) (U(2))
+       : (gen <T> (3)), (gen <T> (4)));
+}
+
+template <typename T, typename U>
+void
+qux ()
+{
+  U a, b;
+  asm ((gen <T> (6))
+       : (gen <T> (1)) (a), (gen <T> (1)) (b)
+       : (gen <T> (2)) (U(1)), (gen <T> (2)) (U(2))
+       : (gen <T> (3)), (gen <T> (4)));
+}
+
+void
+corge ()
+{
+  qux <string_view, int> ();
+}
+
+/* { dg-final { scan-assembler "foo" } } */
+/* { dg-final { scan-assembler "hoo" } } */

        Jakub

Reply via email to