https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/131003
Implements GCC's constexpr string ASM extension https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html >From 84508166a9784701dad659881bf75f9764e53f54 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Wed, 12 Mar 2025 12:23:14 +0100 Subject: [PATCH] [Clang][WIP] Constant Expressions inside of gcc'asm strings Implements GCC's constexpr string ASM extension https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html --- clang/include/clang/AST/Expr.h | 5 + clang/include/clang/AST/RecursiveASTVisitor.h | 8 +- clang/include/clang/AST/Stmt.h | 50 +++--- .../clang/Basic/DiagnosticParseKinds.td | 5 +- .../clang/Basic/DiagnosticSemaKinds.td | 36 +++-- clang/include/clang/Sema/Sema.h | 25 ++- clang/lib/AST/ASTImporter.cpp | 12 +- clang/lib/AST/ExprConstant.cpp | 83 ++++++---- clang/lib/AST/Stmt.cpp | 103 ++++++++---- clang/lib/AST/StmtPrinter.cpp | 21 ++- clang/lib/AST/StmtProfile.cpp | 8 +- clang/lib/CodeGen/CGStmt.cpp | 16 +- clang/lib/Parse/ParseStmtAsm.cpp | 2 +- clang/lib/Parse/Parser.cpp | 37 +++-- clang/lib/Sema/SemaDeclCXX.cpp | 99 ++++++++---- clang/lib/Sema/SemaStmtAsm.cpp | 147 +++++++++++------- clang/lib/Sema/TreeTransform.h | 50 ++++-- clang/lib/Serialization/ASTReaderStmt.cpp | 10 +- clang/lib/Serialization/ASTWriterStmt.cpp | 8 +- 19 files changed, 473 insertions(+), 252 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index cfe49acf20b77..7f9e50dfe78bd 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -787,6 +787,11 @@ 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..7bf0a6911c515 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,20 +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, + IdentifierInfo **names, Expr **constraints, Expr **exprs, + Expr *asmstr, unsigned numclobbers, + Expr **clobbers, unsigned numlabels, SourceLocation rparenloc); /// Build an empty inline-assembly statement. @@ -3326,9 +3326,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,12 +3399,12 @@ class GCCAsmStmt : public AsmStmt { return {}; } - StringRef getOutputConstraint(unsigned i) const; + std::string getOutputConstraint(unsigned i) const; - const StringLiteral *getOutputConstraintLiteral(unsigned i) const { + const Expr *getOutputConstraintExpr(unsigned i) const { return Constraints[i]; } - StringLiteral *getOutputConstraintLiteral(unsigned i) { + Expr *getOutputConstraintExpr(unsigned i) { return Constraints[i]; } @@ -3425,12 +3427,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 +3443,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 +3493,12 @@ class GCCAsmStmt : public AsmStmt { private: void setOutputsAndInputsAndClobbers(const ASTContext &C, IdentifierInfo **Names, - StringLiteral **Constraints, + Expr **Constraints, Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels, - StringLiteral **Clobbers, + Expr **Clobbers, unsigned NumClobbers); public: @@ -3505,10 +3509,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 { + Expr *getClobberExpr(unsigned i) { return Clobbers[i]; } + const Expr *getClobberExpr(unsigned i) const { return Clobbers[i]; } 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/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c034de0e633da..e1b1083675612 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5585,9 +5585,18 @@ 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 +11049,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 +15338,15 @@ 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..86fe8554cba85 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17928,43 +17928,70 @@ 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 { - LValue String; - EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); - Info.InConstantContext = true; +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; - FullExpressionRAII Scope(Info); - APSInt SizeValue; - if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) - return false; + FullExpressionRAII Scope(Info); + APSInt SizeValue; + if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) + return false; - uint64_t Size = SizeValue.getZExtValue(); + uint64_t Size = SizeValue.getZExtValue(); - if (!::EvaluatePointer(PtrExpression, String, Info)) - return false; + if constexpr(std::is_same_v<APValue, T>) + Result = APValue(APValue::UninitArray{}, Size, Size); + //else + // Result.reserve(Size); - QualType CharTy = PtrExpression->getType()->getPointeeType(); - for (uint64_t I = 0; I < Size; ++I) { - APValue Char; - if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String, - Char)) + if (!::EvaluatePointer(PtrExpression, String, Info)) + return false; + + QualType CharTy = PtrExpression->getType()->getPointeeType(); + for (uint64_t I = 0; I < Size; ++I) { + APValue Char; + if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String, + Char)) + return false; + + if constexpr(std::is_same_v<APValue, T>) { + Result.getArrayInitializedElt(I) = std::move(Char); + } + else { + APSInt C = Char.getInt(); + Result.push_back(static_cast<char>(C.getExtValue())); + } + + if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) + return false; + } + if (!Scope.destroy()) return false; - APSInt C = Char.getInt(); - Result.push_back(static_cast<char>(C.getExtValue())); - if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) + if (!CheckMemoryLeaks(Info)) return false; - } - if (!Scope.destroy()) - return false; - if (!CheckMemoryLeaks(Info)) - return false; + return true; +} - return true; + +bool Expr::EvaluateCharRangeAsString(std::string &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const { + return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, PtrExpression, Ctx, Status); +} + +bool Expr::EvaluateCharRangeAsString(APValue &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const { + return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, PtrExpression, Ctx, Status); } bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const { diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index c8ff2ecea20cc..6e0d95714b641 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -455,11 +455,11 @@ std::string AsmStmt::generateAsmString(const ASTContext &C) const { llvm_unreachable("unknown asm statement kind!"); } -StringRef AsmStmt::getOutputConstraint(unsigned i) const { +std::string AsmStmt::getOutputConstraint(unsigned i) const { if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(this)) return gccAsmStmt->getOutputConstraint(i); if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(this)) - return msAsmStmt->getOutputConstraint(i); + return msAsmStmt->getOutputConstraint(i).str(); llvm_unreachable("unknown asm statement kind!"); } @@ -471,11 +471,11 @@ const Expr *AsmStmt::getOutputExpr(unsigned i) const { llvm_unreachable("unknown asm statement kind!"); } -StringRef AsmStmt::getInputConstraint(unsigned i) const { +std::string AsmStmt::getInputConstraint(unsigned i) const { if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(this)) return gccAsmStmt->getInputConstraint(i); if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(this)) - return msAsmStmt->getInputConstraint(i); + return msAsmStmt->getInputConstraint(i).str(); llvm_unreachable("unknown asm statement kind!"); } @@ -487,11 +487,11 @@ const Expr *AsmStmt::getInputExpr(unsigned i) const { llvm_unreachable("unknown asm statement kind!"); } -StringRef AsmStmt::getClobber(unsigned i) const { +std::string AsmStmt::getClobber(unsigned i) const { if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(this)) return gccAsmStmt->getClobber(i); if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(this)) - return msAsmStmt->getClobber(i); + return msAsmStmt->getClobber(i).str(); llvm_unreachable("unknown asm statement kind!"); } @@ -510,8 +510,31 @@ char GCCAsmStmt::AsmStringPiece::getModifier() const { return isLetter(Str[0]) ? Str[0] : '\0'; } -StringRef GCCAsmStmt::getClobber(unsigned i) const { - return getClobberStringLiteral(i)->getString(); +std::string GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(const Expr* E) { + if(auto* SL = llvm::dyn_cast<StringLiteral>(E)) + return SL->getString().str(); + assert(E->getDependence() == ExprDependence::None && "cannot extract a string from a dependent expression"); + auto* CE = cast<ConstantExpr>(E); + APValue Res = CE->getAPValueResult(); + assert(Res.isArray() && "expected an array"); + + std::string Out; + Out.reserve(Res.getArraySize()); + for(unsigned I = 0; I < Res.getArraySize(); ++I) { + APValue C = Res.getArrayInitializedElt(I); + assert(C.isInt()); + auto Ch = static_cast<char>(C.getInt().getExtValue()); + Out.push_back(Ch); + } + return Out; +} + +std::string GCCAsmStmt::getAsmString() const { + return ExtractStringFromGCCAsmStmtComponent(getAsmStringExpr()); +} + +std::string GCCAsmStmt::getClobber(unsigned i) const { + return ExtractStringFromGCCAsmStmtComponent(getClobberExpr(i)); } Expr *GCCAsmStmt::getOutputExpr(unsigned i) { @@ -521,8 +544,8 @@ Expr *GCCAsmStmt::getOutputExpr(unsigned i) { /// getOutputConstraint - Return the constraint string for the specified /// output operand. All output constraints are known to be non-empty (either /// '=' or '+'). -StringRef GCCAsmStmt::getOutputConstraint(unsigned i) const { - return getOutputConstraintLiteral(i)->getString(); +std::string GCCAsmStmt::getOutputConstraint(unsigned i) const { + return ExtractStringFromGCCAsmStmtComponent(getOutputConstraintExpr(i)); } Expr *GCCAsmStmt::getInputExpr(unsigned i) { @@ -543,18 +566,18 @@ StringRef GCCAsmStmt::getLabelName(unsigned i) const { /// getInputConstraint - Return the specified input constraint. Unlike output /// constraints, these can be empty. -StringRef GCCAsmStmt::getInputConstraint(unsigned i) const { - return getInputConstraintLiteral(i)->getString(); +std::string GCCAsmStmt::getInputConstraint(unsigned i) const { + return ExtractStringFromGCCAsmStmtComponent(getInputConstraintExpr(i)); } void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C, IdentifierInfo **Names, - StringLiteral **Constraints, + Expr **Constraints, Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels, - StringLiteral **Clobbers, + Expr **Clobbers, unsigned NumClobbers) { this->NumOutputs = NumOutputs; this->NumInputs = NumInputs; @@ -573,11 +596,11 @@ void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C, unsigned NumConstraints = NumOutputs + NumInputs; C.Deallocate(this->Constraints); - this->Constraints = new (C) StringLiteral*[NumConstraints]; + this->Constraints = new (C) Expr*[NumConstraints]; std::copy(Constraints, Constraints + NumConstraints, this->Constraints); C.Deallocate(this->Clobbers); - this->Clobbers = new (C) StringLiteral*[NumClobbers]; + this->Clobbers = new (C) Expr*[NumClobbers]; std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers); } @@ -609,9 +632,10 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const { /// true, otherwise return false. unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, const ASTContext &C, unsigned &DiagOffs) const { - StringRef Str = getAsmString()->getString(); - const char *StrStart = Str.begin(); - const char *StrEnd = Str.end(); + + std::string Str = getAsmString(); + const char *StrStart = Str.data(); + const char *StrEnd = Str.data() + Str.size(); const char *CurPtr = StrStart; // "Simple" inline asms have no constraints or operands, just convert the asm @@ -739,15 +763,21 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, // Str contains "x4" (Operand without the leading %). std::string Str(Begin, CurPtr - Begin); - // (BeginLoc, EndLoc) represents the range of the operand we are currently // processing. Unlike Str, the range includes the leading '%'. - SourceLocation BeginLoc = getAsmString()->getLocationOfByte( - Percent - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); - SourceLocation EndLoc = getAsmString()->getLocationOfByte( - CurPtr - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); + SourceLocation BeginLoc, EndLoc; + if(auto* SL = dyn_cast<StringLiteral>(getAsmStringExpr())) { + BeginLoc = SL->getLocationOfByte( + Percent - StrStart, SM, LO, TI, &LastAsmStringToken, + &LastAsmStringOffset); + EndLoc = SL->getLocationOfByte( + CurPtr - StrStart, SM, LO, TI, &LastAsmStringToken, + &LastAsmStringOffset); + } + else { + BeginLoc = getAsmStringExpr()->getBeginLoc(); + EndLoc = getAsmStringExpr()->getEndLoc(); + } Pieces.emplace_back(N, std::move(Str), BeginLoc, EndLoc); continue; @@ -778,12 +808,19 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, // (BeginLoc, EndLoc) represents the range of the operand we are currently // processing. Unlike Str, the range includes the leading '%'. - SourceLocation BeginLoc = getAsmString()->getLocationOfByte( + SourceLocation BeginLoc, EndLoc; + if(auto* SL = dyn_cast<StringLiteral>(getAsmStringExpr())) { + BeginLoc = SL->getLocationOfByte( Percent - StrStart, SM, LO, TI, &LastAsmStringToken, &LastAsmStringOffset); - SourceLocation EndLoc = getAsmString()->getLocationOfByte( + EndLoc = SL->getLocationOfByte( NameEnd + 1 - StrStart, SM, LO, TI, &LastAsmStringToken, &LastAsmStringOffset); + } + else { + BeginLoc = getAsmStringExpr()->getBeginLoc(); + EndLoc = getAsmStringExpr()->getEndLoc(); + } Pieces.emplace_back(N, std::move(Str), BeginLoc, EndLoc); @@ -863,9 +900,9 @@ void MSAsmStmt::setInputExpr(unsigned i, Expr *E) { GCCAsmStmt::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, + Expr **constraints, Expr **exprs, + Expr *asmstr, unsigned numclobbers, + Expr **clobbers, unsigned numlabels, SourceLocation rparenloc) : AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs, numinputs, numclobbers), @@ -879,10 +916,10 @@ GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, std::copy(exprs, exprs + NumExprs, Exprs); unsigned NumConstraints = NumOutputs + NumInputs; - Constraints = new (C) StringLiteral*[NumConstraints]; + Constraints = new (C) Expr*[NumConstraints]; std::copy(constraints, constraints + NumConstraints, Constraints); - Clobbers = new (C) StringLiteral*[NumClobbers]; + Clobbers = new (C) Expr*[NumClobbers]; std::copy(clobbers, clobbers + NumClobbers, Clobbers); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index e0063ec5f25eb..c8623225b486c 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -497,6 +497,19 @@ void StmtPrinter::VisitReturnStmt(ReturnStmt *Node) { if (Policy.IncludeNewlines) OS << NL; } +static void PrintGCCAsmString(raw_ostream &OS, Expr*E) { + if(E->getDependence()) { + OS << "<<dependent expr>>"; + return; + } + + std::string Str = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(E); + llvm::SmallVector<char> Out; + // FIXME: Unify with StringLiteral::outputString + EscapeStringForDiagnostic(Str, Out); + OS << '"' << Out << '"'; +} + void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { Indent() << "asm "; @@ -507,7 +520,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "goto "; OS << "("; - VisitStringLiteral(Node->getAsmString()); + PrintGCCAsmString(OS, Node->getAsmStringExpr()); // Outputs if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 || @@ -524,7 +537,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "] "; } - VisitStringLiteral(Node->getOutputConstraintLiteral(i)); + PrintGCCAsmString(OS, Node->getOutputConstraintExpr(i)); OS << " ("; Visit(Node->getOutputExpr(i)); OS << ")"; @@ -545,7 +558,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "] "; } - VisitStringLiteral(Node->getInputConstraintLiteral(i)); + PrintGCCAsmString(OS, Node->getInputConstraintExpr(i)); OS << " ("; Visit(Node->getInputExpr(i)); OS << ")"; @@ -559,7 +572,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { if (i != 0) OS << ", "; - VisitStringLiteral(Node->getClobberStringLiteral(i)); + PrintGCCAsmString(OS, Node->getClobberExpr(i)); } // Labels diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index bf72de3854f4b..96852858b3b9e 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -328,20 +328,20 @@ void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) { VisitStmt(S); ID.AddBoolean(S->isVolatile()); ID.AddBoolean(S->isSimple()); - VisitStringLiteral(S->getAsmString()); + VisitExpr(S->getAsmStringExpr()); ID.AddInteger(S->getNumOutputs()); for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) { ID.AddString(S->getOutputName(I)); - VisitStringLiteral(S->getOutputConstraintLiteral(I)); + VisitExpr(S->getOutputConstraintExpr(I)); } ID.AddInteger(S->getNumInputs()); for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) { ID.AddString(S->getInputName(I)); - VisitStringLiteral(S->getInputConstraintLiteral(I)); + VisitExpr(S->getInputConstraintExpr(I)); } ID.AddInteger(S->getNumClobbers()); for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) - VisitStringLiteral(S->getClobberStringLiteral(I)); + VisitExpr(S->getClobberExpr(I)); ID.AddInteger(S->getNumLabels()); for (auto *L : S->labels()) VisitDecl(L->getLabel()); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 99b6f563d7c82..6ef9625f75118 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -2586,9 +2586,11 @@ static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect, // Slap the source location of the inline asm into a !srcloc metadata on the // call. - if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S)) - Result.setMetadata("srcloc", - getAsmSrcLocInfo(gccAsmStmt->getAsmString(), CGF)); + if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S)) { + if(const StringLiteral* SL = dyn_cast<StringLiteral>(gccAsmStmt->getAsmStringExpr())) + Result.setMetadata("srcloc", + getAsmSrcLocInfo(SL, CGF)); + } else { // At least put the line number on MS inline asm blobs. llvm::Constant *Loc = @@ -2705,9 +2707,9 @@ static void EmitHipStdParUnsupportedAsm(CodeGenFunction *CGF, const AsmStmt &S) { constexpr auto Name = "__ASM__hipstdpar_unsupported"; - StringRef Asm; + std::string Asm; if (auto GCCAsm = dyn_cast<GCCAsmStmt>(&S)) - Asm = GCCAsm->getAsmString()->getString(); + Asm = GCCAsm->getAsmString(); auto &Ctx = CGF->CGM.getLLVMContext(); @@ -3050,7 +3052,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // Clobbers for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) { - StringRef Clobber = S.getClobber(i); + std::string Clobber = S.getClobber(i); if (Clobber == "memory") ReadOnly = ReadNone = false; @@ -3071,7 +3073,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { if (Constraints.find("=&A") != std::string::npos) continue; std::string::size_type position1 = - Constraints.find("={" + Clobber.str() + "}"); + Constraints.find("={" + Clobber + "}"); if (position1 != std::string::npos) { Constraints.insert(position1 + 1, "&"); continue; diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp index 04c3a8700c10c..94144225ff406 100644 --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -809,7 +809,7 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { ConsumeToken(); } // Parse the asm-string list for clobbers if present. - if (!AteExtraColon && isTokenStringLiteral()) { + if (!AteExtraColon && (isTokenStringLiteral() || Tok.is(tok::l_paren))) { while (true) { ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false)); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0710542f5e938..baf264c808820 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1668,28 +1668,37 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { /// string-literal /// ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { - if (!isTokenStringLiteral()) { - Diag(Tok, diag::err_expected_string_literal) - << /*Source='in...'*/0 << "'asm'"; - return ExprError(); - } - ExprResult AsmString(ParseStringLiteralExpression()); - if (!AsmString.isInvalid()) { + ExprResult AsmString; + if(isTokenStringLiteral()) { + AsmString = ParseStringLiteralExpression(); + if(AsmString.isInvalid()) + return AsmString; + const auto *SL = cast<StringLiteral>(AsmString.get()); if (!SL->isOrdinary()) { Diag(Tok, diag::err_asm_operand_wide_string_literal) - << SL->isWide() - << SL->getSourceRange(); + << SL->isWide() + << SL->getSourceRange(); return ExprError(); } - if (ForAsmLabel && SL->getString().empty()) { - Diag(Tok, diag::err_asm_operand_wide_string_literal) - << 2 /* an empty */ << SL->getSourceRange(); + } + else if(!ForAsmLabel && getLangOpts().CPlusPlus11 && Tok.is(tok::l_paren)) { + ParenParseOption ExprType = SimpleExpr; + SourceLocation RParenLoc; + ParsedType CastTy; + + AsmString = ParseParenExpression(ExprType, true/*stopIfCastExpr*/, + false, CastTy, RParenLoc); + if(!AsmString.isUsable()) return ExprError(); - } } - return AsmString; + else { + Diag(Tok, diag::err_asm_expected_string) + << /*and expression=*/(getLangOpts().CPlusPlus11? 0 : 1); + } + + return Actions.ActOnGCCAsmStmtString(AsmString.get(), ForAsmLabel); } /// ParseSimpleAsm diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 96aac7871db1e..332099bbbd437 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17261,17 +17261,34 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { } } -bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, - std::string &Result, - ASTContext &Ctx, - bool ErrorOnInvalidMessage) { +template <typename ResultType> +static bool EvaluateAsStringImpl(Sema & SemaRef, + Expr *Message, + ResultType &Result, ASTContext &Ctx, + Sema::StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { + assert(Message); assert(!Message->isTypeDependent() && !Message->isValueDependent() && "can't evaluate a dependant static assert message"); if (const auto *SL = dyn_cast<StringLiteral>(Message)) { assert(SL->isUnevaluated() && "expected an unevaluated string"); - Result.assign(SL->getString().begin(), SL->getString().end()); + if constexpr(std::is_same_v<APValue, ResultType>) { + Result = APValue(APValue::UninitArray{}, SL->getLength(), SL->getLength()); + const ConstantArrayType *CAT = SemaRef.getASTContext().getAsConstantArrayType(SL->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); + llvm::APSInt Value(SemaRef.getASTContext().getTypeSize(CharType), + CharType->isUnsignedIntegerType()); + for(unsigned I = 0; I < SL->getLength(); I++) { + Value = SL->getCodeUnit(I); + Result.getArrayInitializedElt(I) = APValue(Value); + } + } + else { + Result.assign(SL->getString().begin(), SL->getString().end()); + } return true; } @@ -17279,15 +17296,15 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, QualType T = Message->getType().getNonReferenceType(); auto *RD = T->getAsCXXRecordDecl(); if (!RD) { - Diag(Loc, diag::err_static_assert_invalid_message); + SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid) << EvalContext; return false; } auto FindMember = [&](StringRef Member, bool &Empty, bool Diag = false) -> std::optional<LookupResult> { - DeclarationName DN = PP.getIdentifierInfo(Member); - LookupResult MemberLookup(*this, DN, Loc, Sema::LookupMemberName); - LookupQualifiedName(MemberLookup, RD); + DeclarationName DN = SemaRef.PP.getIdentifierInfo(Member); + LookupResult MemberLookup(SemaRef, DN, Loc, Sema::LookupMemberName); + SemaRef.LookupQualifiedName(MemberLookup, RD); Empty = MemberLookup.empty(); OverloadCandidateSet Candidates(MemberLookup.getNameLoc(), OverloadCandidateSet::CSK_Normal); @@ -17300,7 +17317,8 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, std::optional<LookupResult> SizeMember = FindMember("size", SizeNotFound); std::optional<LookupResult> DataMember = FindMember("data", DataNotFound); if (SizeNotFound || DataNotFound) { - Diag(Loc, diag::err_static_assert_missing_member_function) + SemaRef.Diag(Loc, diag::err_user_defined_msg_missing_member_function) + << EvalContext << ((SizeNotFound && DataNotFound) ? 2 : SizeNotFound ? 0 : 1); @@ -17316,47 +17334,49 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, } auto BuildExpr = [&](LookupResult &LR) { - ExprResult Res = BuildMemberReferenceExpr( + ExprResult Res = SemaRef.BuildMemberReferenceExpr( Message, Message->getType(), Message->getBeginLoc(), false, CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr); if (Res.isInvalid()) return ExprError(); - Res = BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true); + Res = SemaRef.BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true); if (Res.isInvalid()) return ExprError(); if (Res.get()->isTypeDependent() || Res.get()->isValueDependent()) return ExprError(); - return TemporaryMaterializationConversion(Res.get()); + return SemaRef.TemporaryMaterializationConversion(Res.get()); }; ExprResult SizeE = BuildExpr(*SizeMember); ExprResult DataE = BuildExpr(*DataMember); - QualType SizeT = Context.getSizeType(); + QualType SizeT = SemaRef.Context.getSizeType(); QualType ConstCharPtr = - Context.getPointerType(Context.getConstType(Context.CharTy)); + SemaRef.Context.getPointerType(SemaRef.Context.getConstType(SemaRef.Context.CharTy)); ExprResult EvaluatedSize = SizeE.isInvalid() ? ExprError() - : BuildConvertedConstantExpression( - SizeE.get(), SizeT, CCEK_StaticAssertMessageSize); + : SemaRef.BuildConvertedConstantExpression( + SizeE.get(), SizeT, Sema::CCEK_StaticAssertMessageSize); if (EvaluatedSize.isInvalid()) { - Diag(Loc, diag::err_static_assert_invalid_mem_fn_ret_ty) << /*size*/ 0; + SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty) + << EvalContext << /*size*/ 0; return false; } ExprResult EvaluatedData = DataE.isInvalid() ? ExprError() - : BuildConvertedConstantExpression(DataE.get(), ConstCharPtr, - CCEK_StaticAssertMessageData); + : SemaRef.BuildConvertedConstantExpression(DataE.get(), ConstCharPtr, + Sema::CCEK_StaticAssertMessageData); if (EvaluatedData.isInvalid()) { - Diag(Loc, diag::err_static_assert_invalid_mem_fn_ret_ty) << /*data*/ 1; + SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty) + << EvalContext << /*data*/ 1; return false; } if (!ErrorOnInvalidMessage && - Diags.isIgnored(diag::warn_static_assert_message_constexpr, Loc)) + SemaRef.Diags.isIgnored(diag::warn_user_defined_msg_constexpr, Loc)) return true; Expr::EvalResult Status; @@ -17365,16 +17385,30 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, if (!Message->EvaluateCharRangeAsString(Result, EvaluatedSize.get(), EvaluatedData.get(), Ctx, Status) || !Notes.empty()) { - Diag(Message->getBeginLoc(), - ErrorOnInvalidMessage ? diag::err_static_assert_message_constexpr - : diag::warn_static_assert_message_constexpr); + SemaRef.Diag(Message->getBeginLoc(), + ErrorOnInvalidMessage ? diag::err_user_defined_msg_constexpr + : diag::warn_user_defined_msg_constexpr) + << EvalContext; for (const auto &Note : Notes) - Diag(Note.first, Note.second); + SemaRef.Diag(Note.first, Note.second); return !ErrorOnInvalidMessage; } return true; } +bool Sema::EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { + return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, ErrorOnInvalidMessage); + +} + +bool Sema::EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { + return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, ErrorOnInvalidMessage); +} + Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, Expr *AssertMessage, SourceLocation RParenLoc, @@ -17420,8 +17454,9 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, // the message is grammatically valid without evaluating it. if (!Failed && AssertMessage && Cond.getBoolValue()) { std::string Str; - EvaluateStaticAssertMessageAsString(AssertMessage, Str, Context, - /*ErrorOnInvalidMessage=*/false); + EvaluateAsString(AssertMessage, Str, Context, + StringEvaluationContext::StaticAssert, + /*ErrorOnInvalidMessage=*/false); } // CWG2518 @@ -17436,10 +17471,10 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, bool HasMessage = AssertMessage; if (AssertMessage) { std::string Str; - HasMessage = - EvaluateStaticAssertMessageAsString( - AssertMessage, Str, Context, /*ErrorOnInvalidMessage=*/true) || - !Str.empty(); + HasMessage = EvaluateAsString(AssertMessage, Str, Context, + StringEvaluationContext::StaticAssert, + /*ErrorOnInvalidMessage=*/true) || + !Str.empty(); Msg << Str; } Expr *InnerCond = nullptr; diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index a0b203fbdfec2..e168f57f8f303 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -17,6 +17,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Ownership.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "llvm/ADT/ArrayRef.h" @@ -203,15 +204,15 @@ static StringRef extractRegisterName(const Expr *Expression, // clobbers list. If there's a conflict, returns the location of the // conflicted clobber, else returns nullptr static SourceLocation -getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, - StringLiteral **Clobbers, int NumClobbers, +getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints, + Expr **Clobbers, int NumClobbers, unsigned NumLabels, const TargetInfo &Target, ASTContext &Cont) { llvm::StringSet<> InOutVars; // Collect all the input and output registers from the extended asm // statement in order to check for conflicts with the clobber list for (unsigned int i = 0; i < Exprs.size() - NumLabels; ++i) { - StringRef Constraint = Constraints[i]->getString(); + std::string Constraint = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraints[i]); StringRef InOutReg = Target.getConstraintRegister( Constraint, extractRegisterName(Exprs[i], Target)); if (InOutReg != "") @@ -220,7 +221,7 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, // Check for each item in the clobber list if it conflicts with the input // or output for (int i = 0; i < NumClobbers; ++i) { - StringRef Clobber = Clobbers[i]->getString(); + std::string Clobber = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Clobbers[i]); // We only check registers, therefore we don't check cc and memory // clobbers if (Clobber == "cc" || Clobber == "memory" || Clobber == "unwind") @@ -233,6 +234,36 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, return SourceLocation(); } +ExprResult Sema::ActOnGCCAsmStmtString(Expr* Expr, bool ForAsmLabel) { + if(!Expr) + return ExprError(); + + if(auto* SL = dyn_cast<StringLiteral>(Expr)) { + assert(SL->isOrdinary()); + if (ForAsmLabel && SL->getString().empty()) { + Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string) + << SL->getSourceRange(); + } + return SL; + } + if(DiagnoseUnexpandedParameterPack(Expr)) + return ExprError(); + if(Expr->getDependence() != ExprDependence::None) + return Expr; + APValue V; + if(!EvaluateAsString(Expr, V, getASTContext(), StringEvaluationContext::Asm, + /*ErrorOnInvalid=*/true)) + return ExprError(); + + if (ForAsmLabel && V.getArrayInitializedElts() == 0) { + Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string); + } + + ConstantExpr* Res = ConstantExpr::Create(getASTContext(), Expr, ConstantResultStorageKind::APValue); + Res->SetResult(V, getASTContext()); + return Res; +} + StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -241,38 +272,44 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned NumLabels, SourceLocation RParenLoc) { unsigned NumClobbers = clobbers.size(); - StringLiteral **Constraints = - reinterpret_cast<StringLiteral**>(constraints.data()); - StringLiteral *AsmString = cast<StringLiteral>(asmString); - StringLiteral **Clobbers = reinterpret_cast<StringLiteral**>(clobbers.data()); SmallVector<TargetInfo::ConstraintInfo, 4> OutputConstraintInfos; - // The parser verifies that there is a string literal here. - assert(AsmString->isOrdinary()); - FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext()); llvm::StringMap<bool> FeatureMap; Context.getFunctionFeatureMap(FeatureMap, FD); - for (unsigned i = 0; i != NumOutputs; i++) { - StringLiteral *Literal = Constraints[i]; - assert(Literal->isOrdinary()); + auto CreateGCCAsmStmt = [&] { + return new (Context) + GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, + NumInputs, Names, constraints.data(), Exprs.data(), asmString, + NumClobbers, clobbers.data(), NumLabels, RParenLoc); + }; + + if(asmString->getDependence() != ExprDependence::None + || llvm::any_of(constraints, [] (Expr* E) { + return E->getDependence() != ExprDependence::None; + }) + || llvm::any_of(clobbers, [] (Expr* E) { + return E->getDependence() != ExprDependence::None; + })) + return CreateGCCAsmStmt(); + for (unsigned i = 0; i != NumOutputs; i++) { + Expr* Constraint = constraints[i]; StringRef OutputName; if (Names[i]) OutputName = Names[i]->getName(); - TargetInfo::ConstraintInfo Info(Literal->getString(), OutputName); + std::string ConstraintStr = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); + + TargetInfo::ConstraintInfo Info(ConstraintStr, OutputName); if (!Context.getTargetInfo().validateOutputConstraint(Info) && !(LangOpts.HIPStdPar && LangOpts.CUDAIsDevice)) { - targetDiag(Literal->getBeginLoc(), + targetDiag(Constraint->getBeginLoc(), diag::err_asm_invalid_output_constraint) << Info.getConstraintStr(); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } ExprResult ER = CheckPlaceholderExpr(Exprs[i]); @@ -335,35 +372,30 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(OutputExpr->getType()); if (!Context.getTargetInfo().validateOutputSize( - FeatureMap, Literal->getString(), Size)) { + FeatureMap, GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), Size)) { targetDiag(OutputExpr->getBeginLoc(), diag::err_asm_invalid_output_size) << Info.getConstraintStr(); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } } SmallVector<TargetInfo::ConstraintInfo, 4> InputConstraintInfos; for (unsigned i = NumOutputs, e = NumOutputs + NumInputs; i != e; i++) { - StringLiteral *Literal = Constraints[i]; - assert(Literal->isOrdinary()); + Expr *Constraint = constraints[i]; StringRef InputName; if (Names[i]) InputName = Names[i]->getName(); - TargetInfo::ConstraintInfo Info(Literal->getString(), InputName); + std::string ConstraintStr = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); + + TargetInfo::ConstraintInfo Info(ConstraintStr, InputName); if (!Context.getTargetInfo().validateInputConstraint(OutputConstraintInfos, Info)) { - targetDiag(Literal->getBeginLoc(), diag::err_asm_invalid_input_constraint) + targetDiag(Constraint->getBeginLoc(), diag::err_asm_invalid_input_constraint) << Info.getConstraintStr(); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } ExprResult ER = CheckPlaceholderExpr(Exprs[i]); @@ -449,7 +481,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(Ty); if (!Context.getTargetInfo().validateInputSize(FeatureMap, - Literal->getString(), Size)) + ConstraintStr, Size)) return targetDiag(InputExpr->getBeginLoc(), diag::err_asm_invalid_input_size) << Info.getConstraintStr(); @@ -459,46 +491,45 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, // Check that the clobbers are valid. for (unsigned i = 0; i != NumClobbers; i++) { - StringLiteral *Literal = Clobbers[i]; - assert(Literal->isOrdinary()); + Expr *ClobberExpr = clobbers[i]; - StringRef Clobber = Literal->getString(); + std::string Clobber = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(ClobberExpr); if (!Context.getTargetInfo().isValidClobber(Clobber)) { - targetDiag(Literal->getBeginLoc(), diag::err_asm_unknown_register_name) + targetDiag(ClobberExpr->getBeginLoc(), diag::err_asm_unknown_register_name) << Clobber; return new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + NumInputs, Names, constraints.data(), Exprs.data(), asmString, + NumClobbers, clobbers.data(), NumLabels, RParenLoc); } if (Clobber == "unwind") { - UnwindClobberLoc = Literal->getBeginLoc(); + UnwindClobberLoc = ClobberExpr->getBeginLoc(); } } // Using unwind clobber and asm-goto together is not supported right now. if (UnwindClobberLoc && NumLabels > 0) { targetDiag(*UnwindClobberLoc, diag::err_asm_unwind_and_goto); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, - Names, Constraints, Exprs.data(), AsmString, NumClobbers, - Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } - GCCAsmStmt *NS = - new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), - AsmString, NumClobbers, Clobbers, NumLabels, - RParenLoc); + GCCAsmStmt *NS = CreateGCCAsmStmt(); // Validate the asm string, ensuring it makes sense given the operands we // have. + + auto GetLocation = [this](const Expr* Str, unsigned Offset) { + if(auto* SL = dyn_cast<StringLiteral>(Str)) + return getLocationOfStringLiteralByte(SL, Offset); + return Str->getBeginLoc(); + }; + SmallVector<GCCAsmStmt::AsmStringPiece, 8> Pieces; unsigned DiagOffs; if (unsigned DiagID = NS->AnalyzeAsmString(Pieces, Context, DiagOffs)) { - targetDiag(getLocationOfStringLiteralByte(AsmString, DiagOffs), DiagID) - << AsmString->getSourceRange(); + targetDiag(GetLocation(asmString, DiagOffs), DiagID) + << asmString->getSourceRange(); return NS; } @@ -529,7 +560,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, } // Now that we have the right indexes go ahead and check. - StringLiteral *Literal = Constraints[ConstraintIdx]; + Expr *Constraint = constraints[ConstraintIdx]; const Type *Ty = Exprs[ConstraintIdx]->getType().getTypePtr(); if (Ty->isDependentType() || Ty->isIncompleteType()) continue; @@ -537,7 +568,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(Ty); std::string SuggestedModifier; if (!Context.getTargetInfo().validateConstraintModifier( - Literal->getString(), Piece.getModifier(), Size, + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), Piece.getModifier(), Size, SuggestedModifier)) { targetDiag(Exprs[ConstraintIdx]->getBeginLoc(), diag::warn_asm_mismatched_size_modifier); @@ -546,8 +577,10 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, auto B = targetDiag(Piece.getRange().getBegin(), diag::note_asm_missing_constraint_modifier) << SuggestedModifier; - SuggestedModifier = "%" + SuggestedModifier + Piece.getString(); - B << FixItHint::CreateReplacement(Piece.getRange(), SuggestedModifier); + if(isa<StringLiteral>(Constraint)) { + SuggestedModifier = "%" + SuggestedModifier + Piece.getString(); + B << FixItHint::CreateReplacement(Piece.getRange(), SuggestedModifier); + } } } } @@ -708,7 +741,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, // Check for conflicts between clobber list and input or output lists SourceLocation ConstraintLoc = - getClobberConflictLocation(Exprs, Constraints, Clobbers, NumClobbers, + getClobberConflictLocation(Exprs, constraints.data(), clobbers.data(), NumClobbers, NumLabels, Context.getTargetInfo(), Context); if (ConstraintLoc.isValid()) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a09f623ff8fbf..06d41a8154ca3 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8552,25 +8552,39 @@ template<typename Derived> StmtResult TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) { + SmallVector<Expr*, 8> Constraints; SmallVector<Expr*, 8> Exprs; SmallVector<IdentifierInfo *, 4> Names; - ExprResult AsmString; SmallVector<Expr*, 8> Clobbers; bool ExprsChanged = false; + auto RebuildString = [&] (Expr* E) { + ExprResult Result = getDerived().TransformExpr(E); + if (Result.isInvalid()) + return Result; + if(!Result.isInvalid() && Result.get() != E) { + ExprsChanged = true; + Result = SemaRef.ActOnGCCAsmStmtString(Result.get(), /*ForLabel=*/false); + } + return Result; + }; + // Go through the outputs. for (unsigned I = 0, E = S->getNumOutputs(); I != E; ++I) { Names.push_back(S->getOutputIdentifier(I)); - // No need to transform the constraint literal. - Constraints.push_back(S->getOutputConstraintLiteral(I)); + ExprResult Result = RebuildString(S->getOutputConstraintExpr(I)); + if (Result.isInvalid()) + return StmtError(); + + Constraints.push_back(Result.get()); // Transform the output expr. Expr *OutputExpr = S->getOutputExpr(I); - ExprResult Result = getDerived().TransformExpr(OutputExpr); + Result = getDerived().TransformExpr(OutputExpr); if (Result.isInvalid()) return StmtError(); @@ -8583,12 +8597,15 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) { for (unsigned I = 0, E = S->getNumInputs(); I != E; ++I) { Names.push_back(S->getInputIdentifier(I)); - // No need to transform the constraint literal. - Constraints.push_back(S->getInputConstraintLiteral(I)); + ExprResult Result = RebuildString(S->getInputConstraintExpr(I)); + if (Result.isInvalid()) + return StmtError(); + + Constraints.push_back(Result.get()); // Transform the input expr. Expr *InputExpr = S->getInputExpr(I); - ExprResult Result = getDerived().TransformExpr(InputExpr); + Result = getDerived().TransformExpr(InputExpr); if (Result.isInvalid()) return StmtError(); @@ -8607,15 +8624,22 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) { ExprsChanged |= Result.get() != S->getLabelExpr(I); Exprs.push_back(Result.get()); } - if (!getDerived().AlwaysRebuild() && !ExprsChanged) - return S; // Go through the clobbers. - for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I) - Clobbers.push_back(S->getClobberStringLiteral(I)); + for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I) { + ExprResult Result = RebuildString(S->getClobberExpr(I)); + if (Result.isInvalid()) + return StmtError(); + Clobbers.push_back(Result.get()); + } + + ExprResult AsmString = RebuildString(S->getAsmStringExpr()); + if (AsmString.isInvalid()) + return StmtError(); + + if (!getDerived().AlwaysRebuild() && !ExprsChanged) + return S; - // No need to transform the asm string literal. - AsmString = S->getAsmString(); return getDerived().RebuildGCCAsmStmt(S->getAsmLoc(), S->isSimple(), S->isVolatile(), S->getNumOutputs(), S->getNumInputs(), Names.data(), diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 48f9f89bd6e4c..7ad1216cb53e2 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -381,7 +381,7 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { VisitAsmStmt(S); S->NumLabels = Record.readInt(); S->setRParenLoc(readSourceLocation()); - S->setAsmString(cast_or_null<StringLiteral>(Record.readSubStmt())); + S->setAsmString(cast_or_null<Expr>(Record.readSubStmt())); unsigned NumOutputs = S->getNumOutputs(); unsigned NumInputs = S->getNumInputs(); @@ -390,18 +390,18 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { // Outputs and inputs SmallVector<IdentifierInfo *, 16> Names; - SmallVector<StringLiteral*, 16> Constraints; + SmallVector<Expr*, 16> Constraints; SmallVector<Stmt*, 16> Exprs; for (unsigned I = 0, N = NumOutputs + NumInputs; I != N; ++I) { Names.push_back(Record.readIdentifier()); - Constraints.push_back(cast_or_null<StringLiteral>(Record.readSubStmt())); + Constraints.push_back(cast_or_null<Expr>(Record.readSubStmt())); Exprs.push_back(Record.readSubStmt()); } // Constraints - SmallVector<StringLiteral*, 16> Clobbers; + SmallVector<Expr*, 16> Clobbers; for (unsigned I = 0; I != NumClobbers; ++I) - Clobbers.push_back(cast_or_null<StringLiteral>(Record.readSubStmt())); + Clobbers.push_back(cast_or_null<Expr>(Record.readSubStmt())); // Labels for (unsigned I = 0, N = NumLabels; I != N; ++I) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index aa5a7854394a0..32211ced4ff15 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -361,25 +361,25 @@ void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) { VisitAsmStmt(S); Record.push_back(S->getNumLabels()); Record.AddSourceLocation(S->getRParenLoc()); - Record.AddStmt(S->getAsmString()); + Record.AddStmt(S->getAsmStringExpr()); // Outputs for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) { Record.AddIdentifierRef(S->getOutputIdentifier(I)); - Record.AddStmt(S->getOutputConstraintLiteral(I)); + Record.AddStmt(S->getOutputConstraintExpr(I)); Record.AddStmt(S->getOutputExpr(I)); } // Inputs for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) { Record.AddIdentifierRef(S->getInputIdentifier(I)); - Record.AddStmt(S->getInputConstraintLiteral(I)); + Record.AddStmt(S->getInputConstraintExpr(I)); Record.AddStmt(S->getInputExpr(I)); } // Clobbers for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) - Record.AddStmt(S->getClobberStringLiteral(I)); + Record.AddStmt(S->getClobberExpr(I)); // Labels for (unsigned I = 0, N = S->getNumLabels(); I != N; ++I) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits