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,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 Index: include/llvm/IR/Attributes.td =================================================================== --- /dev/null +++ include/llvm/IR/Attributes.td @@ -0,0 +1,187 @@ +/// Attribute base class. +class Attr { + // String representation of this attribute in the IR. + string AttrString = S; +} + +/// Enum attribute. +class EnumAttr : Attr; + +/// StringBool attribute. +class StrBoolAttr : Attr; + +/// 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 { + // 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">; +def : CompatRule<"isEqual">; +def : CompatRule<"isEqual">; + +class MergeRule { + // 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">; +def : MergeRule<"setAND">; +def : MergeRule<"setAND">; +def : MergeRule<"setAND">; +def : MergeRule<"setOR">; +def : MergeRule<"adjustCallerSSPLevel">; 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: 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: 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 +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 +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 +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/IR/AttributesCompatFunc.td =================================================================== --- /dev/null +++ lib/IR/AttributesCompatFunc.td @@ -0,0 +1 @@ +include "llvm/IR/Attributes.td" 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/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/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -86,39 +86,6 @@ typedef DenseMap > 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: test/Analysis/BasicAA/intrinsics.ll =================================================================== --- test/Analysis/BasicAA/intrinsics.ll +++ test/Analysis/BasicAA/intrinsics.ll @@ -38,6 +38,6 @@ declare <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8*, i32) nounwind readonly declare void @llvm.arm.neon.vst1.p0i8.v8i16(i8*, <8 x i16>, i32) nounwind -; CHECK: attributes #0 = { nounwind readonly argmemonly } -; CHECK: attributes #1 = { nounwind argmemonly } +; CHECK: attributes #0 = { argmemonly nounwind readonly } +; CHECK: attributes #1 = { argmemonly nounwind } ; CHECK: attributes [[ATTR]] = { nounwind } Index: test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll =================================================================== --- test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll +++ test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll @@ -22,8 +22,8 @@ declare <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8*, i32) nounwind readonly declare void @llvm.arm.neon.vst1.p0i8.v8i16(i8*, <8 x i16>, i32) nounwind -; CHECK: attributes #0 = { nounwind readonly argmemonly } -; CHECK: attributes #1 = { nounwind argmemonly } +; CHECK: attributes #0 = { argmemonly nounwind readonly } +; CHECK: attributes #1 = { argmemonly nounwind } ; CHECK: attributes [[NUW]] = { nounwind } !0 = !{!"tbaa root", null} Index: test/Bitcode/compatibility-3.6.ll =================================================================== --- test/Bitcode/compatibility-3.6.ll +++ test/Bitcode/compatibility-3.6.ll @@ -1180,8 +1180,8 @@ ; CHECK: attributes #27 = { uwtable } ; CHECK: attributes #28 = { "cpu"="cortex-a8" } ; CHECK: attributes #29 = { nounwind readnone } -; CHECK: attributes #30 = { nounwind readonly argmemonly } -; CHECK: attributes #31 = { nounwind argmemonly } +; CHECK: attributes #30 = { argmemonly nounwind readonly } +; CHECK: attributes #31 = { argmemonly nounwind } ; CHECK: attributes #32 = { nounwind readonly } ; CHECK: attributes #33 = { builtin } Index: test/Bitcode/compatibility-3.7.ll =================================================================== --- test/Bitcode/compatibility-3.7.ll +++ test/Bitcode/compatibility-3.7.ll @@ -1243,8 +1243,8 @@ ; CHECK: attributes #30 = { uwtable } ; CHECK: attributes #31 = { "cpu"="cortex-a8" } ; CHECK: attributes #32 = { nounwind readnone } -; CHECK: attributes #33 = { nounwind readonly argmemonly } -; CHECK: attributes #34 = { nounwind argmemonly } +; CHECK: attributes #33 = { argmemonly nounwind readonly } +; CHECK: attributes #34 = { argmemonly nounwind } ; CHECK: attributes #35 = { nounwind readonly } ; CHECK: attributes #36 = { builtin } Index: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ test/Bitcode/compatibility.ll @@ -1498,8 +1498,8 @@ ; CHECK: attributes #30 = { uwtable } ; CHECK: attributes #31 = { "cpu"="cortex-a8" } ; CHECK: attributes #32 = { nounwind readnone } -; CHECK: attributes #33 = { nounwind readonly argmemonly } -; CHECK: attributes #34 = { nounwind argmemonly } +; CHECK: attributes #33 = { argmemonly nounwind readonly } +; CHECK: attributes #34 = { argmemonly nounwind } ; CHECK: attributes #35 = { nounwind readonly } ; CHECK: attributes #36 = { builtin } Index: test/Transforms/Inline/attributes.ll =================================================================== --- test/Transforms/Inline/attributes.ll +++ test/Transforms/Inline/attributes.ll @@ -160,3 +160,87 @@ ; CHECK-NEXT: @test_target_features_callee1 ; CHECK-NEXT: ret i32 } + +define i32 @less-precise-fpmad_callee0(i32 %i) "less-precise-fpmad"="false" { + ret i32 %i +; CHECK: @less-precise-fpmad_callee0(i32 %i) [[FPMAD_FALSE:#[0-9]+]] { +; CHECK-NEXT: ret i32 +} + +define i32 @less-precise-fpmad_callee1(i32 %i) "less-precise-fpmad"="true" { + ret i32 %i +; CHECK: @less-precise-fpmad_callee1(i32 %i) [[FPMAD_TRUE:#[0-9]+]] { +; 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: @test_less-precise-fpmad0(i32 %i) [[FPMAD_FALSE]] { +; CHECK-NEXT: ret i32 +} + +define i32 @test_less-precise-fpmad1(i32 %i) "less-precise-fpmad"="false" { + %1 = call i32 @less-precise-fpmad_callee1(i32 %i) + ret i32 %1 +; CHECK: @test_less-precise-fpmad1(i32 %i) [[FPMAD_FALSE]] { +; CHECK-NEXT: ret i32 +} + +define i32 @test_less-precise-fpmad2(i32 %i) "less-precise-fpmad"="true" { + %1 = call i32 @less-precise-fpmad_callee0(i32 %i) + ret i32 %1 +; CHECK: @test_less-precise-fpmad2(i32 %i) [[FPMAD_FALSE]] { +; CHECK-NEXT: ret i32 +} + +define i32 @test_less-precise-fpmad3(i32 %i) "less-precise-fpmad"="true" { + %1 = call i32 @less-precise-fpmad_callee1(i32 %i) + ret i32 %1 +; CHECK: @test_less-precise-fpmad3(i32 %i) [[FPMAD_TRUE]] { +; CHECK-NEXT: ret i32 +} + +define i32 @no-implicit-float_callee0(i32 %i) { + ret i32 %i +; CHECK: @no-implicit-float_callee0(i32 %i) { +; CHECK-NEXT: ret i32 +} + +define i32 @no-implicit-float_callee1(i32 %i) noimplicitfloat { + ret i32 %i +; CHECK: @no-implicit-float_callee1(i32 %i) [[NOIMPLICITFLOAT:#[0-9]+]] { +; CHECK-NEXT: ret i32 +} + +define i32 @test_no-implicit-float0(i32 %i) { + %1 = call i32 @no-implicit-float_callee0(i32 %i) + ret i32 %1 +; CHECK: @test_no-implicit-float0(i32 %i) { +; CHECK-NEXT: ret i32 +} + +define i32 @test_no-implicit-float1(i32 %i) { + %1 = call i32 @no-implicit-float_callee1(i32 %i) + ret i32 %1 +; CHECK: @test_no-implicit-float1(i32 %i) [[NOIMPLICITFLOAT]] { +; CHECK-NEXT: ret i32 +} + +define i32 @test_no-implicit-float2(i32 %i) noimplicitfloat { + %1 = call i32 @no-implicit-float_callee0(i32 %i) + ret i32 %1 +; CHECK: @test_no-implicit-float2(i32 %i) [[NOIMPLICITFLOAT]] { +; CHECK-NEXT: ret i32 +} + +define i32 @test_no-implicit-float3(i32 %i) noimplicitfloat { + %1 = call i32 @no-implicit-float_callee1(i32 %i) + ret i32 %1 +; CHECK: @test_no-implicit-float3(i32 %i) [[NOIMPLICITFLOAT]] { +; CHECK-NEXT: ret i32 +} + +; CHECK: attributes [[FPMAD_FALSE]] = { "less-precise-fpmad"="false" } +; CHECK: attributes [[FPMAD_TRUE]] = { "less-precise-fpmad"="true" } +; CHECK: attributes [[NOIMPLICITFLOAT]] = { noimplicitfloat } Index: test/Transforms/Inline/inline_invoke.ll =================================================================== --- test/Transforms/Inline/inline_invoke.ll +++ test/Transforms/Inline/inline_invoke.ll @@ -344,5 +344,5 @@ ; CHECK: attributes [[NUW]] = { nounwind } ; CHECK: attributes #1 = { nounwind readnone } ; CHECK: attributes #2 = { ssp uwtable } -; CHECK: attributes #3 = { nounwind argmemonly } +; CHECK: attributes #3 = { argmemonly nounwind } ; CHECK: attributes #4 = { noreturn nounwind } Index: test/Transforms/MemCpyOpt/memcpy.ll =================================================================== --- test/Transforms/MemCpyOpt/memcpy.ll +++ test/Transforms/MemCpyOpt/memcpy.ll @@ -206,6 +206,6 @@ declare void @f2(%struct.big*) ; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { nounwind argmemonly } +; CHECK: attributes #1 = { argmemonly nounwind } ; CHECK: attributes #2 = { nounwind ssp } ; CHECK: attributes #3 = { nounwind ssp uwtable } 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 } ; CHECK: attributes #1 = { nonlazybind } ; CHECK: attributes [[NUW]] = { nounwind } Index: utils/TableGen/Attribute.cpp =================================================================== --- /dev/null +++ utils/TableGen/Attribute.cpp @@ -0,0 +1,188 @@ +//===- 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 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 &Records); + void printStrAttrClasses(raw_ostream &OS, + const std::vector &Records); + void printStrBoolAttrClasses(raw_ostream &OS, + const std::vector &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 &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 &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 &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 &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 &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 &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: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -4,6 +4,7 @@ AsmMatcherEmitter.cpp AsmWriterEmitter.cpp AsmWriterInst.cpp + Attribute.cpp CallingConvEmitter.cpp CodeEmitterGen.cpp CodeGenDAGPatterns.cpp Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -41,7 +41,8 @@ PrintEnums, PrintSets, GenOptParserDefs, - GenCTags + GenCTags, + GenAttribute }; namespace { @@ -85,6 +86,8 @@ "Generate option definitions"), clEnumValN(GenCTags, "gen-ctags", "Generate ctags-compatible index"), + clEnumValN(GenAttribute, "gen-attr", + "Generate attribute"), clEnumValEnd)); cl::opt @@ -165,6 +168,9 @@ case GenCTags: EmitCTags(Records, OS); break; + case GenAttribute: + EmitAttribute(Records, OS); + break; } return false; 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); } // End llvm namespace