-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Clang] Constant Expressions inside of GCC' asm strings #131003
Conversation
Implements GCC's constexpr string ASM extension https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html
✅ With the latest revision this PR passed the C/C++ code formatter. |
@ojhunt @erichkeane @AaronBallman I'm looking for early feedback at this point. Obviously this has no tests yes This
|
clang/lib/AST/ExprConstant.cpp
Outdated
APSInt SizeValue; | ||
if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) | ||
return false; | ||
FullExpressionRAII Scope(Info); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What weirdness is going on with the formatting here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
clang/lib/AST/ExprConstant.cpp
Outdated
} | ||
else { | ||
APSInt C = Char.getInt(); | ||
Result.push_back(static_cast<char>(C.getExtValue())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do some range checking before the cast? I would expect based on CharTy
that we could expect C
here to have a bitwidth of 8, right?
5221a3f
to
6e535f3
Compare
@llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) ChangesImplements GCC's constexpr string ASM extension 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:
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]
|
@llvm/pr-subscribers-clang-codegen Author: cor3ntin (cor3ntin) ChangesImplements GCC's constexpr string ASM extension 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:
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]
|
@llvm/pr-subscribers-clang-modules Author: cor3ntin (cor3ntin) ChangesImplements GCC's constexpr string ASM extension 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:
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]
|
clang/lib/CodeGen/CGStmt.cpp
Outdated
if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S)) { | ||
if (const StringLiteral *SL = | ||
dyn_cast<StringLiteral>(gccAsmStmt->getAsmStringExpr())) | ||
Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do the diagnostics look like if you get a parse failure inside the inline asm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch; I completely forgot to fix that!
|
||
// FIXME: If we wanted to, we could allocate all of these in one big array. | ||
StringLiteral **Constraints = nullptr; | ||
StringLiteral **Clobbers = nullptr; | ||
Expr **Constraints = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another suggestion to add to the lets make these sane
bug report, we could probably use ArrayRef
for these to make the pointer-pointer less difficult to use.
Implements GCC's constexpr string ASM extension
https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html