[llvm] [EarlyCSE] Support memset loads (PR #194268)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 30 14:03:52 PDT 2026


https://github.com/nataliakokoromyti updated https://github.com/llvm/llvm-project/pull/194268

>From 13ef31156bdf7c8c638c7d99c31b48cff8759edd Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 27 Apr 2026 12:25:22 -0700
Subject: [PATCH 1/3] support memset loads

---
 llvm/lib/Transforms/Scalar/EarlyCSE.cpp      | 34 +++++++++-
 llvm/test/Transforms/EarlyCSE/memset-load.ll | 65 ++++++++++++++++++++
 2 files changed, 96 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Transforms/EarlyCSE/memset-load.ll

diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index ff3d55e5747e5..1950ff6978fab 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -821,6 +821,13 @@ class EarlyCSE {
             Info.IsVolatile = false;
             break;
           }
+        } else if (isa<MemSetInst>(Inst)) {
+          auto *MI = cast<MemIntrinsic>(Inst);
+          Info.PtrVal = MI->getDest();
+          Info.MatchingId = 0;
+          Info.ReadMem = false;
+          Info.WriteMem = true;
+          Info.IsVolatile = MI->isVolatile();
         }
       }
     }
@@ -885,9 +892,9 @@ class EarlyCSE {
     // field in the MemIntrinsicInfo structure.  That field contains
     // non-negative values only.
     int getMatchingId() const {
-      if (IntrID != 0)
-        return Info.MatchingId;
-      return -1;
+      if (IntrID == 0)
+        return -1;
+      return Info.MatchingId;
     }
 
     Value *getPointerOperand() const {
@@ -1226,6 +1233,27 @@ Value *EarlyCSE::getMatchingValue(LoadValue &InVal, ParseMemoryInst &MemInst,
                                   unsigned CurrentGeneration) {
   if (InVal.DefInst == nullptr)
     return nullptr;
+  if (auto *MSI = dyn_cast<MemSetInst>(InVal.DefInst)) {
+    if (!MemInst.isLoad() || MemInst.isVolatile() || !MemInst.isUnordered())
+      return nullptr;
+    auto *Val = dyn_cast<ConstantInt>(MSI->getValue());
+    if (!Val || !Val->isZero())
+      return nullptr;
+    auto Len = MSI->getLengthInBytes();
+    if (!Len)
+      return nullptr;
+    TypeSize LoadSize = SQ.DL.getTypeStoreSize(MemInst.getValueType());
+    if (LoadSize.isScalable() || Len->ult(LoadSize.getFixedValue()))
+      return nullptr;
+    // Byte-wise memset does not preserve out-of-band external pointer state.
+    if (SQ.DL.hasExternalState(MemInst.getValueType()))
+      return nullptr;
+    if (!isOperatingOnInvariantMemAt(MemInst.get(), InVal.Generation) &&
+        !isSameMemGeneration(InVal.Generation, CurrentGeneration, InVal.DefInst,
+                             MemInst.get()))
+      return nullptr;
+    return Constant::getNullValue(MemInst.getValueType());
+  }
   if (InVal.MatchingId != MemInst.getMatchingId())
     return nullptr;
   // We don't yet handle removing loads with ordering of any kind.
diff --git a/llvm/test/Transforms/EarlyCSE/memset-load.ll b/llvm/test/Transforms/EarlyCSE/memset-load.ll
new file mode 100644
index 0000000000000..adb28b482c7f0
--- /dev/null
+++ b/llvm/test/Transforms/EarlyCSE/memset-load.ll
@@ -0,0 +1,65 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='early-cse<memssa>' < %s | FileCheck %s
+
+target datalayout = "pe1:64:64:64:32"
+
+define ptr @load_from_zero_memset(ptr %p) {
+; CHECK-LABEL: define ptr @load_from_zero_memset(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[P]], i8 0, i64 8, i1 false)
+; CHECK-NEXT:    ret ptr null
+;
+entry:
+  call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 8, i1 false)
+  %v = load ptr, ptr %p, align 8
+  ret ptr %v
+}
+
+define ptr @load_from_nonzero_memset(ptr %p) {
+; CHECK-LABEL: define ptr @load_from_nonzero_memset(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[P]], i8 1, i64 8, i1 false)
+; CHECK-NEXT:    [[V:%.*]] = load ptr, ptr [[P]], align 8
+; CHECK-NEXT:    ret ptr [[V]]
+;
+entry:
+  call void @llvm.memset.p0.i64(ptr %p, i8 1, i64 8, i1 false)
+  %v = load ptr, ptr %p, align 8
+  ret ptr %v
+}
+
+define ptr @load_from_zero_memset_with_clobber(ptr %p) {
+; CHECK-LABEL: define ptr @load_from_zero_memset_with_clobber(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[P]], i8 0, i64 8, i1 false)
+; CHECK-NEXT:    call void @clobber(ptr [[P]])
+; CHECK-NEXT:    [[V:%.*]] = load ptr, ptr [[P]], align 8
+; CHECK-NEXT:    ret ptr [[V]]
+;
+entry:
+  call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 8, i1 false)
+  call void @clobber(ptr %p)
+  %v = load ptr, ptr %p, align 8
+  ret ptr %v
+}
+
+define ptr addrspace(1) @load_from_zero_memset_external(ptr addrspace(1) %p) {
+; CHECK-LABEL: define ptr addrspace(1) @load_from_zero_memset_external(
+; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @llvm.memset.p1.i64(ptr addrspace(1) [[P]], i8 0, i64 8, i1 false)
+; CHECK-NEXT:    [[V:%.*]] = load ptr addrspace(1), ptr addrspace(1) [[P]], align 8
+; CHECK-NEXT:    ret ptr addrspace(1) [[V]]
+;
+entry:
+  call void @llvm.memset.p1.i64(ptr addrspace(1) %p, i8 0, i64 8, i1 false)
+  %v = load ptr addrspace(1), ptr addrspace(1) %p, align 8
+  ret ptr addrspace(1) %v
+}
+
+declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
+declare void @llvm.memset.p1.i64(ptr addrspace(1) nocapture writeonly, i8, i64, i1 immarg)
+declare void @clobber(ptr)

>From ce54872a4049e1cb467d540f0d9d2f5c67a5e487 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Thu, 30 Apr 2026 13:56:18 -0700
Subject: [PATCH 2/3] restore getMatchingId

---
 llvm/lib/Transforms/Scalar/EarlyCSE.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index 1950ff6978fab..728ca2864abdb 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -892,9 +892,9 @@ class EarlyCSE {
     // field in the MemIntrinsicInfo structure.  That field contains
     // non-negative values only.
     int getMatchingId() const {
-      if (IntrID == 0)
-        return -1;
-      return Info.MatchingId;
+      if (IntrID != 0)
+        return Info.MatchingId;
+      return -1;
     }
 
     Value *getPointerOperand() const {

>From 406cae26fb8e4d00948bf59447520ab19e102096 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Thu, 30 Apr 2026 14:03:42 -0700
Subject: [PATCH 3/3] Update llvm/lib/Transforms/Scalar/EarlyCSE.cpp

Co-authored-by: Nikita Popov <github at npopov.com>
---
 llvm/lib/Transforms/Scalar/EarlyCSE.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index 728ca2864abdb..d72a5c4bd70e0 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -822,7 +822,7 @@ class EarlyCSE {
             break;
           }
         } else if (isa<MemSetInst>(Inst)) {
-          auto *MI = cast<MemIntrinsic>(Inst);
+        } else if (auto *MI = dyn_cast<MemSetInst>(Inst)) {
           Info.PtrVal = MI->getDest();
           Info.MatchingId = 0;
           Info.ReadMem = false;



More information about the llvm-commits mailing list