Skip to content

Commit

Permalink
[clang][frontend] Support applying the annotate attribute to statemen…
Browse files Browse the repository at this point in the history
…ts (#111841)

By allowing AnnotateAttr to be applied to statements, users can place arbitrary information in the AST for later use.

For example, this can be used for HW-targeted language extensions that involve specialized loop annotations.
  • Loading branch information
ericastor authored Oct 10, 2024
1 parent 2190ffa commit 73e74e4
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 41 deletions.
17 changes: 17 additions & 0 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,23 @@ class InheritableParamAttr : public InheritableAttr {
}
};

class InheritableParamOrStmtAttr : public InheritableParamAttr {
protected:
InheritableParamOrStmtAttr(ASTContext &Context,
const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed,
bool InheritEvenIfAlreadyPresent)
: InheritableParamAttr(Context, CommonInfo, AK, IsLateParsed,
InheritEvenIfAlreadyPresent) {}

public:
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() >= attr::FirstInheritableParamOrStmtAttr &&
A->getKind() <= attr::LastInheritableParamOrStmtAttr;
}
};

class HLSLAnnotationAttr : public InheritableAttr {
protected:
HLSLAnnotationAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,11 @@ class TargetSpecificAttr<TargetSpec target> {
/// redeclarations, even when it's written on a parameter.
class InheritableParamAttr : InheritableAttr;

/// A attribute that is either a declaration attribute or a statement attribute,
/// and if used as a declaration attribute, is inherited by later
/// redeclarations, even when it's written on a parameter.
class InheritableParamOrStmtAttr : InheritableParamAttr;

/// An attribute which changes the ABI rules for a specific parameter.
class ParameterABIAttr : InheritableParamAttr {
let Subjects = SubjectList<[ParmVar]>;
Expand Down Expand Up @@ -928,7 +933,7 @@ def AnalyzerNoReturn : InheritableAttr {
let Documentation = [Undocumented];
}

def Annotate : InheritableParamAttr {
def Annotate : InheritableParamOrStmtAttr {
let Spellings = [Clang<"annotate">];
let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">];
// Ensure that the annotate attribute can be used with
Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4528,9 +4528,10 @@ class Sema final : public SemaBase {
/// declaration.
void AddAlignValueAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E);

/// AddAnnotationAttr - Adds an annotation Annot with Args arguments to D.
void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Annot, MutableArrayRef<Expr *> Args);
/// CreateAnnotationAttr - Creates an annotation Annot with Args arguments.
Attr *CreateAnnotationAttr(const AttributeCommonInfo &CI, StringRef Annot,
MutableArrayRef<Expr *> Args);
Attr *CreateAnnotationAttr(const ParsedAttr &AL);

bool checkMSInheritanceAttrOnDefinition(CXXRecordDecl *RD, SourceRange Range,
bool BestCase,
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2777,3 +2777,31 @@ bool Sema::isDeclaratorFunctionLike(Declarator &D) {
});
return Result;
}

Attr *Sema::CreateAnnotationAttr(const AttributeCommonInfo &CI, StringRef Annot,
MutableArrayRef<Expr *> Args) {

auto *A = AnnotateAttr::Create(Context, Annot, Args.data(), Args.size(), CI);
if (!ConstantFoldAttrArgs(
CI, MutableArrayRef<Expr *>(A->args_begin(), A->args_end()))) {
return nullptr;
}
return A;
}

Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) {
// Make sure that there is a string literal as the annotation's first
// argument.
StringRef Str;
if (!checkStringLiteralArgumentAttr(AL, 0, Str))
return nullptr;

llvm::SmallVector<Expr *, 4> Args;
Args.reserve(AL.getNumArgs() - 1);
for (unsigned Idx = 1; Idx < AL.getNumArgs(); Idx++) {
assert(!AL.isArgIdent(Idx));
Args.push_back(AL.getArgAsExpr(Idx));
}

return CreateAnnotationAttr(AL, Str, Args);
}
25 changes: 3 additions & 22 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3958,30 +3958,11 @@ static void handleTransparentUnionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
RD->addAttr(::new (S.Context) TransparentUnionAttr(S.Context, AL));
}

void Sema::AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Str, MutableArrayRef<Expr *> Args) {
auto *Attr = AnnotateAttr::Create(Context, Str, Args.data(), Args.size(), CI);
if (ConstantFoldAttrArgs(
CI, MutableArrayRef<Expr *>(Attr->args_begin(), Attr->args_end()))) {
D->addAttr(Attr);
}
}

static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// Make sure that there is a string literal as the annotation's first
// argument.
StringRef Str;
if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
return;

llvm::SmallVector<Expr *, 4> Args;
Args.reserve(AL.getNumArgs() - 1);
for (unsigned Idx = 1; Idx < AL.getNumArgs(); Idx++) {
assert(!AL.isArgIdent(Idx));
Args.push_back(AL.getArgAsExpr(Idx));
auto *Attr = S.CreateAnnotationAttr(AL);
if (Attr) {
D->addAttr(Attr);
}

S.AddAnnotationAttr(D, AL, Str, Args);
}

static void handleAlignValueAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaStmtAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
return handleMSConstexprAttr(S, St, A, Range);
case ParsedAttr::AT_NoConvergent:
return handleNoConvergentAttr(S, St, A, Range);
case ParsedAttr::AT_Annotate:
return S.CreateAnnotationAttr(A);
default:
// N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
// declaration attribute is not written on a statement, but this code is
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,7 @@ namespace {
NamedDecl *FirstQualifierInScope = nullptr,
bool AllowInjectedClassName = false);

const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
Expand Down Expand Up @@ -2182,6 +2183,18 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
Arg, PackIndex);
}

const AnnotateAttr *
TemplateInstantiator::TransformAnnotateAttr(const AnnotateAttr *AA) {
SmallVector<Expr *> Args;
for (Expr *Arg : AA->args()) {
ExprResult Res = getDerived().TransformExpr(Arg);
if (Res.isUsable())
Args.push_back(Res.get());
}
return AnnotateAttr::CreateImplicit(getSema().Context, AA->getAnnotation(),
Args.data(), Args.size(), AA->getRange());
}

const CXXAssumeAttr *
TemplateInstantiator::TransformCXXAssumeAttr(const CXXAssumeAttr *AA) {
ExprResult Res = getDerived().TransformExpr(AA->getAssumption());
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,10 @@ static void instantiateDependentAnnotationAttr(
ActualArgs.insert(ActualArgs.begin(), Args.begin() + 1, Args.end());
std::swap(Args, ActualArgs);
}
S.AddAnnotationAttr(New, *Attr, Str, Args);
auto *AA = S.CreateAnnotationAttr(*Attr, Str, Args);
if (AA) {
New->addAttr(AA);
}
}

static Expr *instantiateDependentFunctionAttrCondition(
Expand Down
3 changes: 3 additions & 0 deletions clang/test/AST/attr-print-emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class C {
ANNOTATE_ATTR int annotated_attr ANNOTATE_ATTR = 0;
// CHECK: __attribute__((annotate("Annotated"))) int annotated_attr __attribute__((annotate("Annotated"))) = 0;

void increment() { [[clang::annotate("Annotated")]] annotated_attr++; }
// CHECK: {{\[\[}}clang::annotate("Annotated")]] annotated_attr++;

// FIXME: We do not print the attribute as written after the type specifier.
int ANNOTATE_ATTR annotated_attr_fixme = 0;
// CHECK: __attribute__((annotate("Annotated"))) int annotated_attr_fixme = 0;
Expand Down
3 changes: 3 additions & 0 deletions clang/test/Sema/annotate.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
void __attribute__((annotate("foo"))) foo(float *a) {
__attribute__((annotate("bar"))) int x;
[[clang::annotate("bar")]] int x2;
[[clang::annotate("bar")]] x2 += 1;
__attribute__((annotate(1))) int y; // expected-error {{expected string literal as argument of 'annotate' attribute}}
[[clang::annotate(1)]] int y2; // expected-error {{expected string literal as argument of 'annotate' attribute}}
__attribute__((annotate("bar", 1))) int z;
[[clang::annotate("bar", 1)]] int z2;
[[clang::annotate("bar", 1)]] z2 += 1;

int u = __builtin_annotation(z, (char*) 0); // expected-error {{second argument to __builtin_annotation must be a non-wide string constant}}
int v = __builtin_annotation(z, (char*) L"bar"); // expected-error {{second argument to __builtin_annotation must be a non-wide string constant}}
Expand All @@ -15,4 +17,5 @@ void __attribute__((annotate("foo"))) foo(float *a) {

__attribute__((annotate())) int c; // expected-error {{'annotate' attribute takes at least 1 argument}}
[[clang::annotate()]] int c2; // expected-error {{'annotate' attribute takes at least 1 argument}}
[[clang::annotate()]] c2 += 1; // expected-error {{'annotate' attribute takes at least 1 argument}}
}
38 changes: 38 additions & 0 deletions clang/test/SemaTemplate/attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ namespace attribute_annotate {
template<typename T> [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations();
void UseAnnotations() { HasAnnotations<int>(); }

// CHECK: FunctionTemplateDecl {{.*}} HasStmtAnnotations
// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAZ"
// CHECK: FunctionDecl {{.*}} HasStmtAnnotations
// CHECK: TemplateArgument type 'int'
// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAZ"
template<typename T> void HasStmtAnnotations() {
int x = 0;
[[clang::annotate("ANNOTATE_BAZ")]] x++;
}
void UseStmtAnnotations() { HasStmtAnnotations<int>(); }

// CHECK: FunctionTemplateDecl {{.*}} HasPackAnnotations
// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is
// CHECK-NEXT: FunctionDecl {{.*}} HasPackAnnotations 'void ()'
Expand Down Expand Up @@ -95,6 +106,33 @@ void UseAnnotations() { HasAnnotations<int>(); }
template <int... Is> [[clang::annotate("ANNOTATE_BAZ", Is...)]] void HasPackAnnotations();
void UsePackAnnotations() { HasPackAnnotations<1, 2, 3>(); }

// CHECK: FunctionTemplateDecl {{.*}} HasStmtPackAnnotations
// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is
// CHECK-NEXT: FunctionDecl {{.*}} HasStmtPackAnnotations 'void ()'
// CHECK: AttributedStmt {{.*}}
// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_QUUX"
// CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>'
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int'
// CHECK: FunctionDecl {{.*}} used HasStmtPackAnnotations 'void ()'
// CHECK-NEXT: TemplateArgument{{.*}} pack
// CHECK-NEXT: TemplateArgument{{.*}} integral '1'
// CHECK-NEXT: TemplateArgument{{.*}} integral '2'
// CHECK-NEXT: TemplateArgument{{.*}} integral '3'
// CHECK: AttributedStmt {{.*}}
// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_QUUX"
// CHECK-NEXT: PackExpansionExpr {{.*}}
// CHECK-NEXT: SubstNonTypeTemplateParmPackExpr {{.*}}
// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is
// CHECK-NEXT: TemplateArgument pack '<1, 2, 3>'
// CHECK-NEXT: TemplateArgument integral '1'
// CHECK-NEXT: TemplateArgument integral '2'
// CHECK-NEXT: TemplateArgument integral '3'
template <int... Is> void HasStmtPackAnnotations() {
int x = 0;
[[clang::annotate("ANNOTATE_QUUX", Is...)]] x++;
}
void UseStmtPackAnnotations() { HasStmtPackAnnotations<1, 2, 3>(); }

template <int... Is> [[clang::annotate(Is...)]] void HasOnlyPackAnnotation() {} // expected-error {{expected string literal as argument of 'annotate' attribute}}

void UseOnlyPackAnnotations() {
Expand Down
30 changes: 16 additions & 14 deletions clang/utils/TableGen/ClangAttrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3282,16 +3282,16 @@ namespace {
} // end anonymous namespace

static const AttrClassDescriptor AttrClassDescriptors[] = {
{ "ATTR", "Attr" },
{ "TYPE_ATTR", "TypeAttr" },
{ "STMT_ATTR", "StmtAttr" },
{ "DECL_OR_STMT_ATTR", "DeclOrStmtAttr" },
{ "INHERITABLE_ATTR", "InheritableAttr" },
{ "DECL_OR_TYPE_ATTR", "DeclOrTypeAttr" },
{ "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" },
{ "PARAMETER_ABI_ATTR", "ParameterABIAttr" },
{ "HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"}
};
{"ATTR", "Attr"},
{"TYPE_ATTR", "TypeAttr"},
{"STMT_ATTR", "StmtAttr"},
{"DECL_OR_STMT_ATTR", "DeclOrStmtAttr"},
{"INHERITABLE_ATTR", "InheritableAttr"},
{"DECL_OR_TYPE_ATTR", "DeclOrTypeAttr"},
{"INHERITABLE_PARAM_ATTR", "InheritableParamAttr"},
{"INHERITABLE_PARAM_OR_STMT_ATTR", "InheritableParamOrStmtAttr"},
{"PARAMETER_ABI_ATTR", "ParameterABIAttr"},
{"HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"}};

static void emitDefaultDefine(raw_ostream &OS, StringRef name,
const char *superName) {
Expand Down Expand Up @@ -4319,10 +4319,12 @@ static void GenerateMutualExclusionsChecks(const Record &Attr,

// This means the attribute is either a statement attribute, a decl
// attribute, or both; find out which.
bool CurAttrIsStmtAttr =
Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr");
bool CurAttrIsDeclAttr =
!CurAttrIsStmtAttr || Attr.isSubClassOf("DeclOrStmtAttr");
bool CurAttrIsStmtAttr = Attr.isSubClassOf("StmtAttr") ||
Attr.isSubClassOf("DeclOrStmtAttr") ||
Attr.isSubClassOf("InheritableParamOrStmtAttr");
bool CurAttrIsDeclAttr = !CurAttrIsStmtAttr ||
Attr.isSubClassOf("DeclOrStmtAttr") ||
Attr.isSubClassOf("InheritableParamOrStmtAttr");

std::vector<std::string> DeclAttrs, StmtAttrs;

Expand Down

0 comments on commit 73e74e4

Please sign in to comment.