forked from OSchip/llvm-project
Provide a way to specify inliner's attribute compatibility and merging.
This reapplies r252949. I've changed the type of FuncName to be std::string instead of StringRef in emitFnAttrCompatCheck. Original commit message for r252949: Provide a way to specify inliner's attribute compatibility and merging 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 llvm-svn: 252990
This commit is contained in:
parent
8bb168b160
commit
c7dfb76fe7
|
@ -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
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include "llvm/IR/Attributes.td"
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
std::string 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) {
|
||||
std::string 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 {
|
||||
|
|
Loading…
Reference in New Issue