diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index f555ca543ffdc..3dbae7ae4561f 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -1587,6 +1587,29 @@ Currently, only the following parameter attributes are defined: This is not a valid attribute for return values. +`nofreeobj` +: On arguments, this indicates that the underlying object of the argument + cannot be freed during the execution of the function. More precisely, any + free of the underlying object must happen-before function entry or function + exit must happen-before the free, otherwise the behavior is undefined. + + On return values, this indicates that the underlying object of the return + value cannot be freed from this point forward. More precisely, any free + of the underlying object must happen-before the function returns, otherwise + the behavior is undefined. + + Unlike `nofree`, it is not possible to free the underlying object through + a different pointer either. + + `nofreeobj` on arguments implies `nofree`. `nofreeobj` is primarily useful + in cases where it's not possible to mark the argument as `noalias`. + The combination of `nofree` and `noalias` does *not* imply `nofreeobj` + by itself, because it still allows freeing the underlying object through + a different pointer, as long as the object is never accessed through the + `noalias` pointer. However, it does imply `nofreeobj` if such an access + exists, for example in the form of the implied read of a `dereferenceable` + attribute. + (nest)= `nest` @@ -12908,7 +12931,7 @@ address width of 32 bits (`p1:64:64:64:32` {ref}`datalayout string = inttoptr to [, !dereferenceable !][, !dereferenceable_or_null !][, !nofree !] ; yields ty2 + = inttoptr to [, !dereferenceable !][, !dereferenceable_or_null !][, !nofreeobj !] ; yields ty2 ``` ##### Overview: @@ -12932,10 +12955,11 @@ metadata name `` corresponding to a metadata node with one `i64` entry. See `dereferenceable_or_null` metadata. -The optional `!nofree` metadata must reference a single metadata name +The optional `!nofreeobj` metadata must reference a single metadata name `` corresponding to a metadata node with no entries. -The existence of the `!nofree` metadata on the instruction tells the optimizer -that the memory pointed by the pointer will not be freed after this point. +The existence of the `!nofreeobj` metadata on the instruction tells the +optimizer that the memory pointed by the pointer will not be freed after this +point. ##### Semantics: diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index af037473e3653..6c53cf2cc7e2d 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -54,6 +54,11 @@ Makes programs 10x faster by doing Special New Thing. ### Changes to the LLVM IR +* Added `nofreeobj` attribute for attributes and returns, which forbids + freeing the underlying object (as opposed to only frees through that specific + pointer). Renamed `!nofree` metadata to `!nofreeobj`, as it has the same + semantics. + ### Changes to LLVM infrastructure ### Changes to building LLVM diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 358f9a65a80af..226be19624db0 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -826,6 +826,7 @@ enum AttributeKindCodes { ATTR_KIND_NOOUTLINE = 107, ATTR_KIND_FLATTEN = 108, ATTR_KIND_NOIPA = 109, + ATTR_KIND_NOFREEOBJ = 110, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 4e45100b54d38..ea523cad69cd5 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -201,8 +201,12 @@ def NoDuplicate : EnumAttr<"noduplicate", IntersectPreserve, [FnAttr]>; def NoExt : EnumAttr<"noext", IntersectPreserve, [ParamAttr, RetAttr]>; /// Function does not deallocate memory. +/// Argument cannot be freed based on the argument pointer. def NoFree : EnumAttr<"nofree", IntersectAnd, [FnAttr, ParamAttr]>; +/// Underlying object of argument/return cannot be freed. +def NoFreeObj : EnumAttr<"nofreeobj", IntersectAnd, [ParamAttr, RetAttr]>; + /// Argument is dead if the call unwinds. def DeadOnUnwind : EnumAttr<"dead_on_unwind", IntersectAnd, [ParamAttr]>; diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index d10d16075fecb..65c488f50064c 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -54,7 +54,7 @@ LLVM_FIXED_MD_KIND(MD_coro_outside_frame, "coro.outside.frame", 39) LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40) LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41) LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42) -LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43) +LLVM_FIXED_MD_KIND(MD_nofreeobj, "nofreeobj", 43) LLVM_FIXED_MD_KIND(MD_captures, "captures", 44) LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45) LLVM_FIXED_MD_KIND(MD_implicit_ref, "implicit.ref", 46) diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index c57323ed41525..aaccc9de11d49 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2176,6 +2176,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::NoDuplicate; case bitc::ATTR_KIND_NOFREE: return Attribute::NoFree; + case bitc::ATTR_KIND_NOFREEOBJ: + return Attribute::NoFreeObj; case bitc::ATTR_KIND_NO_IMPLICIT_FLOAT: return Attribute::NoImplicitFloat; case bitc::ATTR_KIND_NO_INLINE: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index f4ce522c23b8e..9378846900570 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -866,6 +866,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_NO_DUPLICATE; case Attribute::NoFree: return bitc::ATTR_KIND_NOFREE; + case Attribute::NoFreeObj: + return bitc::ATTR_KIND_NOFREEOBJ; case Attribute::NoImplicitFloat: return bitc::ATTR_KIND_NO_IMPLICIT_FLOAT; case Attribute::NoInline: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 4087b25951a1c..e69b848c0eb66 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -2520,7 +2520,9 @@ AttributeMask AttributeFuncs::typeIncompatible(Type *Ty, AttributeSet AS, .addAttribute(Attribute::DeadOnUnwind) .addAttribute(Attribute::Initializes) .addAttribute(Attribute::Captures) - .addAttribute(Attribute::DeadOnReturn); + .addAttribute(Attribute::DeadOnReturn) + .addAttribute(Attribute::NoFree) + .addAttribute(Attribute::NoFreeObj); if (ASK & ASK_UNSAFE_TO_DROP) Incompatible.addAttribute(Attribute::Nest) .addAttribute(Attribute::SwiftError) diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp index 6d0170dd23e75..50882ec0d7b10 100644 --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -861,12 +861,21 @@ bool Value::canBeFreed() const { // another pointer to the same allocation. Readonly implies nofree. if ((A->hasNoFreeAttr() || A->onlyReadsMemory()) && A->hasNoAliasAttr()) return false; + + // nofreeobj means that the underlying object cannot be freed, even + // through a different pointer. + if (A->hasAttribute(Attribute::NoFreeObj)) + return false; } if (auto *ITP = dyn_cast(this); - ITP && ITP->hasMetadata(LLVMContext::MD_nofree)) + ITP && ITP->hasMetadata(LLVMContext::MD_nofreeobj)) return false; + if (auto *CB = dyn_cast(this)) + if (CB->hasRetAttr(Attribute::NoFreeObj)) + return false; + const Function *F = nullptr; if (auto *I = dyn_cast(this)) F = I->getFunction(); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index fa0fe7a2e6092..09949bc8cff42 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -357,7 +357,7 @@ class Verifier : public InstVisitor, VerifierSupport { void visitNoFPClassMetadata(Instruction &I, MDNode *Range, Type *Ty); void visitNoaliasAddrspaceMetadata(Instruction &I, MDNode *Range, Type *Ty); void visitDereferenceableMetadata(Instruction &I, MDNode *MD); - void visitNofreeMetadata(Instruction &I, MDNode *MD); + void visitNoFreeObjMetadata(Instruction &I, MDNode *MD); void visitProfMetadata(Instruction &I, MDNode *MD); void visitCallStackMetadata(MDNode *MD); void visitMemProfMetadata(Instruction &I, MDNode *MD); @@ -5229,11 +5229,12 @@ void Verifier::visitDereferenceableMetadata(Instruction& I, MDNode* MD) { &I); } -void Verifier::visitNofreeMetadata(Instruction &I, MDNode *MD) { - Check(I.getType()->isPointerTy(), "nofree applies only to pointer types", &I); - Check((isa(I)), "nofree applies only to inttoptr instruction", +void Verifier::visitNoFreeObjMetadata(Instruction &I, MDNode *MD) { + Check(I.getType()->isPointerTy(), "nofreeobj applies only to pointer types", &I); - Check(MD->getNumOperands() == 0, "nofree metadata must be empty", &I); + Check((isa(I)), + "nofreeobj applies only to inttoptr instruction", &I); + Check(MD->getNumOperands() == 0, "nofreeobj metadata must be empty", &I); } void Verifier::visitProfMetadata(Instruction &I, MDNode *MD) { @@ -5842,8 +5843,8 @@ void Verifier::visitInstruction(Instruction &I) { if (MDNode *MD = I.getMetadata(LLVMContext::MD_dereferenceable_or_null)) visitDereferenceableMetadata(I, MD); - if (MDNode *MD = I.getMetadata(LLVMContext::MD_nofree)) - visitNofreeMetadata(I, MD); + if (MDNode *MD = I.getMetadata(LLVMContext::MD_nofreeobj)) + visitNoFreeObjMetadata(I, MD); if (MDNode *TBAA = I.getMetadata(LLVMContext::MD_tbaa)) TBAAVerifyHelper.visitTBAAMetadata(&I, TBAA); diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 4c33848dacf51..745f47aa561ee 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -1052,6 +1052,7 @@ Function *CodeExtractor::constructFunctionDeclaration( case Attribute::Range: case Attribute::Initializes: case Attribute::NoExt: + case Attribute::NoFreeObj: // These are not really attributes. case Attribute::None: case Attribute::EndAttrKinds: diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll index f7e69b0c1f04f..09c8e74a5c675 100644 --- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll +++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll @@ -301,6 +301,23 @@ define void @infer_missing_noalias2(ptr dereferenceable(8) readonly %p) { ret void } +; CHECK-LABEL: 'nofreeobj_arg' +; CHECK: %p +define void @nofreeobj_arg(ptr dereferenceable(8) nofreeobj %p) { + call void @mayfree() + %v = load i32, ptr %p + ret void +} + +; CHECK-LABEL: 'nofreeobj_ret' +; CHECK: %p +define void @nofreeobj_ret() { + %p = call dereferenceable(8) nofreeobj ptr @foo() + call void @mayfree() + %v = load i32, ptr %p + ret void +} + ; Just check that we don't crash. ; CHECK-LABEL: 'opaque_type_crasher' define void @opaque_type_crasher(ptr dereferenceable(16) %a, i1 %arg) { diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll index f696f2dd12323..3d5272f053251 100644 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -597,6 +597,11 @@ define void @noipa() noipa { ret void } +; CHECK: define nofreeobj ptr @nofreeobj(ptr nofreeobj %p) +define nofreeobj ptr @nofreeobj(ptr nofreeobj %p) { + ret ptr %p +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { memory(none) } diff --git a/llvm/test/Transforms/LICM/hoist-speculatable-load.ll b/llvm/test/Transforms/LICM/hoist-speculatable-load.ll index 31236e8f29d60..ab754a1f9a9d6 100644 --- a/llvm/test/Transforms/LICM/hoist-speculatable-load.ll +++ b/llvm/test/Transforms/LICM/hoist-speculatable-load.ll @@ -4,7 +4,7 @@ define void @f(i32 %ptr_i, ptr %ptr2, i1 %cond) { ; CHECK-LABEL: @f( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[PTR_I:%.*]] to ptr, !nofree [[META0:![0-9]+]] +; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[PTR_I:%.*]] to ptr, !nofreeobj [[META0:![0-9]+]] ; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i32 16), "dereferenceable"(ptr [[PTR]], i32 16) ] ; CHECK-NEXT: br i1 [[COND:%.*]], label [[FOR_BODY_LR_PH:%.*]], label [[IF0:%.*]] ; CHECK: if0: @@ -27,7 +27,7 @@ define void @f(i32 %ptr_i, ptr %ptr2, i1 %cond) { ; CHECK-NEXT: ret void ; entry: - %ptr = inttoptr i32 %ptr_i to ptr, !nofree !{} + %ptr = inttoptr i32 %ptr_i to ptr, !nofreeobj !{} call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i32 16), "dereferenceable"(ptr %ptr, i32 16) ] br i1 %cond, label %for.body.lr.ph, label %if0 diff --git a/llvm/test/Verifier/nofree.ll b/llvm/test/Verifier/nofree.ll new file mode 100644 index 0000000000000..2775613c75426 --- /dev/null +++ b/llvm/test/Verifier/nofree.ll @@ -0,0 +1,16 @@ +; RUN: not opt -S < %s 2>&1 | FileCheck %s + +; CHECK: Attribute 'nofree' applied to incompatible type! +define void @test(i32 nofree %p) { + ret void +} + +; CHECK: Attribute 'nofreeobj' applied to incompatible type! +define void @test2(i32 nofreeobj %p) { + ret void +} + +; CHECK: Attribute 'nofreeobj' applied to incompatible type! +define nofreeobj i32 @test3() { + ret i32 0 +} diff --git a/llvm/test/Verifier/nofree_metadata.ll b/llvm/test/Verifier/nofree_metadata.ll index e04f5b9f1c522..40a4d3e28bcd1 100644 --- a/llvm/test/Verifier/nofree_metadata.ll +++ b/llvm/test/Verifier/nofree_metadata.ll @@ -2,14 +2,14 @@ declare ptr @dummy() -; CHECK: nofree applies only to inttoptr instruction +; CHECK: nofreeobj applies only to inttoptr instruction define void @test_not_inttoptr() { - call ptr @dummy(), !nofree !{} + call ptr @dummy(), !nofreeobj !{} ret void } -; CHECK: nofree metadata must be empty +; CHECK: nofreeobj metadata must be empty define void @test_invalid_arg(i32 %p) { - inttoptr i32 %p to ptr, !nofree !{i32 0} + inttoptr i32 %p to ptr, !nofreeobj !{i32 0} ret void }