[Clang] Constant Expressions inside of GCC' asm strings (#131003)

Implements GCC's constexpr string ASM extension
https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html
This commit is contained in:
cor3ntin 2025-03-17 20:10:46 +01:00 committed by GitHub
parent 2443fe537f
commit 911b200ce3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 742 additions and 288 deletions

View File

@ -1959,6 +1959,32 @@ 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 the following member functions
.. code-block:: c++
constexpr const char* data() const;
constexpr size_t size() const;
such as ``std::string``, ``std::string_view``, ``std::vector<char>``.
This mechanism follow the same rules as ``static_assert`` messages in
C++26, see ``[dcl.pre]/p12``.
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
====================

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^

View File

@ -787,6 +787,10 @@ public:
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).

View File

@ -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.
})

View File

@ -3193,7 +3193,7 @@ public:
/// 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 @@ public:
/// 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 @@ public:
//===--- 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 setAsmStringExpr(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 @@ public:
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 @@ public:
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 @@ public:
return const_cast<GCCAsmStmt*>(this)->getInputExpr(i);
}
static std::string ExtractStringFromGCCAsmStmtComponent(const Expr *E);
//===--- Labels ---===//
bool isAsmGoto() const {
@ -3489,12 +3490,9 @@ public:
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 @@ public:
/// 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; }

View File

@ -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">;

View File

@ -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<
@ -9517,6 +9524,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">;

View File

@ -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)

View File

@ -5585,9 +5585,15 @@ public:
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);
@ -11035,6 +11041,7 @@ private:
///@{
public:
ExprResult ActOnGCCAsmStmtString(Expr *Stm, bool ForAsmLabel);
StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
bool IsVolatile, unsigned NumOutputs,
unsigned NumInputs, IdentifierInfo **Names,
@ -15323,6 +15330,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

View File

@ -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());

View File

@ -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,
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 {

View File

@ -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,32 @@ 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 +545,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,19 +567,14 @@ 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,
Stmt **Exprs,
unsigned NumOutputs,
unsigned NumInputs,
unsigned NumLabels,
StringLiteral **Clobbers,
unsigned NumClobbers) {
void GCCAsmStmt::setOutputsAndInputsAndClobbers(
const ASTContext &C, IdentifierInfo **Names, Expr **Constraints,
Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels,
Expr **Clobbers, unsigned NumClobbers) {
this->NumOutputs = NumOutputs;
this->NumInputs = NumInputs;
this->NumClobbers = NumClobbers;
@ -573,11 +592,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 +628,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 +759,20 @@ 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 +803,18 @@ 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(
Percent - StrStart, SM, LO, TI, &LastAsmStringToken,
&LastAsmStringOffset);
SourceLocation EndLoc = getAsmString()->getLocationOfByte(
NameEnd + 1 - 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(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,13 +894,12 @@ 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,
SourceLocation rparenloc)
Expr **constraints, Expr **exprs, Expr *asmstr,
unsigned numclobbers, Expr **clobbers,
unsigned numlabels, SourceLocation rparenloc)
: AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs,
numinputs, numclobbers),
RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) {
RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) {
unsigned NumExprs = NumOutputs + NumInputs + NumLabels;
Names = new (C) IdentifierInfo*[NumExprs];
@ -879,10 +909,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);
}

View File

@ -507,7 +507,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
OS << "goto ";
OS << "(";
VisitStringLiteral(Node->getAsmString());
Visit(Node->getAsmStringExpr());
// Outputs
if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 ||
@ -524,7 +524,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
OS << "] ";
}
VisitStringLiteral(Node->getOutputConstraintLiteral(i));
Visit(Node->getOutputConstraintExpr(i));
OS << " (";
Visit(Node->getOutputExpr(i));
OS << ")";
@ -545,7 +545,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
OS << "] ";
}
VisitStringLiteral(Node->getInputConstraintLiteral(i));
Visit(Node->getInputConstraintExpr(i));
OS << " (";
Visit(Node->getInputExpr(i));
OS << ")";
@ -559,7 +559,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
if (i != 0)
OS << ", ";
VisitStringLiteral(Node->getClobberStringLiteral(i));
Visit(Node->getClobberExpr(i));
}
// Labels

View File

@ -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());

View File

@ -2586,11 +2586,14 @@ 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));
else {
// At least put the line number on MS inline asm blobs.
const StringLiteral *SL;
if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S);
gccAsmStmt &&
(SL = dyn_cast<StringLiteral>(gccAsmStmt->getAsmStringExpr()))) {
Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF));
} else {
// At least put the line number on MS inline asm blobs and GCC asm constexpr
// strings.
llvm::Constant *Loc =
llvm::ConstantInt::get(CGF.Int64Ty, S.getAsmLoc().getRawEncoding());
Result.setMetadata("srcloc",
@ -2705,9 +2708,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 +3053,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 +3074,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;

View File

@ -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));
@ -884,8 +884,8 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,
SmallVectorImpl<Expr *> &Constraints,
SmallVectorImpl<Expr *> &Exprs) {
// 'asm-operands' isn't present?
if (!isTokenStringLiteral() && Tok.isNot(tok::l_square))
// 'asm-operands' isn't present
if (Tok.isOneOf(tok::colon, tok::coloncolon, tok::r_paren))
return false;
while (true) {

View File

@ -19,6 +19,7 @@
#include "clang/Basic/FileManager.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaCodeCompletion.h"
@ -1668,28 +1669,40 @@ 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;
EnterExpressionEvaluationContext ConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
AsmString = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false,
CastTy, RParenLoc);
if (!AsmString.isInvalid())
AsmString = Actions.ActOnConstantExpression(AsmString);
if (AsmString.isInvalid())
return ExprError();
}
} else {
Diag(Tok, diag::err_asm_expected_string) << /*and expression=*/(
(getLangOpts().CPlusPlus11 && !ForAsmLabel) ? 0 : 1);
}
return AsmString;
return Actions.ActOnGCCAsmStmtString(AsmString.get(), ForAsmLabel);
}
/// ParseSimpleAsm

View File

@ -17289,17 +17289,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;
}
@ -17307,16 +17324,14 @@ 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);
Empty = MemberLookup.empty();
auto FindMember = [&](StringRef Member) -> std::optional<LookupResult> {
DeclarationName DN = SemaRef.PP.getIdentifierInfo(Member);
LookupResult MemberLookup(SemaRef, DN, Loc, Sema::LookupMemberName);
SemaRef.LookupQualifiedName(MemberLookup, RD);
OverloadCandidateSet Candidates(MemberLookup.getNameLoc(),
OverloadCandidateSet::CSK_Normal);
if (MemberLookup.empty())
@ -17324,67 +17339,63 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
return std::move(MemberLookup);
};
bool SizeNotFound, DataNotFound;
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)
<< ((SizeNotFound && DataNotFound) ? 2
: SizeNotFound ? 0
: 1);
return false;
}
std::optional<LookupResult> SizeMember = FindMember("size");
std::optional<LookupResult> DataMember = FindMember("data");
if (!SizeMember || !DataMember) {
if (!SizeMember)
FindMember("size", SizeNotFound, /*Diag=*/true);
if (!DataMember)
FindMember("data", DataNotFound, /*Diag=*/true);
SemaRef.Diag(Loc, diag::err_user_defined_msg_missing_member_function)
<< EvalContext
<< ((!SizeMember && !DataMember) ? 2
: !SizeMember ? 0
: 1);
return false;
}
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 ConstCharPtr =
Context.getPointerType(Context.getConstType(Context.CharTy));
QualType SizeT = SemaRef.Context.getSizeType();
QualType ConstCharPtr = SemaRef.Context.getPointerType(
SemaRef.Context.getConstType(SemaRef.Context.CharTy));
ExprResult EvaluatedSize =
SizeE.isInvalid() ? ExprError()
: BuildConvertedConstantExpression(
SizeE.get(), SizeT, CCEK_StaticAssertMessageSize);
SizeE.isInvalid()
? ExprError()
: 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;
@ -17393,16 +17404,31 @@ 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,
@ -17448,8 +17474,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
@ -17464,10 +17491,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;

View File

@ -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,
unsigned NumLabels,
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,8 @@ 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 +235,37 @@ 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 +274,45 @@ 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 +375,34 @@ 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]);
@ -448,8 +487,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
return StmtError();
unsigned Size = Context.getTypeSize(Ty);
if (!Context.getTargetInfo().validateInputSize(FeatureMap,
Literal->getString(), Size))
if (!Context.getTargetInfo().validateInputSize(FeatureMap, ConstraintStr,
Size))
return targetDiag(InputExpr->getBeginLoc(),
diag::err_asm_invalid_input_size)
<< Info.getConstraintStr();
@ -459,46 +498,47 @@ 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);
return new (Context) GCCAsmStmt(
Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, 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 +569,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,8 +577,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
unsigned Size = Context.getTypeSize(Ty);
std::string SuggestedModifier;
if (!Context.getTargetInfo().validateConstraintModifier(
Literal->getString(), Piece.getModifier(), Size,
SuggestedModifier)) {
GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint),
Piece.getModifier(), Size, SuggestedModifier)) {
targetDiag(Exprs[ConstraintIdx]->getBeginLoc(),
diag::warn_asm_mismatched_size_modifier);
@ -546,8 +586,11 @@ 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);
}
}
}
}
@ -707,10 +750,9 @@ 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,
NumLabels,
Context.getTargetInfo(), Context);
SourceLocation ConstraintLoc = getClobberConflictLocation(
Exprs, constraints.data(), clobbers.data(), NumClobbers, NumLabels,
Context.getTargetInfo(), Context);
if (ConstraintLoc.isValid())
targetDiag(ConstraintLoc, diag::error_inoutput_conflict_with_clobber);

View File

@ -8556,21 +8556,34 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
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.isUsable())
return Result;
if (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 +8596,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 +8623,22 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
ExprsChanged |= Result.get() != S->getLabelExpr(I);
Exprs.push_back(Result.get());
}
// Go through the clobbers.
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;
// Go through the clobbers.
for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I)
Clobbers.push_back(S->getClobberStringLiteral(I));
// 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(),

View File

@ -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->setAsmStringExpr(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) {

View File

@ -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) {

View File

@ -0,0 +1,52 @@
// REQUIRES: x86-registered-target
// RUN: %clang_cc1 -triple x86_64 %s -S -o /dev/null -Werror -verify
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
struct string_view {
int S;
const char* D;
constexpr string_view() : S(0), D(0){}
constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
constexpr int size() const {
return S;
}
constexpr const char* data() const {
return D;
}
};
int func() {return 0;};
void f() {
asm((string_view("")) ::(string_view("r"))(func()));
// CHECK: %[[CALL:.*]] = call noundef i32 @_Z4funcv
// CHECK: call void asm sideeffect "", "r,~{dirflag},~{fpsr},~{flags}"
asm("" :::(string_view("memory")));
// CHECK: call void asm sideeffect "", "~{memory},~{dirflag},~{fpsr},~{flags}"
}
void foo(unsigned long long addr, unsigned long long a0) {
register unsigned long long result asm("rax");
register unsigned long long b0 asm("rdi");
b0 = a0;
asm((string_view("call *%1")) : (string_view("=r")) (result)
: (string_view("r"))(addr), (string_view("r")) (b0) : (string_view("memory")));
// CHECK:{{.*}} call i64 asm "call *$1", "={rax},r,{rdi},~{memory},~{dirflag},~{fpsr},~{flags}"
}
void test_srcloc() {
asm((string_view( // expected-error {{invalid instruction mnemonic 'nonsense'}} \
// expected-error {{invalid instruction mnemonic 'foobar'}} \
// expected-note@1 {{instantiated into assembly here}} \
// expected-note@2 {{instantiated into assembly here}}
R"o(nonsense
foobar)o")
) ::(string_view("r"))(func()));
}

View File

@ -12,3 +12,48 @@ void f() {
[[]] asm("");
[[gnu::deprecated]] asm(""); // expected-warning {{'deprecated' attribute ignored}}
}
#if !__has_extension(gnu_asm_constexpr_strings)
#error Extension 'gnu_asm_constexpr_strings' should be available by default
#endif
struct string_view {
int S;
const char* D;
constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
constexpr int size() const {
return S;
}
constexpr const char* data() const {
return D;
}
};
// Neither gcc nor clang support expressions in label
int foo1 asm ((string_view("test"))); // expected-error {{expected string literal in 'asm'}}
int func() asm ((string_view("test"))); // expected-error {{expected string literal in 'asm'}}
void f2() {
asm(string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
asm("" : string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
asm("" : : string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
asm("" : : : string_view("")); // expected-error {{expected ')'}}
asm("" :: string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
asm(::string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
int i;
asm((string_view("")));
asm((::string_view("")));
asm("" : (::string_view("+g")) (i));
asm("" : (::string_view("+g"))); // expected-error {{expected '(' after 'asm operand'}}
asm("" : (::string_view("+g")) (i) : (::string_view("g")) (0));
asm("" : (::string_view("+g")) (i) : (::string_view("g"))); // expected-error {{expected '(' after 'asm operand'}}
asm("" : (::string_view("+g")) (i) : (::string_view("g")) (0) : (string_view("memory")));
asm((0)); // expected-error {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}}
}

View File

@ -0,0 +1,127 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux
template <bool Leak>
struct RAIIBase {
constexpr RAIIBase(const char* in) {
s = __builtin_strlen(in);
d = new char[s + 1]; // expected-note 4{{allocation performed here was not deallocated}}
for(int i = 0 ; i < s; i++)
d[i] = in[i];
}
int s;
char* d;
constexpr unsigned long size() const {
return s;
}
constexpr const char* data() const {
return d;
}
constexpr ~RAIIBase() {
if constexpr(!Leak)
delete[] d;
}
};
using RAII = RAIIBase<false>;
using RAIILeak = RAIIBase<true>;
void test_leaks(int i) {
asm((RAII("nop")) : (RAII("+ir")) (i) : (RAII("g")) (i) : (RAII("memory")));
asm((RAIILeak("nop"))); // expected-error {{the expression in this asm operand must be produced by a constant expression}}
asm((RAII("nop"))
: (RAIILeak("+ir")) (i) // expected-error {{the expression in this asm operand must be produced by a constant expression}}
::
);
asm((RAII("nop"))
: (RAII("+ir")) (i)
: (RAIILeak("g")) (i) // expected-error {{the expression in this asm operand must be produced by a constant expression}}
:
);
asm((RAII("nop"))
: (RAII("+ir")) (i)
: (RAII("g")) (i)
: (RAIILeak("memory")) // expected-error {{the expression in this asm operand must be produced by a constant expression}}
);
}
struct NotAString{};
struct MessageInvalidSize {
constexpr unsigned long size(int) const; // expected-note {{'size' declared here}}
constexpr const char* data() const;
};
struct MessageInvalidData {
constexpr unsigned long size() const;
constexpr const char* data(int) const; // expected-note {{'data' declared here}}
};
struct WMessage {
constexpr unsigned long long size() const {return 0;};
constexpr const wchar_t* data() const {return L"";}
};
struct string_view {
int S;
const char* D;
constexpr string_view() : S(0), D(0){}
constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
constexpr int size() const {
return S;
}
constexpr const char* data() const {
return D;
}
};
void f() {
asm(("")); // expected-error {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}}
asm((NotAString{})); // expected-error {{the string object in this asm operand is missing 'data()' and 'size()' member functions}}
asm((MessageInvalidData{})); // expected-error {{the expression in this asm operand must have a 'data()' member function returning an object convertible to 'const char *'}} \
// expected-error {{too few arguments to function call, expected 1, have 0}}
asm((MessageInvalidSize{})); // expected-error {{the expression in this asm operand must have a 'size()' member function returning an object convertible to 'std::size_t'}} \
// expected-error {{too few arguments to function call, expected 1, have 0}}
asm((WMessage{})); // expected-error {{value of type 'const wchar_t *' is not implicitly convertible to 'const char *'}} \
// expected-error {{the expression in this asm operand must have a 'data()' member function returning an object convertible to 'const char *'}}
}
template <typename... U>
void test_packs() {
asm((U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
asm("" : (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
asm("" :: (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
asm("" ::: (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
}
template <typename T>
void test_dependent1(int i) {
asm((T{})); // #err-int
asm("" : (T{"+g"})(i)); // #err-int2
asm("" :: (T{"g"})(i)); // #err-int3
asm("" ::: (T{"memory"})); // #err-int4
}
template void test_dependent1<int>(int);
// expected-note@-1 {{in instantiation of function template specialization}}
// expected-error@#err-int {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}}
// expected-error@#err-int2 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[3]'}}
// expected-error@#err-int3 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[2]'}}
// expected-error@#err-int4 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[7]'}}
template void test_dependent1<string_view>(int);
template <typename T>
void test_dependent2(int i) {
asm("" : (T{"g"})(i)); // #err-invalid1
asm("" :: (T{"+g"})(i)); // #err-invalid2
asm("" ::: (T{"foo"})); // #err-invalid3
}
template void test_dependent2<string_view>(int);
// expected-note@-1 {{in instantiation of function template specialization}}
// expected-error@#err-invalid1 {{invalid output constraint 'g' in asm}}
// expected-error@#err-invalid2 {{invalid input constraint '+g' in asm}}
// expected-error@#err-invalid3 {{unknown register name 'foo' in asm}}