From patchwork Wed Jun 5 04:45:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andi Kleen X-Patchwork-Id: 1943647 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=PzIYDmKV; 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 4VvFNd1Y01z20Q5 for ; Wed, 5 Jun 2024 14:49:05 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5E6093816B1B for ; Wed, 5 Jun 2024 04:49:03 +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.14]) by sourceware.org (Postfix) with ESMTPS id 287703816B00 for ; Wed, 5 Jun 2024 04:48:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 287703816B00 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 287703816B00 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.14 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1717562923; cv=none; b=ESqCELDkEbkaECBWBE3+t2XCHu5crT4w32EhRb7Ypm2RJY+D3kOb3gExD0sy4uJb5PDQt3rD/i/WmZfPm0ZvXLxtrNX1Lf1LP2fElHDHXe6SbHDOObRBAlEZz3z3HZjjt7aPgfrQxvTQtgdIixnsuP5Z4FZh0uphRAOMmh/X/VQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1717562923; c=relaxed/simple; bh=7mp3PC4VcKjmLQH5lrzm2mPAXtgfFawtbHI2IzSAntI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=X7TCB23TLYNSX36f7jM+n+cYf6KjLnjx0udXp6huaw0xPdy4otXhsXfdXTVDEo3XKc29U4ccmbYxMKOZknNQLsVfS9uKK9lOrSg3RrgbHIydVj57+9a32S9haizkcO6GpjezJQv/tLTm5H1E2CR3H3/SPkNxpf22/3sSSoxhg4I= 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=1717562920; x=1749098920; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=7mp3PC4VcKjmLQH5lrzm2mPAXtgfFawtbHI2IzSAntI=; b=PzIYDmKVm9JrCk0IW92qH75HFR9uWid42JxZlDSwxxCEUkoNgkHRHn27 JkLMinxv55S17sNrj6IarWDOTI3CGDBzZ367pFJsvCvaMuA19TmCFeXiP nNYnD5elDTy1GlA6OAgfDRDgizD/GBHf/hgVjd3rcKkKo089fjtBopmc6 ekOV4MBDCHKtFFD9mXL5CL4TPbQqpsVHF/0KUZDpy4H1tlFRrsb4u4JON 5SECj3nmYXro3du/iFboWCP9SRTY6iv0u4slXdiklMNTYHSnbBOFYrbvq Z4eH7WPkEQBPFMnndzCk6mTxWUWo6u/Lr2p2MZ6zt8pg8L20YMko/GBoI Q==; X-CSE-ConnectionGUID: +4TTGTm/QQ2sYoj6J2jIGA== X-CSE-MsgGUID: s7rrVe9ETROl/MjGBUGhBw== X-IronPort-AV: E=McAfee;i="6600,9927,11093"; a="17969316" X-IronPort-AV: E=Sophos;i="6.08,215,1712646000"; d="scan'208";a="17969316" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Jun 2024 21:48:38 -0700 X-CSE-ConnectionGUID: Nn9TXHTjRjmnEBOzt+Bupw== X-CSE-MsgGUID: bdX5Bz2hSASgRUMCwfRbeQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.08,215,1712646000"; d="scan'208";a="37463368" Received: from tassilo.jf.intel.com ([10.54.38.190]) by fmviesa008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Jun 2024 21:48:36 -0700 From: Andi Kleen To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, jakub@redhat.com, Andi Kleen Subject: [PATCH v3 1/2] Factor out static_assert constexpr string extraction for reuse Date: Tue, 4 Jun 2024 21:45:21 -0700 Message-ID: <20240605044827.2697253-2-ak@linux.intel.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240605044827.2697253-1-ak@linux.intel.com> References: <20240605044827.2697253-1-ak@linux.intel.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.1 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 The only semantics changes are slightly more vague error messages to generalize. gcc/cp/ChangeLog: * cp-tree.h (class cexpr_str): Add. * semantics.cc (finish_static_assert): Convert to use cexpr_str. (cexpr_str::type_check): Extract constexpr string code to here. (cexpr_str::extract): ... and here. gcc/testsuite/ChangeLog: * g++.dg/cpp26/static_assert1.C: Update to new error message. --- gcc/cp/cp-tree.h | 18 ++ gcc/cp/semantics.cc | 256 +++++++++++--------- gcc/testsuite/g++.dg/cpp0x/udlit-error1.C | 2 +- gcc/testsuite/g++.dg/cpp26/static_assert1.C | 32 +-- 4 files changed, 176 insertions(+), 132 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 565e4a9290e2..3446041937b2 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9015,6 +9015,24 @@ struct push_access_scope_guard } }; +/* Extracting strings from constexpr. */ + +class cexpr_str +{ +public: + cexpr_str (tree message) : message(message) {} + cexpr_str (const cexpr_str &) = delete; + ~cexpr_str () { XDELETEVEC (buf); } + + bool type_check (location_t location); + bool extract (location_t location, const char * & msg, int &len); + tree message; +private: + tree message_data = NULL_TREE; + tree message_sz = NULL_TREE; + char *buf = nullptr; +}; + /* 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..5cf698185ba4 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -11558,28 +11558,18 @@ init_cp_semantics (void) } -/* 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. 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 +cexpr_str::type_check (location_t location) { tsubst_flags_t complain = tf_warning_or_error; - tree message_sz = NULL_TREE, message_data = NULL_TREE; 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)) @@ -11595,10 +11585,10 @@ finish_static_assert (tree condition, tree message, location_t location, false, complain); if (message_sz == error_mark_node || message_data == error_mark_node) { - error_at (location, "% message must be a string " - "literal or object with % and " - "% members"); - return; + error_at (location, "constexpr string must be a string " + "literal or object with % and " + "% members"); + return false; } releasing_vec size_args, data_args; message_sz = finish_call_expr (message_sz, &size_args, false, false, @@ -11606,26 +11596,144 @@ finish_static_assert (tree condition, tree message, location_t location, message_data = finish_call_expr (message_data, &data_args, false, false, complain); if (message_sz == error_mark_node || message_data == error_mark_node) - return; + return false; message_sz = build_converted_constant_expr (size_type_node, message_sz, - complain); + complain); if (message_sz == error_mark_node) { - error_at (location, "% message % " - "must be implicitly convertible to " - "%"); - return; + error_at (location, "constexpr string % " + "must be implicitly convertible to " + "%"); + return false; } message_data = build_converted_constant_expr (const_string_type_node, - message_data, complain); + message_data, complain); if (message_data == error_mark_node) { - error_at (location, "% message % " - "must be implicitly convertible to " - "%"); - return; + error_at (location, "constexpr string % " + "must be implicitly convertible to " + "%"); + return false; } } + return true; +} + +/* Extract constant string at LOCATION into output string MSG without LEN. + Returns true if successfull, otherwise false. */ + +bool +cexpr_str::extract (location_t location, const char * & msg, int &len) +{ + tsubst_flags_t complain = tf_warning_or_error; + + msg = 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, + "constexpr string % " + "must be a constant expression"); + return false; + } + else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz) + != tree_to_uhwi (msz)) + { + error_at (location, + "constexpr string message % " + "%qE too large", msz); + return false; + } + 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, + "constexpr string % " + "must be a constant expression", i); + return false; + } + 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, + "constexpr string % " + "must be a core constant expression"); + return false; + } + } + } + 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; + } + + return true; +} + +/* 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; + + cexpr_str cstr(message); + if (!cstr.type_check (location)) + return; /* Save the condition in case it was a concept check. */ tree orig_condition = condition; @@ -11638,7 +11746,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 +11779,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, - "% message % " - "must be a constant expression"); - return; - } - else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz) - != tree_to_uhwi (msz)) - { - error_at (location, - "% message % " - "%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, - "% message % " - "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, - "% message % " - "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 (!cstr.extract (location, msg, len)) + return; /* See if we can find which clause was failing (for logical AND). */ tree bad = find_failing_clause (NULL, orig_condition); @@ -11768,8 +11796,6 @@ finish_static_assert (tree condition, tree message, location_t location, else error_at (cloc, "static assertion failed: %.*s", len, msg); - XDELETEVEC (buf); - diagnose_failing_condition (bad, cloc, show_expr_p); } else if (condition && condition != error_mark_node) diff --git a/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C b/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C index 6d6cd454540a..ea939c52c339 100644 --- a/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C +++ b/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C @@ -12,7 +12,7 @@ void operator""_x(const char *, decltype(sizeof(0))); extern "C"_x { void g(); } // { dg-error "before user-defined string literal" } static_assert(true, "foo"_x); // { dg-error "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "invalid use of 'void'" "" { target *-*-* } .-2 } [[deprecated("oof"_x)]] // { dg-error "string literal with user-defined suffix is invalid in this context" "" { target c++26 } } diff --git a/gcc/testsuite/g++.dg/cpp26/static_assert1.C b/gcc/testsuite/g++.dg/cpp26/static_assert1.C index 59724ae32ce3..7840b6b04d27 100644 --- a/gcc/testsuite/g++.dg/cpp26/static_assert1.C +++ b/gcc/testsuite/g++.dg/cpp26/static_assert1.C @@ -6,25 +6,25 @@ static_assert (true, ""); static_assert (true, ("")); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "request for member 'size' in '\\\(\\\"\\\"\\\)', which is of non-class type 'const char \\\[1\\\]'" "" { target *-*-* } .-2 } static_assert (true, "" + 0); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "request for member 'size' in '\\\(const char\\\*\\\)\\\"\\\"', which is of non-class type 'const char\\\*'" "" { target *-*-* } .-2 } static_assert (true, 0); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "request for member 'size' in '0', which is of non-class type 'int'" "" { target *-*-* } .-2 } struct A {}; static_assert (true, A {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "'struct A' has no member named 'size'" "" { target *-*-* } .-2 } struct B { int size; }; static_assert (true, B {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "'struct B' has no member named 'data'" "" { target *-*-* } .-2 } struct C { constexpr int size () const { return 0; } }; static_assert (true, C {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "'struct C' has no member named 'data'" "" { target *-*-* } .-2 } struct D { constexpr int size () const { return 0; } int data; }; static_assert (true, D {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } @@ -37,13 +37,13 @@ static_assert (true, E {}); // { dg-warning "'static_assert' with non-string mes struct F { constexpr const char *size () const { return ""; } constexpr const char *data () const { return ""; } }; static_assert (true, F {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message 'size\\\(\\\)' must be implicitly convertible to 'std::size_t'" "" { target *-*-* } .-1 } + // { dg-error "constexpr string 'size\\\(\\\)' must be implicitly convertible to 'std::size_t'" "" { target *-*-* } .-1 } // { dg-error "could not convert 'F\\\(\\\).F::size\\\(\\\)' from 'const char\\\*' to '\[^']*'" "" { target *-*-* } .-2 } // { dg-error "conversion from 'const char\\\*' to '\[^']*' in a converted constant expression" "" { target *-*-* } .-3 } struct G { constexpr long size () const { return 0; } constexpr float data () const { return 0.0f; } }; static_assert (true, G {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 } + // { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 } // { dg-error "could not convert 'G\\\(\\\).G::data\\\(\\\)' from 'float' to 'const char\\\*'" "" { target *-*-* } .-2 } struct H { short size () const { return 0; } constexpr const char *data () const { return ""; } }; @@ -59,7 +59,7 @@ static_assert (true, J (1)); // { dg-warning "'static_assert' with non-string me static_assert (false, J (0)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } // { dg-error "static assertion failed" "" { target *-*-* } .-1 } static_assert (false, J (1)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 } + // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 } struct K { constexpr operator int () { return 4; } }; struct L { constexpr operator const char * () { return "test"; } }; struct M { constexpr K size () const { return {}; } @@ -72,7 +72,7 @@ struct N { constexpr int size () const { return 3; } constexpr const char *data () const { return new char[3] { 'b', 'a', 'd' }; } }; // { dg-error "'\\\* N\\\(\\\).N::data\\\(\\\)' is not a constant expression because allocated storage has not been deallocated" "" { target c++20 } } static_assert (true, N {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } } static_assert (false, N {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } } - // { dg-error "'static_assert' message 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } .-1 } + // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } .-1 } #endif constexpr const char a[] = { 't', 'e', 's', 't' }; struct O { constexpr int size () const { return 4; } @@ -133,7 +133,7 @@ static_assert (false, string_view (4, "testwithextrachars")); // { dg-warning "' // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 } static_assert (false, string_view (42, "test")); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } // { dg-error "array subscript value '41' is outside the bounds of array type 'const char \\\[5\\\]'" "" { target *-*-* } .-1 } - // { dg-error "'static_assert' message 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-2 } + // { dg-error "constexpr string 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-2 } template struct array { @@ -143,7 +143,7 @@ struct array { }; static_assert (true, array { 'O', 'K' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } static_assert (true, array { L'O', L'K' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - // { dg-error "'static_assert' message 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 } + // { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 } // { dg-error "could not convert 'array{const wchar_t \\\[2\\\]{\[0-9]+, \[0-9]+}}.array::data\\\(\\\)' from 'const wchar_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-2 } static_assert (false, array { 't', 'e', 's', 't' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 } @@ -235,7 +235,7 @@ namespace NN template struct G { static_assert (false, T{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } - }; // { dg-error "'static_assert' message must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } + }; // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 } // { dg-error "request for member 'size' in '0', which is of non-class type 'long int'" "" { target *-*-* } .-2 } F fe; G gl; @@ -263,7 +263,7 @@ namespace NN static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '' is not a constant expression" "" { target c++14 } } static_assert (true, J{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } } static_assert (false, J{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } } - // { dg-error "'static_assert' message 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } .-1 } + // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } .-1 } #endif #if __cpp_if_consteval >= 202106L struct K { @@ -286,14 +286,14 @@ namespace NN }; static_assert (true, M{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } } static_assert (false, M{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } } - // { dg-error "'static_assert' message 'size\\\(\\\)' must be a constant expression" "" { target c++23 } .-1 } + // { dg-error "'constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } .-1 } struct N { static constexpr int size () { return 4; } static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '' is not a constant expression" "" { target c++23 } } }; static_assert (true, N{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } } static_assert (false, N{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } } - // { dg-error "'static_assert' message 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } .-1 } + // { dg-error "'constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } .-1 } #endif struct O { constexpr int operator () () const { return 12; } }; struct P { constexpr const char *operator () () const { return "another test"; } }; From patchwork Wed Jun 5 04:45:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andi Kleen X-Patchwork-Id: 1943648 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=lxwBuhD/; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; 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 [IPv6:2620:52:3:1:0:246e:9693:128c]) (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 4VvFPr55hJz20Tb for ; Wed, 5 Jun 2024 14:50:08 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8622338053D5 for ; Wed, 5 Jun 2024 04:50:06 +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.14]) by sourceware.org (Postfix) with ESMTPS id 9D71D3816B08 for ; Wed, 5 Jun 2024 04:48:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9D71D3816B08 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 9D71D3816B08 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.14 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1717562928; cv=none; b=RyYmVOwbwkaw/ib2KFc7RJKZkVH5YAfP8uDs47GUxAMhqeBbwTCTashYDvOpLJrcPbEqgR/MdLQ863BnM6nkt6zoNtx1cN7e9TYzKSyII/bak0sT2HsPFMpFsSBx3eQuJWoA5u31rj79JVYp5bbuzPdkjA1Ej4IBQ3CTBwwqoTU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1717562928; c=relaxed/simple; bh=5iuO4ey3uxh5siuSplnr9iS/gLNyPn+on1ZmWh0N6c0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=i5m+mOo9DMZFRg43SLVlgFLU0Wog+CxGx+4y8QcpyPdemX/RJIjG8wLl4JWsRR4t4HW/c4Vnif/21hjcNk3541K3jr7Wu6/ZoT5hJ3Vyqa2q7xVAMtYsiSjuaalhimpGB+I4UKtwA+HPit2Pqb8PjRKx5rrNlQ/m7rXYigTHWSY= 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=1717562924; x=1749098924; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=5iuO4ey3uxh5siuSplnr9iS/gLNyPn+on1ZmWh0N6c0=; b=lxwBuhD/pvL7rlMbjukVQATjJ1XlifcCGI5mZJxRUiv1BxIiXNEW24lw eYqmwq5ZaHYHuNwrGEDCmmIYJbF3wcLiJJPqC6xkAe3pQrswm5vNEkn+S PBYeP6vMWsB03LL9dYSPwYNcCF4FBMOx4gxRvkwvlHFkyMmCa7ZOq5Wh+ cCiuxk8NCq91+HQVA4Mh0PzTh23p7DzV5ArnwAidHD9MRVHgiu1hTyu8T Tp97Hs4SK2q7Jh9nOHdQOSePV2iWLt9nVPOn6tEj2vk8AFP8c2mdFbrL4 L6DI9448cDGXLEntCrOYxCk4QYlZcACImQ8i8dYrm4MEcWc5bIS4Y0hxj A==; X-CSE-ConnectionGUID: a+F+cLQCR0yg0hmp76FSGA== X-CSE-MsgGUID: yhR10ICvTluFxY3Cf0bgJg== X-IronPort-AV: E=McAfee;i="6600,9927,11093"; a="17969318" X-IronPort-AV: E=Sophos;i="6.08,215,1712646000"; d="scan'208";a="17969318" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Jun 2024 21:48:38 -0700 X-CSE-ConnectionGUID: MfK9hZmRTrq9xAOtlr3urw== X-CSE-MsgGUID: oK4lEIHvQYaeIE+AvGSneQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.08,215,1712646000"; d="scan'208";a="37463370" Received: from tassilo.jf.intel.com ([10.54.38.190]) by fmviesa008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Jun 2024 21:48:37 -0700 From: Andi Kleen To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, jakub@redhat.com, Andi Kleen Subject: [PATCH v3 2/2] C++: Support constexpr strings for asm statements Date: Tue, 4 Jun 2024 21:45:22 -0700 Message-ID: <20240605044827.2697253-3-ak@linux.intel.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240605044827.2697253-1-ak@linux.intel.com> References: <20240605044827.2697253-1-ak@linux.intel.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.1 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 __GXX_CONSTEXPR_ASM__ gcc/cp/ChangeLog: * parser.cc (cp_parser_asm_string_expression): New function to handle constexpr strings for asm. (cp_parser_asm_definition): Use cp_parser_asm_string_expression. (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/ext/asm11.C: Adjust to new error message. * g++.dg/ext/asm9.C: Dito. * g++.dg/parse/asm1.C: Dito. * g++.dg/parse/asm2.C: Dito. * g++.dg/parse/asm3.C: Dito. * g++.dg/cpp1z/constexpr-asm-1.C: New test. * g++.dg/cpp1z/constexpr-asm-2.C: New test. * g++.dg/cpp1z/constexpr-asm-3.C: New test. --- gcc/c-family/c-cppbuiltin.cc | 5 +- gcc/cp/parser.cc | 86 ++++++++++++++------ 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 +++++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-3.C | 31 +++++++ gcc/testsuite/g++.dg/ext/asm11.C | 22 ++--- gcc/testsuite/g++.dg/ext/asm9.C | 3 +- gcc/testsuite/g++.dg/parse/asm1.C | 1 + gcc/testsuite/g++.dg/parse/asm2.C | 1 + gcc/testsuite/g++.dg/parse/asm3.C | 1 + 11 files changed, 194 insertions(+), 42 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 create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-asm-3.C diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index d9b84a0f1b97..dfd8f6f0c485 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, "__GXX_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..1a1baa814373 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -22824,6 +22824,53 @@ 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) +{ + cp_token *tok = cp_lexer_peek_token (parser->lexer); + + if (tok->type == CPP_OPEN_PAREN) + { + 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); + 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); + cexpr_str cstr (string); + if (!cstr.type_check (tok->location)) + return error_mark_node; + const char *msg; + int len; + if (!cstr.extract (tok->location, msg, len)) + return error_mark_node; + parens.require_close (parser); + string = build_string (len, msg); + return string; + } + else if (!cp_parser_is_string_literal (tok)) + { + error_at (tok->location, + "expected string-literal or constexpr in brackets"); + return error_mark_node; + } + return cp_parser_string_literal (parser, false, false); +} + /* Parse an asm-definition. asm-qualifier: @@ -22836,19 +22883,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 +23014,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); if (string == error_mark_node) { cp_parser_skip_to_closing_parenthesis (parser, true, false, @@ -29627,7 +29673,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 +29696,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); /* Look for the `)'. */ parens.require_close (parser); @@ -29667,8 +29711,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 +29745,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); /* Look for the `('. */ matching_parens parens; @@ -29737,8 +29779,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 +29793,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); /* 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..17e26c5004c1 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{__GXX_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..7cc6b37d6208 --- /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..7d0eb590afbe --- /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" } } */ diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-3.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-3.C new file mode 100644 index 000000000000..d33631876bdc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-3.C @@ -0,0 +1,31 @@ +/* { 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 () : /* { dg-error "expected string-literal or constexpr in brackets" } */ + genoutput() (a) : + geninput() (1) : + genclobber()); +} diff --git a/gcc/testsuite/g++.dg/ext/asm11.C b/gcc/testsuite/g++.dg/ext/asm11.C index 7939aacc0f42..97a299a7ecb2 100644 --- a/gcc/testsuite/g++.dg/ext/asm11.C +++ b/gcc/testsuite/g++.dg/ext/asm11.C @@ -6,15 +6,15 @@ void foo () { int i; - asm (); // { dg-error "expected string-literal before" } - asm (1); // { dg-error "expected string-literal before" } - asm (int); // { dg-error "expected string-literal before" } - asm (: "=r" (i)); // { dg-error "expected string-literal before" } - asm (1 : "=r" (i)); // { dg-error "expected string-literal before" } - asm (int : "=r" (i)); // { dg-error "expected string-literal before" } - asm (: : "r" (i)); // { dg-error "expected string-literal before" } - asm (1 : : "r" (i)); // { dg-error "expected string-literal before" } - asm (int : : "r" (i)); // { dg-error "expected string-literal before" } - asm (: : : "memory"); // { dg-error "expected string-literal before" } - asm (1 : : : "memory"); // { dg-error "expected string-literal before" } + asm (); // { dg-error "expected string-literal" } + asm (1); // { dg-error "expected string-literal" } + asm (int); // { dg-error "expected string-literal" } + asm (: "=r" (i)); // { dg-error "expected string-literal" } + asm (1 : "=r" (i)); // { dg-error "expected string-literal" } + asm (int : "=r" (i)); // { dg-error "expected string-literal" } + asm (: : "r" (i)); // { dg-error "expected string-literal" } + asm (1 : : "r" (i)); // { dg-error "expected string-literal" } + asm (int : : "r" (i)); // { dg-error "expected string-literal" } + asm (: : : "memory"); // { dg-error "expected string-literal" } + asm (1 : : : "memory"); // { dg-error "expected string-literal" } } diff --git a/gcc/testsuite/g++.dg/ext/asm9.C b/gcc/testsuite/g++.dg/ext/asm9.C index 9daa01bbf5f9..3bce845c97a0 100644 --- a/gcc/testsuite/g++.dg/ext/asm9.C +++ b/gcc/testsuite/g++.dg/ext/asm9.C @@ -3,5 +3,6 @@ void foo() { - asm("" ::: X); // { dg-error "before" } + asm("" ::: X); // { dg-error "string-literal" } + // { dg-error "before" "" { target *-*-* } .-1 } } diff --git a/gcc/testsuite/g++.dg/parse/asm1.C b/gcc/testsuite/g++.dg/parse/asm1.C index 009ffbd3ad35..9ce24b564dce 100644 --- a/gcc/testsuite/g++.dg/parse/asm1.C +++ b/gcc/testsuite/g++.dg/parse/asm1.C @@ -3,4 +3,5 @@ void foo() { asm("" : 0); // { dg-error "numeric constant|token" } + // { dg-error "string-literal" "" { target *-*-* } .-1 } } diff --git a/gcc/testsuite/g++.dg/parse/asm2.C b/gcc/testsuite/g++.dg/parse/asm2.C index 09924c9c7c31..d4e1c6e80de5 100644 --- a/gcc/testsuite/g++.dg/parse/asm2.C +++ b/gcc/testsuite/g++.dg/parse/asm2.C @@ -3,4 +3,5 @@ void foo() { asm("" :: 0); // { dg-error "numeric constant|token" } + // { dg-error "string-literal" "" { target *-*-* } .-1 } } diff --git a/gcc/testsuite/g++.dg/parse/asm3.C b/gcc/testsuite/g++.dg/parse/asm3.C index 91f19e48cd5a..dccee5ac6813 100644 --- a/gcc/testsuite/g++.dg/parse/asm3.C +++ b/gcc/testsuite/g++.dg/parse/asm3.C @@ -3,4 +3,5 @@ void foo() { asm ("%[x]" : [0](x)); // { dg-error "numeric constant|token" } + // { dg-error "string-literal" "" { target *-*-* } .-1 } }