Added support for the Builtin attribute.
authorMichael Gottesman <mgottesman@apple.com>
Thu, 27 Jun 2013 00:25:01 +0000 (00:25 +0000)
committerMichael Gottesman <mgottesman@apple.com>
Thu, 27 Jun 2013 00:25:01 +0000 (00:25 +0000)
The Builtin attribute is an attribute that can be placed on function call site that signal that even though a function is declared as being a builtin,

rdar://problem/13727199

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

15 files changed:
docs/LangRef.rst
include/llvm/IR/Attributes.h
include/llvm/IR/Instructions.h
include/llvm/Support/CallSite.h
lib/Analysis/MemoryBuiltins.cpp
lib/AsmParser/LLLexer.cpp
lib/AsmParser/LLParser.cpp
lib/AsmParser/LLParser.h
lib/AsmParser/LLToken.h
lib/IR/Attributes.cpp
lib/IR/Instructions.cpp
lib/IR/Verifier.cpp
lib/Transforms/Utils/SimplifyLibCalls.cpp
test/Assembler/attribute-builtin.ll [new file with mode: 0644]
test/Transforms/InstCombine/simplify-libcalls.ll

index cd72cc963e9e0f6c426ac14178bef7ddab2674cc..b98332b3f6e23c66ab0d4cacaa29f435ea64713f 100644 (file)
@@ -822,6 +822,12 @@ example:
     computing edge weights, basic blocks post-dominated by a cold
     function call are also considered to be cold; and, thus, given low
     weight.
+``builtin``
+    This indicates that the callee function at a call site should be
+    recognized as a built-in function, even though the function's declaration
+    uses the ``nobuiltin'' attribute. This is only valid at call sites for
+    direct calls to functions which are declared with the ``nobuiltin``
+    attribute.
 ``nonlazybind``
     This attribute suppresses lazy symbol binding for the function. This
     may make calls to the function faster, at the cost of extra program
@@ -835,11 +841,11 @@ example:
     This attribute disables prologue / epilogue emission for the
     function. This can have very system-specific consequences.
 ``nobuiltin``
-    This indicates that the callee function at a call site is not
-    recognized as a built-in function. LLVM will retain the original call
-    and not replace it with equivalent code based on the semantics of the
-    built-in function. This is only valid at call sites, not on function
-    declarations or definitions.
+    This indicates that the callee function at a call site is not recognized as
+    a built-in function. LLVM will retain the original call and not replace it
+    with equivalent code based on the semantics of the built-in function, unless
+    the call site uses the ``builtin`` attribute. This is valid at call sites
+    and on function declarations and definitions.
 ``noduplicate``
     This attribute indicates that calls to the function cannot be
     duplicated. A call to a ``noduplicate`` function may be moved
index 0d14709fe9f9fc4e997d6ae2d53c7cca967de618..0bf59cf1f61a3690b1d57b2f4f633056454e07cc 100644 (file)
@@ -67,6 +67,8 @@ public:
                            ///< 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
     Cold,                  ///< Marks function as being in a cold path.
     InlineHint,            ///< Source said inlining was desirable
index 3a8738f0bf93742956fde04a4377d3fbbc0befe1..e05c3a823e6e36adda241441f8512eceeb12d74d 100644 (file)
@@ -1278,7 +1278,11 @@ public:
   void removeAttribute(unsigned i, Attribute attr);
 
   /// \brief Determine whether this call has the given attribute.
-  bool hasFnAttr(Attribute::AttrKind A) const;
+  bool hasFnAttr(Attribute::AttrKind A) const {
+    assert(A != Attribute::NoBuiltin &&
+           "Use CallInst::isNoBuiltin() to check for Attribute::NoBuiltin");
+    return hasFnAttrImpl(A);
+  }
 
   /// \brief Determine whether the call or the callee has the given attributes.
   bool paramHasAttr(unsigned i, Attribute::AttrKind A) const;
@@ -1288,6 +1292,13 @@ public:
     return AttributeList.getParamAlignment(i);
   }
 
+  /// \brief Return true if the call should not be treated as a call to a
+  /// builtin.
+  bool isNoBuiltin() const {
+    return hasFnAttrImpl(Attribute::NoBuiltin) &&
+      !hasFnAttrImpl(Attribute::Builtin);
+  }
+
   /// \brief Return true if the call should not be inlined.
   bool isNoInline() const { return hasFnAttr(Attribute::NoInline); }
   void setIsNoInline() {
@@ -1378,6 +1389,9 @@ public:
     return isa<Instruction>(V) && classof(cast<Instruction>(V));
   }
 private:
+
+  bool hasFnAttrImpl(Attribute::AttrKind A) const;
+
   // Shadow Instruction::setInstructionSubclassData with a private forwarding
   // method so that subclasses cannot accidentally use it.
   void setInstructionSubclassData(unsigned short D) {
@@ -3021,7 +3035,11 @@ public:
   void removeAttribute(unsigned i, Attribute attr);
 
   /// \brief Determine whether this call has the NoAlias attribute.
-  bool hasFnAttr(Attribute::AttrKind A) const;
+  bool hasFnAttr(Attribute::AttrKind A) const {
+    assert(A != Attribute::NoBuiltin &&
+           "Use CallInst::isNoBuiltin() to check for Attribute::NoBuiltin");
+    return hasFnAttrImpl(A);
+  }
 
   /// \brief Determine whether the call or the callee has the given attributes.
   bool paramHasAttr(unsigned i, Attribute::AttrKind A) const;
@@ -3031,6 +3049,15 @@ public:
     return AttributeList.getParamAlignment(i);
   }
 
+  /// \brief Return true if the call should not be treated as a call to a
+  /// builtin.
+  bool isNoBuiltin() const {
+    // We assert in hasFnAttr if one passes in Attribute::NoBuiltin, so we have
+    // to check it by hand.
+    return hasFnAttrImpl(Attribute::NoBuiltin) &&
+      !hasFnAttrImpl(Attribute::Builtin);
+  }
+
   /// \brief Return true if the call should not be inlined.
   bool isNoInline() const { return hasFnAttr(Attribute::NoInline); }
   void setIsNoInline() {
@@ -3137,6 +3164,8 @@ private:
   virtual unsigned getNumSuccessorsV() const;
   virtual void setSuccessorV(unsigned idx, BasicBlock *B);
 
+  bool hasFnAttrImpl(Attribute::AttrKind A) const;
+
   // Shadow Instruction::setInstructionSubclassData with a private forwarding
   // method so that subclasses cannot accidentally use it.
   void setInstructionSubclassData(unsigned short D) {
index d80d9d8ad15a067b24ab9847fed7449d6214f7d8..961c38e83f9967ad0fa96036cd31f5cec579e825 100644 (file)
@@ -198,6 +198,12 @@ public:
     CALLSITE_DELEGATE_GETTER(getParamAlignment(i));
   }
 
+  /// \brief Return true if the call should not be treated as a call to a
+  /// builtin.
+  bool isNoBuiltin() const {
+    CALLSITE_DELEGATE_GETTER(isNoBuiltin());
+  }
+
   /// @brief Return true if the call should not be inlined.
   bool isNoInline() const {
     CALLSITE_DELEGATE_GETTER(isNoInline());
index 166034702ce8b7e57fff35edae201368ae01c5b7..39ec96519737a80b04e1a6476b6118716ead98de 100644 (file)
@@ -77,7 +77,7 @@ static Function *getCalledFunction(const Value *V, bool LookThroughBitCast) {
   if (!CS.getInstruction())
     return 0;
 
-  if (CS.hasFnAttr(Attribute::NoBuiltin))
+  if (CS.isNoBuiltin())
     return 0;
 
   Function *Callee = CS.getCalledFunction();
index 82d9975ca68d8897aad66d5ee1d6d1f453d6bc40..1b5422e3b4e28a73bc65375609f7bc7041fa9519 100644 (file)
@@ -563,6 +563,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(attributes);
 
   KEYWORD(alwaysinline);
+  KEYWORD(builtin);
   KEYWORD(byval);
   KEYWORD(cold);
   KEYWORD(inlinehint);
index b22d251f9e9ae3b3f7c967427d9ec8a86db74802..9349007bbb593fed30103129146d12553bceb652 100644 (file)
@@ -810,13 +810,13 @@ bool LLParser::ParseUnnamedAttrGrp() {
   assert(Lex.getKind() == lltok::AttrGrpID);
   unsigned VarID = Lex.getUIntVal();
   std::vector<unsigned> unused;
-  LocTy NoBuiltinLoc;
+  LocTy BuiltinLoc;
   Lex.Lex();
 
   if (ParseToken(lltok::equal, "expected '=' here") ||
       ParseToken(lltok::lbrace, "expected '{' here") ||
       ParseFnAttributeValuePairs(NumberedAttrBuilders[VarID], unused, true,
-                                 NoBuiltinLoc) ||
+                                 BuiltinLoc) ||
       ParseToken(lltok::rbrace, "expected end of attribute group"))
     return true;
 
@@ -830,15 +830,15 @@ bool LLParser::ParseUnnamedAttrGrp() {
 ///   ::= <attr> | <attr> '=' <value>
 bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
                                           std::vector<unsigned> &FwdRefAttrGrps,
-                                          bool inAttrGrp, LocTy &NoBuiltinLoc) {
+                                          bool inAttrGrp, LocTy &BuiltinLoc) {
   bool HaveError = false;
 
   B.clear();
 
   while (true) {
     lltok::Kind Token = Lex.getKind();
-    if (Token == lltok::kw_nobuiltin)
-      NoBuiltinLoc = Lex.getLoc();
+    if (Token == lltok::kw_builtin)
+      BuiltinLoc = Lex.getLoc();
     switch (Token) {
     default:
       if (!inAttrGrp) return HaveError;
@@ -909,6 +909,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
       continue;
     }
     case lltok::kw_alwaysinline:      B.addAttribute(Attribute::AlwaysInline); break;
+    case lltok::kw_builtin:           B.addAttribute(Attribute::Builtin); break;
     case lltok::kw_cold:              B.addAttribute(Attribute::Cold); break;
     case lltok::kw_inlinehint:        B.addAttribute(Attribute::InlineHint); break;
     case lltok::kw_minsize:           B.addAttribute(Attribute::MinSize); break;
@@ -1165,6 +1166,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
 
     case lltok::kw_alignstack:
     case lltok::kw_alwaysinline:
+    case lltok::kw_builtin:
     case lltok::kw_inlinehint:
     case lltok::kw_minsize:
     case lltok::kw_naked:
@@ -1223,6 +1225,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
 
     case lltok::kw_alignstack:
     case lltok::kw_alwaysinline:
+    case lltok::kw_builtin:
     case lltok::kw_cold:
     case lltok::kw_inlinehint:
     case lltok::kw_minsize:
@@ -2983,7 +2986,7 @@ bool LLParser::ParseFunctionHeader(Function *&Fn, bool isDefine) {
   bool isVarArg;
   AttrBuilder FuncAttrs;
   std::vector<unsigned> FwdRefAttrGrps;
-  LocTy NoBuiltinLoc;
+  LocTy BuiltinLoc;
   std::string Section;
   unsigned Alignment;
   std::string GC;
@@ -2994,7 +2997,7 @@ bool LLParser::ParseFunctionHeader(Function *&Fn, bool isDefine) {
       ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr,
                          &UnnamedAddrLoc) ||
       ParseFnAttributeValuePairs(FuncAttrs, FwdRefAttrGrps, false,
-                                 NoBuiltinLoc) ||
+                                 BuiltinLoc) ||
       (EatIfPresent(lltok::kw_section) &&
        ParseStringConstant(Section)) ||
       ParseOptionalAlignment(Alignment) ||
@@ -3002,8 +3005,8 @@ bool LLParser::ParseFunctionHeader(Function *&Fn, bool isDefine) {
        ParseStringConstant(GC)))
     return true;
 
-  if (FuncAttrs.contains(Attribute::NoBuiltin))
-    return Error(NoBuiltinLoc, "'nobuiltin' attribute not valid on function");
+  if (FuncAttrs.contains(Attribute::Builtin))
+    return Error(BuiltinLoc, "'builtin' attribute not valid on function");
 
   // If the alignment was parsed as an attribute, move to the alignment field.
   if (FuncAttrs.hasAlignmentAttr()) {
@@ -3927,7 +3930,7 @@ bool LLParser::ParseCall(Instruction *&Inst, PerFunctionState &PFS,
                          bool isTail) {
   AttrBuilder RetAttrs, FnAttrs;
   std::vector<unsigned> FwdRefAttrGrps;
-  LocTy NoBuiltinLoc;
+  LocTy BuiltinLoc;
   CallingConv::ID CC;
   Type *RetType = 0;
   LocTy RetTypeLoc;
@@ -3942,7 +3945,7 @@ bool LLParser::ParseCall(Instruction *&Inst, PerFunctionState &PFS,
       ParseValID(CalleeID) ||
       ParseParameterList(ArgList, PFS) ||
       ParseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false,
-                                 NoBuiltinLoc))
+                                 BuiltinLoc))
     return true;
 
   // If RetType is a non-function pointer type, then this is the short syntax
index 1f2879e948d9c43652f2f73a6df11cd71f594980..594281e908245d4623ae7ed951883f71ee7eb0ed 100644 (file)
@@ -242,7 +242,7 @@ namespace llvm {
     bool ParseUnnamedAttrGrp();
     bool ParseFnAttributeValuePairs(AttrBuilder &B,
                                     std::vector<unsigned> &FwdRefAttrGrps,
-                                    bool inAttrGrp, LocTy &NoBuiltinLoc);
+                                    bool inAttrGrp, LocTy &BuiltinLoc);
 
     // Type Parsing.
     bool ParseType(Type *&Result, bool AllowVoid = false);
index e889a2bfd0e7e8a5a763fa6da8980e78ea73c149..bbe6de0c85148ad5860af52c839a27a162a81ce8 100644 (file)
@@ -95,6 +95,7 @@ namespace lltok {
     kw_attributes,
     kw_alwaysinline,
     kw_sanitize_address,
+    kw_builtin,
     kw_byval,
     kw_cold,
     kw_inlinehint,
index e48ebb133527f46e4a095b98b1b41e06127a5c7b..2160ea24641cffaa01c4af19fc62df8716abd976 100644 (file)
@@ -157,6 +157,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return "sanitize_address";
   if (hasAttribute(Attribute::AlwaysInline))
     return "alwaysinline";
+  if (hasAttribute(Attribute::Builtin))
+    return "builtin";
   if (hasAttribute(Attribute::ByVal))
     return "byval";
   if (hasAttribute(Attribute::InlineHint))
@@ -399,6 +401,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
   case Attribute::NoBuiltin:       return 1ULL << 38;
   case Attribute::Returned:        return 1ULL << 39;
   case Attribute::Cold:            return 1ULL << 40;
+  case Attribute::Builtin:         return 1ULL << 41;
   }
   llvm_unreachable("Unsupported attribute type");
 }
index d58877ef773af6083358c4f0fabce9296a6f1093..5878f77dc1744bf87b1c15114aab18497690d47e 100644 (file)
@@ -346,7 +346,7 @@ void CallInst::removeAttribute(unsigned i, Attribute attr) {
   setAttributes(PAL);
 }
 
-bool CallInst::hasFnAttr(Attribute::AttrKind A) const {
+bool CallInst::hasFnAttrImpl(Attribute::AttrKind A) const {
   if (AttributeList.hasAttribute(AttributeSet::FunctionIndex, A))
     return true;
   if (const Function *F = getCalledFunction())
@@ -574,7 +574,7 @@ void InvokeInst::setSuccessorV(unsigned idx, BasicBlock *B) {
   return setSuccessor(idx, B);
 }
 
-bool InvokeInst::hasFnAttr(Attribute::AttrKind A) const {
+bool InvokeInst::hasFnAttrImpl(Attribute::AttrKind A) const {
   if (AttributeList.hasAttribute(AttributeSet::FunctionIndex, A))
     return true;
   if (const Function *F = getCalledFunction())
index 1b1b3b8a1ea17424997371347d0ec8afec7b3468..7123eaf18a0c605e025259bc441d3143fdded60f 100644 (file)
@@ -692,6 +692,7 @@ void Verifier::VerifyAttributeTypes(AttributeSet Attrs, unsigned Idx,
         I->getKindAsEnum() == Attribute::SanitizeMemory ||
         I->getKindAsEnum() == Attribute::MinSize ||
         I->getKindAsEnum() == Attribute::NoDuplicate ||
+        I->getKindAsEnum() == Attribute::Builtin ||
         I->getKindAsEnum() == Attribute::NoBuiltin ||
         I->getKindAsEnum() == Attribute::Cold) {
       if (!isFunction)
@@ -877,6 +878,13 @@ void Verifier::visitFunction(Function &F) {
   // Check function attributes.
   VerifyFunctionAttrs(FT, Attrs, &F);
 
+  // On function declarations/definitions, we do not support the builtin
+  // attribute. We do not check this in VerifyFunctionAttrs since that is
+  // checking for Attributes that can/can not ever be on functions.
+  Assert1(!Attrs.hasAttribute(AttributeSet::FunctionIndex,
+                              Attribute::Builtin),
+          "Attribute 'builtin' can only be applied to a callsite.", &F);
+
   // Check that this function meets the restrictions on this calling convention.
   switch (F.getCallingConv()) {
   default:
@@ -1435,6 +1443,14 @@ void Verifier::VerifyCallSite(CallSite CS) {
               "Function has metadata parameter but isn't an intrinsic", I);
   }
 
+  // If the call site has the 'builtin' attribute, verify that it's applied to a
+  // direct call to a function with the 'nobuiltin' attribute.
+  if (CS.hasFnAttr(Attribute::Builtin))
+    Assert1(CS.getCalledFunction() &&
+            CS.getCalledFunction()->hasFnAttribute(Attribute::NoBuiltin),
+            "Attribute 'builtin' can only be used in a call to a function with "
+            "the 'nobuiltin' attribute.", I);
+
   visitInstruction(*I);
 }
 
index 1b5b55d455826a8ee9ceb1c78a59570afecc134b..094c20154af570b6a7f8bd1110af81fada20cfd7 100644 (file)
@@ -1940,7 +1940,7 @@ LibCallSimplifier::~LibCallSimplifier() {
 }
 
 Value *LibCallSimplifier::optimizeCall(CallInst *CI) {
-  if (CI->hasFnAttr(Attribute::NoBuiltin)) return 0;
+  if (CI->isNoBuiltin()) return 0;
   return Impl->optimizeCall(CI);
 }
 
diff --git a/test/Assembler/attribute-builtin.ll b/test/Assembler/attribute-builtin.ll
new file mode 100644 (file)
index 0000000..0b322aa
--- /dev/null
@@ -0,0 +1,52 @@
+
+; Make sure that llvm-as/llvm-dis properly assembly/disassembly the 'builtin'
+; attribute.
+;
+; rdar://13727199
+
+; RUN: llvm-as -disable-verify < %s | \
+;      llvm-dis -disable-verify | \
+;      llvm-as -disable-verify | \
+;      llvm-dis -disable-verify | \
+;      FileCheck -check-prefix=ASSEMBLES %s
+
+; CHECK-ASSEMBLES: declare i8* @foo(i8*) [[NOBUILTIN:#[0-9]+]]
+; CHECK-ASSEMBLES: call i8* @foo(i8* %x) [[BUILTIN:#[0-9]+]]
+; CHECK-ASSEMBLES: attributes [[NOBUILTIN]] = { nobuiltin }
+; CHECK-ASSEMBLES: attributes [[BUILTIN]] = { builtin }
+
+declare i8* @foo(i8*) #1
+define i8* @bar(i8* %x) {
+  %y = call i8* @foo(i8* %x) #0
+  ret i8* %y
+}
+
+; Make sure that we do not accept the 'builtin' attribute on function
+; definitions, function declarations, and on call sites that call functions
+; which do not have nobuiltin on them.
+; rdar://13727199
+
+; RUN: not llvm-as <%s 2>&1  | FileCheck -check-prefix=BAD %s
+
+; CHECK-BAD: Attribute 'builtin' can only be used in a call to a function with the 'nobuiltin' attribute.
+; CHECK-BAD-NEXT: %y = call i8* @lar(i8* %x) #1
+; CHECK-BAD: Attribute 'builtin' can only be applied to a callsite.
+; CHECK-BAD-NEXT: i8* (i8*)* @car
+; CHECK-BAD: Attribute 'builtin' can only be applied to a callsite.
+; CHECK-BAD-NEXT: i8* (i8*)* @mar
+
+declare i8* @lar(i8*)
+
+define i8* @har(i8* %x) {
+  %y = call i8* @lar(i8* %x) #0
+  ret i8* %y
+}
+
+define i8* @car(i8* %x) #0 {
+  ret i8* %x
+}
+
+declare i8* @mar(i8*) #0
+
+attributes #0 = { builtin }
+attributes #1 = { nobuiltin }
index 4f3a506bee33b8f08e55679dad69f0364fc53625..ad29a445de641b2e5ba36e7a2845ddf9717601af 100644 (file)
@@ -130,3 +130,15 @@ define i32 @MemCpy() {
 }
 
 declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) nounwind
+
+declare i32 @strcmp(i8*, i8*) #0
+
+define void @test9(i8* %x) {
+; CHECK: @test9
+; CHECK-NOT: strcmp
+  %y = call i32 @strcmp(i8* %x, i8* %x) #1
+  ret void
+}
+
+attributes #0 = { nobuiltin }
+attributes #1 = { builtin }