mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 04:56:36 +00:00
[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:
parent
2443fe537f
commit
911b200ce3
@ -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
|
||||
====================
|
||||
|
||||
|
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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.
|
||||
})
|
||||
|
@ -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; }
|
||||
|
@ -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">;
|
||||
|
@ -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">;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
52
clang/test/CodeGenCXX/gnu-asm-constexpr.cpp
Normal file
52
clang/test/CodeGenCXX/gnu-asm-constexpr.cpp
Normal 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()));
|
||||
}
|
@ -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}}
|
||||
}
|
||||
|
127
clang/test/SemaCXX/gnu-asm-constexpr.cpp
Normal file
127
clang/test/SemaCXX/gnu-asm-constexpr.cpp
Normal 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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user