diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1282,6 +1282,13 @@ undefined. Note that this does not refer to padding introduced by the type's storage representation. +``uniqueptr`` + This attribute applies to pointer arguments. The attribute indicates that + the argument is the only pointer referencing the underlying object + :ref:`based ` on the argument before calling the function. + If there are other pointers referencing that underlying object before the + function is called, the behavior is undefined. + .. _gc: Garbage Collector Strategy Names diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -645,6 +645,7 @@ ATTR_KIND_NULL_POINTER_IS_VALID = 67, ATTR_KIND_NOUNDEF = 68, ATTR_KIND_BYREF = 69, + ATTR_KIND_UNIQUEPTR = 70, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -68,6 +68,8 @@ /// Return true if this argument has the byref attribute. bool hasByRefAttr() const; + bool hasUniquePtrAttr() const; + /// Return true if this argument has the swiftself attribute. bool hasSwiftSelfAttr() const; diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -157,6 +157,8 @@ /// Return value is always equal to this argument. def Returned : EnumAttr<"returned">; +def UniquePtr: EnumAttr<"uniqueptr">; + /// Parameter is required to be a trivial constant. def ImmArg : EnumAttr<"immarg">; diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -146,7 +146,7 @@ // then it has not escaped before entering the function. Check if it escapes // inside the function. if (const Argument *A = dyn_cast(V)) - if (A->hasByValAttr() || A->hasNoAliasAttr()) { + if (A->hasByValAttr() || A->hasNoAliasAttr() || A->hasUniquePtrAttr()) { // Note even if the argument is marked nocapture, we still need to check // for copies made inside the function. The nocapture attribute only // specifies that there are no copies made that outlive the function. diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -698,6 +698,7 @@ KEYWORD(zeroext); KEYWORD(immarg); KEYWORD(byref); + KEYWORD(uniqueptr); KEYWORD(type); KEYWORD(opaque); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1390,6 +1390,7 @@ case lltok::kw_swiftself: case lltok::kw_immarg: case lltok::kw_byref: + case lltok::kw_uniqueptr: HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute on a function"); @@ -1690,6 +1691,7 @@ B.addByRefAttr(Ty); continue; } + case lltok::kw_uniqueptr: B.addAttribute(Attribute::UniquePtr); 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; @@ -1811,6 +1813,7 @@ case lltok::kw_swiftself: case lltok::kw_immarg: case lltok::kw_byref: + case lltok::kw_uniqueptr: HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute"); break; diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -241,6 +241,7 @@ kw_zeroext, kw_immarg, kw_byref, + kw_uniqueptr, kw_type, kw_opaque, diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1535,6 +1535,9 @@ return Attribute::NoUndef; case bitc::ATTR_KIND_BYREF: return Attribute::ByRef; + case bitc::ATTR_KIND_UNIQUEPTR: + return Attribute::UniquePtr; + } } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -738,6 +738,9 @@ return bitc::ATTR_KIND_NOUNDEF; case Attribute::ByRef: return bitc::ATTR_KIND_BYREF; + case Attribute::UniquePtr: + return bitc::ATTR_KIND_UNIQUEPTR; + case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -449,6 +449,8 @@ return "immarg"; if (hasAttribute(Attribute::NoUndef)) return "noundef"; + if (hasAttribute(Attribute::UniquePtr)) + return "uniqueptr"; if (hasAttribute(Attribute::ByVal)) { std::string Result; diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -108,6 +108,13 @@ return hasAttribute(Attribute::ByRef); } +bool Argument::hasUniquePtrAttr() const { + if (!getType()->isPointerTy()) + return false; + return hasAttribute(Attribute::UniquePtr); +} + + bool Argument::hasSwiftSelfAttr() const { return getParent()->hasParamAttribute(getArgNo(), Attribute::SwiftSelf); } diff --git a/llvm/test/Transforms/EarlyCSE/uniqueptr.ll b/llvm/test/Transforms/EarlyCSE/uniqueptr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/EarlyCSE/uniqueptr.ll @@ -0,0 +1,50 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -early-cse-memssa -S %s | FileCheck %s + +target datalayout = "e-m:e-i64:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + +%struct = type { i64, i64, i64 } + +; uniqueptr guarantees there are pointers other than %arg pointing to %arg's +; underlying object. So external_effect cannot modify the object and the second +; load can be removed. +define i64 @test_noescape(%struct * uniqueptr %arg) { +; CHECK-LABEL: @test_noescape( +; CHECK-NEXT: [[ARG_F0:%.*]] = getelementptr inbounds [[STRUCT:%.*]], %struct* [[ARG:%.*]], i64 0, i32 0 +; CHECK-NEXT: [[L_0_ARG_F0:%.*]] = load i64, i64* [[ARG_F0]], align 8 +; CHECK-NEXT: call void @external_effect() +; CHECK-NEXT: [[RES:%.*]] = add i64 [[L_0_ARG_F0]], [[L_0_ARG_F0]] +; CHECK-NEXT: ret i64 [[RES]] +; + %arg.f0 = getelementptr inbounds %struct, %struct* %arg, i64 0, i32 0 + %l.0.arg.f0 = load i64, i64* %arg.f0, align 8 + call void @external_effect() + %l.1.arg.f0 = load i64, i64* %arg.f0, align 8 + %res = add i64 %l.1.arg.f0, %l.0.arg.f0 + ret i64 %res +} + +@global.ptr = global %struct* null, align 8 + +; Cannot remove the second load, because %arg escapes before calling +; @external_effect. +define i64 @test_escape_before_call(%struct * uniqueptr %arg) { +; CHECK-LABEL: @test_escape_before_call( +; CHECK-NEXT: [[ARG_F0:%.*]] = getelementptr inbounds [[STRUCT:%.*]], %struct* [[ARG:%.*]], i64 0, i32 0 +; CHECK-NEXT: [[L_0_ARG_F0:%.*]] = load i64, i64* [[ARG_F0]], align 8 +; CHECK-NEXT: store %struct* [[ARG]], %struct** @global.ptr, align 8 +; CHECK-NEXT: call void @external_effect() +; CHECK-NEXT: [[L_1_ARG_F0:%.*]] = load i64, i64* [[ARG_F0]], align 8 +; CHECK-NEXT: [[RES:%.*]] = add i64 [[L_1_ARG_F0]], [[L_0_ARG_F0]] +; CHECK-NEXT: ret i64 [[RES]] +; + %arg.f0 = getelementptr inbounds %struct, %struct* %arg, i64 0, i32 0 + %l.0.arg.f0 = load i64, i64* %arg.f0, align 8 + store %struct* %arg, %struct** @global.ptr + call void @external_effect() + %l.1.arg.f0 = load i64, i64* %arg.f0, align 8 + %res = add i64 %l.1.arg.f0, %l.0.arg.f0 + ret i64 %res +} + +declare void @external_effect()