Skip to content

Commit 8450816

Browse files
committed
[Clang][WIP] Constant Expressions inside of gcc'asm strings
Implements GCC's constexpr string ASM extension https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html
1 parent 90a08fb commit 8450816

19 files changed

+473
-252
lines changed

clang/include/clang/AST/Expr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,11 @@ class Expr : public ValueStmt {
787787
const Expr *PtrExpression, ASTContext &Ctx,
788788
EvalResult &Status) const;
789789

790+
bool EvaluateCharRangeAsString(APValue &Result,
791+
const Expr *SizeExpression,
792+
const Expr *PtrExpression, ASTContext &Ctx,
793+
EvalResult &Status) const;
794+
790795
/// If the current Expr can be evaluated to a pointer to a null-terminated
791796
/// constant string, return the constant string (without the terminating
792797
/// null).

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,15 +2410,15 @@ DEF_TRAVERSE_DECL(ImplicitConceptSpecializationDecl, {
24102410
}
24112411

24122412
DEF_TRAVERSE_STMT(GCCAsmStmt, {
2413-
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmString());
2413+
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmStringExpr());
24142414
for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) {
2415-
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintLiteral(I));
2415+
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintExpr(I));
24162416
}
24172417
for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) {
2418-
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintLiteral(I));
2418+
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintExpr(I));
24192419
}
24202420
for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) {
2421-
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberStringLiteral(I));
2421+
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberExpr(I));
24222422
}
24232423
// children() iterates over inputExpr and outputExpr.
24242424
})

clang/include/clang/AST/Stmt.h

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3193,7 +3193,7 @@ class AsmStmt : public Stmt {
31933193
/// getOutputConstraint - Return the constraint string for the specified
31943194
/// output operand. All output constraints are known to be non-empty (either
31953195
/// '=' or '+').
3196-
StringRef getOutputConstraint(unsigned i) const;
3196+
std::string getOutputConstraint(unsigned i) const;
31973197

31983198
/// isOutputPlusConstraint - Return true if the specified output constraint
31993199
/// is a "+" constraint (which is both an input and an output) or false if it
@@ -3214,14 +3214,14 @@ class AsmStmt : public Stmt {
32143214

32153215
/// getInputConstraint - Return the specified input constraint. Unlike output
32163216
/// constraints, these can be empty.
3217-
StringRef getInputConstraint(unsigned i) const;
3217+
std::string getInputConstraint(unsigned i) const;
32183218

32193219
const Expr *getInputExpr(unsigned i) const;
32203220

32213221
//===--- Other ---===//
32223222

32233223
unsigned getNumClobbers() const { return NumClobbers; }
3224-
StringRef getClobber(unsigned i) const;
3224+
std::string getClobber(unsigned i) const;
32253225

32263226
static bool classof(const Stmt *T) {
32273227
return T->getStmtClass() == GCCAsmStmtClass ||
@@ -3302,20 +3302,20 @@ class GCCAsmStmt : public AsmStmt {
33023302
friend class ASTStmtReader;
33033303

33043304
SourceLocation RParenLoc;
3305-
StringLiteral *AsmStr;
3305+
Expr *AsmStr;
33063306

33073307
// FIXME: If we wanted to, we could allocate all of these in one big array.
3308-
StringLiteral **Constraints = nullptr;
3309-
StringLiteral **Clobbers = nullptr;
3308+
Expr **Constraints = nullptr;
3309+
Expr **Clobbers = nullptr;
33103310
IdentifierInfo **Names = nullptr;
33113311
unsigned NumLabels = 0;
33123312

33133313
public:
33143314
GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple,
33153315
bool isvolatile, unsigned numoutputs, unsigned numinputs,
3316-
IdentifierInfo **names, StringLiteral **constraints, Expr **exprs,
3317-
StringLiteral *asmstr, unsigned numclobbers,
3318-
StringLiteral **clobbers, unsigned numlabels,
3316+
IdentifierInfo **names, Expr **constraints, Expr **exprs,
3317+
Expr *asmstr, unsigned numclobbers,
3318+
Expr **clobbers, unsigned numlabels,
33193319
SourceLocation rparenloc);
33203320

33213321
/// Build an empty inline-assembly statement.
@@ -3326,9 +3326,11 @@ class GCCAsmStmt : public AsmStmt {
33263326

33273327
//===--- Asm String Analysis ---===//
33283328

3329-
const StringLiteral *getAsmString() const { return AsmStr; }
3330-
StringLiteral *getAsmString() { return AsmStr; }
3331-
void setAsmString(StringLiteral *E) { AsmStr = E; }
3329+
const Expr *getAsmStringExpr() const { return AsmStr; }
3330+
Expr *getAsmStringExpr() { return AsmStr; }
3331+
void setAsmString(Expr *E) { AsmStr = E; }
3332+
3333+
std::string getAsmString() const;
33323334

33333335
/// AsmStringPiece - this is part of a decomposed asm string specification
33343336
/// (for use with the AnalyzeAsmString function below). An asm string is
@@ -3397,12 +3399,12 @@ class GCCAsmStmt : public AsmStmt {
33973399
return {};
33983400
}
33993401

3400-
StringRef getOutputConstraint(unsigned i) const;
3402+
std::string getOutputConstraint(unsigned i) const;
34013403

3402-
const StringLiteral *getOutputConstraintLiteral(unsigned i) const {
3404+
const Expr *getOutputConstraintExpr(unsigned i) const {
34033405
return Constraints[i];
34043406
}
3405-
StringLiteral *getOutputConstraintLiteral(unsigned i) {
3407+
Expr *getOutputConstraintExpr(unsigned i) {
34063408
return Constraints[i];
34073409
}
34083410

@@ -3425,12 +3427,12 @@ class GCCAsmStmt : public AsmStmt {
34253427
return {};
34263428
}
34273429

3428-
StringRef getInputConstraint(unsigned i) const;
3430+
std::string getInputConstraint(unsigned i) const;
34293431

3430-
const StringLiteral *getInputConstraintLiteral(unsigned i) const {
3432+
const Expr *getInputConstraintExpr(unsigned i) const {
34313433
return Constraints[i + NumOutputs];
34323434
}
3433-
StringLiteral *getInputConstraintLiteral(unsigned i) {
3435+
Expr *getInputConstraintExpr(unsigned i) {
34343436
return Constraints[i + NumOutputs];
34353437
}
34363438

@@ -3441,6 +3443,8 @@ class GCCAsmStmt : public AsmStmt {
34413443
return const_cast<GCCAsmStmt*>(this)->getInputExpr(i);
34423444
}
34433445

3446+
static std::string ExtractStringFromGCCAsmStmtComponent(const Expr* E);
3447+
34443448
//===--- Labels ---===//
34453449

34463450
bool isAsmGoto() const {
@@ -3489,12 +3493,12 @@ class GCCAsmStmt : public AsmStmt {
34893493
private:
34903494
void setOutputsAndInputsAndClobbers(const ASTContext &C,
34913495
IdentifierInfo **Names,
3492-
StringLiteral **Constraints,
3496+
Expr **Constraints,
34933497
Stmt **Exprs,
34943498
unsigned NumOutputs,
34953499
unsigned NumInputs,
34963500
unsigned NumLabels,
3497-
StringLiteral **Clobbers,
3501+
Expr **Clobbers,
34983502
unsigned NumClobbers);
34993503

35003504
public:
@@ -3505,10 +3509,10 @@ class GCCAsmStmt : public AsmStmt {
35053509
/// This returns -1 if the operand name is invalid.
35063510
int getNamedOperand(StringRef SymbolicName) const;
35073511

3508-
StringRef getClobber(unsigned i) const;
3512+
std::string getClobber(unsigned i) const;
35093513

3510-
StringLiteral *getClobberStringLiteral(unsigned i) { return Clobbers[i]; }
3511-
const StringLiteral *getClobberStringLiteral(unsigned i) const {
3514+
Expr *getClobberExpr(unsigned i) { return Clobbers[i]; }
3515+
const Expr *getClobberExpr(unsigned i) const {
35123516
return Clobbers[i];
35133517
}
35143518

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,10 @@ def warn_cxx20_compat_label_end_of_compound_statement : Warning<
336336
def err_address_of_label_outside_fn : Error<
337337
"use of address-of-label extension outside of a function body">;
338338
def err_asm_operand_wide_string_literal : Error<
339-
"cannot use %select{unicode|wide|an empty}0 string literal in 'asm'">;
339+
"cannot use %select{unicode|wide}0 string literal in 'asm'">;
340+
341+
def err_asm_expected_string : Error<
342+
"expected string literal %select{or parenthesized constant expression |}0in 'asm'">;
340343
def err_expected_selector_for_method : Error<
341344
"expected selector for Objective-C method">;
342345
def err_expected_property_name : Error<"expected property name">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,23 +1663,30 @@ def err_static_assert_requirement_failed : Error<
16631663
"static assertion failed due to requirement '%0'%select{: %2|}1">;
16641664
def note_expr_evaluates_to : Note<
16651665
"expression evaluates to '%0 %1 %2'">;
1666-
def err_static_assert_invalid_message : Error<
1667-
"the message in a static assertion must be a string literal or an "
1666+
1667+
1668+
def subst_user_defined_msg : TextSubstitution<
1669+
"%select{the message|the expression}0 in "
1670+
"%select{a static assertion|this asm operand}0">;
1671+
1672+
def err_user_defined_msg_invalid : Error<
1673+
"%sub{subst_user_defined_msg}0 must be a string literal or an "
16681674
"object with 'data()' and 'size()' member functions">;
1669-
def err_static_assert_missing_member_function : Error<
1670-
"the message object in this static assertion is missing %select{"
1675+
def err_user_defined_msg_missing_member_function : Error<
1676+
"the %select{message|string}0 object in "
1677+
"%select{this static assertion|this asm operand}0 is missing %select{"
16711678
"a 'size()' member function|"
16721679
"a 'data()' member function|"
1673-
"'data()' and 'size()' member functions}0">;
1674-
def err_static_assert_invalid_mem_fn_ret_ty : Error<
1675-
"the message in a static assertion must have a '%select{size|data}0()' member "
1676-
"function returning an object convertible to '%select{std::size_t|const char *}0'">;
1677-
def warn_static_assert_message_constexpr : Warning<
1678-
"the message in this static assertion is not a "
1679-
"constant expression">,
1680+
"'data()' and 'size()' member functions}1">;
1681+
def err_user_defined_msg_invalid_mem_fn_ret_ty : Error<
1682+
"%sub{subst_user_defined_msg}0 must have a '%select{size|data}1()' member "
1683+
"function returning an object convertible to '%select{std::size_t|const char *}1'">;
1684+
def warn_user_defined_msg_constexpr : Warning<
1685+
"%select{the message|the expression}0 in "
1686+
"%select{this static assertion|this asm operand}0 is not a constant expression">,
16801687
DefaultError, InGroup<DiagGroup<"invalid-static-assert-message">>;
1681-
def err_static_assert_message_constexpr : Error<
1682-
"the message in a static assertion must be produced by a "
1688+
def err_user_defined_msg_constexpr : Error<
1689+
"%sub{subst_user_defined_msg}0 must be produced by a "
16831690
"constant expression">;
16841691

16851692
def warn_consteval_if_always_true : Warning<
@@ -9514,6 +9521,9 @@ def warn_redefine_extname_not_applied : Warning<
95149521

95159522
// inline asm.
95169523
let CategoryName = "Inline Assembly Issue" in {
9524+
def err_asm_operand_empty_string : Error<
9525+
"cannot use an empty string literal in 'asm'">;
9526+
95179527
def err_asm_pmf_through_constraint_not_permitted
95189528
: Error<"cannot pass a pointer-to-member through register-constrained "
95199529
"inline assembly parameter">;

clang/include/clang/Sema/Sema.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5585,9 +5585,18 @@ class Sema final : public SemaBase {
55855585
void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
55865586
void ActOnFinishDelayedMemberInitializers(Decl *Record);
55875587

5588-
bool EvaluateStaticAssertMessageAsString(Expr *Message, std::string &Result,
5589-
ASTContext &Ctx,
5590-
bool ErrorOnInvalidMessage);
5588+
enum class StringEvaluationContext {
5589+
StaticAssert = 0,
5590+
Asm = 1
5591+
};
5592+
5593+
bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx,
5594+
StringEvaluationContext EvalContext,
5595+
bool ErrorOnInvalidMessage);
5596+
bool EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx,
5597+
StringEvaluationContext EvalContext,
5598+
bool ErrorOnInvalidMessage);
5599+
55915600
Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc,
55925601
Expr *AssertExpr, Expr *AssertMessageExpr,
55935602
SourceLocation RParenLoc);
@@ -11040,6 +11049,7 @@ class Sema final : public SemaBase {
1104011049
///@{
1104111050

1104211051
public:
11052+
ExprResult ActOnGCCAsmStmtString(Expr* Stm, bool ForAsmLabel);
1104311053
StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
1104411054
bool IsVolatile, unsigned NumOutputs,
1104511055
unsigned NumInputs, IdentifierInfo **Names,
@@ -15328,6 +15338,15 @@ void Sema::PragmaStack<Sema::AlignPackInfo>::Act(SourceLocation PragmaLocation,
1532815338
PragmaMsStackAction Action,
1532915339
llvm::StringRef StackSlotLabel,
1533015340
AlignPackInfo Value);
15341+
15342+
inline
15343+
const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
15344+
Sema::StringEvaluationContext Ctx) {
15345+
DB << llvm::to_underlying(Ctx);
15346+
return DB;
15347+
}
15348+
15349+
1533115350
} // end namespace clang
1533215351

1533315352
#endif

clang/lib/AST/ASTImporter.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6821,25 +6821,25 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
68216821
Names.push_back(ToII);
68226822
}
68236823

6824-
SmallVector<StringLiteral *, 4> Clobbers;
6824+
SmallVector<Expr *, 4> Clobbers;
68256825
for (unsigned I = 0, E = S->getNumClobbers(); I != E; I++) {
6826-
if (auto ClobberOrErr = import(S->getClobberStringLiteral(I)))
6826+
if (auto ClobberOrErr = import(S->getClobberExpr(I)))
68276827
Clobbers.push_back(*ClobberOrErr);
68286828
else
68296829
return ClobberOrErr.takeError();
68306830

68316831
}
68326832

6833-
SmallVector<StringLiteral *, 4> Constraints;
6833+
SmallVector<Expr *, 4> Constraints;
68346834
for (unsigned I = 0, E = S->getNumOutputs(); I != E; I++) {
6835-
if (auto OutputOrErr = import(S->getOutputConstraintLiteral(I)))
6835+
if (auto OutputOrErr = import(S->getOutputConstraintExpr(I)))
68366836
Constraints.push_back(*OutputOrErr);
68376837
else
68386838
return OutputOrErr.takeError();
68396839
}
68406840

68416841
for (unsigned I = 0, E = S->getNumInputs(); I != E; I++) {
6842-
if (auto InputOrErr = import(S->getInputConstraintLiteral(I)))
6842+
if (auto InputOrErr = import(S->getInputConstraintExpr(I)))
68436843
Constraints.push_back(*InputOrErr);
68446844
else
68456845
return InputOrErr.takeError();
@@ -6861,7 +6861,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
68616861
ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc());
68626862
if (!AsmLocOrErr)
68636863
return AsmLocOrErr.takeError();
6864-
auto AsmStrOrErr = import(S->getAsmString());
6864+
auto AsmStrOrErr = import(S->getAsmStringExpr());
68656865
if (!AsmStrOrErr)
68666866
return AsmStrOrErr.takeError();
68676867
ExpectedSLoc RParenLocOrErr = import(S->getRParenLoc());

0 commit comments

Comments
 (0)