From patchwork Mon Jun 3 03:45:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andi Kleen X-Patchwork-Id: 1942647 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=fI4vMQVx; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Vt04p2B23z20PW for ; Mon, 3 Jun 2024 13:46:00 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6A95E3959C51 for ; Mon, 3 Jun 2024 03:45:56 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.17]) by sourceware.org (Postfix) with ESMTPS id CBE84395249B for ; Mon, 3 Jun 2024 03:45:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CBE84395249B Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=linux.intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org CBE84395249B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.17 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1717386336; cv=none; b=rkLHnSQ8gKZ6BaoXwUqlQTuvs5pLoOASAqKe5yxYoUCDmwb3780k6pHLx2jbyREDcn+fwjzz+lXUrT4wdDCBJEsVEQUPaYxEUPn4vAjRHrxSqKSr1Wrp7kwiDMKzdpKLroUuXfmwi2Pca5VF7aEeDDqsc8AwFIyWUC8cgmWrl+M= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1717386336; c=relaxed/simple; bh=tM+fVfOitUQCmPmDvxh4CIYzxL4nV9Na8sHGNFAAcV4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=amOL/OzGIuUcpmXvvFD9SnVmg6K1jvPW5gsEewZgZmtPHaJcz3JIKPUk3XbLtWLyRWvFXKW+ezsD7hT1VJF9OEsvnqSFFbCXLQRwQ34Fq7anzRJALOk4tCjKn5mn0CtHC/xYz2p8XwggJLbTNTyfnJvRXeJrrB9ydaapGoGEC8I= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1717386333; x=1748922333; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=tM+fVfOitUQCmPmDvxh4CIYzxL4nV9Na8sHGNFAAcV4=; b=fI4vMQVx8Wqv/CZTzcSAWfesKRyRUii+iiRsZWS2XHAMlMOLuxeqCD7m KQXPa/4oLq8m3nrBUKsNpyZ6hbodc/7Lzr88EsmYhm7Guyg7QydtcpM59 VtKrlZl+awcVeENF4VTqFkx2t3LCkAVqbKV7ENNkJdTjDfiBx2Vdsq/Ov nuMHR2rI6rSfw6FGtPweyblgIhc2uCPdBrdAbtro0lYiuJdsbxukp5BQr PziyMRlgecSX/bqsEItZluE42nbdWbSwnkdUqBDvXvFy4IilRoKalY1Cl gS5wYcsdn0wZdB1n7egdJbWg6hdtihj0iPp5DnFHMSjVN09He5F09kfv2 Q==; X-CSE-ConnectionGUID: rNqglgVBTnWrU0bllpqAtQ== X-CSE-MsgGUID: T1mn241ZTlq0WgsnVOBUVg== X-IronPort-AV: E=McAfee;i="6600,9927,11091"; a="13989650" X-IronPort-AV: E=Sophos;i="6.08,210,1712646000"; d="scan'208";a="13989650" Received: from orviesa001.jf.intel.com ([10.64.159.141]) by orvoesa109.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jun 2024 20:45:32 -0700 X-CSE-ConnectionGUID: Sk4vuetjTUePR24JEzgzTA== X-CSE-MsgGUID: NwQwTrHOSc+NT/56uejaAA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.08,210,1712646000"; d="scan'208";a="74234184" Received: from tassilo.jf.intel.com ([10.54.38.190]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jun 2024 20:45:32 -0700 From: Andi Kleen To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, jason@redhat.com, Andi Kleen Subject: [PATCH v2 2/2] C++: Support constexpr strings for asm statements Date: Sun, 2 Jun 2024 20:45:20 -0700 Message-ID: <20240603034520.1552764-2-ak@linux.intel.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240603034520.1552764-1-ak@linux.intel.com> References: <20240603034520.1552764-1-ak@linux.intel.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org 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 +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 {'f','o','o'}) : + (array{'=','r'}) (a) : + (array{'r'}) (1) : + (array{'m','e','m','o','r','y'})); +} + +/* { dg-final { scan-assembler "foo" } } */