diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1740,6 +1740,8 @@ ` instead of builtins, in order to reduce the number of builtins that we need to implement. +.. _langext-__builtin_assume: + ``__builtin_assume`` ------------------------------ diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3464,6 +3464,14 @@ }]; } +def Assumption : InheritableAttr { + let Spellings = [Clang<"assume">]; + let Subjects = SubjectList<[Function]>; + let InheritEvenIfAlreadyPresent = 1; + let Documentation = [AssumptionDocs]; + let Args = [StringArgument<"Assumption">]; +} + def InternalLinkage : InheritableAttr { let Spellings = [Clang<"internal_linkage">]; let Subjects = SubjectList<[Var, Function, CXXRecord]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3918,6 +3918,27 @@ }]; } +def AssumptionDocs : Documentation { + let Category = DocCatFunction; + let Heading = "assume"; + let Content = [{ +Clang supports the ``__attribute__((assume("assumption")))`` attribute to +provide additional information to the optimizer. The string-literal, here +"assumption", will be attached to the function declaration such that later +analysis and optimization passes can assume the "assumption" to hold. +This is similar to :ref:`__builtin_assume ` but instead of an +expression that can be assumed to be non-zero, the assumption is expressed as +a string and it holds for the entire function. + +A function can have multiple assume attributes and they propagate from prior +declarations to later definitions. Multiple assumptions are aggregated into a +single comma separated string. Thus, one can provide multiple assumptions via +a comma separated string, i.a., +``__attribute__((assume("assumption1,assumption2")))``. + +}]; +} + def NoStackProtectorDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2016,6 +2016,18 @@ llvm::toStringRef(CodeGenOpts.UniformWGSize)); } } + + std::string AssumptionValueStr; + for (AssumptionAttr *AssumptionA : + TargetDecl->specific_attrs()) { + std::string AS = AssumptionA->getAssumption().str(); + if (!AS.empty() && !AssumptionValueStr.empty()) + AssumptionValueStr += ","; + AssumptionValueStr += AS; + } + + if (!AssumptionValueStr.empty()) + FuncAttrs.addAttribute("llvm.assume", AssumptionValueStr); } // Attach "no-builtins" attributes to: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7727,6 +7727,9 @@ case ParsedAttr::AT_Unavailable: handleAttrWithMessage(S, D, AL); break; + case ParsedAttr::AT_Assumption: + handleAttrWithMessage(S, D, AL); + break; case ParsedAttr::AT_ObjCDirect: handleObjCDirectAttr(S, D, AL); break; diff --git a/clang/test/CodeGen/assume_attr.c b/clang/test/CodeGen/assume_attr.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/assume_attr.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -emit-llvm -triple i386-linux-gnu %s -o - | FileCheck %s +// RUN: %clang_cc1 -x c -emit-pch -o %t %s +// RUN: %clang_cc1 -include-pch %t %s -emit-llvm -o - | FileCheck %s +// expected-no-diagnostics + +// TODO: for "foo" and "bar", "after" is not added as it appears "after" the first use or definition respectively. There might be a way to allow that. + +// CHECK: define{{.*}} void @bar() #0 +// CHECK: define{{.*}} void @baz() #1 +// CHECK: declare{{.*}} void @foo() #2 +// CHECK: attributes #0 +// CHECK-SAME: "llvm.assume"="bar:before1,bar:before2,bar:before3,bar:def1,bar:def2" +// CHECK: attributes #1 +// CHECK-SAME: "llvm.assume"="baz:before1,baz:before2,baz:before3,baz:def1,baz:def2,baz:after" +// CHECK: attributes #2 +// CHECK-SAME: "llvm.assume"="foo:before1,foo:before2,foo:before3" + +#ifndef HEADER +#define HEADER + +/// foo: declarations only + +__attribute__((assume("foo:before1"))) void foo(void); + +__attribute__((assume("foo:before2"))) +__attribute__((assume("foo:before3"))) void +foo(void); + +/// baz: static function declarations and a definition + +__attribute__((assume("baz:before1"))) static void baz(void); + +__attribute__((assume("baz:before2"))) +__attribute__((assume("baz:before3"))) static void +baz(void); + +// Definition +__attribute__((assume("baz:def1,baz:def2"))) static void baz(void) { foo(); } + +__attribute__((assume("baz:after"))) static void baz(void); + +/// bar: external function declarations and a definition + +__attribute__((assume("bar:before1"))) void bar(void); + +__attribute__((assume("bar:before2"))) +__attribute__((assume("bar:before3"))) void +bar(void); + +// Definition +__attribute__((assume("bar:def1,bar:def2"))) void bar(void) { baz(); } + +__attribute__((assume("bar:after"))) void bar(void); + +/// back to foo + +__attribute__((assume("foo:after"))) void foo(void); + +#endif diff --git a/clang/test/CodeGenCXX/assume_attr.cpp b/clang/test/CodeGenCXX/assume_attr.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/assume_attr.cpp @@ -0,0 +1,120 @@ +// RUN: %clang_cc1 -emit-llvm -triple i386-linux-gnu %s -o - | FileCheck %s +// RUN: %clang_cc1 -x c++ -emit-pch -o %t %s +// RUN: %clang_cc1 -include-pch %t %s -emit-llvm -o - | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +/// foo: declarations only + +__attribute__((assume("foo:before1"))) void foo(); + +__attribute__((assume("foo:before2"))) +__attribute__((assume("foo:before3"))) void +foo(); + +/// baz: static function declarations and a definition + +__attribute__((assume("baz:before1"))) static void baz(); + +__attribute__((assume("baz:before2"))) +__attribute__((assume("baz:before3"))) static void +baz(); + +// Definition +__attribute__((assume("baz:def1,baz:def2"))) static void baz() { foo(); } + +__attribute__((assume("baz:after"))) static void baz(); + +/// bar: external function declarations and a definition + +__attribute__((assume("bar:before1"))) void bar(); + +__attribute__((assume("bar:before2"))) +__attribute__((assume("bar:before3"))) void +bar(); + +// Definition +__attribute__((assume("bar:def1,bar:def2"))) void bar() { baz(); } + +__attribute__((assume("bar:after"))) void bar(); + +/// back to foo + +__attribute__((assume("foo:after"))) void foo(); + +/// class tests +class C { + __attribute__((assume("C:private_method"))) void private_method(); + __attribute__((assume("C:private_static"))) static void private_static(); + +public: + __attribute__((assume("C:public_method1"))) void public_method(); + __attribute__((assume("C:public_static1"))) static void public_static(); +}; + +__attribute__((assume("C:public_method2"))) void C::public_method() { + private_method(); +} + +__attribute__((assume("C:public_static2"))) void C::public_static() { + private_static(); +} + +/// template tests +template +__attribute__((assume("template_func"))) void template_func() {} + +template <> +__attribute__((assume("template_func"))) void template_func() {} + +template <> +void template_func() {} + +template +struct S { + __attribute__((assume("S::method"))) void method(); +}; + +template <> +__attribute__((assume("S::method"))) void S::method() {} + +template <> +void S::method() {} + +// CHECK: define void @_Z3barv() #0 +// CHECK: define internal void @_ZL3bazv() #1 +// CHECK: define void @_ZN1C13public_methodEv(%class.C* %this) #2 +// CHECK: declare void @_ZN1C14private_methodEv(%class.C*) #3 +// CHECK: define void @_ZN1C13public_staticEv() #4 +// CHECK: declare void @_ZN1C14private_staticEv() #5 +// CHECK: define void @_Z13template_funcIfEvv() #6 +// CHECK: define void @_Z13template_funcIiEvv() #7 +// CHECK: define void @_ZN1SIfE6methodEv(%struct.S* %this) #8 +// CHECK: define void @_ZN1SIiE6methodEv(%struct.S.0* %this) #9 +// CHECK: declare void @_Z3foov() #10 +// CHECK: attributes #0 +// CHECK-SAME: "llvm.assume"="bar:before1,bar:before2,bar:before3,bar:def1,bar:def2" +// CHECK: attributes #1 +// CHECK-SAME: "llvm.assume"="baz:before1,baz:before2,baz:before3,baz:def1,baz:def2,baz:after" +// CHECK: attributes #2 +// CHECK-SAME: "llvm.assume"="C:public_method1,C:public_method2" +// CHECK: attributes #3 +// CHECK-SAME: "llvm.assume"="C:private_method" +// CHECK: attributes #4 +// CHECK-SAME: "llvm.assume"="C:public_static1,C:public_static2" +// CHECK: attributes #5 +// CHECK-SAME: "llvm.assume"="C:private_static" +// CHECK: attributes #6 +// CHECK-SAME: "llvm.assume"="template_func,template_func" +// CHECK: attributes #7 +// CHECK-SAME: "llvm.assume"="template_func" +// CHECK: attributes #8 +// CHECK-SAME: "llvm.assume"="S::method,S::method" +// CHECK: attributes #9 +// CHECK-SAME: "llvm.assume"="S::method" +// CHECK: attributes #10 +// CHECK-SAME: "llvm.assume"="foo:before1,foo:before2,foo:before3" + +#endif