Provide a way to specify inliner's attribute compatibility and merging
authorAkira Hatanaka <ahatanaka@apple.com>
Thu, 12 Nov 2015 20:59:43 +0000 (20:59 +0000)
committerAkira Hatanaka <ahatanaka@apple.com>
Thu, 12 Nov 2015 20:59:43 +0000 (20:59 +0000)
rules using table-gen. NFC.

This commit adds new classes CompatRule and MergeRule to Attributes.td,
which are used to generate code to check attribute compatibility and
merge attributes of the caller and callee.

rdar://problem/19836465

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252949 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/IR/Attributes.h
include/llvm/IR/Attributes.td
lib/Analysis/InlineCost.cpp
lib/IR/Attributes.cpp
lib/IR/AttributesCompatFunc.td [new file with mode: 0644]
lib/IR/CMakeLists.txt
lib/IR/Makefile
lib/Transforms/IPO/Inliner.cpp
utils/TableGen/Attributes.cpp

index 52a9ca8..1bb4393 100644 (file)
@@ -33,6 +33,7 @@ class AttributeSetImpl;
 class AttributeSetNode;
 class Constant;
 template<typename T> struct DenseMapInfo;
+class Function;
 class LLVMContext;
 class Type;
 
@@ -527,6 +528,13 @@ namespace AttributeFuncs {
 /// \brief Which attributes cannot be applied to a type.
 AttrBuilder typeIncompatible(Type *Ty);
 
+/// \returns Return true if the two functions have compatible target-independent
+/// attributes for inlining purposes.
+bool areInlineCompatible(const Function &Caller, const Function &Callee);
+
+/// \brief Merge caller's and callee's attributes.
+void mergeAttributesForInlining(Function &Caller, const Function &Callee);
+
 } // end AttributeFuncs namespace
 
 } // end llvm namespace
index 5ba76ef..f9a78c8 100644 (file)
@@ -158,3 +158,28 @@ def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">;
 def NoInfsFPMath : StrBoolAttr<"no-infs-fp-math">;
 def NoNansFPMath : StrBoolAttr<"no-nans-fp-math">;
 def UnsafeFPMath : StrBoolAttr<"unsafe-fp-math">;
+
+class CompatRule<string F> {
+  // The name of the function called to check the attribute of the caller and
+  // callee and decide whether inlining should be allowed. The function's
+  // signature must match "bool(const Function&, const Function &)", where the
+  // first parameter is the reference to the caller and the second parameter is
+  // the reference to the callee. It must return false if the attributes of the
+  // caller and callee are incompatible, and true otherwise.
+  string CompatFunc = F;
+}
+
+def : CompatRule<"isEqual<SanitizeAddressAttr>">;
+def : CompatRule<"isEqual<SanitizeThreadAttr>">;
+def : CompatRule<"isEqual<SanitizeMemoryAttr>">;
+
+class MergeRule<string F> {
+  // The name of the function called to merge the attributes of the caller and
+  // callee. The function's signature must match
+  // "void(Function&, const Function &)", where the first parameter is the
+  // reference to the caller and the second parameter is the reference to the
+  // callee.
+  string MergeFunc = F;
+}
+
+def : MergeRule<"adjustCallerSSPLevel">;
index 26f2e7f..b908434 100644 (file)
@@ -1349,22 +1349,13 @@ InlineCost InlineCostAnalysis::getInlineCost(CallSite CS, int Threshold) {
   return getInlineCost(CS, CS.getCalledFunction(), Threshold);
 }
 
-/// \brief Test that two functions either have or have not the given attribute
-///        at the same time.
-template<typename AttrKind>
-static bool attributeMatches(Function *F1, Function *F2, AttrKind Attr) {
-  return F1->getFnAttribute(Attr) == F2->getFnAttribute(Attr);
-}
-
 /// \brief Test that there are no attribute conflicts between Caller and Callee
 ///        that prevent inlining.
 static bool functionsHaveCompatibleAttributes(Function *Caller,
                                               Function *Callee,
                                               TargetTransformInfo &TTI) {
   return TTI.areInlineCompatible(Caller, Callee) &&
-         attributeMatches(Caller, Callee, Attribute::SanitizeAddress) &&
-         attributeMatches(Caller, Callee, Attribute::SanitizeMemory) &&
-         attributeMatches(Caller, Callee, Attribute::SanitizeThread);
+         AttributeFuncs::areInlineCompatible(*Caller, *Callee);
 }
 
 InlineCost InlineCostAnalysis::getInlineCost(CallSite CS, Function *Callee,
index bdefe59..e2013c4 100644 (file)
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/Attributes.h"
+#include "llvm/IR/Function.h"
 #include "AttributeImpl.h"
 #include "LLVMContextImpl.h"
 #include "llvm/ADT/STLExtras.h"
@@ -1407,3 +1408,80 @@ AttrBuilder AttributeFuncs::typeIncompatible(Type *Ty) {
 
   return Incompatible;
 }
+
+template<typename AttrClass>
+static bool isEqual(const Function &Caller, const Function &Callee) {
+  return Caller.getFnAttribute(AttrClass::Kind) ==
+         Callee.getFnAttribute(AttrClass::Kind);
+}
+
+/// \brief Compute the logical AND of the attributes of the caller and the
+/// callee.
+///
+/// This function sets the caller's attribute to false if the callee's attribute
+/// is false.
+template<typename AttrClass>
+static void setAND(Function &Caller, const Function &Callee) {
+  if (AttrClass::isSet(Caller, AttrClass::Kind) &&
+      !AttrClass::isSet(Callee, AttrClass::Kind))
+    AttrClass::set(Caller, AttrClass::Kind, false);
+}
+
+/// \brief Compute the logical OR of the attributes of the caller and the
+/// callee.
+///
+/// This function sets the caller's attribute to true if the callee's attribute
+/// is true.
+template<typename AttrClass>
+static void setOR(Function &Caller, const Function &Callee) {
+  if (!AttrClass::isSet(Caller, AttrClass::Kind) &&
+      AttrClass::isSet(Callee, AttrClass::Kind))
+    AttrClass::set(Caller, AttrClass::Kind, true);
+}
+
+/// \brief If the inlined function had a higher stack protection level than the
+/// calling function, then bump up the caller's stack protection level.
+static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) {
+  // If upgrading the SSP attribute, clear out the old SSP Attributes first.
+  // Having multiple SSP attributes doesn't actually hurt, but it adds useless
+  // clutter to the IR.
+  AttrBuilder B;
+  B.addAttribute(Attribute::StackProtect)
+    .addAttribute(Attribute::StackProtectStrong)
+    .addAttribute(Attribute::StackProtectReq);
+  AttributeSet OldSSPAttr = AttributeSet::get(Caller.getContext(),
+                                              AttributeSet::FunctionIndex,
+                                              B);
+
+  if (Callee.hasFnAttribute(Attribute::SafeStack)) {
+    Caller.removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
+    Caller.addFnAttr(Attribute::SafeStack);
+  } else if (Callee.hasFnAttribute(Attribute::StackProtectReq) &&
+             !Caller.hasFnAttribute(Attribute::SafeStack)) {
+    Caller.removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
+    Caller.addFnAttr(Attribute::StackProtectReq);
+  } else if (Callee.hasFnAttribute(Attribute::StackProtectStrong) &&
+             !Caller.hasFnAttribute(Attribute::SafeStack) &&
+             !Caller.hasFnAttribute(Attribute::StackProtectReq)) {
+    Caller.removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
+    Caller.addFnAttr(Attribute::StackProtectStrong);
+  } else if (Callee.hasFnAttribute(Attribute::StackProtect) &&
+             !Caller.hasFnAttribute(Attribute::SafeStack) &&
+             !Caller.hasFnAttribute(Attribute::StackProtectReq) &&
+             !Caller.hasFnAttribute(Attribute::StackProtectStrong))
+    Caller.addFnAttr(Attribute::StackProtect);
+}
+
+#define GET_ATTR_COMPAT_FUNC
+#include "AttributesCompatFunc.inc"
+
+bool AttributeFuncs::areInlineCompatible(const Function &Caller,
+                                         const Function &Callee) {
+  return hasCompatibleFnAttrs(Caller, Callee);
+}
+
+
+void AttributeFuncs::mergeAttributesForInlining(Function &Caller,
+                                                const Function &Callee) {
+  mergeFnAttrs(Caller, Callee);
+}
diff --git a/lib/IR/AttributesCompatFunc.td b/lib/IR/AttributesCompatFunc.td
new file mode 100644 (file)
index 0000000..7c85b3d
--- /dev/null
@@ -0,0 +1 @@
+include "llvm/IR/Attributes.td"
index 472178f..eb67c52 100644 (file)
@@ -1,3 +1,7 @@
+set(LLVM_TARGET_DEFINITIONS AttributesCompatFunc.td)
+tablegen(LLVM AttributesCompatFunc.inc -gen-attrs)
+add_public_tablegen_target(AttributeCompatFuncTableGen)
+
 add_llvm_library(LLVMCore
   AsmWriter.cpp
   Attributes.cpp
index d5fc403..c69d41d 100644 (file)
@@ -11,16 +11,19 @@ LIBRARYNAME = LLVMCore
 BUILD_ARCHIVE = 1
 
 BUILT_SOURCES = $(PROJ_OBJ_ROOT)/include/llvm/IR/Intrinsics.gen \
-                $(PROJ_OBJ_ROOT)/include/llvm/IR/Attributes.inc
+                $(PROJ_OBJ_ROOT)/include/llvm/IR/Attributes.inc \
+                $(PROJ_OBJ_ROOT)/lib/IR/AttributesCompatFunc.inc
 
 include $(LEVEL)/Makefile.common
 
 GENFILE:=$(PROJ_OBJ_ROOT)/include/llvm/IR/Intrinsics.gen
 ATTRINCFILE:=$(PROJ_OBJ_ROOT)/include/llvm/IR/Attributes.inc
+ATTRCOMPATFUNCINCFILE:=$(PROJ_OBJ_ROOT)/lib/IR/AttributesCompatFunc.inc
 
 INTRINSICTD  := $(PROJ_SRC_ROOT)/include/llvm/IR/Intrinsics.td
 INTRINSICTDS := $(wildcard $(PROJ_SRC_ROOT)/include/llvm/IR/Intrinsics*.td)
 ATTRIBUTESTD  := $(PROJ_SRC_ROOT)/include/llvm/IR/Attributes.td
+ATTRCOMPATFUNCTD  := $(PROJ_SRC_ROOT)/lib/IR/AttributesCompatFunc.td
 
 $(ObjDir)/Intrinsics.gen.tmp: $(ObjDir)/.dir $(INTRINSICTDS) $(LLVM_TBLGEN)
        $(Echo) Building Intrinsics.gen.tmp from Intrinsics.td
@@ -40,6 +43,15 @@ $(ATTRINCFILE): $(ObjDir)/Attributes.inc.tmp $(PROJ_OBJ_ROOT)/include/llvm/IR/.d
          $(EchoCmd) Updated Attributes.inc because Attributes.inc.tmp \
            changed significantly. )
 
+$(ObjDir)/AttributesCompatFunc.inc.tmp: $(ObjDir)/.dir $(ATTRCOMPATFUNCTD) $(LLVM_TBLGEN)
+       $(Echo) Building AttributesCompatFunc.inc.tmp from $(ATTRCOMPATFUNCTD)
+       $(Verb) $(LLVMTableGen) $(call SYSPATH, $(ATTRCOMPATFUNCTD)) -o $(call SYSPATH, $@) -gen-attrs
+
+$(ATTRCOMPATFUNCINCFILE): $(ObjDir)/AttributesCompatFunc.inc.tmp $(PROJ_OBJ_ROOT)/include/llvm/IR/.dir
+       $(Verb) $(CMP) -s $@ $< || ( $(CP) $< $@ && \
+         $(EchoCmd) Updated AttributesCompatFunc.inc because AttributesCompatFunc.inc.tmp \
+           changed significantly. )
+
 install-local:: $(GENFILE)
        $(Echo) Installing $(DESTDIR)$(PROJ_includedir)/llvm/IR/Intrinsics.gen
        $(Verb) $(DataInstall) $(GENFILE) $(DESTDIR)$(PROJ_includedir)/llvm/IR/Intrinsics.gen
@@ -47,3 +59,7 @@ install-local:: $(GENFILE)
 install-local:: $(ATTRINCFILE)
        $(Echo) Installing $(DESTDIR)$(PROJ_includedir)/llvm/IR/Attributes.inc
        $(Verb) $(DataInstall) $(ATTRINCFILE) $(DESTDIR)$(PROJ_includedir)/llvm/IR/Attributes.inc
+
+install-local:: $(ATTRCOMPATFUNCINCFILE)
+       $(Echo) Installing $(DESTDIR)$(PROJ_libdir)/IR/AttributesCompatFunc.inc
+       $(Verb) $(DataInstall) $(ATTRCOMPATFUNCINCFILE) $(DESTDIR)$(PROJ_libdir)/IR/AttributesCompatFunc.inc
index 14b1767..20b385a 100644 (file)
@@ -86,39 +86,6 @@ void Inliner::getAnalysisUsage(AnalysisUsage &AU) const {
 typedef DenseMap<ArrayType*, std::vector<AllocaInst*> >
 InlinedArrayAllocasTy;
 
-/// \brief If the inlined function had a higher stack protection level than the
-/// calling function, then bump up the caller's stack protection level.
-static void AdjustCallerSSPLevel(Function *Caller, Function *Callee) {
-  // If upgrading the SSP attribute, clear out the old SSP Attributes first.
-  // Having multiple SSP attributes doesn't actually hurt, but it adds useless
-  // clutter to the IR.
-  AttrBuilder B;
-  B.addAttribute(Attribute::StackProtect)
-    .addAttribute(Attribute::StackProtectStrong)
-    .addAttribute(Attribute::StackProtectReq);
-  AttributeSet OldSSPAttr = AttributeSet::get(Caller->getContext(),
-                                              AttributeSet::FunctionIndex,
-                                              B);
-
-  if (Callee->hasFnAttribute(Attribute::SafeStack)) {
-    Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
-    Caller->addFnAttr(Attribute::SafeStack);
-  } else if (Callee->hasFnAttribute(Attribute::StackProtectReq) &&
-             !Caller->hasFnAttribute(Attribute::SafeStack)) {
-    Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
-    Caller->addFnAttr(Attribute::StackProtectReq);
-  } else if (Callee->hasFnAttribute(Attribute::StackProtectStrong) &&
-             !Caller->hasFnAttribute(Attribute::SafeStack) &&
-             !Caller->hasFnAttribute(Attribute::StackProtectReq)) {
-    Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
-    Caller->addFnAttr(Attribute::StackProtectStrong);
-  } else if (Callee->hasFnAttribute(Attribute::StackProtect) &&
-             !Caller->hasFnAttribute(Attribute::SafeStack) &&
-             !Caller->hasFnAttribute(Attribute::StackProtectReq) &&
-             !Caller->hasFnAttribute(Attribute::StackProtectStrong))
-    Caller->addFnAttr(Attribute::StackProtect);
-}
-
 /// If it is possible to inline the specified call site,
 /// do so and update the CallGraph for this operation.
 ///
@@ -146,7 +113,7 @@ static bool InlineCallIfPossible(Pass &P, CallSite CS, InlineFunctionInfo &IFI,
   if (!InlineFunction(CS, IFI, &AAR, InsertLifetime))
     return false;
 
-  AdjustCallerSSPLevel(Caller, Callee);
+  AttributeFuncs::mergeAttributesForInlining(*Caller, *Callee);
 
   // Look at all of the allocas that we inlined through this call site.  If we
   // have already inlined other allocas through other calls into this function,
index 385a244..df986a5 100644 (file)
@@ -27,6 +27,12 @@ public:
 
 private:
   void emitTargetIndependentEnums(raw_ostream &OS);
+  void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr);
+
+  void printEnumAttrClasses(raw_ostream &OS,
+                            const std::vector<Record *> &Records);
+  void printStrBoolAttrClasses(raw_ostream &OS,
+                               const std::vector<Record *> &Records);
 
   RecordKeeper &Records;
 };
@@ -46,8 +52,97 @@ void Attributes::emitTargetIndependentEnums(raw_ostream &OS) {
   OS << "#endif\n";
 }
 
+void Attributes::emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr) {
+  OS << "#ifdef GET_ATTR_COMPAT_FUNC\n";
+  OS << "#undef GET_ATTR_COMPAT_FUNC\n";
+
+  OS << "struct EnumAttr {\n";
+  OS << "  static bool isSet(const Function &Fn,\n";
+  OS << "                    Attribute::AttrKind Kind) {\n";
+  OS << "    return Fn.hasFnAttribute(Kind);\n";
+  OS << "  }\n\n";
+  OS << "  static void set(Function &Fn,\n";
+  OS << "                  Attribute::AttrKind Kind, bool Val) {\n";
+  OS << "    if (Val)\n";
+  OS << "      Fn.addFnAttr(Kind);\n";
+  OS << "    else\n";
+  OS << "      Fn.removeFnAttr(Kind);\n";
+  OS << "  }\n";
+  OS << "};\n\n";
+
+  OS << "struct StrBoolAttr {\n";
+  OS << "  static bool isSet(const Function &Fn,\n";
+  OS << "                    StringRef Kind) {\n";
+  OS << "    auto A = Fn.getFnAttribute(Kind);\n";
+  OS << "    return A.getValueAsString().equals(\"true\");\n";
+  OS << "  }\n\n";
+  OS << "  static void set(Function &Fn,\n";
+  OS << "                  StringRef Kind, bool Val) {\n";
+  OS << "    Fn.addFnAttr(Kind, Val ? \"true\" : \"false\");\n";
+  OS << "  }\n";
+  OS << "};\n\n";
+
+  printEnumAttrClasses(OS ,Records.getAllDerivedDefinitions("EnumAttr"));
+  printStrBoolAttrClasses(OS , Records.getAllDerivedDefinitions("StrBoolAttr"));
+
+  OS << "static inline bool hasCompatibleFnAttrs(const Function &Caller,\n"
+     << "                                        const Function &Callee) {\n";
+  OS << "  bool Ret = true;\n\n";
+
+  const std::vector<Record *> &CompatRules =
+    Records.getAllDerivedDefinitions("CompatRule");
+
+  for (auto *Rule : CompatRules) {
+    StringRef FuncName = Rule->getValueAsString("CompatFunc");
+    OS << "  Ret &= " << FuncName << "(Caller, Callee);\n";
+  }
+
+  OS << "\n";
+  OS << "  return Ret;\n";
+  OS << "}\n\n";
+
+  const std::vector<Record *> &MergeRules =
+      Records.getAllDerivedDefinitions("MergeRule");
+  OS << "static inline void mergeFnAttrs(Function &Caller,\n"
+     << "                                const Function &Callee) {\n";
+
+  for (auto *Rule : MergeRules) {
+    StringRef FuncName = Rule->getValueAsString("MergeFunc");
+    OS << "  " << FuncName << "(Caller, Callee);\n";
+  }
+
+  OS << "}\n\n";
+
+  OS << "#endif\n";
+}
+
+void Attributes::printEnumAttrClasses(raw_ostream &OS,
+                                      const std::vector<Record *> &Records) {
+  OS << "// EnumAttr classes\n";
+  for (const auto *R : Records) {
+    OS << "struct " << R->getName() << "Attr : EnumAttr {\n";
+    OS << "  constexpr static const enum Attribute::AttrKind Kind = ";
+    OS << "Attribute::" << R->getName() << ";\n";
+    OS << "};\n";
+  }
+  OS << "\n";
+}
+
+void Attributes::printStrBoolAttrClasses(raw_ostream &OS,
+                                         const std::vector<Record *> &Records) {
+  OS << "// StrBoolAttr classes\n";
+  for (const auto *R : Records) {
+    OS << "struct " << R->getName() << "Attr : StrBoolAttr {\n";
+    OS << "  constexpr static const char * const Kind = \"";
+    OS << R->getValueAsString("AttrString") << "\";\n";
+    OS << "};\n";
+  }
+  OS << "\n";
+}
+
 void Attributes::emit(raw_ostream &OS) {
   emitTargetIndependentEnums(OS);
+  emitFnAttrCompatCheck(OS, false);
 }
 
 namespace llvm {