Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions llvm/docs/LangRef.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Comment thread
antoniofrighetto marked this conversation as resolved.
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`
Expand Down Expand Up @@ -12908,7 +12931,7 @@ address width of 32 bits (`p1:64:64:64:32` {ref}`datalayout string<langref_datal
##### Syntax:

```
<result> = inttoptr <ty> <value> to <ty2>[, !dereferenceable !<deref_bytes_node>][, !dereferenceable_or_null !<deref_bytes_node>][, !nofree !<empty_node>] ; yields ty2
<result> = inttoptr <ty> <value> to <ty2>[, !dereferenceable !<deref_bytes_node>][, !dereferenceable_or_null !<deref_bytes_node>][, !nofreeobj !<empty_node>] ; yields ty2
```

##### Overview:
Expand All @@ -12932,10 +12955,11 @@ metadata name `<deref_bytes_node>` 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
`<empty_node>` 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:

Expand Down
5 changes: 5 additions & 0 deletions llvm/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ enum AttributeKindCodes {
ATTR_KIND_NOOUTLINE = 107,
ATTR_KIND_FLATTEN = 108,
ATTR_KIND_NOIPA = 109,
ATTR_KIND_NOFREEOBJ = 110,
};

enum ComdatSelectionKindCodes {
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;

Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/IR/FixedMetadataKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 10 additions & 1 deletion llvm/lib/IR/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IntToPtrInst>(this);
ITP && ITP->hasMetadata(LLVMContext::MD_nofree))
ITP && ITP->hasMetadata(LLVMContext::MD_nofreeobj))
return false;

if (auto *CB = dyn_cast<CallBase>(this))
if (CB->hasRetAttr(Attribute::NoFreeObj))
return false;

const Function *F = nullptr;
if (auto *I = dyn_cast<Instruction>(this))
F = I->getFunction();
Expand Down
15 changes: 8 additions & 7 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class Verifier : public InstVisitor<Verifier>, 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);
Expand Down Expand Up @@ -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<IntToPtrInst>(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<IntToPtrInst>(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) {
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
17 changes: 17 additions & 0 deletions llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions llvm/test/Bitcode/attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Transforms/LICM/hoist-speculatable-load.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand Down
16 changes: 16 additions & 0 deletions llvm/test/Verifier/nofree.ll
Original file line number Diff line number Diff line change
@@ -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
}
8 changes: 4 additions & 4 deletions llvm/test/Verifier/nofree_metadata.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading