Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -860,6 +860,14 @@ let Documentation = [Undocumented]; } +def AssumeAligned : InheritableAttr { + let Spellings = [GCC<"assume_aligned">]; + let Subjects = SubjectList<[ObjCMethod, Function], WarnDiag, + "ExpectedFunctionOrMethod">; + let Args = [IntArgument<"Alignment">, DefaultIntArgument<"Offset", 0>]; + let Documentation = [Undocumented]; +} + def NoReturn : InheritableAttr { let Spellings = [GCC<"noreturn">, Declspec<"noreturn">]; // FIXME: Does GCC allow this on the function instead? Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -3347,7 +3347,20 @@ 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 AssumeAlignedAttr *AA = + TargetDecl->getAttr()) { + llvm::Value *OffsetValue = nullptr; + if (int Offset = AA->getOffset()) + OffsetValue = llvm::ConstantInt::get(IntPtrTy, Offset); + + EmitAlignmentAssumption(Ret.getScalarVal(), AA->getAlignment(), + OffsetValue); + } + + return Ret; } LValue CodeGenFunction:: 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; @@ -1115,7 +1116,7 @@ } } -static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &Attr, +static bool attrPointerArgCheck(Sema &S, QualType T, const AttributeList &Attr, SourceRange R, bool isReturnValue = false) { T = T.getNonReferenceType(); possibleTransparentUnionPointerType(T); @@ -1140,7 +1141,7 @@ // Is the function argument a pointer type? // FIXME: Should also highlight argument in decl in the diagnostic. - if (!attrNonNullArgCheck(S, getFunctionOrMethodParamType(D, Idx), Attr, + if (!attrPointerArgCheck(S, getFunctionOrMethodParamType(D, Idx), Attr, Ex->getSourceRange())) continue; @@ -1188,7 +1189,7 @@ } // Is the argument a pointer type? - if (!attrNonNullArgCheck(S, D->getType(), Attr, D->getSourceRange())) + if (!attrPointerArgCheck(S, D->getType(), Attr, D->getSourceRange())) return; D->addAttr(::new (S.Context) @@ -1199,7 +1200,7 @@ static void handleReturnsNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) { QualType ResultType = getFunctionOrMethodResultType(D); - if (!attrNonNullArgCheck(S, ResultType, Attr, Attr.getRange(), + if (!attrPointerArgCheck(S, ResultType, Attr, Attr.getRange(), /* isReturnValue */ true)) return; @@ -1208,6 +1209,42 @@ Attr.getAttributeSpellingListIndex())); } +static void handleAssumeAlignedAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + QualType ResultType = getFunctionOrMethodResultType(D); + if (!attrPointerArgCheck(S, ResultType, Attr, Attr.getRange(), + /* isReturnValue */ true)) + return; + + if (Attr.getNumArgs() > 2) { + S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) + << Attr.getName() << 2; + return; + } else if (Attr.getNumArgs() < 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_too_few_arguments) + << Attr.getName() << 1; + return; + } + + uint32_t Alignment, Offset = 0; + if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), Alignment, 1)) + return; + if (Attr.getNumArgs() > 1 && !checkUInt32Argument(S, Attr, + Attr.getArgAsExpr(1), + Offset, 2)) + return; + + if (!llvm::isPowerOf2_32(Alignment)) { + S.Diag(Attr.getLoc(), diag::err_alignment_not_power_of_two) + << Attr.getArgAsExpr(0)->getSourceRange(); + return; + } + + D->addAttr(::new (S.Context) + AssumeAlignedAttr(Attr.getRange(), S.Context, Alignment, Offset, + Attr.getAttributeSpellingListIndex())); +} + 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, @@ -4198,6 +4235,9 @@ case AttributeList::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, Attr); break; + case AttributeList::AT_AssumeAligned: + handleAssumeAlignedAttr(S, D, Attr); + break; case AttributeList::AT_Overloadable: handleSimpleAttribute(S, D, Attr); break; 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}} +