public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse
@ 2024-06-03  3:45 Andi Kleen
  2024-06-03  3:45 ` [PATCH v2 2/2] C++: Support constexpr strings for asm statements Andi Kleen
  2024-06-03 14:36 ` [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse Jason Merrill
  0 siblings, 2 replies; 5+ messages in thread
From: Andi Kleen @ 2024-06-03  3:45 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub, jason, Andi Kleen

No intentional semantics change.

gcc/cp/ChangeLog:

	* cp-tree.h (struct cstr): Add structure.
	(get_cstr): Declare.
	(extract_cstr): Declare.
	(free_cstr): Declare.
	* semantics.cc (finish_static_assert): Factor out constant
	string expression extraction code and move to...
	(get_cstr): Here.
	(extract_cstr): Dito.
	(free_cstr): Dito.
---
 gcc/cp/cp-tree.h    |  17 +++
 gcc/cp/semantics.cc | 292 +++++++++++++++++++++++++-------------------
 2 files changed, 184 insertions(+), 125 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 565e4a9290e2..25b8033db788 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -9015,6 +9015,23 @@ struct push_access_scope_guard
   }
 };
 
+/* Extracting strings from constexpr */
+
+/* Temporary data for extracting constant string.  */
+struct cstr
+{
+  tree message;
+  tree message_data;
+  tree message_sz;
+  char *buf;
+};
+
+bool get_cstr (tree message, location_t location, const char *what, const char *what2,
+	       cstr &cstr);
+bool extract_cstr (const char *what, const char *what2, location_t location,
+		   cstr &cstr, const char * & msg, int &len);
+void free_cstr (cstr &cstr);
+
 /* True if TYPE is an extended floating-point type.  */
 
 inline bool
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index f90c304a65b7..9b0a2aee4e49 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -11558,74 +11558,196 @@ init_cp_semantics (void)
 }
 \f
 
-/* Build a STATIC_ASSERT for a static assertion with the condition
-   CONDITION and the message text MESSAGE.  LOCATION is the location
-   of the static assertion in the source code.  When MEMBER_P, this
-   static assertion is a member of a class.  If SHOW_EXPR_P is true,
-   print the condition (because it was instantiation-dependent).  */
+/* Get constant string from MESSAGE at LOCATION for WHAT and WHAT2 and save into CSTR. Returns
+   true if successfull, otherwise false.  */
 
-void
-finish_static_assert (tree condition, tree message, location_t location,
-		      bool member_p, bool show_expr_p)
+bool
+get_cstr (tree message, location_t location, const char *what, const char *what2,
+	  cstr &cstr)
 {
   tsubst_flags_t complain = tf_warning_or_error;
-  tree message_sz = NULL_TREE, message_data = NULL_TREE;
+  cstr = { message, NULL_TREE, NULL_TREE, NULL };
 
   if (message == NULL_TREE
       || message == error_mark_node
-      || condition == NULL_TREE
-      || condition == error_mark_node)
-    return;
-
-  if (check_for_bare_parameter_packs (condition)
       || check_for_bare_parameter_packs (message))
-    return;
+    return false;
 
   if (TREE_CODE (message) != STRING_CST
       && !type_dependent_expression_p (message))
     {
-      message_sz
+      cstr.message_sz
 	= finish_class_member_access_expr (message,
 					   get_identifier ("size"),
 					   false, complain);
-      if (message_sz != error_mark_node)
-	message_data
+      if (cstr.message_sz != error_mark_node)
+	cstr.message_data
 	  = finish_class_member_access_expr (message,
 					     get_identifier ("data"),
 					     false, complain);
-      if (message_sz == error_mark_node || message_data == error_mark_node)
+      if (cstr.message_sz == error_mark_node || cstr.message_data == error_mark_node)
 	{
-	  error_at (location, "%<static_assert%> message must be a string "
-			      "literal or object with %<size%> and "
-			      "%<data%> members");
-	  return;
+	  error_at (location, "%qs %s must be a string "
+		    "literal or object with %<size%> and "
+		    "%<data%> members", what, what2);
+	  return false;
 	}
       releasing_vec size_args, data_args;
-      message_sz = finish_call_expr (message_sz, &size_args, false, false,
+      cstr.message_sz = finish_call_expr (cstr.message_sz, &size_args, false, false,
 				     complain);
-      message_data = finish_call_expr (message_data, &data_args, false, false,
+      cstr.message_data = finish_call_expr (cstr.message_data, &data_args, false, false,
 				       complain);
-      if (message_sz == error_mark_node || message_data == error_mark_node)
-	return;
-      message_sz = build_converted_constant_expr (size_type_node, message_sz,
-						  complain);
-      if (message_sz == error_mark_node)
+      if (cstr.message_sz == error_mark_node || cstr.message_data == error_mark_node)
+	return false;
+      cstr.message_sz = build_converted_constant_expr (size_type_node, cstr.message_sz,
+						       complain);
+      if (cstr.message_sz == error_mark_node)
 	{
-	  error_at (location, "%<static_assert%> message %<size()%> "
-			      "must be implicitly convertible to "
-			      "%<std::size_t%>");
-	  return;
+	  error_at (location, "%qs %s %<size()%> "
+		    "must be implicitly convertible to "
+		    "%<std::size_t%>", what, what2);
+	  return false;
 	}
-      message_data = build_converted_constant_expr (const_string_type_node,
-						    message_data, complain);
-      if (message_data == error_mark_node)
+      cstr.message_data = build_converted_constant_expr (const_string_type_node,
+							 cstr.message_data, complain);
+      if (cstr.message_data == error_mark_node)
 	{
-	  error_at (location, "%<static_assert%> message %<data()%> "
-			      "must be implicitly convertible to "
-			      "%<const char*%>");
-	  return;
+	  error_at (location, "%qs %s %<data()%> "
+		    "must be implicitly convertible to "
+		    "%<const char*%>", what, what2);
+	  return false;
+	}
+    }
+  return true;
+}
+
+/* Extract constant string CSTR into output paramter MSG with output LEN,
+   using WHAT and WHAT2 for error messages.
+   Returns true if successfull, otherwise false. CSTR must be freed with free_cstr
+   if return was true.  */
+
+bool
+extract_cstr (const char *what, const char *what2, location_t location,
+	      cstr &cstr, const char * & msg, int &len)
+{
+  tsubst_flags_t complain = tf_warning_or_error;
+
+  msg = NULL;
+  if (cstr.message_sz && cstr.message_data)
+    {
+      tree msz = cxx_constant_value (cstr.message_sz, NULL_TREE, complain);
+      if (!tree_fits_uhwi_p (msz))
+	{
+	  error_at (location,
+		    "%qs %s %<size()%> "
+		    "must be a constant expression", what, what2);
+	  return false;
+	}
+      else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz)
+	       != tree_to_uhwi (msz))
+	{
+	  error_at (location,
+		    "%qs message %<size()%> "
+		    "%qE too large", what, msz);
+	  return false;
+	}
+      len = tree_to_uhwi (msz);
+      tree data = maybe_constant_value (cstr.message_data, NULL_TREE,
+					mce_true);
+      if (!reduced_constant_expression_p (data))
+	data = NULL_TREE;
+      if (len)
+	{
+	  if (data)
+	    msg = c_getstr (data);
+	  if (msg == NULL)
+	    cstr.buf = XNEWVEC (char, len);
+	  for (int i = 0; i < len; ++i)
+	    {
+	      tree t = cstr.message_data;
+	      if (i)
+		t = build2 (POINTER_PLUS_EXPR,
+			    TREE_TYPE (cstr.message_data), cstr.message_data,
+			    size_int (i));
+	      t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
+	      tree t2 = cxx_constant_value (t, NULL_TREE, complain);
+	      if (!tree_fits_shwi_p (t2))
+		{
+		  error_at (location,
+			    "%qs %s %<data()[%d]%> "
+			    "must be a constant expression", what, what2, i);
+		  XDELETEVEC (cstr.buf);
+		  return false;
+		}
+	      if (msg == NULL)
+		cstr.buf[i] = tree_to_shwi (t2);
+	      /* If c_getstr worked, just verify the first and
+		 last characters using constant evaluation.  */
+	      else if (len > 2 && i == 0)
+		i = len - 2;
+	    }
+	  if (msg == NULL)
+	    msg = cstr.buf;
+	}
+      else if (!data)
+	{
+	  /* We don't have any function to test whether some
+	     expression is a core constant expression.  So, instead
+	     test whether (message.data (), 0) is a constant
+	     expression.  */
+	  data = build2 (COMPOUND_EXPR, integer_type_node,
+			 cstr.message_data, integer_zero_node);
+	  tree t = cxx_constant_value (data, NULL_TREE, complain);
+	  if (!integer_zerop (t))
+	    {
+	      error_at (location,
+			"%qs %s %<data()%> "
+			"must be a core constant expression", what, what2);
+	      return false;
+	    }
 	}
     }
+  else
+    {
+      tree eltype = TREE_TYPE (TREE_TYPE (cstr.message));
+      int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (eltype));
+      msg = TREE_STRING_POINTER (cstr.message);
+      len = TREE_STRING_LENGTH (cstr.message) / sz - 1;
+    }
+
+  return true;
+}
+
+/* Free constant string data CSTR.  */
+
+void
+free_cstr (cstr &cstr)
+{
+  XDELETEVEC (cstr.buf);
+}
+
+/* Build a STATIC_ASSERT for a static assertion with the condition
+   CONDITION and the message text MESSAGE.  LOCATION is the location
+   of the static assertion in the source code.  When MEMBER_P, this
+   static assertion is a member of a class.  If SHOW_EXPR_P is true,
+   print the condition (because it was instantiation-dependent).  */
+
+void
+finish_static_assert (tree condition, tree message, location_t location,
+		      bool member_p, bool show_expr_p)
+{
+  tsubst_flags_t complain = tf_warning_or_error;
+
+  if (condition == NULL_TREE
+      || condition == error_mark_node)
+    return;
+
+  if (check_for_bare_parameter_packs (condition))
+    return;
+
+  cstr cstr;
+  if (!get_cstr (message, location, "static_assert", "message", cstr))
+    return;
 
   /* Save the condition in case it was a concept check.  */
   tree orig_condition = condition;
@@ -11638,7 +11760,7 @@ finish_static_assert (tree condition, tree message, location_t location,
     defer:
       tree assertion = make_node (STATIC_ASSERT);
       STATIC_ASSERT_CONDITION (assertion) = orig_condition;
-      STATIC_ASSERT_MESSAGE (assertion) = message;
+      STATIC_ASSERT_MESSAGE (assertion) = cstr.message;
       STATIC_ASSERT_SOURCE_LOCATION (assertion) = location;
 
       if (member_p)
@@ -11671,88 +11793,8 @@ finish_static_assert (tree condition, tree message, location_t location,
 
 	  int len;
 	  const char *msg = NULL;
-	  char *buf = NULL;
-	  if (message_sz && message_data)
-	    {
-	      tree msz = cxx_constant_value (message_sz, NULL_TREE, complain);
-	      if (!tree_fits_uhwi_p (msz))
-		{
-		  error_at (location,
-			    "%<static_assert%> message %<size()%> "
-			    "must be a constant expression");
-		  return;
-		}
-	      else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz)
-		       != tree_to_uhwi (msz))
-		{
-		  error_at (location,
-			    "%<static_assert%> message %<size()%> "
-			    "%qE too large", msz);
-		  return;
-		}
-	      len = tree_to_uhwi (msz);
-	      tree data = maybe_constant_value (message_data, NULL_TREE,
-						mce_true);
-	      if (!reduced_constant_expression_p (data))
-		data = NULL_TREE;
-	      if (len)
-		{
-		  if (data)
-		    msg = c_getstr (data);
-		  if (msg == NULL)
-		    buf = XNEWVEC (char, len);
-		  for (int i = 0; i < len; ++i)
-		    {
-		      tree t = message_data;
-		      if (i)
-			t = build2 (POINTER_PLUS_EXPR,
-				    TREE_TYPE (message_data), message_data,
-				    size_int (i));
-		      t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
-		      tree t2 = cxx_constant_value (t, NULL_TREE, complain);
-		      if (!tree_fits_shwi_p (t2))
-			{
-			  error_at (location,
-				    "%<static_assert%> message %<data()[%d]%> "
-				    "must be a constant expression", i);
-			  XDELETEVEC (buf);
-			  return;
-			}
-		      if (msg == NULL)
-			buf[i] = tree_to_shwi (t2);
-		      /* If c_getstr worked, just verify the first and
-			 last characters using constant evaluation.  */
-		      else if (len > 2 && i == 0)
-			i = len - 2;
-		    }
-		  if (msg == NULL)
-		    msg = buf;
-		}
-	      else if (!data)
-		{
-		  /* We don't have any function to test whether some
-		     expression is a core constant expression.  So, instead
-		     test whether (message.data (), 0) is a constant
-		     expression.  */
-		  data = build2 (COMPOUND_EXPR, integer_type_node,
-				 message_data, integer_zero_node);
-		  tree t = cxx_constant_value (data, NULL_TREE, complain);
-		  if (!integer_zerop (t))
-		    {
-		      error_at (location,
-				"%<static_assert%> message %<data()%> "
-				"must be a core constant expression");
-		      return;
-		    }
-		}
-	    }
-	  else
-	    {
-	      tree eltype = TREE_TYPE (TREE_TYPE (message));
-	      int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (eltype));
-	      msg = TREE_STRING_POINTER (message);
-	      len = TREE_STRING_LENGTH (message) / sz - 1;
-	    }
+	  if (!extract_cstr ("static_assert", "message", location, cstr, msg, len))
+	    return;
 
 	  /* See if we can find which clause was failing (for logical AND).  */
 	  tree bad = find_failing_clause (NULL, orig_condition);
@@ -11768,7 +11810,7 @@ finish_static_assert (tree condition, tree message, location_t location,
 	  else
 	    error_at (cloc, "static assertion failed: %.*s", len, msg);
 
-	  XDELETEVEC (buf);
+	  free_cstr (cstr);
 
 	  diagnose_failing_condition (bad, cloc, show_expr_p);
 	}
-- 
2.45.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] C++: Support constexpr strings for asm statements
  2024-06-03  3:45 [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse Andi Kleen
@ 2024-06-03  3:45 ` Andi Kleen
  2024-06-03 15:01   ` Jason Merrill
  2024-06-03 14:36 ` [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse Jason Merrill
  1 sibling, 1 reply; 5+ messages in thread
From: Andi Kleen @ 2024-06-03  3:45 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub, jason, Andi Kleen

Some programing styles use a lot of inline assembler, and it is common
to use very complex preprocessor macros to generate the assembler
strings for the asm statements. In C++ there would be a typesafe alternative
using templates and constexpr to generate the assembler strings, but
unfortunately the asm statement requires plain string literals, so this
doesn't work.

This patch modifies the C++ parser to accept strings generated by
constexpr instead of just plain strings. This requires new syntax
because e.g. asm("..." : "r" (expr)) would be ambigious with a function
call. I chose () to make it unique. For example now you can write

constexpr const char *genasm() { return "insn"; }
constexpr const char *genconstraint() { return "r"; }

	asm(genasm() :: (genconstraint()) (input));

The constexpr strings are allowed for the asm template, the
constraints and the clobbers (every time current asm accepts a string)

This version allows the same constexprs as C++26 static_assert,
following Jakub's suggestion.

The drawback of this scheme is that the constexpr doesn't have
full control over the input/output/clobber lists, but that can be
usually handled with a switch statement.  One could imagine
more flexible ways to handle that, for example supporting constexpr
vectors for the clobber list, or similar. But even without
that it is already useful.

Bootstrapped and full test on x86_64-linux.

gcc/c-family/ChangeLog:

	* c-cppbuiltin.cc (c_cpp_builtins): Define
	  __GNU_CONSTEXPR_ASM__.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_asm_string_expression): New function to
	  handle constexprs for strings.
	(cp_parser_using_directive): Use
	  cp_parser_asm_string_expression.
	(cp_parser_asm_definition): Dito.
	(cp_parser_yield_expression): Dito.
	(cp_parser_asm_specification_opt): Dito.
	(cp_parser_asm_operand_list): Dito.
	(cp_parser_asm_clobber_list): Dito.

gcc/ChangeLog:

	* doc/extend.texi: Document constexpr asm.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/constexpr-asm-1.C: New test.
	* g++.dg/cpp1z/constexpr-asm-2.C: New test.
---
 gcc/c-family/c-cppbuiltin.cc                 |  5 +-
 gcc/cp/parser.cc                             | 81 ++++++++++++++------
 gcc/doc/extend.texi                          | 35 +++++++--
 gcc/testsuite/g++.dg/cpp1z/constexpr-asm-1.C | 30 ++++++++
 gcc/testsuite/g++.dg/cpp1z/constexpr-asm-2.C | 21 +++++
 5 files changed, 142 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-asm-1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-asm-2.C

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index d9b84a0f1b97..ff2c63d6f667 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -954,7 +954,10 @@ c_cpp_builtins (cpp_reader *pfile)
 	}
 
       if (cxx_dialect >= cxx11)
-        cpp_define (pfile, "__GXX_EXPERIMENTAL_CXX0X__");
+	{
+	  cpp_define (pfile, "__GXX_EXPERIMENTAL_CXX0X__");
+	  cpp_define (pfile, "__GNU_CONSTEXPR_ASM__");
+	}
 
       /* Binary literals have been allowed in g++ before C++11
 	 and were standardized for C++14.  */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 779625144db4..038ed3cf424c 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -22824,6 +22824,48 @@ cp_parser_using_directive (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 }
 
+/* Parse a string literal or constant expression yielding a string.
+   The constant expression uses extra parens to avoid ambiguity with "x" (expr).
+   WHAT is an identifier for error messages.
+
+   asm-string-expr:
+     string-literal
+     ( constant-expr ) */
+
+static tree
+cp_parser_asm_string_expression (cp_parser *parser, const char *what)
+{
+  location_t sloc = cp_lexer_peek_token (parser->lexer)->location;
+
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      matching_parens parens;
+      parens.consume_open (parser);
+      tree string = cp_parser_constant_expression (parser);
+      cstr cstr;
+      if (string != error_mark_node)
+	string = cxx_constant_value (string, tf_error);
+      if (TREE_CODE (string) == NOP_EXPR)
+	string = TREE_OPERAND (string, 0);
+      if (TREE_CODE (string) == ADDR_EXPR
+	  && TREE_CODE (TREE_OPERAND (string, 0)) == STRING_CST)
+	string = TREE_OPERAND (string, 0);
+      if (TREE_CODE (string) == VIEW_CONVERT_EXPR)
+	string = TREE_OPERAND (string, 0);
+      if (!get_cstr (string, sloc, "asm", what, cstr))
+	return error_mark_node;
+      const char *msg;
+      int len;
+      if (!extract_cstr ("asm", what, sloc, cstr, msg, len))
+	return error_mark_node;
+      parens.require_close (parser);
+      string = build_string (len, msg);
+      free_cstr (cstr);
+      return string;
+    }
+  return cp_parser_string_literal (parser, false, false);
+}
+
 /* Parse an asm-definition.
 
   asm-qualifier:
@@ -22836,19 +22878,19 @@ cp_parser_using_directive (cp_parser* parser)
     asm-qualifier-list asm-qualifier
 
    asm-definition:
-     asm ( string-literal ) ;
+     asm ( constant-expr ) ;
 
    GNU Extension:
 
    asm-definition:
-     asm asm-qualifier-list [opt] ( string-literal ) ;
-     asm asm-qualifier-list [opt] ( string-literal : asm-operand-list [opt] ) ;
-     asm asm-qualifier-list [opt] ( string-literal : asm-operand-list [opt]
+     asm asm-qualifier-list [opt] ( asm-string-expr ) ;
+     asm asm-qualifier-list [opt] ( asm-string-expr : asm-operand-list [opt] ) ;
+     asm asm-qualifier-list [opt] ( asm-string-expr : asm-operand-list [opt]
 				    : asm-operand-list [opt] ) ;
-     asm asm-qualifier-list [opt] ( string-literal : asm-operand-list [opt]
+     asm asm-qualifier-list [opt] ( asm-string-expr : asm-operand-list [opt]
 				    : asm-operand-list [opt]
 			  : asm-clobber-list [opt] ) ;
-     asm asm-qualifier-list [opt] ( string-literal : : asm-operand-list [opt]
+     asm asm-qualifier-list [opt] ( asm-string-expr : : asm-operand-list [opt]
 				    : asm-clobber-list [opt]
 				    : asm-goto-list ) ;
 
@@ -22967,8 +23009,7 @@ cp_parser_asm_definition (cp_parser* parser)
   if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
     return;
   /* Look for the string.  */
-  tree string = cp_parser_string_literal (parser, /*translate=*/false,
-					  /*wide_ok=*/false);
+  tree string = cp_parser_asm_string_expression (parser, "instruction");
   if (string == error_mark_node)
     {
       cp_parser_skip_to_closing_parenthesis (parser, true, false,
@@ -29627,7 +29668,7 @@ cp_parser_yield_expression (cp_parser* parser)
 /* Parse an (optional) asm-specification.
 
    asm-specification:
-     asm ( string-literal )
+     asm ( asm-string-expr )
 
    If the asm-specification is present, returns a STRING_CST
    corresponding to the string-literal.  Otherwise, returns
@@ -29650,9 +29691,7 @@ cp_parser_asm_specification_opt (cp_parser* parser)
   parens.require_open (parser);
 
   /* Look for the string-literal.  */
-  tree asm_specification = cp_parser_string_literal (parser,
-						     /*translate=*/false,
-						     /*wide_ok=*/false);
+  tree asm_specification = cp_parser_asm_string_expression (parser, "instruction");
 
   /* Look for the `)'.  */
   parens.require_close (parser);
@@ -29667,8 +29706,8 @@ cp_parser_asm_specification_opt (cp_parser* parser)
      asm-operand-list , asm-operand
 
    asm-operand:
-     string-literal ( expression )
-     [ string-literal ] string-literal ( expression )
+     asm-string-expr ( expression )
+     [ asm-string-expr ] asm-string-expr ( expression )
 
    Returns a TREE_LIST representing the operands.  The TREE_VALUE of
    each node is the expression.  The TREE_PURPOSE is itself a
@@ -29701,10 +29740,8 @@ cp_parser_asm_operand_list (cp_parser* parser)
 	}
       else
 	name = NULL_TREE;
-      /* Look for the string-literal.  */
-      tree string_literal = cp_parser_string_literal (parser,
-						      /*translate=*/false,
-						      /*wide_ok=*/false);
+      /* Look for the string.  */
+      tree string_literal = cp_parser_asm_string_expression (parser, "operand");
 
       /* Look for the `('.  */
       matching_parens parens;
@@ -29737,8 +29774,8 @@ cp_parser_asm_operand_list (cp_parser* parser)
 /* Parse an asm-clobber-list.
 
    asm-clobber-list:
-     string-literal
-     asm-clobber-list , string-literal
+     const-expression
+     asm-clobber-list , const-expression
 
    Returns a TREE_LIST, indicating the clobbers in the order that they
    appeared.  The TREE_VALUE of each node is a STRING_CST.  */
@@ -29751,9 +29788,7 @@ cp_parser_asm_clobber_list (cp_parser* parser)
   while (true)
     {
       /* Look for the string literal.  */
-      tree string_literal = cp_parser_string_literal (parser,
-						      /*translate=*/false,
-						      /*wide_ok=*/false);
+      tree string_literal = cp_parser_asm_string_expression (parser, "clobber");
       /* Add it to the list.  */
       clobbers = tree_cons (NULL_TREE, string_literal, clobbers);
       /* If the next token is not a `,', then the list is
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 799a36586dc9..023b17f600ec 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10700,14 +10700,30 @@ contain any instructions recognized by the assembler, including directives.
 GCC does not parse the assembler instructions themselves and 
 does not know what they mean or even whether they are valid assembler input. 
 
-You may place multiple assembler instructions together in a single @code{asm} 
-string, separated by the characters normally used in assembly code for the 
-system. A combination that works in most places is a newline to break the 
+You may place multiple assembler instructions together in a single @code{asm}
+string, separated by the characters normally used in assembly code for the
+system. A combination that works in most places is a newline to break the
 line, plus a tab character (written as @samp{\n\t}).
-Some assemblers allow semicolons as a line separator. However, 
-note that some assembler dialects use semicolons to start a comment. 
+Some assemblers allow semicolons as a line separator. However,
+note that some assembler dialects use semicolons to start a comment.
 @end table
 
+@node asm constexprs
+With gnu++11 or later the string can also be a compile time constant expression
+inside parens.  The constant expression can return a string or a container
+with data and size members, following similar rules as C++26 @code{static_assert}
+message. Any string is converted to the character set of the source code.
+When this feature is available the @code{__GNU_CONSTEXPR_ASM__} cpp symbol is defined.
+
+@example
+constexpr const char *genfoo() @{ return "foo"; @}
+
+void function()
+@{
+  asm((genfoo()));
+@}
+@end example
+
 @subsubheading Remarks
 Using extended @code{asm} (@pxref{Extended Asm}) typically produces
 smaller, safer, and more efficient code, and in most cases it is a
@@ -10850,20 +10866,27 @@ perform a jump to one of the labels listed in the @var{GotoLabels}.
 @item AssemblerTemplate
 This is a literal string that is the template for the assembler code. It is a 
 combination of fixed text and tokens that refer to the input, output, 
-and goto parameters. @xref{AssemblerTemplate}.
+and goto parameters. @xref{AssemblerTemplate}. With gnu++11 or later it can
+also be a constant expression inside parens (see @ref{asm constexprs}).
 
 @item OutputOperands
 A comma-separated list of the C variables modified by the instructions in the 
 @var{AssemblerTemplate}.  An empty list is permitted.  @xref{OutputOperands}.
+With gnu++11 or later the strings can also be constant expressions inside parens
+(see @ref{asm constexprs})
 
 @item InputOperands
 A comma-separated list of C expressions read by the instructions in the 
 @var{AssemblerTemplate}.  An empty list is permitted.  @xref{InputOperands}.
+With gnu++11 or later the strings can also be constant expressions inside parens
+(see @ref{asm constexprs})
 
 @item Clobbers
 A comma-separated list of registers or other values changed by the 
 @var{AssemblerTemplate}, beyond those listed as outputs.
 An empty list is permitted.  @xref{Clobbers and Scratch Registers}.
+With gnu++11 or later the strings can also be constant expressions inside parens
+(see @ref{asm constexprs})
 
 @item GotoLabels
 When you are using the @code{goto} form of @code{asm}, this section contains 
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-1.C
new file mode 100644
index 000000000000..b1305571a2c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-1.C
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu++11" } */
+
+constexpr const char *genfoo()
+{
+	return "foo %1,%0";
+}
+
+constexpr const char *genoutput()
+{
+	return "=r";
+}
+
+constexpr const char *geninput()
+{
+	return "r";
+}
+
+constexpr const char *genclobber()
+{
+	return "memory";
+}
+
+void f()
+{
+	int a;
+	asm((genfoo()) : (genoutput()) (a) : (geninput()) (1) : (genclobber()));
+}
+
+/* { dg-final { scan-assembler "foo" } } */
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-2.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-2.C
new file mode 100644
index 000000000000..a34fd14ec748
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-2.C
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu++11" } */
+
+using size_t = typeof(sizeof(0));
+template <typename T, size_t N>
+struct array {
+  constexpr size_t size () const { return N; }
+  constexpr const T *data () const { return a; }
+  const T a[N];
+};
+
+void f()
+{
+	int a;
+	asm((array<char, 3> {'f','o','o'}) :
+	    (array<char, 2>{'=','r'})  (a) :
+	    (array<char, 1>{'r'}) (1) :
+	    (array<char, 6>{'m','e','m','o','r','y'}));
+}
+
+/* { dg-final { scan-assembler "foo" } } */
-- 
2.45.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse
  2024-06-03  3:45 [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse Andi Kleen
  2024-06-03  3:45 ` [PATCH v2 2/2] C++: Support constexpr strings for asm statements Andi Kleen
@ 2024-06-03 14:36 ` Jason Merrill
  1 sibling, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2024-06-03 14:36 UTC (permalink / raw)
  To: Andi Kleen, gcc-patches; +Cc: jakub

On 6/2/24 23:45, Andi Kleen wrote:
> No intentional semantics change.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (struct cstr): Add structure.
> 	(get_cstr): Declare.
> 	(extract_cstr): Declare.
> 	(free_cstr): Declare.
> 	* semantics.cc (finish_static_assert): Factor out constant
> 	string expression extraction code and move to...
> 	(get_cstr): Here.
> 	(extract_cstr): Dito.
> 	(free_cstr): Dito.
> ---
>   gcc/cp/cp-tree.h    |  17 +++
>   gcc/cp/semantics.cc | 292 +++++++++++++++++++++++++-------------------
>   2 files changed, 184 insertions(+), 125 deletions(-)
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 565e4a9290e2..25b8033db788 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -9015,6 +9015,23 @@ struct push_access_scope_guard
>     }
>   };
>   
> +/* Extracting strings from constexpr */
> +
> +/* Temporary data for extracting constant string.  */
> +struct cstr
> +{
> +  tree message;
> +  tree message_data;
> +  tree message_sz;
> +  char *buf;
> +};
> +
> +bool get_cstr (tree message, location_t location, const char *what, const char *what2,
> +	       cstr &cstr);
> +bool extract_cstr (const char *what, const char *what2, location_t location,
> +		   cstr &cstr, const char * & msg, int &len);
> +void free_cstr (cstr &cstr);

get_ and extract_ are confusingly similar names.  Let's make cstr more 
of a class, initialized at least from 'message', and perhaps from the 
other context information.

Then get_ can be something like cstr::type_check.  And then free_cstr is 
the destructor, and the copy constructor is deleted.

And please don't use the same name for the class and parameter/variables 
of that type.

> +	  error_at (location, "%qs %s must be a string "

 From ABOUT-GCC-NLS: "Avoid using %s to compose a diagnostic message 
from multiple translatable strings; instead, write out the full 
diagnostic message for each variant. Only use %s for message components 
that do not need translation, such as keywords."

Also, if you are passing an English string for a diagnostic to a 
function that isn't from diagnostic.h, you need to use the intl.h macros 
to mark it for translation.

Jason


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] C++: Support constexpr strings for asm statements
  2024-06-03  3:45 ` [PATCH v2 2/2] C++: Support constexpr strings for asm statements Andi Kleen
@ 2024-06-03 15:01   ` Jason Merrill
  2024-06-03 15:53     ` Andi Kleen
  0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2024-06-03 15:01 UTC (permalink / raw)
  To: Andi Kleen, gcc-patches; +Cc: jakub

On 6/2/24 23:45, Andi Kleen wrote:
> Some programing styles use a lot of inline assembler, and it is common
> to use very complex preprocessor macros to generate the assembler
> strings for the asm statements. In C++ there would be a typesafe alternative
> using templates and constexpr to generate the assembler strings, but
> unfortunately the asm statement requires plain string literals, so this
> doesn't work.
> 
> This patch modifies the C++ parser to accept strings generated by
> constexpr instead of just plain strings. This requires new syntax
> because e.g. asm("..." : "r" (expr)) would be ambigious with a function
> call. I chose () to make it unique. For example now you can write
> 
> constexpr const char *genasm() { return "insn"; }
> constexpr const char *genconstraint() { return "r"; }
> 
> 	asm(genasm() :: (genconstraint()) (input));

Looks plausible.  What happens when someone forgets the parens, as seems 
a likely mistake?

> The constexpr strings are allowed for the asm template, the
> constraints and the clobbers (every time current asm accepts a string)
> 
> This version allows the same constexprs as C++26 static_assert,
> following Jakub's suggestion.
> 
> The drawback of this scheme is that the constexpr doesn't have
> full control over the input/output/clobber lists, but that can be
> usually handled with a switch statement.  One could imagine
> more flexible ways to handle that, for example supporting constexpr
> vectors for the clobber list, or similar. But even without
> that it is already useful.
> 
> Bootstrapped and full test on x86_64-linux.
> 
> gcc/c-family/ChangeLog:
> 
> 	* c-cppbuiltin.cc (c_cpp_builtins): Define
> 	  __GNU_CONSTEXPR_ASM__.

__GXX instead of __GNU would be more consistent with the other old 
predefined macros.

Jason


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] C++: Support constexpr strings for asm statements
  2024-06-03 15:01   ` Jason Merrill
@ 2024-06-03 15:53     ` Andi Kleen
  0 siblings, 0 replies; 5+ messages in thread
From: Andi Kleen @ 2024-06-03 15:53 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, jakub

On Mon, Jun 03, 2024 at 11:01:02AM -0400, Jason Merrill wrote:
> On 6/2/24 23:45, Andi Kleen wrote:
> > Some programing styles use a lot of inline assembler, and it is common
> > to use very complex preprocessor macros to generate the assembler
> > strings for the asm statements. In C++ there would be a typesafe alternative
> > using templates and constexpr to generate the assembler strings, but
> > unfortunately the asm statement requires plain string literals, so this
> > doesn't work.
> > 
> > This patch modifies the C++ parser to accept strings generated by
> > constexpr instead of just plain strings. This requires new syntax
> > because e.g. asm("..." : "r" (expr)) would be ambigious with a function
> > call. I chose () to make it unique. For example now you can write
> > 
> > constexpr const char *genasm() { return "insn"; }
> > constexpr const char *genconstraint() { return "r"; }
> > 
> > 	asm(genasm() :: (genconstraint()) (input));
> 
> Looks plausible.  What happens when someone forgets the parens, as seems a
> likely mistake?

constexpr-asm-1.C:27:13: error: expected string-literal before ‘genfoo’
   27 |         asm(genfoo() : genoutput() (a) : geninput() (1) :
   genclobber());
         |             ^~~~~~


Admittedly not great, I will try to give a better message.


-Andi

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-06-03 15:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-03  3:45 [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse Andi Kleen
2024-06-03  3:45 ` [PATCH v2 2/2] C++: Support constexpr strings for asm statements Andi Kleen
2024-06-03 15:01   ` Jason Merrill
2024-06-03 15:53     ` Andi Kleen
2024-06-03 14:36 ` [PATCH v2 1/2] Factor out static_assert constexpr string extraction for reuse Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).