From 687b32ee47fa9c025c286b9f35a0dddb53defeef Mon Sep 17 00:00:00 2001 From: Hal Finkel Date: Sun, 7 Sep 2014 22:58:14 +0000 Subject: [PATCH] Add __builtin_assume and __builtin_assume_aligned using @llvm.assume. This makes use of the recently-added @llvm.assume intrinsic to implement a __builtin_assume(bool) intrinsic (to provide additional information to the optimizer). This hooks up __assume in MS-compatibility mode to mirror __builtin_assume (the semantics have been intentionally kept compatible), and implements GCC's __builtin_assume_aligned as assume((p - o) & mask == 0). LLVM now contains special logic to deal with assumptions of this form. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@217349 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LanguageExtensions.rst | 41 ++++++++++++++++- include/clang/Basic/Builtins.def | 4 ++ include/clang/Basic/DiagnosticSemaKinds.td | 5 ++- include/clang/Sema/Sema.h | 1 + lib/AST/ExprConstant.cpp | 2 + lib/CodeGen/CGBuiltin.cpp | 24 ++++++++-- lib/CodeGen/CGExpr.cpp | 28 ++++++++++++ lib/CodeGen/CodeGenFunction.h | 4 ++ lib/Sema/SemaChecking.cpp | 46 +++++++++++++++++++- lib/Sema/SemaDeclAttr.cpp | 2 +- test/CodeGen/builtin-assume-aligned.c | 44 +++++++++++++++++++ test/CodeGen/builtin-assume.c | 22 +++++++++- test/Sema/builtin-assume-aligned.c | 43 ++++++++++++++++++ test/Sema/builtin-assume.c | 9 +++- test/SemaCXX/builtin-assume-aligned-tmpl.cpp | 22 ++++++++++ 15 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 test/CodeGen/builtin-assume-aligned.c create mode 100644 test/Sema/builtin-assume-aligned.c create mode 100644 test/SemaCXX/builtin-assume-aligned-tmpl.cpp diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst index cada69cfef..9a70198941 100644 --- a/docs/LanguageExtensions.rst +++ b/docs/LanguageExtensions.rst @@ -1228,8 +1228,9 @@ Builtin Functions Clang supports a number of builtin library functions with the same syntax as GCC, including things like ``__builtin_nan``, ``__builtin_constant_p``, ``__builtin_choose_expr``, ``__builtin_types_compatible_p``, -``__sync_fetch_and_add``, etc. In addition to the GCC builtins, Clang supports -a number of builtins that GCC does not, which are listed here. +``__builtin_assume_aligned``, ``__sync_fetch_and_add``, etc. In addition to +the GCC builtins, Clang supports a number of builtins that GCC does not, which +are listed here. Please note that Clang does not and will not support all of the GCC builtins for vector operations. Instead of using builtins, you should use the functions @@ -1239,6 +1240,42 @@ implemented directly in terms of :ref:`extended vector support ` instead of builtins, in order to reduce the number of builtins that we need to implement. +``__builtin_assume`` +------------------------------ + +``__builtin_assume`` is used to provide the optimizer with a boolean +invariant that is defined to be true. + +**Syntax**: + +.. code-block:: c++ + + __builtin_assume(bool) + +**Example of Use**: + +.. code-block:: c++ + + int foo(int x) { + __builtin_assume(x != 0); + + // The optimizer may short-circuit this check using the invariant. + if (x == 0) + return do_something(); + + return do_something_else(); + } + +**Description**: + +The boolean argument to this function is defined to be true. The optimizer may +analyze the form of the expression provided as the argument and deduce from +that information used to optimize the program. If the condition is violated +during execution, the behavior is undefined. The argument itself is never +evaluated, so any side effects of the expression will be discarded. + +Query for this feature with ``__has_builtin(__builtin_assume)``. + ``__builtin_readcyclecounter`` ------------------------------ diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def index e705382e11..bb6975c494 100644 --- a/include/clang/Basic/Builtins.def +++ b/include/clang/Basic/Builtins.def @@ -412,6 +412,7 @@ BUILTIN(__builtin_va_start, "vA.", "nt") BUILTIN(__builtin_va_end, "vA", "n") BUILTIN(__builtin_va_copy, "vAA", "n") BUILTIN(__builtin_stdarg_start, "vA.", "n") +BUILTIN(__builtin_assume_aligned, "v*vC*z.", "nc") BUILTIN(__builtin_bcmp, "iv*v*z", "n") BUILTIN(__builtin_bcopy, "vv*v*z", "n") BUILTIN(__builtin_bzero, "vv*z", "nF") @@ -1173,6 +1174,9 @@ LIBBUILTIN(_Block_object_dispose, "vvC*iC", "f", "Blocks.h", ALL_LANGUAGES) // Annotation function BUILTIN(__builtin_annotation, "v.", "tn") +// Invariants +BUILTIN(__builtin_assume, "vb", "n") + // Multiprecision Arithmetic Builtins. BUILTIN(__builtin_addcb, "UcUcCUcCUcCUc*", "n") BUILTIN(__builtin_addcs, "UsUsCUsCUsCUs*", "n") diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 995e7a5189..e826578a15 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -451,7 +451,7 @@ def note_strncat_wrong_size : Note< "the terminating null byte">; def warn_assume_side_effects : Warning< - "the argument to __assume has side effects that will be discarded">, + "the argument to %0 has side effects that will be discarded">, InGroup>; /// main() @@ -2078,8 +2078,9 @@ def err_no_accessor_for_property : Error< def error_cannot_find_suitable_accessor : Error< "cannot find suitable %select{getter|setter}0 for property %1">; -def err_attribute_aligned_not_power_of_two : Error< +def err_alignment_not_power_of_two : Error< "requested alignment is not a power of 2">; + def err_attribute_aligned_too_great : Error< "requested alignment must be %0 bytes or smaller">; def warn_redeclaration_without_attribute_prev_attribute_ignored : Warning< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a877855aa1..de9b761152 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -8375,6 +8375,7 @@ public: private: bool SemaBuiltinPrefetch(CallExpr *TheCall); bool SemaBuiltinAssume(CallExpr *TheCall); + bool SemaBuiltinAssumeAligned(CallExpr *TheCall); bool SemaBuiltinLongjmp(CallExpr *TheCall); ExprResult SemaBuiltinAtomicOverloaded(ExprResult TheCallResult); ExprResult SemaAtomicOpsOverloaded(ExprResult TheCallResult, diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 930c6ef253..155d9be8f9 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -6092,6 +6092,7 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { return Success(Operand, E); } + case Builtin::BI__builtin_assume_aligned: case Builtin::BI__builtin_expect: return Visit(E->getArg(0)); @@ -7967,6 +7968,7 @@ public: default: return ExprEvaluatorBaseTy::VisitCallExpr(E); case Builtin::BI__assume: + case Builtin::BI__builtin_assume: // The argument is not evaluated! return true; } diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 401764a8dd..69ad762d18 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -374,6 +374,27 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, "expval"); return RValue::get(Result); } + case Builtin::BI__builtin_assume_aligned: { + Value *PtrValue = EmitScalarExpr(E->getArg(0)); + Value *OffsetValue = + (E->getNumArgs() > 2) ? EmitScalarExpr(E->getArg(2)) : nullptr; + + Value *AlignmentValue = EmitScalarExpr(E->getArg(1)); + ConstantInt *AlignmentCI = cast(AlignmentValue); + unsigned Alignment = (unsigned) AlignmentCI->getZExtValue(); + + EmitAlignmentAssumption(PtrValue, Alignment, OffsetValue); + return RValue::get(PtrValue); + } + case Builtin::BI__assume: + case Builtin::BI__builtin_assume: { + if (E->getArg(0)->HasSideEffects(getContext())) + return RValue::get(nullptr); + + Value *ArgValue = EmitScalarExpr(E->getArg(0)); + Value *FnAssume = CGM.getIntrinsic(Intrinsic::assume); + return RValue::get(Builder.CreateCall(FnAssume, ArgValue)); + } case Builtin::BI__builtin_bswap16: case Builtin::BI__builtin_bswap32: case Builtin::BI__builtin_bswap64: { @@ -1510,9 +1531,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__noop: // __noop always evaluates to an integer literal zero. return RValue::get(ConstantInt::get(IntTy, 0)); - case Builtin::BI__assume: - // Until LLVM supports assumptions at the IR level, this becomes nothing. - return RValue::get(nullptr); case Builtin::BI_InterlockedExchange: case Builtin::BI_InterlockedExchangePointer: return EmitBinaryAtomic(*this, llvm::AtomicRMWInst::Xchg, E); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index fc9e60d132..426ec7496c 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -711,6 +711,34 @@ EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, return isPre ? IncVal : InVal; } +void CodeGenFunction::EmitAlignmentAssumption(llvm::Value *PtrValue, + unsigned Alignment, + llvm::Value *OffsetValue) { + llvm::Value *PtrIntValue = + Builder.CreatePtrToInt(PtrValue, IntPtrTy, "ptrint"); + + llvm::Value *Mask = llvm::ConstantInt::get(IntPtrTy, + Alignment > 0 ? Alignment - 1 : 0); + if (OffsetValue) { + bool IsOffsetZero = false; + if (llvm::ConstantInt *CI = dyn_cast(OffsetValue)) + IsOffsetZero = CI->isZero(); + + if (!IsOffsetZero) { + if (OffsetValue->getType() != IntPtrTy) + OffsetValue = Builder.CreateIntCast(OffsetValue, IntPtrTy, + /*isSigned*/true, "offsetcast"); + PtrIntValue = Builder.CreateSub(PtrIntValue, OffsetValue, "offsetptr"); + } + } + + llvm::Value *Zero = llvm::ConstantInt::get(IntPtrTy, 0); + llvm::Value *MaskedPtr = Builder.CreateAnd(PtrIntValue, Mask, "maskedptr"); + llvm::Value *InvCond = Builder.CreateICmpEQ(MaskedPtr, Zero, "maskcond"); + + llvm::Value *FnAssume = CGM.getIntrinsic(llvm::Intrinsic::assume); + Builder.CreateCall(FnAssume, InvCond); +} //===----------------------------------------------------------------------===// // LValue Expression Emission diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index c03b9ab26b..4841b53506 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1743,6 +1743,10 @@ public: bool isInc, bool isPre); ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre); + + void EmitAlignmentAssumption(llvm::Value *PtrValue, unsigned Alignment, + llvm::Value *OffsetValue = nullptr); + //===--------------------------------------------------------------------===// // Declaration Emission //===--------------------------------------------------------------------===// diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 87042d1c07..435b951b7c 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -189,9 +189,14 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return ExprError(); break; case Builtin::BI__assume: + case Builtin::BI__builtin_assume: if (SemaBuiltinAssume(TheCall)) return ExprError(); break; + case Builtin::BI__builtin_assume_aligned: + if (SemaBuiltinAssumeAligned(TheCall)) + return ExprError(); + break; case Builtin::BI__builtin_object_size: if (SemaBuiltinConstantArgRange(TheCall, 1, 0, 3)) return ExprError(); @@ -2059,7 +2064,46 @@ bool Sema::SemaBuiltinAssume(CallExpr *TheCall) { if (Arg->HasSideEffects(Context)) return Diag(Arg->getLocStart(), diag::warn_assume_side_effects) - << Arg->getSourceRange(); + << Arg->getSourceRange() + << cast(TheCall->getCalleeDecl())->getIdentifier(); + + return false; +} + +/// Handle __builtin_assume_aligned. This is declared +/// as (const void*, size_t, ...) and can take one optional constant int arg. +bool Sema::SemaBuiltinAssumeAligned(CallExpr *TheCall) { + unsigned NumArgs = TheCall->getNumArgs(); + + if (NumArgs > 3) + return Diag(TheCall->getLocEnd(), + diag::err_typecheck_call_too_many_args_at_most) + << 0 /*function call*/ << 3 << NumArgs + << TheCall->getSourceRange(); + + // The alignment must be a constant integer. + Expr *Arg = TheCall->getArg(1); + + // We can't check the value of a dependent argument. + if (!Arg->isTypeDependent() && !Arg->isValueDependent()) { + llvm::APSInt Result; + if (SemaBuiltinConstantArg(TheCall, 1, Result)) + return true; + + if (!Result.isPowerOf2()) + return Diag(TheCall->getLocStart(), + diag::err_alignment_not_power_of_two) + << Arg->getSourceRange(); + } + + if (NumArgs > 2) { + ExprResult Arg(TheCall->getArg(2)); + InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, + Context.getSizeType(), false); + Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (Arg.isInvalid()) return true; + TheCall->setArg(2, Arg.get()); + } return false; } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 91b299fa66..e3bb2c993e 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -2786,7 +2786,7 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, // An alignment specification of zero has no effect. if (!(TmpAttr.isAlignas() && !Alignment) && !llvm::isPowerOf2_64(Alignment.getZExtValue())) { - Diag(AttrLoc, diag::err_attribute_aligned_not_power_of_two) + Diag(AttrLoc, diag::err_alignment_not_power_of_two) << E->getSourceRange(); return; } diff --git a/test/CodeGen/builtin-assume-aligned.c b/test/CodeGen/builtin-assume-aligned.c new file mode 100644 index 0000000000..bcae8c7f14 --- /dev/null +++ b/test/CodeGen/builtin-assume-aligned.c @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s + +// CHECK-LABEL: @test1 +int test1(int *a) { +// CHECK: %ptrint = ptrtoint +// CHECK: %maskedptr = and i64 %ptrint, 31 +// CHECK: %maskcond = icmp eq i64 %maskedptr, 0 +// CHECK: call void @llvm.assume(i1 %maskcond) + a = __builtin_assume_aligned(a, 32, 0ull); + return a[0]; +} + +// CHECK-LABEL: @test2 +int test2(int *a) { +// CHECK: %ptrint = ptrtoint +// CHECK: %maskedptr = and i64 %ptrint, 31 +// CHECK: %maskcond = icmp eq i64 %maskedptr, 0 +// CHECK: call void @llvm.assume(i1 %maskcond) + a = __builtin_assume_aligned(a, 32, 0); + return a[0]; +} + +// CHECK-LABEL: @test3 +int test3(int *a) { +// CHECK: %ptrint = ptrtoint +// CHECK: %maskedptr = and i64 %ptrint, 31 +// CHECK: %maskcond = icmp eq i64 %maskedptr, 0 +// CHECK: call void @llvm.assume(i1 %maskcond) + a = __builtin_assume_aligned(a, 32); + return a[0]; +} + +// CHECK-LABEL: @test4 +int test4(int *a, int b) { +// CHECK-DAG: %ptrint = ptrtoint +// CHECK-DAG: %conv = sext i32 +// CHECK: %offsetptr = sub i64 %ptrint, %conv +// CHECK: %maskedptr = and i64 %offsetptr, 31 +// CHECK: %maskcond = icmp eq i64 %maskedptr, 0 +// CHECK: call void @llvm.assume(i1 %maskcond) + a = __builtin_assume_aligned(a, 32, b); + return a[0]; +} + diff --git a/test/CodeGen/builtin-assume.c b/test/CodeGen/builtin-assume.c index a381a4c1df..041e9f48d0 100644 --- a/test/CodeGen/builtin-assume.c +++ b/test/CodeGen/builtin-assume.c @@ -1,8 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple i386-mingw32 -fms-extensions -emit-llvm -o - %s | FileCheck %s // CHECK-LABEL: @test1 -int test1(int *a) { - __assume(a != 0); +int test1(int *a, int i) { +// CHECK: %0 = load i32** %a.addr +// CHECK: %cmp = icmp ne i32* %0, null +// CHECK: call void @llvm.assume(i1 %cmp) +#ifdef _MSC_VER + __assume(a != 0) +#else + __builtin_assume(a != 0); +#endif + +// Nothing is generated for an assume with side effects... +// CHECK-NOT: load i32** %i.addr +// CHECK-NOT: call void @llvm.assume +#ifdef _MSC_VER + __assume(++i != 0) +#else + __builtin_assume(++i != 0); +#endif + return a[0]; } diff --git a/test/Sema/builtin-assume-aligned.c b/test/Sema/builtin-assume-aligned.c new file mode 100644 index 0000000000..884a7fb689 --- /dev/null +++ b/test/Sema/builtin-assume-aligned.c @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int test1(int *a) { + a = __builtin_assume_aligned(a, 32, 0ull); + return a[0]; +} + +int test2(int *a) { + a = __builtin_assume_aligned(a, 32, 0); + return a[0]; +} + +int test3(int *a) { + a = __builtin_assume_aligned(a, 32); + return a[0]; +} + +int test4(int *a) { + a = __builtin_assume_aligned(a, -32); // expected-error {{requested alignment is not a power of 2}} + a = __builtin_assume_aligned(a, 1ULL << 63); + return a[0]; +} + +int test5(int *a, unsigned *b) { + a = __builtin_assume_aligned(a, 32, b); // expected-warning {{incompatible pointer to integer conversion passing 'unsigned int *' to parameter of type}} + return a[0]; +} + +int test6(int *a) { + a = __builtin_assume_aligned(a, 32, 0, 0); // expected-error {{too many arguments to function call, expected at most 3, have 4}} + return a[0]; +} + +int test7(int *a) { + a = __builtin_assume_aligned(a, 31); // expected-error {{requested alignment is not a power of 2}} + return a[0]; +} + +int test8(int *a, int j) { + a = __builtin_assume_aligned(a, j); // expected-error {{must be a constant integer}} + return a[0]; +} + diff --git a/test/Sema/builtin-assume.c b/test/Sema/builtin-assume.c index 1f6a3a0cd9..512eeeccdc 100644 --- a/test/Sema/builtin-assume.c +++ b/test/Sema/builtin-assume.c @@ -1,11 +1,18 @@ // RUN: %clang_cc1 -triple i386-mingw32 -fms-extensions -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s int foo(int *a, int i) { +#ifdef _MSC_VER __assume(i != 4); - __assume(++i > 2); //expected-warning {{the argument to __assume has side effects that will be discarded}} + __assume(++i > 2); //expected-warning {{the argument to '__assume' has side effects that will be discarded}} int test = sizeof(struct{char qq[(__assume(i != 5), 7)];}); +#else + __builtin_assume(i != 4); + __builtin_assume(++i > 2); //expected-warning {{the argument to '__builtin_assume' has side effects that will be discarded}} + int test = sizeof(struct{char qq[(__builtin_assume(i != 5), 7)];}); +#endif return a[i]; } diff --git a/test/SemaCXX/builtin-assume-aligned-tmpl.cpp b/test/SemaCXX/builtin-assume-aligned-tmpl.cpp new file mode 100644 index 0000000000..a1d78ae5dd --- /dev/null +++ b/test/SemaCXX/builtin-assume-aligned-tmpl.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +int test9(int *a) { + a = (int *) __builtin_assume_aligned(a, z + 1); // expected-error {{requested alignment is not a power of 2}} + return a[0]; +} + +void test9i(int *a) { + test9<42>(a); // expected-note {{in instantiation of function template specialization 'test9<42>' requested here}} +} + +template +int test10(int *a, T z) { + a = (int *) __builtin_assume_aligned(a, z + 1); // expected-error {{must be a constant integer}} + return a[0]; +} + +int test10i(int *a) { + return test10(a, 42); // expected-note {{in instantiation of function template specialization 'test10' requested here}} +} + -- 2.40.0