Index: include/llvm/Analysis/InlineCost.h =================================================================== --- include/llvm/Analysis/InlineCost.h +++ include/llvm/Analysis/InlineCost.h @@ -134,6 +134,9 @@ // adding a replacement API. InlineCost getInlineCost(CallSite CS, Function *Callee, int Threshold); + bool hasCompatibleFnAttrs(const Function &Caller, + const Function &Callee) const; + /// \brief Minimal filter to detect invalid constructs for inlining. bool isInlineViable(Function &Callee); }; 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 struct DenseMapInfo; +class Function; class LLVMContext; class Type; @@ -64,56 +65,7 @@ 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. - 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 - 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. - 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 - + #include "llvm/IR/EnumAttributes.inc" EndAttrKinds ///< Sentinal value useful for loops }; private: @@ -554,6 +506,8 @@ /// \brief Which attributes cannot be applied to a type. AttributeSet typeIncompatible(Type *Ty, uint64_t Index); +bool hasCompatibleFnAttrs(const Function &Caller, const Function &Callee); + } // end AttributeFuncs namespace } // end llvm namespace Index: include/llvm/IR/Attributes.td =================================================================== --- /dev/null +++ include/llvm/IR/Attributes.td @@ -0,0 +1,111 @@ +// Base class for Attr and Inv. +class AttrBase; + +// Attribute base class. +class Attr : AttrBase { + // String representation of this attribute in the IR. + string AttrString = S; + + // Indicates whether this attribute has a string key. + bit IsStringAttr = IsStr; +} + +// Enum attribute. +class EnumAttr : Attr; + +// Enum attribute with an int value. +class EnumIntAttr : Attr; + +// String attribute. +class StrAttr : Attr; + +// String attribute with a boolean value ("true" or "false"). +class StrBoolAttr : Attr; + +// String attribute with a string value. +class StrStrAttr : Attr; + +// Boolean complement of attribute "A". "A" must derive from EnumAttr, StrAttr, +// or StrBoolAttr. +class Inv : AttrBase { + Attr OrigAttr = A; +} + +// Function inlining rules. + +// Disallow inlining if the caller and callee disagree on the value of function +// attribute "A". +class InlineCheckEquality { + Attr CheckedAttr = A; +} + +// Disallow inlining if the caller has attribute "CallerA" and the callee has +// one of the attributes in list "CalleeA". +class InlineCheckCompat CalleeA> { + AttrBase CallerAttr = CallerA; + list CalleeAttrs = CalleeA; +} + +// Target-independent enum attributes. +def Alignment : EnumIntAttr<"align">; +def AlwaysInline : EnumAttr<"alwaysinline">; +def Builtin : EnumAttr<"builtin">; +def ByVal : EnumAttr<"byval">; +def InAlloca : EnumAttr<"inalloca">; +def Cold : EnumAttr<"cold">; +def InlineHint : EnumAttr<"inlinehint">; +def InReg : EnumAttr<"inreg">; +def JumpTable : EnumAttr<"jumptable">; +def MinSize : EnumAttr<"minsize">; +def Naked : EnumAttr<"naked">; +def Nest : EnumAttr<"nest">; +def NoAlias : EnumAttr<"noalias">; +def NoBuiltin : EnumAttr<"nobuiltin">; +def NoCapture : EnumAttr<"nocapture">; +def NoDuplicate : EnumAttr<"noduplicate">; +def NoImplicitFloat : EnumAttr<"noimplicitfloat">; +def NoInline : EnumAttr<"noinline">; +def NonLazyBind : EnumAttr<"nonlazybind">; +def NonNull : EnumAttr<"nonnull">; +def Dereferenceable : EnumIntAttr<"dereferenceable">; +def DereferenceableOrNull : EnumIntAttr<"dereferenceable_or_null">; +def NoRedZone : EnumAttr<"noredzone">; +def NoReturn : EnumAttr<"noreturn">; +def NoUnwind : EnumAttr<"nounwind">; +def OptimizeForSize : EnumAttr<"optsize">; +def OptimizeNone : EnumAttr<"optnone">; +def ReadNone : EnumAttr<"readnone">; +def ReadOnly : EnumAttr<"readonly">; +def Returned : EnumAttr<"returned">; +def ReturnsTwice : EnumAttr<"returns_twice">; +def SExt : EnumAttr<"signext">; +def StackAlignment : EnumIntAttr<"alignstack">; +def StackProtect : EnumAttr<"ssp">; +def StackProtectReq : EnumAttr<"sspreq">; +def StackProtectStrong : EnumAttr<"sspstrong">; +def StructRet : EnumAttr<"sret">; +def SanitizeAddress : EnumAttr<"sanitize_address">; +def SanitizeThread : EnumAttr<"sanitize_thread">; +def SanitizeMemory : EnumAttr<"sanitize_memory">; +def UWTable : EnumAttr<"uwtable">; +def ZExt : EnumAttr<"zeroext">; + +// Target-independent string attributes. +def TargetCPU : StrStrAttr<"target-cpu">; +def TargetFeatures : StrStrAttr<"target-features">; +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">; + +// Function inlining rules. +def : InlineCheckEquality; +def : InlineCheckEquality; +def : InlineCheckEquality; +def : InlineCheckEquality; +def : InlineCheckEquality; + +def : InlineCheckCompat]>; +def : InlineCheckCompat]>; +def : InlineCheckCompat]>; +def : InlineCheckCompat]>; Index: lib/Analysis/IPA/InlineCost.cpp =================================================================== --- lib/Analysis/IPA/InlineCost.cpp +++ lib/Analysis/IPA/InlineCost.cpp @@ -1288,24 +1288,6 @@ return getInlineCost(CS, CS.getCalledFunction(), Threshold); } -/// \brief Test that two functions either have or have not the given attribute -/// at the same time. -template -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) { - return attributeMatches(Caller, Callee, "target-cpu") && - attributeMatches(Caller, Callee, "target-features") && - attributeMatches(Caller, Callee, Attribute::SanitizeAddress) && - attributeMatches(Caller, Callee, Attribute::SanitizeMemory) && - attributeMatches(Caller, Callee, Attribute::SanitizeThread); -} - InlineCost InlineCostAnalysis::getInlineCost(CallSite CS, Function *Callee, int Threshold) { // Cannot inline indirect calls. @@ -1322,7 +1304,7 @@ // Never inline functions with conflicting attributes (unless callee has // always-inline attribute). - if (!functionsHaveCompatibleAttributes(CS.getCaller(), Callee)) + if (!hasCompatibleFnAttrs(*CS.getCaller(), *Callee)) return llvm::InlineCost::getNever(); // Don't inline this call if the caller has the optnone attribute. @@ -1353,6 +1335,11 @@ return llvm::InlineCost::get(CA.getCost(), CA.getThreshold()); } +bool InlineCostAnalysis::hasCompatibleFnAttrs(const Function &Caller, + const Function &Callee) const { + return AttributeFuncs::hasCompatibleFnAttrs(Caller, Callee); +} + bool InlineCostAnalysis::isInlineViable(Function &F) { bool ReturnsTwice = F.hasFnAttribute(Attribute::ReturnsTwice); for (Function::iterator BI = F.begin(), BE = F.end(); BI != BE; ++BI) { 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" @@ -1311,3 +1312,12 @@ return AttributeSet::get(Ty->getContext(), Index, Incompatible); } + +#include "FnAttrCompatCheck.inc" + +/// \brief Test that there are no attribute conflicts between Caller and Callee +/// that prevent inlining. +bool AttributeFuncs::hasCompatibleFnAttrs(const Function &Caller, + const Function &Callee) { + return ::hasCompatibleFnAttrs(Caller, Callee); +} Index: lib/IR/Makefile =================================================================== --- lib/IR/Makefile +++ lib/IR/Makefile @@ -10,14 +10,19 @@ 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/EnumAttributes.inc \ + $(PROJ_OBJ_ROOT)/lib/IR/FnAttrCompatCheck.inc include $(LEVEL)/Makefile.common GENFILE:=$(PROJ_OBJ_ROOT)/include/llvm/IR/Intrinsics.gen +ENUMATTRINCFILE:=$(PROJ_OBJ_ROOT)/include/llvm/IR/EnumAttributes.inc +FNATTRCHECKINCFILE:=$(PROJ_OBJ_ROOT)/lib/IR/FnAttrCompatCheck.inc INTRINSICTD := $(PROJ_SRC_ROOT)/include/llvm/IR/Intrinsics.td INTRINSICTDS := $(wildcard $(PROJ_SRC_ROOT)/include/llvm/IR/Intrinsics*.td) +ENUMATTRIBUTESTD := $(PROJ_SRC_ROOT)/include/llvm/IR/Attributes.td $(ObjDir)/Intrinsics.gen.tmp: $(ObjDir)/.dir $(INTRINSICTDS) $(LLVM_TBLGEN) $(Echo) Building Intrinsics.gen.tmp from Intrinsics.td @@ -28,6 +33,32 @@ $(EchoCmd) Updated Intrinsics.gen because Intrinsics.gen.tmp \ changed significantly. ) +$(ObjDir)/EnumAttributes.inc.tmp: $(ObjDir)/.dir $(ENUMATTRIBUTESTD) $(LLVM_TBLGEN) + $(Echo) Building EnumAttributes.inc.tmp from $(ENUMATTRIBUTESTD) + $(Verb) $(LLVMTableGen) $(call SYSPATH, $(ENUMATTRIBUTESTD)) -o $(call SYSPATH, $@) -gen-attr-enum + +$(ENUMATTRINCFILE): $(ObjDir)/EnumAttributes.inc.tmp $(PROJ_OBJ_ROOT)/include/llvm/IR/.dir + $(Verb) $(CMP) -s $@ $< || ( $(CP) $< $@ && \ + $(EchoCmd) Updated EnumAttributes.inc because EnumAttributes.inc.tmp \ + changed significantly. ) + +$(ObjDir)/FnAttrCompatCheck.inc.tmp: $(ObjDir)/.dir $(ENUMATTRIBUTESTD) $(LLVM_TBLGEN) + $(Echo) Building FnAttrCompatCheck.inc.tmp from $(ENUMATTRIBUTESTD) + $(Verb) $(LLVMTableGen) $(call SYSPATH, $(ENUMATTRIBUTESTD)) -o $(call SYSPATH, $@) -gen-fnattr-compat-check + +$(FNATTRCHECKINCFILE): $(ObjDir)/FnAttrCompatCheck.inc.tmp $(PROJ_OBJ_ROOT)/include/llvm/IR/.dir + $(Verb) $(CMP) -s $@ $< || ( $(CP) $< $@ && \ + $(EchoCmd) Updated FnAttrCompatCheck.inc because FnAttrCompatCheck.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:: $(ENUMATTRINCFILE) + $(Echo) Installing $(DESTDIR)$(PROJ_includedir)/llvm/IR/EnumAttributes.inc + $(Verb) $(DataInstall) $(ENUMATTRINCFILE) $(DESTDIR)$(PROJ_includedir)/llvm/IR/EnumAttributes.inc + +install-local:: $(FNATTRCHECKINCFILE) + $(Echo) Installing $(DESTDIR)$(PROJ_libdir)/IR/FnAttrCompatCheck.inc + $(Verb) $(DataInstall) $(FNATTRCHECKINCFILE) $(DESTDIR)$(PROJ_libdir)/IR/FnAttrCompatCheck.inc Index: test/Transforms/Inline/attributes.ll =================================================================== --- test/Transforms/Inline/attributes.ll +++ test/Transforms/Inline/attributes.ll @@ -160,3 +160,33 @@ ; CHECK-NEXT: @test_target_features_callee1 ; CHECK-NEXT: ret i32 } + +define i32 @less-precise-fpmad_callee0(i32 %i) "less-precise-fpmad"="true" { + %1 = call i32 @noattr_callee(i32 %i) + ret i32 %1 + ; CHECK-LABEL: @less-precise-fpmad_callee0( + ; CHECK-NEXT: @noattr_callee + ; CHECK-NEXT: ret i32 +} + +define i32 @test_less-precise-fpmad0(i32 %i) "less-precise-fpmad"="false" { + %1 = call i32 @less-precise-fpmad_callee0(i32 %i) + ret i32 %1 + ; CHECK-LABEL: @test_less-precise-fpmad0( + ; CHECK-NOT: @less-precise-fpmad_callee0 +} + +define i32 @less-precise-fpmad_callee1(i32 %i) "less-precise-fpmad"="false" { + %1 = call i32 @noattr_callee(i32 %i) + ret i32 %1 + ; CHECK-LABEL: @less-precise-fpmad_callee1( + ; CHECK-NOT: @noattr_callee +} + +define i32 @test_less-precise-fpmad1(i32 %i) "less-precise-fpmad"="true" { + %1 = call i32 @less-precise-fpmad_callee1(i32 %i) + ret i32 %1 + ; CHECK-LABEL: @test_less-precise-fpmad1( + ; CHECK-NEXT: @less-precise-fpmad_callee1 + ; CHECK-NEXT: ret i32 +} Index: utils/TableGen/Attribute.cpp =================================================================== --- /dev/null +++ utils/TableGen/Attribute.cpp @@ -0,0 +1,166 @@ +//===- Attribute.cpp - Generate attributes --------------------------------===// +// +// 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 +#include +#include +using namespace llvm; + +#define DEBUG_TYPE "attr-enum" + +namespace { + +class Attribute { +public: + Attribute(RecordKeeper &R) : Records(R) {} + + void emitTargetIndependentEnums(raw_ostream &OS); + void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr); + +private: + void emitMustMatchRules(raw_ostream &OS); + void emitIncompatAttrsCheck(raw_ostream &OS); + + RecordKeeper &Records; +}; + +} // End anonymous namespace. + +void Attribute::emitTargetIndependentEnums(raw_ostream &OS) { + const std::vector &Attrs = + Records.getAllDerivedDefinitions("Attr"); + + for (auto A : Attrs) + if (!A->getValueAsBit("IsStringAttr")) + OS << A->getName() << ",\n"; +} + +static bool isStringAttr(const Record &A) { + return A.isSubClassOf("StrAttr") || A.isSubClassOf("StrBoolAttr") || + A.isSubClassOf("StrStrAttr"); +} + +static std::string getAttrKey(const Record &A) { + if (isStringAttr(A)) + return '"' + A.getValueAsString("AttrString") + '"'; + return std::string("Attribute::") + A.getName(); +} + +static std::string mustMatchRuleStr(const Record &A) { + std::string S = "attributeMatches(Caller, Callee, "; + return S + getAttrKey(A) + ")"; +} + +void Attribute::emitMustMatchRules(raw_ostream &OS) { + const std::vector &Rules = + Records.getAllDerivedDefinitions("InlineCheckEquality"); + + if (!Rules.empty()) { + OS << "template\n"; + OS << "static bool attributeMatches(const Function &F1,\n" + << " const Function &F2,\n" + << " AttrTy Attr) {\n" + << " return F1.getFnAttribute(Attr) == F2.getFnAttribute(Attr);\n" + << "}\n\n"; + } + + OS << "static bool checkMustMatchAttrs(const Function &Caller,\n" + << " const Function &Callee) {\n" + << " return "; + + if (Rules.empty()) { + OS << "true;\n" + << "}\n\n"; + return; + } + + Record *A = Rules.front()->getValueAsDef("CheckedAttr"); + OS << mustMatchRuleStr(*A); + + for (int I = 1, E = Rules.size(); I != E; ++I) { + OS << " &&\n "; + OS << mustMatchRuleStr(*Rules[I]->getValueAsDef("CheckedAttr")); + } + + OS << ";\n"; + OS << "}\n\n"; +} + +static std::string emitCheckBool(const Record *Attr, std::string FuncName) { + bool Inverted = false; + + if (Attr->isSubClassOf("Inv")) { + Inverted = true; + Attr = Attr->getValueAsDef("OrigAttr"); + } + + if (Attr->isSubClassOf("StrBoolAttr")) { + return FuncName + ".getFnAttribute(" + getAttrKey(*Attr) + ").getValueAsString()" + (Inverted ? " != " : " == ") + "\"true\""; + } + + std::string Ret; + + if (Inverted) + Ret = "!"; + + return Ret + FuncName + ".hasFnAttribute(" + getAttrKey(*Attr) + ")"; +} + +void Attribute::emitIncompatAttrsCheck(raw_ostream &OS) { + const std::vector &Rules = + Records.getAllDerivedDefinitions("InlineCheckCompat"); + + OS << "static bool checkIncompatAttrs(const Function &Caller,\n" + << " const Function &Callee) {\n"; + + for (auto Rule : Rules) { + Record *CallerAttr = Rule->getValueAsDef("CallerAttr"); + std::vector CalleeAttrs = + Rule->getValueAsListOfDefs("CalleeAttrs"); + + OS << " if (" << emitCheckBool(CallerAttr, "Caller") << ")\n"; + OS << " if (" << emitCheckBool(CalleeAttrs[0], "Callee"); + + for (int I = 1, E = CalleeAttrs.size(); I != E; ++I) + OS << " ||\n " << emitCheckBool(CalleeAttrs[I], "Callee"); + + OS << ")\n" + << " return false;\n\n"; + } + + OS << " return true;\n" + << "}\n\n"; +} + +void Attribute::emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr) { + emitMustMatchRules(OS); + emitIncompatAttrsCheck(OS); + + OS << "static bool hasCompatibleFnAttrs(const Function &Caller,\n" + << " const Function &Callee) {\n" + << " return checkMustMatchAttrs(Caller, Callee) &&\n" + << " checkIncompatAttrs(Caller, Callee);\n" + << "}\n\n"; +} + +namespace llvm { + +void EmitAttributeEnum(RecordKeeper &RK, raw_ostream &OS) { + Attribute(RK).emitTargetIndependentEnums(OS); +} + +void EmitFnAttrCompatCheck(RecordKeeper &RK, raw_ostream &OS) { + Attribute(RK).emitFnAttrCompatCheck(OS, false); +} + +} // End llvm namespace. Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -41,7 +41,9 @@ PrintEnums, PrintSets, GenOptParserDefs, - GenCTags + GenCTags, + GenAttributeEnum, + GenFnAttrCompatCheck }; namespace { @@ -85,6 +87,11 @@ "Generate option definitions"), clEnumValN(GenCTags, "gen-ctags", "Generate ctags-compatible index"), + clEnumValN(GenAttributeEnum, "gen-attr-enum", + "Generate attribute enums"), + clEnumValN(GenFnAttrCompatCheck, + "gen-fnattr-compat-check", + "Generate function attribute compatiblity"), clEnumValEnd)); cl::opt @@ -165,6 +172,12 @@ case GenCTags: EmitCTags(Records, OS); break; + case GenAttributeEnum: + EmitAttributeEnum(Records, OS); + break; + case GenFnAttrCompatCheck: + EmitFnAttrCompatCheck(Records, OS); + break; } return false; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -78,6 +78,8 @@ void EmitMapTable(RecordKeeper &RK, raw_ostream &OS); void EmitOptParser(RecordKeeper &RK, raw_ostream &OS); void EmitCTags(RecordKeeper &RK, raw_ostream &OS); +void EmitAttributeEnum(RecordKeeper &RK, raw_ostream &OS); +void EmitFnAttrCompatCheck(RecordKeeper &RK, raw_ostream &OS); } // End llvm namespace