Skip to content

[IR] Add nofreeobj parameter/return attribute#206445

Open
nikic wants to merge 4 commits into
llvm:mainfrom
nikic:nofreeobj
Open

[IR] Add nofreeobj parameter/return attribute#206445
nikic wants to merge 4 commits into
llvm:mainfrom
nikic:nofreeobj

Conversation

@nikic

@nikic nikic commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Alternative to #203491.

This adds a nofreeobj attribute, which says that the underlying object cannot be freed. For arguments during the execution of the function, for returns from that point forward. This is unlike nofree which only prevents freed through the specific pointer, and as such requires noalias to be useful for function-internal reasoning.

This means that the old dereferenceable becomes strictly equivalent to dereferenceable + nofreeobj under dereferenceable-at-point semantics.

Also rename !nofree to !nofreeobj, because it's semantics match the nofreeobj attribute, not nofree.

The motivation here is intrinsics like @llvm.amdgcn.kernarg.segment.ptr which are dereferenceable + nofreeobj, but not noalias.

@llvmorg-github-actions llvmorg-github-actions Bot added llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Jun 29, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-llvm-analysis

Author: Nikita Popov (nikic)

Changes

Alternative to #203491.

This adds a nofreeobj attribute, which says that the underlying object cannot be freed. For arguments during the execution of the function, for returns from that point forward. This is unlike nofree which only prevents freed through the specific pointer, and as such requires noalias to be useful for function-internal reasoning.

This means that the old dereferenceable becomes strictly equivalent to dereferenceable + nofreeobj under dereferenceable-at-point semantics.

Also rename !nofree to !nofreeobj, because it's semantics match the nofreeobj attribute, not nofree.

The motivation here is intrinsincs like @<!-- -->llvm.amdgcn.kernarg.segment.ptr which are dereferenceable + nofreeobj, but not noalias.


Full diff: https://github.com/llvm/llvm-project/pull/206445.diff

16 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+16-4)
  • (modified) llvm/docs/ReleaseNotes.md (+5)
  • (modified) llvm/include/llvm/Bitcode/LLVMBitCodes.h (+1)
  • (modified) llvm/include/llvm/IR/Attributes.td (+4)
  • (modified) llvm/include/llvm/IR/FixedMetadataKinds.def (+1-1)
  • (modified) llvm/lib/Bitcode/Reader/BitcodeReader.cpp (+2)
  • (modified) llvm/lib/Bitcode/Writer/BitcodeWriter.cpp (+2)
  • (modified) llvm/lib/IR/Attributes.cpp (+3-1)
  • (modified) llvm/lib/IR/Value.cpp (+10-1)
  • (modified) llvm/lib/IR/Verifier.cpp (+8-7)
  • (modified) llvm/lib/Transforms/Utils/CodeExtractor.cpp (+1)
  • (modified) llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll (+17)
  • (modified) llvm/test/Bitcode/attributes.ll (+5)
  • (modified) llvm/test/Transforms/LICM/hoist-speculatable-load.ll (+2-2)
  • (added) llvm/test/Verifier/nofree.ll (+16)
  • (modified) llvm/test/Verifier/nofree_metadata.ll (+4-4)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 5640eec987133..f8b1ced5837d0 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1574,6 +1574,17 @@ 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 (where "during" is in
+    the same sense as for ``nofree``).
+
+    On return values, this indicates that the underlying object of the return
+    value cannot be freed at all after this point.
+
+    Unlike ``nofree``, it is not possible to free the underlying object through
+    a different pointer either.
+
 .. _nest:
 
 ``nest``
@@ -13341,7 +13352,7 @@ 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:
 """""""""
@@ -13366,10 +13377,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 underlying object of the pointer will not be freed after
+this point.
 
 Semantics:
 """"""""""
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index d1448eb469614..0544fe5fafd2d 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -102,6 +102,11 @@ Makes programs 10x faster by doing Special New Thing.
   outlining. Add the `noinline` and `nooutline` attributes as well in cases
   where inlining and outlining should additionally be disabled.
 
+* 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
 
 * Removed ``Constant::isZeroValue``. It was functionally identical to
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index af4cac996a78c..97c47b09ab9ad 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -821,6 +821,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 552c1f60f2d38..ece7d8ae87b56 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 f6366061c9b52..6ab187db5e9ae 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2152,6 +2152,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 c4058fe66ff0b..430878dbfa839 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -862,6 +862,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 d3f90bd81bbe2..6bbb601a6c08e 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<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();
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 648446555793b..12d16565b11f1 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -356,7 +356,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);
@@ -5179,11 +5179,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) {
@@ -5787,8 +5788,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 975294ab00f73..c612135f44fc2 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
 }

Comment thread llvm/docs/LangRef.rst Outdated
the same sense as for ``nofree``).

On return values, this indicates that the underlying object of the return
value cannot be freed at all after this point.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you like this better:

Suggested change
value cannot be freed at all after this point.
value cannot be freed from this point forward, throughout program's execution.

Comment thread llvm/docs/LangRef.rst Outdated
This is not a valid attribute for return values.

``nofreeobj``
On arguments, this indicates that the underlying object of the argument

@antoniofrighetto antoniofrighetto Jun 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention explicitly nofreeobj strictly implies nofree for arguments?

Comment thread llvm/docs/LangRef.rst Outdated
value cannot be freed at all after this point.

Unlike ``nofree``, it is not possible to free the underlying object through
a different pointer either.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on #195658 (comment), should we be explicit and mention the interaction with noalias (nofreeobj being primarily useful in cases noalias does not apply)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some discussion on the relationship between nofreeobj and nofree/noalias.

@nikic

nikic commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Rebased over the LangRef migration to Markdown.

@efriedma-quic efriedma-quic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need autoupgrade? I guess if existing IR uses !nofree, we just ignore it, so it's not strictly necessary.

@nikic

nikic commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Do we need autoupgrade? I guess if existing IR uses !nofree, we just ignore it, so it's not strictly necessary.

Right -- we effectively drop the metadata, which is safe, just suboptimal. Looking back at #153149 this was added for use by a GPU compiler, so I doubt bitcode upgrade is relevant. It would be easy to add though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding llvm:ir llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants