llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: cor3ntin (cor3ntin) <details> <summary>Changes</summary> Implements GCC's constexpr string ASM extension https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html --- Patch is 77.41 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131003.diff 25 Files Affected: - (modified) clang/docs/LanguageExtensions.rst (+18) - (modified) clang/docs/ReleaseNotes.rst (+9) - (modified) clang/include/clang/AST/Expr.h (+4) - (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+4-4) - (modified) clang/include/clang/AST/Stmt.h (+28-32) - (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+4-1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+23-13) - (modified) clang/include/clang/Basic/Features.def (+1) - (modified) clang/include/clang/Sema/Sema.h (+17-3) - (modified) clang/lib/AST/ASTImporter.cpp (+6-6) - (modified) clang/lib/AST/ExprConstant.cpp (+40-11) - (modified) clang/lib/AST/Stmt.cpp (+78-48) - (modified) clang/lib/AST/StmtPrinter.cpp (+4-4) - (modified) clang/lib/AST/StmtProfile.cpp (+4-4) - (modified) clang/lib/CodeGen/CGStmt.cpp (+9-8) - (modified) clang/lib/Parse/ParseStmtAsm.cpp (+3-3) - (modified) clang/lib/Parse/Parser.cpp (+27-14) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+72-34) - (modified) clang/lib/Sema/SemaStmtAsm.cpp (+107-65) - (modified) clang/lib/Sema/TreeTransform.h (+36-13) - (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+5-5) - (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+4-4) - (added) clang/test/CodeGenCXX/gnu-asm-constexpr.cpp (+38) - (modified) clang/test/Parser/asm.cpp (+45) - (added) clang/test/Sema/gnu-asm-constexpr.cpp (+127) ``````````diff diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index f8d1bc9fe2624..a990eefc8eeac 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1958,6 +1958,24 @@ references can be used instead of numeric references. return -1; } + +Constexpr strings in GNU ASM statememts +======================================= + +In C++11 mode (and greater), Clang supports specifying the template, +constraints, and clobber strings with a parenthesized constant expression +producing an object with ``data`` and ``size`` member functions, +such as ``std::string``. + +Query for this feature with ``__has_extension(gnu_asm_constexpr_strings)``. + +.. code-block:: c++ + + int foo() { + asm((std::string_view("nop")) ::: (std::string_view("memory"))); + } + + Objective-C Features ==================== diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 48da5558b3f38..c438b9a997ad8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -74,6 +74,15 @@ What's New in Clang |release|? C++ Language Changes -------------------- +- Similarly to GCC, Clang now supports constant expressions in + the strings of a GNU ``asm`` statement. + + .. code-block:: c++ + + int foo() { + asm((std::string_view("nop")) ::: (std::string_view("memory"))); + } + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index cfe49acf20b77..28437b5629b00 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -787,6 +787,10 @@ class Expr : public ValueStmt { const Expr *PtrExpression, ASTContext &Ctx, EvalResult &Status) const; + bool EvaluateCharRangeAsString(APValue &Result, const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const; + /// If the current Expr can be evaluated to a pointer to a null-terminated /// constant string, return the constant string (without the terminating /// null). diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index fac4c10987157..87a6c22b35ee8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2410,15 +2410,15 @@ DEF_TRAVERSE_DECL(ImplicitConceptSpecializationDecl, { } DEF_TRAVERSE_STMT(GCCAsmStmt, { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmString()); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmStringExpr()); for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintLiteral(I)); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintExpr(I)); } for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintLiteral(I)); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintExpr(I)); } for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberStringLiteral(I)); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberExpr(I)); } // children() iterates over inputExpr and outputExpr. }) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 604ac51d478cf..88ae3aeefa869 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -3193,7 +3193,7 @@ class AsmStmt : public Stmt { /// getOutputConstraint - Return the constraint string for the specified /// output operand. All output constraints are known to be non-empty (either /// '=' or '+'). - StringRef getOutputConstraint(unsigned i) const; + std::string getOutputConstraint(unsigned i) const; /// isOutputPlusConstraint - Return true if the specified output constraint /// is a "+" constraint (which is both an input and an output) or false if it @@ -3214,14 +3214,14 @@ class AsmStmt : public Stmt { /// getInputConstraint - Return the specified input constraint. Unlike output /// constraints, these can be empty. - StringRef getInputConstraint(unsigned i) const; + std::string getInputConstraint(unsigned i) const; const Expr *getInputExpr(unsigned i) const; //===--- Other ---===// unsigned getNumClobbers() const { return NumClobbers; } - StringRef getClobber(unsigned i) const; + std::string getClobber(unsigned i) const; static bool classof(const Stmt *T) { return T->getStmtClass() == GCCAsmStmtClass || @@ -3302,21 +3302,20 @@ class GCCAsmStmt : public AsmStmt { friend class ASTStmtReader; SourceLocation RParenLoc; - StringLiteral *AsmStr; + Expr *AsmStr; // FIXME: If we wanted to, we could allocate all of these in one big array. - StringLiteral **Constraints = nullptr; - StringLiteral **Clobbers = nullptr; + Expr **Constraints = nullptr; + Expr **Clobbers = nullptr; IdentifierInfo **Names = nullptr; unsigned NumLabels = 0; public: GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple, bool isvolatile, unsigned numoutputs, unsigned numinputs, - IdentifierInfo **names, StringLiteral **constraints, Expr **exprs, - StringLiteral *asmstr, unsigned numclobbers, - StringLiteral **clobbers, unsigned numlabels, - SourceLocation rparenloc); + IdentifierInfo **names, Expr **constraints, Expr **exprs, + Expr *asmstr, unsigned numclobbers, Expr **clobbers, + unsigned numlabels, SourceLocation rparenloc); /// Build an empty inline-assembly statement. explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty) {} @@ -3326,9 +3325,11 @@ class GCCAsmStmt : public AsmStmt { //===--- Asm String Analysis ---===// - const StringLiteral *getAsmString() const { return AsmStr; } - StringLiteral *getAsmString() { return AsmStr; } - void setAsmString(StringLiteral *E) { AsmStr = E; } + const Expr *getAsmStringExpr() const { return AsmStr; } + Expr *getAsmStringExpr() { return AsmStr; } + void setAsmString(Expr *E) { AsmStr = E; } + + std::string getAsmString() const; /// AsmStringPiece - this is part of a decomposed asm string specification /// (for use with the AnalyzeAsmString function below). An asm string is @@ -3397,14 +3398,12 @@ class GCCAsmStmt : public AsmStmt { return {}; } - StringRef getOutputConstraint(unsigned i) const; + std::string getOutputConstraint(unsigned i) const; - const StringLiteral *getOutputConstraintLiteral(unsigned i) const { - return Constraints[i]; - } - StringLiteral *getOutputConstraintLiteral(unsigned i) { + const Expr *getOutputConstraintExpr(unsigned i) const { return Constraints[i]; } + Expr *getOutputConstraintExpr(unsigned i) { return Constraints[i]; } Expr *getOutputExpr(unsigned i); @@ -3425,12 +3424,12 @@ class GCCAsmStmt : public AsmStmt { return {}; } - StringRef getInputConstraint(unsigned i) const; + std::string getInputConstraint(unsigned i) const; - const StringLiteral *getInputConstraintLiteral(unsigned i) const { + const Expr *getInputConstraintExpr(unsigned i) const { return Constraints[i + NumOutputs]; } - StringLiteral *getInputConstraintLiteral(unsigned i) { + Expr *getInputConstraintExpr(unsigned i) { return Constraints[i + NumOutputs]; } @@ -3441,6 +3440,8 @@ class GCCAsmStmt : public AsmStmt { return const_cast<GCCAsmStmt*>(this)->getInputExpr(i); } + static std::string ExtractStringFromGCCAsmStmtComponent(const Expr *E); + //===--- Labels ---===// bool isAsmGoto() const { @@ -3489,12 +3490,9 @@ class GCCAsmStmt : public AsmStmt { private: void setOutputsAndInputsAndClobbers(const ASTContext &C, IdentifierInfo **Names, - StringLiteral **Constraints, - Stmt **Exprs, - unsigned NumOutputs, - unsigned NumInputs, - unsigned NumLabels, - StringLiteral **Clobbers, + Expr **Constraints, Stmt **Exprs, + unsigned NumOutputs, unsigned NumInputs, + unsigned NumLabels, Expr **Clobbers, unsigned NumClobbers); public: @@ -3505,12 +3503,10 @@ class GCCAsmStmt : public AsmStmt { /// This returns -1 if the operand name is invalid. int getNamedOperand(StringRef SymbolicName) const; - StringRef getClobber(unsigned i) const; + std::string getClobber(unsigned i) const; - StringLiteral *getClobberStringLiteral(unsigned i) { return Clobbers[i]; } - const StringLiteral *getClobberStringLiteral(unsigned i) const { - return Clobbers[i]; - } + Expr *getClobberExpr(unsigned i) { return Clobbers[i]; } + const Expr *getClobberExpr(unsigned i) const { return Clobbers[i]; } SourceLocation getBeginLoc() const LLVM_READONLY { return AsmLoc; } SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 092a55f9e9e0c..4dc956f7ae6f7 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -336,7 +336,10 @@ def warn_cxx20_compat_label_end_of_compound_statement : Warning< def err_address_of_label_outside_fn : Error< "use of address-of-label extension outside of a function body">; def err_asm_operand_wide_string_literal : Error< - "cannot use %select{unicode|wide|an empty}0 string literal in 'asm'">; + "cannot use %select{unicode|wide}0 string literal in 'asm'">; + +def err_asm_expected_string : Error< + "expected string literal %select{or parenthesized constant expression |}0in 'asm'">; def err_expected_selector_for_method : Error< "expected selector for Objective-C method">; def err_expected_property_name : Error<"expected property name">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4bdbb797c2b86..2e8db1a2249e4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1663,23 +1663,30 @@ def err_static_assert_requirement_failed : Error< "static assertion failed due to requirement '%0'%select{: %2|}1">; def note_expr_evaluates_to : Note< "expression evaluates to '%0 %1 %2'">; -def err_static_assert_invalid_message : Error< - "the message in a static assertion must be a string literal or an " + + +def subst_user_defined_msg : TextSubstitution< + "%select{the message|the expression}0 in " + "%select{a static assertion|this asm operand}0">; + +def err_user_defined_msg_invalid : Error< + "%sub{subst_user_defined_msg}0 must be a string literal or an " "object with 'data()' and 'size()' member functions">; -def err_static_assert_missing_member_function : Error< - "the message object in this static assertion is missing %select{" +def err_user_defined_msg_missing_member_function : Error< + "the %select{message|string}0 object in " + "%select{this static assertion|this asm operand}0 is missing %select{" "a 'size()' member function|" "a 'data()' member function|" - "'data()' and 'size()' member functions}0">; -def err_static_assert_invalid_mem_fn_ret_ty : Error< - "the message in a static assertion must have a '%select{size|data}0()' member " - "function returning an object convertible to '%select{std::size_t|const char *}0'">; -def warn_static_assert_message_constexpr : Warning< - "the message in this static assertion is not a " - "constant expression">, + "'data()' and 'size()' member functions}1">; +def err_user_defined_msg_invalid_mem_fn_ret_ty : Error< + "%sub{subst_user_defined_msg}0 must have a '%select{size|data}1()' member " + "function returning an object convertible to '%select{std::size_t|const char *}1'">; +def warn_user_defined_msg_constexpr : Warning< + "%select{the message|the expression}0 in " + "%select{this static assertion|this asm operand}0 is not a constant expression">, DefaultError, InGroup<DiagGroup<"invalid-static-assert-message">>; -def err_static_assert_message_constexpr : Error< - "the message in a static assertion must be produced by a " +def err_user_defined_msg_constexpr : Error< + "%sub{subst_user_defined_msg}0 must be produced by a " "constant expression">; def warn_consteval_if_always_true : Warning< @@ -9514,6 +9521,9 @@ def warn_redefine_extname_not_applied : Warning< // inline asm. let CategoryName = "Inline Assembly Issue" in { + def err_asm_operand_empty_string : Error< + "cannot use an empty string literal in 'asm'">; + def err_asm_pmf_through_constraint_not_permitted : Error<"cannot pass a pointer-to-member through register-constrained " "inline assembly parameter">; diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 92b1705d15227..05ce214935fad 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -306,6 +306,7 @@ EXTENSION(statement_attributes_with_gnu_syntax, true) EXTENSION(gnu_asm, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs_full, LangOpts.GNUAsm) +EXTENSION(gnu_asm_constexpr_strings, LangOpts.GNUAsm && LangOpts.CPlusPlus11) EXTENSION(matrix_types, LangOpts.MatrixTypes) EXTENSION(matrix_types_scalar_division, true) EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c034de0e633da..4043699912423 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5585,9 +5585,15 @@ class Sema final : public SemaBase { void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnFinishDelayedMemberInitializers(Decl *Record); - bool EvaluateStaticAssertMessageAsString(Expr *Message, std::string &Result, - ASTContext &Ctx, - bool ErrorOnInvalidMessage); + enum class StringEvaluationContext { StaticAssert = 0, Asm = 1 }; + + bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage); + bool EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage); + Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, Expr *AssertMessageExpr, SourceLocation RParenLoc); @@ -11040,6 +11046,7 @@ class Sema final : public SemaBase { ///@{ public: + ExprResult ActOnGCCAsmStmtString(Expr *Stm, bool ForAsmLabel); StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -15328,6 +15335,13 @@ void Sema::PragmaStack<Sema::AlignPackInfo>::Act(SourceLocation PragmaLocation, PragmaMsStackAction Action, llvm::StringRef StackSlotLabel, AlignPackInfo Value); + +inline const StreamingDiagnostic & +operator<<(const StreamingDiagnostic &DB, Sema::StringEvaluationContext Ctx) { + DB << llvm::to_underlying(Ctx); + return DB; +} + } // end namespace clang #endif diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 82180486f3c7c..1b507761ddf70 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6821,25 +6821,25 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) { Names.push_back(ToII); } - SmallVector<StringLiteral *, 4> Clobbers; + SmallVector<Expr *, 4> Clobbers; for (unsigned I = 0, E = S->getNumClobbers(); I != E; I++) { - if (auto ClobberOrErr = import(S->getClobberStringLiteral(I))) + if (auto ClobberOrErr = import(S->getClobberExpr(I))) Clobbers.push_back(*ClobberOrErr); else return ClobberOrErr.takeError(); } - SmallVector<StringLiteral *, 4> Constraints; + SmallVector<Expr *, 4> Constraints; for (unsigned I = 0, E = S->getNumOutputs(); I != E; I++) { - if (auto OutputOrErr = import(S->getOutputConstraintLiteral(I))) + if (auto OutputOrErr = import(S->getOutputConstraintExpr(I))) Constraints.push_back(*OutputOrErr); else return OutputOrErr.takeError(); } for (unsigned I = 0, E = S->getNumInputs(); I != E; I++) { - if (auto InputOrErr = import(S->getInputConstraintLiteral(I))) + if (auto InputOrErr = import(S->getInputConstraintExpr(I))) Constraints.push_back(*InputOrErr); else return InputOrErr.takeError(); @@ -6861,7 +6861,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) { ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc()); if (!AsmLocOrErr) return AsmLocOrErr.takeError(); - auto AsmStrOrErr = import(S->getAsmString()); + auto AsmStrOrErr = import(S->getAsmStringExpr()); if (!AsmStrOrErr) return AsmStrOrErr.takeError(); ExpectedSLoc RParenLocOrErr = import(S->getRParenLoc()); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index f8e8aaddbfdbd..7803b1026aab9 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17928,10 +17928,12 @@ std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const { return {}; } -bool Expr::EvaluateCharRangeAsString(std::string &Result, - const Expr *SizeExpression, - const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const { +template <typename T> +static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, + ASTContext &Ctx, + Expr::EvalResult &Status) { LValue String; EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); Info.InConstantContext = true; @@ -17943,6 +17945,13 @@ bool Expr::EvaluateCharRangeAsString(std::string &Result, uint64_t Size = SizeValue.getZExtValue(); + // FIXME: better protect against invalid or excessive sizes + if constexpr (std::is_same_v<APValue, T>) + Result = APValue(APValue::UninitArray{}, Size, Size); + else { + if (Size < Result.max_size()) + Result.reserve(Size); + } if (!::EvaluatePointer(PtrExpression, String, Info)) return false; @@ -17953,18 +17962,38 @@ bool Expr::EvaluateCharRangeAsString(std::string &Result, Char)) return false; - APSInt C = Char.getInt(); - Result.push_back(static_cast<char>(C.getExtValue())); + if constexpr (std::is_same_v<APValue, T>) { + Result.getArrayInitializedElt(I) = std::move(Char); + } else { + APSInt C = Char.getInt(); + + assert(C.getBitWidth() <= 8 && + "string element not representable in char"); + + Result.push_back(static_cast<char>(C.getExtValue())); + } + if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) return false; } - if (!Scope.destroy()) - return false; - if (!CheckMemoryLeaks(Info)) - return false; + return Scope.destroy() && CheckMemoryLeaks(Info); +} - return true; +bool Expr::EvaluateCharRangeAsString(std::string &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/131003 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits