[llvm] eec6d87 - [Attributor] Deduce attributes for non-exact functions

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 4 09:36:23 PDT 2020


Author: Luofan Chen
Date: 2020-04-04T11:34:58-05:00
New Revision: eec6d87626e7528d5ffb3609f30fe7f1b00e30a0

URL: https://github.com/llvm/llvm-project/commit/eec6d87626e7528d5ffb3609f30fe7f1b00e30a0
DIFF: https://github.com/llvm/llvm-project/commit/eec6d87626e7528d5ffb3609f30fe7f1b00e30a0.diff

LOG: [Attributor] Deduce attributes for non-exact functions

This patch is based on D63312 and D63319. For now we create shallow wrappers for all functions that are IPO amendable.
See also [this github issue](https://github.com/llvm/llvm-project/issues/172).

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D76404

Added: 
    llvm/test/Transforms/Attributor/wrapper.ll

Modified: 
    llvm/lib/Transforms/IPO/Attributor.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 6a148f991b4e..fe8ce92e66c5 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -57,6 +57,7 @@ STATISTIC(NumFnWithExactDefinition,
           "Number of functions with exact definitions");
 STATISTIC(NumFnWithoutExactDefinition,
           "Number of functions without exact definitions");
+STATISTIC(NumFnShallowWrapperCreated, "Number of shallow wrappers created");
 STATISTIC(NumAttributesTimedOut,
           "Number of abstract attributes timed out before fixpoint");
 STATISTIC(NumAttributesValidFixpoint,
@@ -184,6 +185,12 @@ static cl::opt<bool> EnableHeapToStack("enable-heap-to-stack-conversion",
 static cl::opt<int> MaxHeapToStackSize("max-heap-to-stack-size", cl::init(128),
                                        cl::Hidden);
 
+static cl::opt<bool>
+    AllowShallowWrappers("attributor-allow-shallow-wrappers", cl::Hidden,
+                         cl::desc("Allow the Attributor to create shallow "
+                                  "wrappers for non-exact definitions."),
+                         cl::init(false));
+
 /// Logic operators for the change status enum class.
 ///
 ///{
@@ -8139,6 +8146,69 @@ ChangeStatus Attributor::run() {
   return ManifestChange;
 }
 
+/// Create a shallow wrapper for \p F such that \p F has internal linkage
+/// afterwards. It also sets the original \p F 's name to anonymous
+///
+/// A wrapper is a function with the same type (and attributes) as \p F
+/// that will only call \p F and return the result, if any.
+///
+/// Assuming the declaration of looks like:
+///   rty F(aty0 arg0, ..., atyN argN);
+///
+/// The wrapper will then look as follows:
+///   rty wrapper(aty0 arg0, ..., atyN argN) {
+///     return F(arg0, ..., argN);
+///   }
+///
+static void createShallowWrapper(Function &F) {
+  assert(AllowShallowWrappers &&
+         "Cannot create a wrapper if it is not allowed!");
+  assert(!F.isDeclaration() && "Cannot create a wrapper around a declaration!");
+
+  Module &M = *F.getParent();
+  LLVMContext &Ctx = M.getContext();
+  FunctionType *FnTy = F.getFunctionType();
+
+  Function *Wrapper =
+      Function::Create(FnTy, F.getLinkage(), F.getAddressSpace(), F.getName());
+  F.setName(""); // set the inside function anonymous
+  M.getFunctionList().insert(F.getIterator(), Wrapper);
+
+  F.setLinkage(GlobalValue::InternalLinkage);
+
+  F.replaceAllUsesWith(Wrapper);
+  assert(F.getNumUses() == 0 && "Uses remained after wrapper was created!");
+
+  // Move the COMDAT section to the wrapper.
+  // TODO: Check if we need to keep it for F as well.
+  Wrapper->setComdat(F.getComdat());
+  F.setComdat(nullptr);
+
+  // Copy all metadata and attributes but keep them on F as well.
+  SmallVector<std::pair<unsigned, MDNode *>, 1> MDs;
+  F.getAllMetadata(MDs);
+  for (auto MDIt : MDs)
+    Wrapper->addMetadata(MDIt.first, *MDIt.second);
+  Wrapper->setAttributes(F.getAttributes());
+
+  // Create the call in the wrapper.
+  BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", Wrapper);
+
+  SmallVector<Value *, 8> Args;
+  auto FArgIt = F.arg_begin();
+  for (Argument &Arg : Wrapper->args()) {
+    Args.push_back(&Arg);
+    Arg.setName((FArgIt++)->getName());
+  }
+
+  CallInst *CI = CallInst::Create(&F, Args, "", EntryBB);
+  CI->setTailCall(true);
+  CI->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline);
+  ReturnInst::Create(Ctx, CI->getType()->isVoidTy() ? nullptr : CI, EntryBB);
+
+  NumFnShallowWrapperCreated++;
+}
+
 bool Attributor::isValidFunctionSignatureRewrite(
     Argument &Arg, ArrayRef<Type *> ReplacementTypes) {
 
@@ -8789,6 +8859,12 @@ static bool runAttributorOnFunctions(InformationCache &InfoCache,
   for (Function *F : Functions)
     A.initializeInformationCache(*F);
 
+  // Create shallow wrappers for all functions that are not IPO amendable
+  if (AllowShallowWrappers)
+    for (Function *F : Functions)
+      if (!A.isFunctionIPOAmendable(*F))
+        createShallowWrapper(*F);
+
   for (Function *F : Functions) {
     if (F->hasExactDefinition())
       NumFnWithExactDefinition++;

diff  --git a/llvm/test/Transforms/Attributor/wrapper.ll b/llvm/test/Transforms/Attributor/wrapper.ll
new file mode 100644
index 000000000000..f965dbee2c73
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/wrapper.ll
@@ -0,0 +1,88 @@
+; RUN: opt -passes=attributor-cgscc -attributor-disable=false -attributor-annotate-decl-cs -attributor-allow-shallow-wrappers -S < %s | FileCheck %s --check-prefix=CHECK
+
+; TEST 1: simple test, without argument
+; A wrapper will be generated for this function, Check the wrapper first
+; CHECK-NOT: Function Attrs:
+; CHECK: define linkonce i32 @inner1()
+; CHECK: tail call i32 @0()
+; CHECK: ret
+;
+; Check the original function, which is wrapped and becomes anonymous
+; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; CHECK: define internal i32 @0()
+; CHECK: %a = alloca i32
+; CHECK: store i32 1, i32* %a
+; CHECK: %b = load i32, i32* %a
+; CHECK: ret i32 %b
+define linkonce i32 @inner1() {
+entry:
+  %a = alloca i32
+  store i32 1, i32* %a
+  %b = load i32, i32* %a
+  ret i32 %b
+}
+
+; Check for call
+; CHECK: define i32 @outer1
+; CHECK: call i32 @inner1
+; CHECK: ret
+define i32 @outer1() {
+entry:
+  %ret = call i32 @inner1()
+  ret i32 %ret
+}
+
+; TEST 2: with argument
+; CHECK-NOT: Function Attrs
+; CHECK: define linkonce i32 @inner2(i32 %a, i32 %b)
+; CHECK: tail call i32 @1(i32 %a, i32 %b)
+; CHECK: ret
+;
+; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; CHECK: define internal i32 @1(i32 %a, i32 %b)
+; CHECK: %c = add i32 %a, %b
+; CHECK: ret i32 %c
+define linkonce i32 @inner2(i32 %a, i32 %b) {
+entry:
+  %c = add i32 %a, %b
+  ret i32 %c
+}
+
+; CHECK: define i32 @outer2
+; CHECK: call i32 @inner2
+; CHECK: ret
+define i32 @outer2() {
+entry:
+  %ret = call i32 @inner2(i32 1, i32 2)
+  ret i32 %ret
+}
+
+; TEST 3: check nocurse
+; This function calls itself, there will be no attribute
+; CHECK-NOT: Function Attrs
+; CHECK: define linkonce i32 @inner3(i32 %0)
+; CHECK: tail call i32 @2(i32 %0)
+; CHECK: ret
+;
+; CHECK-NOT: Function Attrs:
+; CHECK: define internal i32 @2(i32 %0)
+define linkonce i32 @inner3(i32) {
+entry:
+  %1 = alloca i32
+  store i32 %0, i32* %1
+  br label %2
+2:
+  %3 = load i32, i32* %1
+  %4 = icmp slt i32 %3, 4
+  br i1 %4, label %5, label %9
+5:
+  %6 = load i32, i32* %1
+  %7 = add nsw i32 %6, 1
+  %8 = call i32 @inner3(i32 %7)
+  store i32 %8, i32* %1
+  br label %2
+9:
+  %10 = load i32, i32* %1
+  ret i32 %10
+}
+


        


More information about the llvm-commits mailing list