jdoerfert created this revision.
jdoerfert added reviewers: JonChesterfield, aaron.ballman, jhuber6.
Herald added a subscriber: bollu.
Herald added a project: clang.
jdoerfert requested review of this revision.
Herald added a subscriber: sstefan1.

The `assume` attribute is a way to provide additional, arbitrary
information to the optimizer. For now, assumptions are restricted to
strings which will be accumulated for a function and emitted as comma
separated string function attribute. The key of the LLVM-IR function
attribute is `llvm.assume`. Similar to `llvm.assume` and
`__builtin_assume`, the `assume` attribute provides a user defined
assumption to the compiler.

A follow up patch will introduce an LLVM-core API to query the
assumptions attached to a function. We also expect to add more options,
e.g., expression arguments, to the `assume` attribute later on.

The `omp [begin] asssumes` pragma will leverage this attribute and
expose the functionality in the absence of OpenMP.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D91979

Files:
  clang/docs/LanguageExtensions.rst
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/assume_attr.c
  clang/test/CodeGenCXX/assume_attr.cpp

Index: clang/test/CodeGenCXX/assume_attr.cpp
===================================================================
--- /dev/null
+++ 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 <typename T>
+__attribute__((assume("template_func<T>"))) void template_func() {}
+
+template <>
+__attribute__((assume("template_func<float>"))) void template_func<float>() {}
+
+template <>
+void template_func<int>() {}
+
+template <typename T>
+struct S {
+  __attribute__((assume("S<T>::method"))) void method();
+};
+
+template <>
+__attribute__((assume("S<float>::method"))) void S<float>::method() {}
+
+template <>
+void S<int>::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<T>,template_func<float>"
+// CHECK:         attributes #7
+// CHECK-SAME:      "llvm.assume"="template_func<T>"
+// CHECK:         attributes #8
+// CHECK-SAME:      "llvm.assume"="S<T>::method,S<float>::method"
+// CHECK:         attributes #9
+// CHECK-SAME:      "llvm.assume"="S<T>::method"
+// CHECK:         attributes #10
+// CHECK-SAME:      "llvm.assume"="foo:before1,foo:before2,foo:before3"
+
+#endif
Index: clang/test/CodeGen/assume_attr.c
===================================================================
--- /dev/null
+++ 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
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -7727,6 +7727,9 @@
   case ParsedAttr::AT_Unavailable:
     handleAttrWithMessage<UnavailableAttr>(S, D, AL);
     break;
+  case ParsedAttr::AT_Assumption:
+    handleAttrWithMessage<AssumptionAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_ObjCDirect:
     handleObjCDirectAttr(S, D, AL);
     break;
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -2016,6 +2016,18 @@
                                llvm::toStringRef(CodeGenOpts.UniformWGSize));
       }
     }
+
+    std::string AssumptionValueStr;
+    for (AssumptionAttr *AssumptionA :
+         TargetDecl->specific_attrs<AssumptionAttr>()) {
+      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:
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ 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 <langext-__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 = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ 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]>;
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -1740,6 +1740,8 @@
 <langext-vectors>` instead of builtins, in order to reduce the number of
 builtins that we need to implement.
 
+.. _langext-__builtin_assume:
+
 ``__builtin_assume``
 ------------------------------
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to