Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -969,6 +969,12 @@ passed in is non-null, or the callee must ensure that the returned pointer is non-null. +``dereferencable`` + This indicates that the parameter or return pointer is dereferencable. This + attribute may only be applied to pointer typed parameters. A pointer that + is dereferencable can be loaded from speculatively without a risk of + trapping. + .. _gc: Garbage Collector Names Index: include/llvm-c/Core.h =================================================================== --- include/llvm-c/Core.h +++ include/llvm-c/Core.h @@ -168,6 +168,7 @@ LLVMInAllocaAttribute = 1ULL << 36, LLVMNonNullAttribute = 1ULL << 37, LLVMJumpTableAttribute = 1ULL << 38, + LLVMDereferencableAttribute = 1ULL << 39, */ } LLVMAttribute; Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -374,7 +374,8 @@ ATTR_KIND_OPTIMIZE_NONE = 37, ATTR_KIND_IN_ALLOCA = 38, ATTR_KIND_NON_NULL = 39, - ATTR_KIND_JUMP_TABLE = 40 + ATTR_KIND_JUMP_TABLE = 40, + ATTR_KIND_DEREFERENCABLE = 41 }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Argument.h =================================================================== --- include/llvm/IR/Argument.h +++ include/llvm/IR/Argument.h @@ -59,6 +59,10 @@ /// its containing function. bool hasNonNullAttr() const; + /// \brief Return true if this argument has the dereferencable attribute on + /// it in its containing function. + bool hasDereferencableAttr() const; + /// \brief Return true if this argument has the byval attribute on it in its /// containing function. bool hasByValAttr() const; Index: include/llvm/IR/Attributes.h =================================================================== --- include/llvm/IR/Attributes.h +++ include/llvm/IR/Attributes.h @@ -88,6 +88,7 @@ NonLazyBind, ///< Function is called early and/or ///< often, so lazy binding isn't worthwhile NonNull, ///< Pointer is known to be not null + Dereferencable, ///< Pointer is known to be dereferencable NoRedZone, ///< Disable redzone NoReturn, ///< Mark the function as not returning NoUnwind, ///< Function doesn't unwind stack Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -612,6 +612,7 @@ KEYWORD(byval); KEYWORD(inalloca); KEYWORD(cold); + KEYWORD(dereferencable); KEYWORD(inlinehint); KEYWORD(inreg); KEYWORD(jumptable); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1052,6 +1052,7 @@ "invalid use of attribute on a function"); break; case lltok::kw_byval: + case lltok::kw_dereferencable: case lltok::kw_inalloca: case lltok::kw_nest: case lltok::kw_noalias: @@ -1284,6 +1285,7 @@ continue; } case lltok::kw_byval: B.addAttribute(Attribute::ByVal); break; + case lltok::kw_dereferencable: B.addAttribute(Attribute::Dereferencable); break; case lltok::kw_inalloca: B.addAttribute(Attribute::InAlloca); break; case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break; case lltok::kw_nest: B.addAttribute(Attribute::Nest); break; @@ -1341,6 +1343,7 @@ switch (Token) { default: // End of attributes. return HaveError; + case lltok::kw_dereferencable: B.addAttribute(Attribute::Dereferencable); break; case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break; case lltok::kw_noalias: B.addAttribute(Attribute::NoAlias); break; case lltok::kw_nonnull: B.addAttribute(Attribute::NonNull); break; Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -106,6 +106,7 @@ kw_byval, kw_inalloca, kw_cold, + kw_dereferencable, kw_inlinehint, kw_inreg, kw_jumptable, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -588,6 +588,8 @@ return Attribute::NonLazyBind; case bitc::ATTR_KIND_NON_NULL: return Attribute::NonNull; + case bitc::ATTR_KIND_DEREFERENCABLE: + return Attribute::Dereferencable; case bitc::ATTR_KIND_NO_RED_ZONE: return Attribute::NoRedZone; case bitc::ATTR_KIND_NO_RETURN: Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -201,6 +201,8 @@ return bitc::ATTR_KIND_NON_LAZY_BIND; case Attribute::NonNull: return bitc::ATTR_KIND_NON_NULL; + case Attribute::Dereferencable: + return bitc::ATTR_KIND_DEREFERENCABLE; case Attribute::NoRedZone: return bitc::ATTR_KIND_NO_RED_ZONE; case Attribute::NoReturn: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -197,6 +197,8 @@ return "nonlazybind"; if (hasAttribute(Attribute::NonNull)) return "nonnull"; + if (hasAttribute(Attribute::Dereferencable)) + return "dereferencable"; if (hasAttribute(Attribute::NoRedZone)) return "noredzone"; if (hasAttribute(Attribute::NoReturn)) @@ -398,6 +400,7 @@ case Attribute::InAlloca: return 1ULL << 43; case Attribute::NonNull: return 1ULL << 44; case Attribute::JumpTable: return 1ULL << 45; + case Attribute::Dereferencable: return 1ULL << 46; } llvm_unreachable("Unsupported attribute type"); } @@ -1184,6 +1187,7 @@ .addAttribute(Attribute::NoAlias) .addAttribute(Attribute::NoCapture) .addAttribute(Attribute::NonNull) + .addAttribute(Attribute::Dereferencable) .addAttribute(Attribute::ReadNone) .addAttribute(Attribute::ReadOnly) .addAttribute(Attribute::StructRet) Index: lib/IR/Function.cpp =================================================================== --- lib/IR/Function.cpp +++ lib/IR/Function.cpp @@ -84,6 +84,14 @@ hasAttribute(getArgNo()+1, Attribute::NonNull); } +/// hasDereferencableAttr - Return true if this argument has the dereferencable +/// attribute on it in its containing function. +bool Argument::hasDereferencableAttr() const { + if (!getType()->isPointerTy()) return false; + return getParent()->getAttributes(). + hasAttribute(getArgNo()+1, Attribute::Dereferencable); +} + /// hasByValAttr - Return true if this argument has the byval attribute on it /// in its containing function. bool Argument::hasByValAttr() const { Index: lib/IR/Value.cpp =================================================================== --- lib/IR/Value.cpp +++ lib/IR/Value.cpp @@ -15,6 +15,7 @@ #include "LLVMContextImpl.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" +#include "llvm/IR/CallSite.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" @@ -488,9 +489,16 @@ if (const GlobalVariable *GV = dyn_cast(V)) return !GV->hasExternalWeakLinkage(); - // byval arguments are ok. + // byval arguments are okay. Arguments specifically marked as + // dereferencable are okay too. if (const Argument *A = dyn_cast(V)) - return A->hasByValAttr(); + return A->hasByValAttr() || A->hasDereferencableAttr(); + + // Return values from call sites specifically marked as dereferencable are + // also okay. + if (ImmutableCallSite CS = V) + if (CS.paramHasAttr(0, Attribute::Dereferencable)) + return true; // For GEPs, determine if the indexing lands within the allocated object. if (const GEPOperator *GEP = dyn_cast(V)) { Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -229,6 +229,11 @@ unreachable } +define dereferencable i8* @f39(i8* dereferencable %a) { +; CHECK: define dereferencable i8* @f39(i8* dereferencable %a) { + ret i8* %a +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } Index: test/Transforms/LICM/hoist-deref-load.ll =================================================================== --- /dev/null +++ test/Transforms/LICM/hoist-deref-load.ll @@ -0,0 +1,86 @@ +; RUN: opt -S -basicaa -licm < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; This test represents the following function: +; void test1(int * __restrict__ a, int * __restrict__ b, int &c, int n) { +; for (int i = 0; i < n; ++i) +; if (a[i] > 0) +; a[i] = c*b[i]; +; } +; and we want to hoist the load of %c out of the loop. This can be done only +; because the dereferencable attribute is on %c. + +; CHECK-LABEL: @test1 +; CHECK: load i32* %c, align 4 +; CHECK: for.body: + +define void @test1(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32* nocapture readonly nonnull dereferencable %c, i32 %n) #0 { +entry: + %cmp11 = icmp sgt i32 %n, 0 + br i1 %cmp11, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.inc + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32* %a, i64 %indvars.iv + %0 = load i32* %arrayidx, align 4 + %cmp1 = icmp sgt i32 %0, 0 + br i1 %cmp1, label %if.then, label %for.inc + +if.then: ; preds = %for.body + %1 = load i32* %c, align 4 + %arrayidx3 = getelementptr inbounds i32* %b, i64 %indvars.iv + %2 = load i32* %arrayidx3, align 4 + %mul = mul nsw i32 %2, %1 + store i32 %mul, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body, %if.then + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.inc, %entry + ret void +} + +; This is the same as @test1, but without the dereferencable attribute on %c. +; Without this attribute, we should not hoist the load of %c. + +; CHECK-LABEL: @test2 +; CHECK: if.then: +; CHECK: load i32* %c, align 4 + +define void @test2(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32* nocapture readonly nonnull %c, i32 %n) #0 { +entry: + %cmp11 = icmp sgt i32 %n, 0 + br i1 %cmp11, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.inc + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32* %a, i64 %indvars.iv + %0 = load i32* %arrayidx, align 4 + %cmp1 = icmp sgt i32 %0, 0 + br i1 %cmp1, label %if.then, label %for.inc + +if.then: ; preds = %for.body + %1 = load i32* %c, align 4 + %arrayidx3 = getelementptr inbounds i32* %b, i64 %indvars.iv + %2 = load i32* %arrayidx3, align 4 + %mul = mul nsw i32 %2, %1 + store i32 %mul, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body, %if.then + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.inc, %entry + ret void +} + +attributes #0 = { nounwind uwtable } +