[PATCH] D7802: [Inliner] Use whitelist instead of blacklist when checking function attribute compatibility and make the check stricter

Duncan P. N. Exon Smith via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 14 15:08:39 PDT 2015


> On 2015-Oct-13, at 22:32, Akira Hatanaka <ahatanak at gmail.com> wrote:
> 
> ahatanak updated this revision to Diff 37320.
> ahatanak added a comment.
> 
> Rebase and add rules to merge caller's and callee's attributes to Attributes.td.
> 
> Merge rules enable modifying the caller's attribute set if the caller and callee have incompatible attribute sets, rather than blocking inlining. For example, if the callee has attribute "noimplicitfloat", but the caller doesn't, the merge rule defined in Attributes.td attaches "noimplicitfloat" to the caller.
> 
> 
> http://reviews.llvm.org/D7802
> 
> Files:
>  include/llvm/IR/Attributes.h
>  include/llvm/IR/Attributes.td
>  include/llvm/IR/CMakeLists.txt
>  lib/Analysis/InlineCost.cpp
>  lib/IR/Attributes.cpp
>  lib/IR/AttributesCompatFunc.td
>  lib/IR/CMakeLists.txt
>  lib/IR/Makefile
>  lib/Transforms/IPO/Inliner.cpp
>  test/Analysis/BasicAA/intrinsics.ll
>  test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll
>  test/Bitcode/compatibility-3.6.ll
>  test/Bitcode/compatibility-3.7.ll
>  test/Bitcode/compatibility.ll
>  test/Transforms/Inline/attributes.ll
>  test/Transforms/Inline/inline_invoke.ll
>  test/Transforms/MemCpyOpt/memcpy.ll
>  test/Transforms/ObjCARC/nested.ll
>  utils/TableGen/Attribute.cpp
>  utils/TableGen/CMakeLists.txt
>  utils/TableGen/TableGen.cpp
>  utils/TableGen/TableGenBackends.h
> 
> <D7802.37320.patch>

This looks like a much better approach.  I have a few minor things.
LGTM after that.

> Index: utils/TableGen/TableGenBackends.h
> ===================================================================
> --- utils/TableGen/TableGenBackends.h
> +++ utils/TableGen/TableGenBackends.h
> @@ -78,6 +78,7 @@
>  void EmitMapTable(RecordKeeper &RK, raw_ostream &OS);
>  void EmitOptParser(RecordKeeper &RK, raw_ostream &OS);
>  void EmitCTags(RecordKeeper &RK, raw_ostream &OS);
> +void EmitAttribute(RecordKeeper &RK, raw_ostream &OS);

Should this be `EmitAttributes()`?  Or am I missing some convention?

>  
>  } // End llvm namespace
>  
> Index: utils/TableGen/TableGen.cpp
> ===================================================================
> --- utils/TableGen/TableGen.cpp
> +++ utils/TableGen/TableGen.cpp
> @@ -41,7 +41,8 @@
>    PrintEnums,
>    PrintSets,
>    GenOptParserDefs,
> -  GenCTags
> +  GenCTags,
> +  GenAttribute

I'll mark up all the other places I thought plural made sense
(attributes instead of attribute).  If there's a good reason for these
to be singular instead of plural, please just correct me.

=> plural

>  };
>  
>  namespace {
> @@ -85,6 +86,8 @@
>                                 "Generate option definitions"),
>                      clEnumValN(GenCTags, "gen-ctags",
>                                 "Generate ctags-compatible index"),
> +                    clEnumValN(GenAttribute, "gen-attr",
> +                               "Generate attribute"),

=> plural

>                      clEnumValEnd));
>  
>    cl::opt<std::string>
> @@ -165,6 +168,9 @@
>    case GenCTags:
>      EmitCTags(Records, OS);
>      break;
> +  case GenAttribute:
> +    EmitAttribute(Records, OS);
> +    break;
>    }
>  
>    return false;
> Index: utils/TableGen/CMakeLists.txt
> ===================================================================
> --- utils/TableGen/CMakeLists.txt
> +++ utils/TableGen/CMakeLists.txt
> @@ -4,6 +4,7 @@
>    AsmMatcherEmitter.cpp
>    AsmWriterEmitter.cpp
>    AsmWriterInst.cpp
> +  Attribute.cpp

=> plural

>    CallingConvEmitter.cpp
>    CodeEmitterGen.cpp
>    CodeGenDAGPatterns.cpp
> Index: utils/TableGen/Attribute.cpp
> ===================================================================
> --- /dev/null
> +++ utils/TableGen/Attribute.cpp

=> plural

> @@ -0,0 +1,188 @@
> +//===- Attribute.cpp - Generate attributes --------------------------------===//

=> plural

> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Support/SourceMgr.h"
> +#include "llvm/Support/MemoryBuffer.h"
> +#include "llvm/TableGen/Error.h"
> +#include "llvm/TableGen/Record.h"
> +#include <algorithm>
> +#include <string>
> +#include <vector>
> +using namespace llvm;
> +
> +#define DEBUG_TYPE "attr-enum"
> +
> +namespace {
> +
> +class Attribute {

=> plural

> +public:
> +  Attribute(RecordKeeper &R) : Records(R) {}
> +  void emit(raw_ostream &OS);
> +
> +private:
> +  void emitTargetIndependentEnums(raw_ostream &OS);
> +  void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr);
> +
> +  void printEnumAttrClasses(raw_ostream &OS,
> +                            const std::vector<Record *> &Records);
> +  void printStrAttrClasses(raw_ostream &OS,
> +                           const std::vector<Record *> &Records);
> +  void printStrBoolAttrClasses(raw_ostream &OS,
> +                               const std::vector<Record *> &Records);
> +
> +  RecordKeeper &Records;
> +};
> +
> +} // End anonymous namespace.
> +
> +static bool isStringAttr(const Record &A) {
> +  return A.isSubClassOf("StrAttr");
> +}
> +
> +void Attribute::emitTargetIndependentEnums(raw_ostream &OS) {
> +  OS << "#ifdef GET_ATTR_ENUM\n";
> +  OS << "#undef GET_ATTR_ENUM\n";
> +
> +  const std::vector<Record*> &Attrs =
> +      Records.getAllDerivedDefinitions("EnumAttr");
> +
> +  for (auto A : Attrs)
> +    if (!isStringAttr(*A))
> +      OS << A->getName() << ",\n";
> +
> +  OS << "#endif\n";
> +}
> +
> +void Attribute::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 StrAttr {\n";
> +  OS << "  static bool isSet(const Function &Fn,\n";
> +  OS << "                    StringRef Kind) {\n";
> +  OS << "    return Fn.hasFnAttribute(Kind);\n";
> +  OS << "  }\n\n";
> +  OS << "  static void set(Function &Fn,\n";
> +  OS << "                  StringRef Kind, bool Val) {\n";
> +  OS << "    if (Val)\n";
> +  OS << "      Fn.addFnAttr(Kind);\n";
> +  OS << "    else {\n";
> +  OS << "      auto &Ctx = Fn.getContext();\n";
> +  OS << "      AttributeSet As;\n";
> +  OS << "      As.addAttribute(Ctx, AttributeSet::FunctionIndex, Kind);\n";
> +  OS << "      Fn.removeAttributes(AttributeSet::FunctionIndex, As);\n";
> +  OS << "    }\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"));
> +  //printStrAttrClasses(OS, Records.getAllDerivedDefinitions("StrAttr"));
> +  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 Attribute::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 Attribute::printStrAttrClasses(raw_ostream &OS,
> +                                    const std::vector<Record *> &Records) {
> +  OS << "// StrAttr classes\n";
> +  for (const auto *R : Records)
> +    OS << "// class " << R->getName() << "\n";
> +  OS << "\n";
> +}
> +
> +void Attribute::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 Attribute::emit(raw_ostream &OS) {
> +  emitTargetIndependentEnums(OS);
> +  emitFnAttrCompatCheck(OS, false);
> +}
> +
> +namespace llvm {
> +
> +void EmitAttribute(RecordKeeper &RK, raw_ostream &OS) {
> +  Attribute(RK).emit(OS);
> +}
> +
> +} // End llvm namespace.
> Index: test/Transforms/ObjCARC/nested.ll
> ===================================================================
> --- test/Transforms/ObjCARC/nested.ll
> +++ test/Transforms/ObjCARC/nested.ll
> @@ -820,6 +820,6 @@
>  }
>  
>  
> -; CHECK: attributes #0 = { nounwind argmemonly }
> +; CHECK: attributes #0 = { argmemonly nounwind }

Is this just because you've sorted the attributes in the .td file?  If
so, maybe you should leave them in the other order as a starting point,
and then sort them as a follow-up if you think it's worth it.  That
should separate the random testcase output changes into a separate
commit.  (If they *have* to be sorted by name, then I guess ignore
this.)

>  ; CHECK: attributes #1 = { nonlazybind }
>  ; CHECK: attributes [[NUW]] = { nounwind }
> Index: lib/Transforms/IPO/Inliner.cpp
> ===================================================================
> --- lib/Transforms/IPO/Inliner.cpp
> +++ lib/Transforms/IPO/Inliner.cpp
> @@ -86,39 +86,6 @@
>  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 @@
>    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: lib/IR/Makefile
> ===================================================================
> --- lib/IR/Makefile
> +++ lib/IR/Makefile
> @@ -10,14 +10,20 @@
>  LIBRARYNAME = LLVMCore
>  BUILD_ARCHIVE = 1
>  
> -BUILT_SOURCES = $(PROJ_OBJ_ROOT)/include/llvm/IR/Intrinsics.gen
> +BUILT_SOURCES = $(PROJ_OBJ_ROOT)/include/llvm/IR/Intrinsics.gen \
> +		 $(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
> @@ -28,6 +34,32 @@
>  	  $(EchoCmd) Updated Intrinsics.gen because Intrinsics.gen.tmp \
>  	    changed significantly. )
>  
> +$(ObjDir)/Attributes.inc.tmp: $(ObjDir)/.dir $(ATTRIBUTESTD) $(LLVM_TBLGEN)
> +	$(Echo) Building Attributes.inc.tmp from $(ATTRIBUTESTD)
> +	$(Verb) $(LLVMTableGen) $(call SYSPATH, $(ATTRIBUTESTD)) -o $(call SYSPATH, $@) -gen-attr
> +
> +$(ATTRINCFILE): $(ObjDir)/Attributes.inc.tmp $(PROJ_OBJ_ROOT)/include/llvm/IR/.dir
> +	$(Verb) $(CMP) -s $@ $< || ( $(CP) $< $@ && \
> +	  $(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-attr
> +
> +$(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
> +
> +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: lib/IR/CMakeLists.txt
> ===================================================================
> --- lib/IR/CMakeLists.txt
> +++ lib/IR/CMakeLists.txt
> @@ -1,3 +1,7 @@
> +set(LLVM_TARGET_DEFINITIONS AttributesCompatFunc.td)
> +tablegen(LLVM AttributesCompatFunc.inc -gen-attr)
> +add_public_tablegen_target(AttributeCompatFuncTableGen)
> +
>  add_llvm_library(LLVMCore
>    AsmWriter.cpp
>    Attributes.cpp
> Index: lib/IR/AttributesCompatFunc.td
> ===================================================================
> --- /dev/null
> +++ lib/IR/AttributesCompatFunc.td
> @@ -0,0 +1 @@
> +include "llvm/IR/Attributes.td"
> Index: lib/IR/Attributes.cpp
> ===================================================================
> --- lib/IR/Attributes.cpp
> +++ lib/IR/Attributes.cpp
> @@ -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"
> @@ -1404,3 +1405,80 @@
>  
>    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);
> +}
> Index: lib/Analysis/InlineCost.cpp
> ===================================================================
> --- lib/Analysis/InlineCost.cpp
> +++ lib/Analysis/InlineCost.cpp
> @@ -1362,9 +1362,7 @@
>                                                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: include/llvm/IR/CMakeLists.txt
> ===================================================================
> --- include/llvm/IR/CMakeLists.txt
> +++ include/llvm/IR/CMakeLists.txt
> @@ -1,5 +1,6 @@
> -set(LLVM_TARGET_DEFINITIONS Intrinsics.td)
> +set(LLVM_TARGET_DEFINITIONS Attributes.td)
> +tablegen(LLVM Attributes.inc -gen-attr)
>  
> +set(LLVM_TARGET_DEFINITIONS Intrinsics.td)
>  tablegen(LLVM Intrinsics.gen -gen-intrinsic)
> -
>  add_public_tablegen_target(intrinsics_gen)
> Index: include/llvm/IR/Attributes.td
> ===================================================================
> --- /dev/null
> +++ include/llvm/IR/Attributes.td
> @@ -0,0 +1,187 @@
> +/// Attribute base class.
> +class Attr<string S> {
> +  // String representation of this attribute in the IR.
> +  string AttrString = S;
> +}
> +
> +/// Enum attribute.
> +class EnumAttr<string S> : Attr<S>;
> +
> +/// StringBool attribute.
> +class StrBoolAttr<string S> : Attr<S>;
> +
> +/// Target-independent enum attributes.
> +
> +/// Alignment of parameter (5 bits) stored as log2 of alignment with +1 bias.
> +/// 0 means unaligned (different from align(1)).
> +def Alignment : EnumAttr<"align">;
> +
> +/// inline=always.
> +def AlwaysInline : EnumAttr<"alwaysinline">;
> +
> +/// Funciton can access memory only using pointers based on its arguments.
> +def ArgMemOnly : EnumAttr<"argmemonly">;
> +
> +/// Callee is recognized as a builtin, despite nobuiltin attribute on its
> +/// declaration.
> +def Builtin : EnumAttr<"builtin">;
> +
> +/// Pass structure by value.
> +def ByVal : EnumAttr<"byval">;
> +
> +/// Marks function as being in a cold path.
> +def Cold : EnumAttr<"cold">;
> +
> +/// Can only be moved to control-equivalent blocks.
> +def Convergent : EnumAttr<"convergent">;
> +
> +/// Pointer is known to be dereferenceable.
> +def Dereferenceable : EnumAttr<"dereferenceable">;
> +
> +/// Pointer is either null or dereferenceable.
> +def DereferenceableOrNull : EnumAttr<"dereferenceable_or_null">;
> +
> +/// Pass structure in an alloca.
> +def InAlloca : EnumAttr<"inalloca">;
> +
> +/// Source said inlining was desirable.
> +def InlineHint : EnumAttr<"inlinehint">;
> +
> +/// Force argument to be passed in register.
> +def InReg : EnumAttr<"inreg">;
> +
> +/// Build jump-instruction tables and replace refs.
> +def JumpTable : EnumAttr<"jumptable">;
> +
> +/// Function must be optimized for size first.
> +def MinSize : EnumAttr<"minsize">;
> +
> +/// Naked function.
> +def Naked : EnumAttr<"naked">;
> +
> +/// Nested function static chain.
> +def Nest : EnumAttr<"nest">;
> +
> +/// Considered to not alias after call.
> +def NoAlias : EnumAttr<"noalias">;
> +
> +/// Callee isn't recognized as a builtin.
> +def NoBuiltin : EnumAttr<"nobuiltin">;
> +
> +/// Function creates no aliases of pointer.
> +def NoCapture : EnumAttr<"nocapture">;
> +
> +/// Call cannot be duplicated.
> +def NoDuplicate : EnumAttr<"noduplicate">;
> +
> +/// Disable implicit floating point insts.
> +def NoImplicitFloat : EnumAttr<"noimplicitfloat">;
> +
> +/// inline=never.
> +def NoInline : EnumAttr<"noinline">;
> +
> +/// Function is called early and/or often, so lazy binding isn't worthwhile.
> +def NonLazyBind : EnumAttr<"nonlazybind">;
> +
> +/// Pointer is known to be not null.
> +def NonNull : EnumAttr<"nonnull">;
> +
> +/// Disable redzone.
> +def NoRedZone : EnumAttr<"noredzone">;
> +
> +/// Mark the function as not returning.
> +def NoReturn : EnumAttr<"noreturn">;
> +
> +/// Function doesn't unwind stack.
> +def NoUnwind : EnumAttr<"nounwind">;
> +
> +/// opt_size.
> +def OptimizeForSize : EnumAttr<"optsize">;
> +
> +/// Function must not be optimized.
> +def OptimizeNone : EnumAttr<"optnone">;
> +
> +/// Function does not access memory.
> +def ReadNone : EnumAttr<"readnone">;
> +
> +/// Function only reads from memory.
> +def ReadOnly : EnumAttr<"readonly">;
> +
> +/// Return value is always equal to this argument.
> +def Returned : EnumAttr<"returned">;
> +
> +/// Function can return twice.
> +def ReturnsTwice : EnumAttr<"returns_twice">;
> +
> +/// Safe Stack protection.
> +def SafeStack : EnumAttr<"safestack">;
> +
> +/// Sign extended before/after call.
> +def SExt : EnumAttr<"signext">;
> +
> +/// Alignment of stack for function (3 bits)  stored as log2 of alignment with
> +/// +1 bias 0 means unaligned (different from alignstack=(1)).
> +def StackAlignment : EnumAttr<"alignstack">;
> +
> +/// Stack protection.
> +def StackProtect : EnumAttr<"ssp">;
> +
> +/// Stack protection required.
> +def StackProtectReq : EnumAttr<"sspreq">;
> +
> +/// Strong Stack protection.
> +def StackProtectStrong : EnumAttr<"sspstrong">;
> +
> +/// Hidden pointer to structure to return.
> +def StructRet : EnumAttr<"sret">;
> +
> +/// AddressSanitizer is on.
> +def SanitizeAddress : EnumAttr<"sanitize_address">;
> +
> +/// ThreadSanitizer is on.
> +def SanitizeThread : EnumAttr<"sanitize_thread">;
> +
> +/// MemorySanitizer is on.
> +def SanitizeMemory : EnumAttr<"sanitize_memory">;
> +
> +/// Function must be in a unwind table.
> +def UWTable : EnumAttr<"uwtable">;
> +
> +/// Zero extended before/after call.
> +def ZExt : EnumAttr<"zeroext">;
> +
> +/// Target-independent string attributes.
> +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<"setAND<LessPreciseFPMADAttr>">;
> +def : MergeRule<"setAND<NoInfsFPMathAttr>">;
> +def : MergeRule<"setAND<NoNansFPMathAttr>">;
> +def : MergeRule<"setAND<UnsafeFPMathAttr>">;
> +def : MergeRule<"setOR<NoImplicitFloatAttr>">;
> +def : MergeRule<"adjustCallerSSPLevel">;

Are these the same rules that existed before?  Ideally this commit (with
the new infrastructure) would be no functionality change (NFC), and a
follow-up would add any new rules.  It's not a big deal though if it's
hard to split up.

> Index: include/llvm/IR/Attributes.h
> ===================================================================
> --- include/llvm/IR/Attributes.h
> +++ include/llvm/IR/Attributes.h
> @@ -33,6 +33,7 @@
>  class AttributeSetNode;
>  class Constant;
>  template<typename T> struct DenseMapInfo;
> +class Function;
>  class LLVMContext;
>  class Type;
>  
> @@ -64,60 +65,8 @@
>    enum AttrKind {
>      // IR-Level Attributes
>      None,                  ///< No attributes have been set
> -    Alignment,             ///< Alignment of parameter (5 bits)
> -                           ///< stored as log2 of alignment with +1 bias
> -                           ///< 0 means unaligned (different from align(1))
> -    AlwaysInline,          ///< inline=always
> -    Builtin,               ///< Callee is recognized as a builtin, despite
> -                           ///< nobuiltin attribute on its declaration.
> -    ByVal,                 ///< Pass structure by value
> -    InAlloca,              ///< Pass structure in an alloca
> -    Cold,                  ///< Marks function as being in a cold path.
> -    Convergent,            ///< Can only be moved to control-equivalent blocks
> -    InlineHint,            ///< Source said inlining was desirable
> -    InReg,                 ///< Force argument to be passed in register
> -    JumpTable,             ///< Build jump-instruction tables and replace refs.
> -    MinSize,               ///< Function must be optimized for size first
> -    Naked,                 ///< Naked function
> -    Nest,                  ///< Nested function static chain
> -    NoAlias,               ///< Considered to not alias after call
> -    NoBuiltin,             ///< Callee isn't recognized as a builtin
> -    NoCapture,             ///< Function creates no aliases of pointer
> -    NoDuplicate,           ///< Call cannot be duplicated
> -    NoImplicitFloat,       ///< Disable implicit floating point insts
> -    NoInline,              ///< inline=never
> -    NonLazyBind,           ///< Function is called early and/or
> -                           ///< often, so lazy binding isn't worthwhile
> -    NonNull,               ///< Pointer is known to be not null
> -    Dereferenceable,       ///< Pointer is known to be dereferenceable
> -    DereferenceableOrNull, ///< Pointer is either null or dereferenceable
> -    NoRedZone,             ///< Disable redzone
> -    NoReturn,              ///< Mark the function as not returning
> -    NoUnwind,              ///< Function doesn't unwind stack
> -    OptimizeForSize,       ///< opt_size
> -    OptimizeNone,          ///< Function must not be optimized.
> -    ReadNone,              ///< Function does not access memory
> -    ReadOnly,              ///< Function only reads from memory
> -    ArgMemOnly,            ///< Funciton can access memory only using pointers
> -                           ///< based on its arguments.
> -    Returned,              ///< Return value is always equal to this argument
> -    ReturnsTwice,          ///< Function can return twice
> -    SExt,                  ///< Sign extended before/after call
> -    StackAlignment,        ///< Alignment of stack for function (3 bits)
> -                           ///< stored as log2 of alignment with +1 bias 0
> -                           ///< means unaligned (different from
> -                           ///< alignstack=(1))
> -    StackProtect,          ///< Stack protection.
> -    StackProtectReq,       ///< Stack protection required.
> -    StackProtectStrong,    ///< Strong Stack protection.
> -    SafeStack,             ///< Safe Stack protection.
> -    StructRet,             ///< Hidden pointer to structure to return
> -    SanitizeAddress,       ///< AddressSanitizer is on.
> -    SanitizeThread,        ///< ThreadSanitizer is on.
> -    SanitizeMemory,        ///< MemorySanitizer is on.
> -    UWTable,               ///< Function must be in a unwind table
> -    ZExt,                  ///< Zero extended before/after call
> -
> +    #define GET_ATTR_ENUM
> +    #include "llvm/IR/Attributes.inc"
>      EndAttrKinds           ///< Sentinal value useful for loops
>    };
>  
> @@ -579,6 +528,13 @@
>  /// \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
> 



More information about the llvm-commits mailing list