Updated per review comments from Richard and Aaron (and rebased).
http://reviews.llvm.org/D4601
Files:
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Sema/Sema.h
lib/CodeGen/CGExpr.cpp
lib/Sema/SemaChecking.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CodeGen/builtin-assume-aligned.c
test/Sema/builtin-assume-aligned.c
test/SemaCXX/builtin-assume-aligned-tmpl.cpp
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -864,6 +864,13 @@
let Documentation = [Undocumented];
}
+def AssumeAligned : InheritableAttr {
+ let Spellings = [GCC<"assume_aligned">];
+ let Subjects = SubjectList<[ObjCMethod, Function]>;
+ let Args = [ExprArgument<"Alignment">, ExprArgument<"Offset", 1>];
+ let Documentation = [AssumeAlignedDocs];
+}
+
def NoReturn : InheritableAttr {
let Spellings = [GCC<"noreturn">, Declspec<"noreturn">];
// FIXME: Does GCC allow this on the function instead?
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -154,6 +154,30 @@
Marks a function as releasing a capability.
}];
}
+
+def AssumeAlignedDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Use ``__attribute__((assume_aligned(<alignment>[,<offset>]))`` on a function
+declaration to specify that the return value of the function (which must be a
+pointer type) has the specified offset, in bytes, from an address with the
+specified alignment. The offset is taken to be zero if omitted.
+
+.. code-block:: c++
+
+ // The returned pointer value has 32-byte alignment.
+ void *a() __attribute__((assume_aligned (32)));
+
+ // The returned pointer value is 4 bytes greater than an address having
+ // 32-byte alignment.
+ void *b() __attribute__((assume_aligned (32, 4)));
+
+Note that this attribute provides information to the compiler regarding a
+condition that the code already ensures is true. It does not cause the compiler
+to enforce the provided alignment assumption.
+ }];
+}
+
def EnableIfDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2705,8 +2705,9 @@
void checkUnusedDeclAttributes(Declarator &D);
- /// Determine if type T is a valid subject for a nonnull attribute.
- bool isValidNonNullAttrType(QualType T);
+ /// Determine if type T is a valid subject for a nonnull and similar
+ /// attributes.
+ bool isValidPointerAttrType(QualType T);
bool CheckRegparmAttr(const AttributeList &attr, unsigned &value);
bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC,
@@ -7331,6 +7332,11 @@
void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T,
unsigned SpellingListIndex, bool IsPackExpansion);
+ /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
+ /// declaration.
+ void AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE,
+ unsigned SpellingListIndex);
+
// OpenMP directives and clauses.
private:
void *VarDataSharingAttributesStack;
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -3376,7 +3376,22 @@
Callee = Builder.CreateBitCast(Callee, CalleeTy, "callee.knr.cast");
}
- return EmitCall(FnInfo, Callee, ReturnValue, Args, TargetDecl);
+ RValue Ret = EmitCall(FnInfo, Callee, ReturnValue, Args, TargetDecl);
+
+ if (Ret.isScalar() && TargetDecl) {
+ if (const auto *AA = TargetDecl->getAttr<AssumeAlignedAttr>()) {
+ llvm::Value *OffsetValue = nullptr;
+ if (const auto *Offset = AA->getOffset())
+ OffsetValue = EmitScalarExpr(Offset);
+
+ llvm::Value *Alignment = EmitScalarExpr(AA->getAlignment());
+ llvm::ConstantInt *AlignmentCI = cast<llvm::ConstantInt>(Alignment);
+ EmitAlignmentAssumption(Ret.getScalarVal(), AlignmentCI->getZExtValue(),
+ OffsetValue);
+ }
+ }
+
+ return Ret;
}
LValue CodeGenFunction::
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -777,7 +777,7 @@
if (!NonNull->args_size()) {
// Easy case: all pointer arguments are nonnull.
for (const auto *Arg : Args)
- if (S.isValidNonNullAttrType(Arg->getType()))
+ if (S.isValidPointerAttrType(Arg->getType()))
CheckNonNullArgument(S, Arg, CallSiteLoc);
return;
}
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -29,6 +29,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/MathExtras.h"
using namespace clang;
using namespace sema;
@@ -1125,7 +1126,7 @@
Attr.getAttributeSpellingListIndex()));
}
-bool Sema::isValidNonNullAttrType(QualType T) {
+bool Sema::isValidPointerAttrType(QualType T) {
T = T.getNonReferenceType();
// The nonnull attribute can be applied to a transparent union that
@@ -1146,13 +1147,13 @@
static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &Attr,
SourceRange AttrParmRange,
- SourceRange NonNullTypeRange,
+ SourceRange TypeRange,
bool isReturnValue = false) {
- if (!S.isValidNonNullAttrType(T)) {
+ if (!S.isValidPointerAttrType(T)) {
S.Diag(Attr.getLoc(), isReturnValue
? diag::warn_attribute_return_pointers_only
: diag::warn_attribute_pointers_only)
- << Attr.getName() << AttrParmRange << NonNullTypeRange;
+ << Attr.getName() << AttrParmRange << TypeRange;
return false;
}
return true;
@@ -1186,7 +1187,7 @@
for (unsigned I = 0, E = getFunctionOrMethodNumParams(D);
I != E && !AnyPointers; ++I) {
QualType T = getFunctionOrMethodParamType(D, I);
- if (T->isDependentType() || S.isValidNonNullAttrType(T))
+ if (T->isDependentType() || S.isValidPointerAttrType(T))
AnyPointers = true;
}
@@ -1237,6 +1238,65 @@
Attr.getAttributeSpellingListIndex()));
}
+static void handleAssumeAlignedAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ Expr *E = Attr.getArgAsExpr(0),
+ *OE = Attr.getNumArgs() > 1 ? Attr.getArgAsExpr(1) : nullptr;
+ S.AddAssumeAlignedAttr(Attr.getRange(), D, E, OE,
+ Attr.getAttributeSpellingListIndex());
+}
+
+void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E,
+ Expr *OE, unsigned SpellingListIndex) {
+ QualType ResultType = getFunctionOrMethodResultType(D);
+ SourceRange SR = getFunctionOrMethodResultSourceRange(D);
+
+ AssumeAlignedAttr TmpAttr(AttrRange, Context, E, OE, SpellingListIndex);
+ SourceLocation AttrLoc = AttrRange.getBegin();
+
+ if (!isValidPointerAttrType(ResultType)) {
+ Diag(AttrLoc, diag::warn_attribute_return_pointers_only)
+ << &TmpAttr << AttrRange << SR;
+ return;
+ }
+
+ if (!E->isTypeDependent() && !E->isValueDependent()) {
+ llvm::APSInt I(64);
+ if (!E->isIntegerConstantExpr(I, Context)) {
+ if (OE)
+ Diag(AttrLoc, diag::err_attribute_argument_n_type)
+ << &TmpAttr << 1 << AANT_ArgumentIntegerConstant
+ << E->getSourceRange();
+ else
+ Diag(AttrLoc, diag::err_attribute_argument_type)
+ << &TmpAttr << AANT_ArgumentIntegerConstant
+ << E->getSourceRange();
+ return;
+ }
+
+ if (!I.isPowerOf2()) {
+ Diag(AttrLoc, diag::err_alignment_not_power_of_two)
+ << E->getSourceRange();
+ return;
+ }
+ }
+
+ if (OE) {
+ if (!OE->isTypeDependent() && !OE->isValueDependent()) {
+ llvm::APSInt I(64);
+ if (!OE->isIntegerConstantExpr(I, Context)) {
+ Diag(AttrLoc, diag::err_attribute_argument_n_type)
+ << &TmpAttr << 2 << AANT_ArgumentIntegerConstant
+ << OE->getSourceRange();
+ return;
+ }
+ }
+ }
+
+ D->addAttr(::new (Context)
+ AssumeAlignedAttr(AttrRange, Context, E, OE, SpellingListIndex));
+}
+
static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
// This attribute must be applied to a function declaration. The first
// argument to the attribute must be an identifier, the name of the resource,
@@ -4212,6 +4272,9 @@
case AttributeList::AT_ReturnsNonNull:
handleReturnsNonNullAttr(S, D, Attr);
break;
+ case AttributeList::AT_AssumeAligned:
+ handleAssumeAlignedAttr(S, D, Attr);
+ break;
case AttributeList::AT_Overloadable:
handleSimpleAttribute<OverloadableAttr>(S, D, Attr);
break;
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -129,6 +129,29 @@
}
}
+static void instantiateDependentAssumeAlignedAttr(
+ Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+ const AssumeAlignedAttr *Aligned, Decl *New) {
+ // The alignment expression is a constant expression.
+ EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated);
+
+ Expr *E, *OE = nullptr;
+ ExprResult Result = S.SubstExpr(Aligned->getAlignment(), TemplateArgs);
+ if (Result.isInvalid())
+ return;
+ E = Result.getAs<Expr>();
+
+ if (Aligned->getOffset()) {
+ Result = S.SubstExpr(Aligned->getOffset(), TemplateArgs);
+ if (Result.isInvalid())
+ return;
+ OE = Result.getAs<Expr>();
+ }
+
+ S.AddAssumeAlignedAttr(Aligned->getLocation(), New, E, OE,
+ Aligned->getSpellingListIndex());
+}
+
static void instantiateDependentEnableIfAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const EnableIfAttr *A, const Decl *Tmpl, Decl *New) {
@@ -176,6 +199,14 @@
continue;
}
+ const AssumeAlignedAttr *AssumeAligned = dyn_cast<AssumeAlignedAttr>(TmplAttr);
+ if (AssumeAligned && (AssumeAligned->getAlignment()->isValueDependent() ||
+ (AssumeAligned->getOffset() &&
+ AssumeAligned->getOffset()->isValueDependent()))) {
+ instantiateDependentAssumeAlignedAttr(*this, TemplateArgs, AssumeAligned, New);
+ continue;
+ }
+
const EnableIfAttr *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr);
if (EnableIf && EnableIf->getCond()->isValueDependent()) {
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
Index: test/CodeGen/builtin-assume-aligned.c
===================================================================
--- test/CodeGen/builtin-assume-aligned.c
+++ test/CodeGen/builtin-assume-aligned.c
@@ -42,3 +42,26 @@
return a[0];
}
+int *m1() __attribute__((assume_aligned(64)));
+
+// CHECK-LABEL: @test5
+int test5() {
+ return *m1();
+// CHECK: %ptrint = ptrtoint
+// CHECK: %maskedptr = and i64 %ptrint, 63
+// CHECK: %maskcond = icmp eq i64 %maskedptr, 0
+// CHECK: call void @llvm.assume(i1 %maskcond)
+}
+
+int *m2() __attribute__((assume_aligned(64, 12)));
+
+// CHECK-LABEL: @test6
+int test6() {
+ return *m2();
+// CHECK: %ptrint = ptrtoint
+// CHECK: %offsetptr = sub i64 %ptrint, 12
+// CHECK: %maskedptr = and i64 %offsetptr, 63
+// CHECK: %maskcond = icmp eq i64 %maskedptr, 0
+// CHECK: call void @llvm.assume(i1 %maskcond)
+}
+
Index: test/Sema/builtin-assume-aligned.c
===================================================================
--- test/Sema/builtin-assume-aligned.c
+++ test/Sema/builtin-assume-aligned.c
@@ -41,3 +41,18 @@
return a[0];
}
+void test_void_assume_aligned(void) __attribute__((assume_aligned(32))); // expected-warning {{'assume_aligned' attribute only applies to return values that are pointers}}
+int test_int_assume_aligned(void) __attribute__((assume_aligned(16))); // expected-warning {{'assume_aligned' attribute only applies to return values that are pointers}}
+void *test_ptr_assume_aligned(void) __attribute__((assume_aligned(64))); // no-warning
+
+int j __attribute__((assume_aligned(8))); // expected-warning {{'assume_aligned' attribute only applies to functions and methods}}
+void *test_no_fn_proto() __attribute__((assume_aligned(32))); // no-warning
+void *test_with_fn_proto(void) __attribute__((assume_aligned(128))); // no-warning
+
+void *test_no_fn_proto() __attribute__((assume_aligned(31))); // expected-error {{requested alignment is not a power of 2}}
+void *test_no_fn_proto() __attribute__((assume_aligned(32, 73))); // no-warning
+
+void *test_no_fn_proto() __attribute__((assume_aligned)); // expected-error {{'assume_aligned' attribute takes at least 1 argument}}
+void *test_no_fn_proto() __attribute__((assume_aligned())); // expected-error {{'assume_aligned' attribute takes at least 1 argument}}
+void *test_no_fn_proto() __attribute__((assume_aligned(32, 45, 37))); // expected-error {{'assume_aligned' attribute takes no more than 2 arguments}}
+
Index: test/SemaCXX/builtin-assume-aligned-tmpl.cpp
===================================================================
--- test/SemaCXX/builtin-assume-aligned-tmpl.cpp
+++ test/SemaCXX/builtin-assume-aligned-tmpl.cpp
@@ -20,3 +20,17 @@
return test10(a, 42); // expected-note {{in instantiation of function template specialization 'test10<int>' requested here}}
}
+template <int q>
+void *atest() __attribute__((assume_aligned(q))); // expected-error {{requested alignment is not a power of 2}}
+
+template <int q, int o>
+void *atest2() __attribute__((assume_aligned(q, o))); // expected-error {{requested alignment is not a power of 2}}
+
+void test20() {
+ atest<31>(); // expected-note {{in instantiation of function template specialization 'atest<31>' requested here}}
+ atest<32>();
+
+ atest2<31, 5>(); // expected-note {{in instantiation of function template specialization 'atest2<31, 5>' requested here}}
+ atest2<32, 4>();
+}
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits