[llvm] [InstCombine] Fold constant byte stores to integer stores (PR #196740)

Pedro Lobo via llvm-commits llvm-commits at lists.llvm.org
Sun May 10 05:20:41 PDT 2026


https://github.com/pedroclobo updated https://github.com/llvm/llvm-project/pull/196740

>From cc72f3a6faec17ef42f96812cac3f1344ec533d1 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 21 Mar 2026 16:40:20 +0000
Subject: [PATCH 1/3] Pre-commit tests

---
 llvm/test/Transforms/InstCombine/store.ll | 111 ++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/store.ll b/llvm/test/Transforms/InstCombine/store.ll
index 45f16d874c04e..5f460656c47b5 100644
--- a/llvm/test/Transforms/InstCombine/store.ll
+++ b/llvm/test/Transforms/InstCombine/store.ll
@@ -399,6 +399,117 @@ define void @store_select_with_null_gep(i1 %cond, ptr %p, i64 %off) {
   ret void
 }
 
+define void @store_const_b8(ptr %p) {
+; CHECK-LABEL: @store_const_b8(
+; CHECK-NEXT:    store b8 1, ptr [[P:%.*]], align 1
+; CHECK-NEXT:    ret void
+;
+  store b8 1, ptr %p
+  ret void
+}
+
+define void @store_const_v4b8_zero(ptr %p) {
+; CHECK-LABEL: @store_const_v4b8_zero(
+; CHECK-NEXT:    store <4 x b8> zeroinitializer, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store <4 x b8> zeroinitializer, ptr %p
+  ret void
+}
+
+define void @store_const_v4b8_splat(ptr %p) {
+; CHECK-LABEL: @store_const_v4b8_splat(
+; CHECK-NEXT:    store <4 x b8> splat (b8 5), ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store <4 x b8> splat (b8 5), ptr %p
+  ret void
+}
+
+define void @store_const_v4b8_mixed(ptr %p) {
+; CHECK-LABEL: @store_const_v4b8_mixed(
+; CHECK-NEXT:    store <4 x b8> <b8 1, b8 poison, b8 3, b8 undef>, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store <4 x b8> <b8 1, b8 poison, b8 3, b8 undef>, ptr %p
+  ret void
+}
+
+define void @store_const_v4b8_data(ptr %p) {
+; CHECK-LABEL: @store_const_v4b8_data(
+; CHECK-NEXT:    store <4 x b8> <b8 1, b8 2, b8 3, b8 4>, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store <4 x b8> <b8 1, b8 2, b8 3, b8 4>, ptr %p
+  ret void
+}
+
+define void @store_const_nxv4b8_splat(ptr %p) {
+; CHECK-LABEL: @store_const_nxv4b8_splat(
+; CHECK-NEXT:    store <vscale x 4 x b8> splat (b8 5), ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store <vscale x 4 x b8> splat (b8 5), ptr %p
+  ret void
+}
+
+define void @store_const_nxv4b8_zero(ptr %p) {
+; CHECK-LABEL: @store_const_nxv4b8_zero(
+; CHECK-NEXT:    store <vscale x 4 x b8> zeroinitializer, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store <vscale x 4 x b8> zeroinitializer, ptr %p
+  ret void
+}
+
+define void @store_const_b8_atomic(ptr %p) {
+; CHECK-LABEL: @store_const_b8_atomic(
+; CHECK-NEXT:    store atomic b8 42, ptr [[P:%.*]] unordered, align 1
+; CHECK-NEXT:    ret void
+;
+  store atomic b8 42, ptr %p unordered, align 1
+  ret void
+}
+
+define void @store_const_v4b8_atomic(ptr %p) {
+; CHECK-LABEL: @store_const_v4b8_atomic(
+; CHECK-NEXT:    store atomic <4 x b8> splat (b8 5), ptr [[P:%.*]] unordered, align 4
+; CHECK-NEXT:    ret void
+;
+  store atomic <4 x b8> splat (b8 5), ptr %p unordered, align 4
+  ret void
+}
+
+define void @store_const_b5(ptr %p) {
+; CHECK-LABEL: @store_const_b5(
+; CHECK-NEXT:    store b5 5, ptr [[P:%.*]], align 1
+; CHECK-NEXT:    ret void
+;
+  store b5 5, ptr %p, align 1
+  ret void
+}
+
+; Byte constants cannot represent pointer provenance.
+; Make sure we do not fold the bitcast and change the store type.
+define void @store_const_b64_from_ptr(ptr %p) {
+; CHECK-LABEL: @store_const_b64_from_ptr(
+; CHECK-NEXT:    store b64 bitcast (ptr @Unknown to b64), ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store b64 bitcast (ptr @Unknown to b64), ptr %p
+  ret void
+}
+
+; Only constants should be folded.
+define void @store_nonconst_b8(b8 %v, ptr %p) {
+; CHECK-LABEL: @store_nonconst_b8(
+; CHECK-NEXT:    store b8 [[V:%.*]], ptr [[P:%.*]], align 1
+; CHECK-NEXT:    ret void
+;
+  store b8 %v, ptr %p
+  ret void
+}
+
 !0 = !{!4, !4, i64 0}
 !1 = !{!"omnipotent char", !2}
 !2 = !{!"Simple C/C++ TBAA"}

>From 6334f6ca836bed66e04c533160f9cc0eb6047062 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sun, 26 Apr 2026 21:40:45 +0100
Subject: [PATCH 2/3] [InstCombine] Fold constant byte stores to integer stores

Byte constants are equivalent to integer constants when stored to memory.
Replacing them in store instructions reduces IR differences and enables existing
optimizations over integer constants.
---
 .../InstCombineLoadStoreAlloca.cpp            | 36 +++++++++++++++++++
 llvm/test/Transforms/InstCombine/store.ll     | 20 +++++------
 2 files changed, 46 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 0fe44d615acea..2aeba2248226a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1462,6 +1462,36 @@ static bool equivalentAddressValues(Value *A, Value *B) {
   return false;
 }
 
+/// Recursively rewrite a ConstantByte into an equivalent ConstantInt.
+static Constant *convertConstantByteToConstantInt(Constant *C) {
+  Type *ITy = Type::getIntFromByteType(C->getType());
+  if (auto *CB = dyn_cast<ConstantByte>(C))
+    return ConstantInt::get(ITy, CB->getValue());
+  if (isa<PoisonValue>(C))
+    return PoisonValue::get(ITy);
+  if (isa<UndefValue>(C))
+    return UndefValue::get(ITy);
+  if (C->isNullValue())
+    return Constant::getNullValue(ITy);
+
+  auto *FVTy = dyn_cast<FixedVectorType>(C->getType());
+  if (!FVTy)
+    return nullptr;
+
+  SmallVector<Constant *> Elts;
+  Elts.reserve(FVTy->getNumElements());
+  for (unsigned I = 0, E = FVTy->getNumElements(); I != E; ++I) {
+    Constant *Elt = C->getAggregateElement(I);
+    if (!Elt)
+      return nullptr;
+    Constant *NewElt = convertConstantByteToConstantInt(Elt);
+    if (!NewElt)
+      return nullptr;
+    Elts.push_back(NewElt);
+  }
+  return ConstantVector::get(Elts);
+}
+
 Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) {
   Value *Val = SI.getOperand(0);
   Value *Ptr = SI.getOperand(1);
@@ -1578,6 +1608,12 @@ Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) {
   if (isa<UndefValue>(Val))
     return eraseInstFromFunction(SI);
 
+  // Replace byte constants with integer constants in stores.
+  if (Val->getType()->isByteOrByteVectorTy())
+    if (auto *C = dyn_cast<Constant>(Val))
+      if (Constant *NewC = convertConstantByteToConstantInt(C))
+        return replaceOperand(SI, 0, NewC);
+
   if (!NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace()))
     if (Value *V = simplifyNonNullOperand(Ptr, /*HasDereferenceable=*/true))
       return replaceOperand(SI, 1, V);
diff --git a/llvm/test/Transforms/InstCombine/store.ll b/llvm/test/Transforms/InstCombine/store.ll
index 5f460656c47b5..e2fc0fdcd456a 100644
--- a/llvm/test/Transforms/InstCombine/store.ll
+++ b/llvm/test/Transforms/InstCombine/store.ll
@@ -401,7 +401,7 @@ define void @store_select_with_null_gep(i1 %cond, ptr %p, i64 %off) {
 
 define void @store_const_b8(ptr %p) {
 ; CHECK-LABEL: @store_const_b8(
-; CHECK-NEXT:    store b8 1, ptr [[P:%.*]], align 1
+; CHECK-NEXT:    store i8 1, ptr [[P:%.*]], align 1
 ; CHECK-NEXT:    ret void
 ;
   store b8 1, ptr %p
@@ -410,7 +410,7 @@ define void @store_const_b8(ptr %p) {
 
 define void @store_const_v4b8_zero(ptr %p) {
 ; CHECK-LABEL: @store_const_v4b8_zero(
-; CHECK-NEXT:    store <4 x b8> zeroinitializer, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    store <4 x i8> zeroinitializer, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   store <4 x b8> zeroinitializer, ptr %p
@@ -419,7 +419,7 @@ define void @store_const_v4b8_zero(ptr %p) {
 
 define void @store_const_v4b8_splat(ptr %p) {
 ; CHECK-LABEL: @store_const_v4b8_splat(
-; CHECK-NEXT:    store <4 x b8> splat (b8 5), ptr [[P:%.*]], align 4
+; CHECK-NEXT:    store <4 x i8> splat (i8 5), ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   store <4 x b8> splat (b8 5), ptr %p
@@ -428,7 +428,7 @@ define void @store_const_v4b8_splat(ptr %p) {
 
 define void @store_const_v4b8_mixed(ptr %p) {
 ; CHECK-LABEL: @store_const_v4b8_mixed(
-; CHECK-NEXT:    store <4 x b8> <b8 1, b8 poison, b8 3, b8 undef>, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    store <4 x i8> <i8 1, i8 poison, i8 3, i8 undef>, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   store <4 x b8> <b8 1, b8 poison, b8 3, b8 undef>, ptr %p
@@ -437,7 +437,7 @@ define void @store_const_v4b8_mixed(ptr %p) {
 
 define void @store_const_v4b8_data(ptr %p) {
 ; CHECK-LABEL: @store_const_v4b8_data(
-; CHECK-NEXT:    store <4 x b8> <b8 1, b8 2, b8 3, b8 4>, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    store <4 x i8> <i8 1, i8 2, i8 3, i8 4>, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   store <4 x b8> <b8 1, b8 2, b8 3, b8 4>, ptr %p
@@ -446,7 +446,7 @@ define void @store_const_v4b8_data(ptr %p) {
 
 define void @store_const_nxv4b8_splat(ptr %p) {
 ; CHECK-LABEL: @store_const_nxv4b8_splat(
-; CHECK-NEXT:    store <vscale x 4 x b8> splat (b8 5), ptr [[P:%.*]], align 4
+; CHECK-NEXT:    store <vscale x 4 x i8> splat (i8 5), ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   store <vscale x 4 x b8> splat (b8 5), ptr %p
@@ -455,7 +455,7 @@ define void @store_const_nxv4b8_splat(ptr %p) {
 
 define void @store_const_nxv4b8_zero(ptr %p) {
 ; CHECK-LABEL: @store_const_nxv4b8_zero(
-; CHECK-NEXT:    store <vscale x 4 x b8> zeroinitializer, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    store <vscale x 4 x i8> zeroinitializer, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   store <vscale x 4 x b8> zeroinitializer, ptr %p
@@ -464,7 +464,7 @@ define void @store_const_nxv4b8_zero(ptr %p) {
 
 define void @store_const_b8_atomic(ptr %p) {
 ; CHECK-LABEL: @store_const_b8_atomic(
-; CHECK-NEXT:    store atomic b8 42, ptr [[P:%.*]] unordered, align 1
+; CHECK-NEXT:    store atomic i8 42, ptr [[P:%.*]] unordered, align 1
 ; CHECK-NEXT:    ret void
 ;
   store atomic b8 42, ptr %p unordered, align 1
@@ -473,7 +473,7 @@ define void @store_const_b8_atomic(ptr %p) {
 
 define void @store_const_v4b8_atomic(ptr %p) {
 ; CHECK-LABEL: @store_const_v4b8_atomic(
-; CHECK-NEXT:    store atomic <4 x b8> splat (b8 5), ptr [[P:%.*]] unordered, align 4
+; CHECK-NEXT:    store atomic <4 x i8> splat (i8 5), ptr [[P:%.*]] unordered, align 4
 ; CHECK-NEXT:    ret void
 ;
   store atomic <4 x b8> splat (b8 5), ptr %p unordered, align 4
@@ -482,7 +482,7 @@ define void @store_const_v4b8_atomic(ptr %p) {
 
 define void @store_const_b5(ptr %p) {
 ; CHECK-LABEL: @store_const_b5(
-; CHECK-NEXT:    store b5 5, ptr [[P:%.*]], align 1
+; CHECK-NEXT:    store i5 5, ptr [[P:%.*]], align 1
 ; CHECK-NEXT:    ret void
 ;
   store b5 5, ptr %p, align 1

>From 8f2f14149161db2c35505812cdfff2e49287c0e5 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sun, 10 May 2026 10:12:05 +0100
Subject: [PATCH 3/3] use constant bitcast

---
 .../InstCombineLoadStoreAlloca.cpp            | 39 +++----------------
 1 file changed, 5 insertions(+), 34 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 2aeba2248226a..0e917fb55aa1e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1462,36 +1462,6 @@ static bool equivalentAddressValues(Value *A, Value *B) {
   return false;
 }
 
-/// Recursively rewrite a ConstantByte into an equivalent ConstantInt.
-static Constant *convertConstantByteToConstantInt(Constant *C) {
-  Type *ITy = Type::getIntFromByteType(C->getType());
-  if (auto *CB = dyn_cast<ConstantByte>(C))
-    return ConstantInt::get(ITy, CB->getValue());
-  if (isa<PoisonValue>(C))
-    return PoisonValue::get(ITy);
-  if (isa<UndefValue>(C))
-    return UndefValue::get(ITy);
-  if (C->isNullValue())
-    return Constant::getNullValue(ITy);
-
-  auto *FVTy = dyn_cast<FixedVectorType>(C->getType());
-  if (!FVTy)
-    return nullptr;
-
-  SmallVector<Constant *> Elts;
-  Elts.reserve(FVTy->getNumElements());
-  for (unsigned I = 0, E = FVTy->getNumElements(); I != E; ++I) {
-    Constant *Elt = C->getAggregateElement(I);
-    if (!Elt)
-      return nullptr;
-    Constant *NewElt = convertConstantByteToConstantInt(Elt);
-    if (!NewElt)
-      return nullptr;
-    Elts.push_back(NewElt);
-  }
-  return ConstantVector::get(Elts);
-}
-
 Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) {
   Value *Val = SI.getOperand(0);
   Value *Ptr = SI.getOperand(1);
@@ -1609,10 +1579,11 @@ Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) {
     return eraseInstFromFunction(SI);
 
   // Replace byte constants with integer constants in stores.
-  if (Val->getType()->isByteOrByteVectorTy())
-    if (auto *C = dyn_cast<Constant>(Val))
-      if (Constant *NewC = convertConstantByteToConstantInt(C))
-        return replaceOperand(SI, 0, NewC);
+  Constant *C;
+  if (Val->getType()->isByteOrByteVectorTy() && match(Val, m_ImmConstant(C)))
+    return replaceOperand(
+        SI, 0,
+        ConstantExpr::getBitCast(C, Type::getIntFromByteType(C->getType())));
 
   if (!NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace()))
     if (Value *V = simplifyNonNullOperand(Ptr, /*HasDereferenceable=*/true))



More information about the llvm-commits mailing list