[llvm] [InferAlign] Infer alignment for memory intrinsics (PR #217172)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 21 13:55:40 PDT 2026


https://github.com/SomeFlyingThing updated https://github.com/llvm/llvm-project/pull/217172

>From 5256dd2d510b55fd4be3890cdd28f894d257ae3e Mon Sep 17 00:00:00 2001
From: SomeFlyingThing <306498559+SomeFlyingThing at users.noreply.github.com>
Date: Wed, 19 Aug 2026 11:56:31 +1200
Subject: [PATCH 1/2] [InferAlignment] Infer alignment for memory intrinsics

Use known-bits and base-pointer alignment information to strengthen the
source and destination alignment attributes of memory intrinsics.

This extends InferAlignment to memcpy, memmove, memset, and their inline
variants through the MemIntrinsic hierarchy.
---
 llvm/lib/Transforms/Scalar/InferAlignment.cpp | 22 +++++++++++++
 .../InferAlignment/memintrinsics.ll           | 31 +++++++++++++++++++
 2 files changed, 53 insertions(+)
 create mode 100644 llvm/test/Transforms/InferAlignment/memintrinsics.ll

diff --git a/llvm/lib/Transforms/Scalar/InferAlignment.cpp b/llvm/lib/Transforms/Scalar/InferAlignment.cpp
index 92f23eaccd673f..908d92be7c16e0 100644
--- a/llvm/lib/Transforms/Scalar/InferAlignment.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAlignment.cpp
@@ -69,6 +69,28 @@ static bool tryToImproveAlign(
   if (!II)
     return false;
 
+  if (auto *MI = dyn_cast<MemIntrinsic>(II)) {
+    bool Changed = false;
+
+    Align OldDestAlign = MI->getDestAlign().valueOrOne();
+    Align NewDestAlign = Fn(MI->getRawDest(), OldDestAlign, Align(1));
+    if (NewDestAlign > OldDestAlign) {
+      MI->setDestAlignment(NewDestAlign);
+      Changed = true;
+    }
+
+    if (auto *MTI = dyn_cast<MemTransferInst>(MI)) {
+      Align OldSourceAlign = MTI->getSourceAlign().valueOrOne();
+      Align NewSourceAlign = Fn(MTI->getRawSource(), OldSourceAlign, Align(1));
+      if (NewSourceAlign > OldSourceAlign) {
+        MTI->setSourceAlignment(NewSourceAlign);
+        Changed = true;
+      }
+    }
+
+    return Changed;
+  }
+
   // TODO: Handle more memory intrinsics.
   switch (II->getIntrinsicID()) {
   case Intrinsic::masked_load:
diff --git a/llvm/test/Transforms/InferAlignment/memintrinsics.ll b/llvm/test/Transforms/InferAlignment/memintrinsics.ll
new file mode 100644
index 00000000000000..4354f1d4a72af9
--- /dev/null
+++ b/llvm/test/Transforms/InferAlignment/memintrinsics.ll
@@ -0,0 +1,31 @@
+; RUN: opt < %s -passes=infer-alignment -S | FileCheck %s
+
+declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)
+declare void @llvm.memmove.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)
+declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1)
+
+define void @memcpy(i64 %len) {
+; CHECK-LABEL: define void @memcpy(
+; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 32 %dst, ptr align 16 %src, i64 %len, i1 false)
+  %dst = alloca [64 x i8], align 32
+  %src = alloca [64 x i8], align 16
+  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %len, i1 false)
+  ret void
+}
+
+define void @memmove(i64 %len) {
+; CHECK-LABEL: define void @memmove(
+; CHECK: call void @llvm.memmove.p0.p0.i64(ptr align 64 %dst, ptr align 8 %src, i64 %len, i1 false)
+  %dst = alloca [64 x i8], align 64
+  %src = alloca [64 x i8], align 8
+  call void @llvm.memmove.p0.p0.i64(ptr %dst, ptr %src, i64 %len, i1 false)
+  ret void
+}
+
+define void @memset(i64 %len) {
+; CHECK-LABEL: define void @memset(
+; CHECK: call void @llvm.memset.p0.i64(ptr align 32 %dst, i8 0, i64 %len, i1 false)
+  %dst = alloca [64 x i8], align 32
+  call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 %len, i1 false)
+  ret void
+}

>From d42f2cc523ee804336253e00f3c375cdbdf135f4 Mon Sep 17 00:00:00 2001
From: SomeFlyingThing <306498559+SomeFlyingThing at users.noreply.github.com>
Date: Sat, 22 Aug 2026 08:55:27 +1200
Subject: [PATCH 2/2] [InferAlign] Generalize intrinsic alignment inference

---
 llvm/lib/Transforms/Scalar/InferAlignment.cpp | 54 ++++++-------------
 .../InferAlignment/memintrinsics.ll           | 21 ++++++--
 2 files changed, 33 insertions(+), 42 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/InferAlignment.cpp b/llvm/lib/Transforms/Scalar/InferAlignment.cpp
index 908d92be7c16e0..9addfbd725389c 100644
--- a/llvm/lib/Transforms/Scalar/InferAlignment.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAlignment.cpp
@@ -69,51 +69,27 @@ static bool tryToImproveAlign(
   if (!II)
     return false;
 
-  if (auto *MI = dyn_cast<MemIntrinsic>(II)) {
-    bool Changed = false;
-
-    Align OldDestAlign = MI->getDestAlign().valueOrOne();
-    Align NewDestAlign = Fn(MI->getRawDest(), OldDestAlign, Align(1));
-    if (NewDestAlign > OldDestAlign) {
-      MI->setDestAlignment(NewDestAlign);
-      Changed = true;
-    }
-
-    if (auto *MTI = dyn_cast<MemTransferInst>(MI)) {
-      Align OldSourceAlign = MTI->getSourceAlign().valueOrOne();
-      Align NewSourceAlign = Fn(MTI->getRawSource(), OldSourceAlign, Align(1));
-      if (NewSourceAlign > OldSourceAlign) {
-        MTI->setSourceAlignment(NewSourceAlign);
-        Changed = true;
-      }
-    }
+  if (!isa<MemIntrinsic>(II) &&
+      II->getIntrinsicID() != Intrinsic::masked_load &&
+      II->getIntrinsicID() != Intrinsic::masked_store)
+    return false;
 
-    return Changed;
-  }
+  bool Changed = false;
+  for (unsigned ArgNo = 0; ArgNo != II->arg_size(); ++ArgNo) {
+    Value *Arg = II->getArgOperand(ArgNo);
+    if (!Arg->getType()->isPointerTy())
+      continue;
 
-  // TODO: Handle more memory intrinsics.
-  switch (II->getIntrinsicID()) {
-  case Intrinsic::masked_load:
-  case Intrinsic::masked_store: {
-    unsigned PtrOpIdx = II->getIntrinsicID() == Intrinsic::masked_load ? 0 : 1;
-    Value *PtrOp = II->getArgOperand(PtrOpIdx);
-    Type *Type = II->getIntrinsicID() == Intrinsic::masked_load
-                     ? II->getType()
-                     : II->getArgOperand(0)->getType();
-
-    Align OldAlign = II->getParamAlign(PtrOpIdx).valueOrOne();
-    Align PrefAlign = DL.getPrefTypeAlign(Type);
-    Align NewAlign = Fn(PtrOp, OldAlign, PrefAlign);
+    Align OldAlign = II->getParamAlign(ArgNo).valueOrOne();
+    Align NewAlign = Fn(Arg, OldAlign, Align(1));
     if (NewAlign <= OldAlign)
-      return false;
+      continue;
 
-    II->addParamAttr(PtrOpIdx,
+    II->addParamAttr(ArgNo,
                      Attribute::getWithAlignment(II->getContext(), NewAlign));
-    return true;
-  }
-  default:
-    return false;
+    Changed = true;
   }
+  return Changed;
 }
 
 using ScopedHT =
diff --git a/llvm/test/Transforms/InferAlignment/memintrinsics.ll b/llvm/test/Transforms/InferAlignment/memintrinsics.ll
index 4354f1d4a72af9..ec9817a6980789 100644
--- a/llvm/test/Transforms/InferAlignment/memintrinsics.ll
+++ b/llvm/test/Transforms/InferAlignment/memintrinsics.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt < %s -passes=infer-alignment -S | FileCheck %s
 
 declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)
@@ -6,7 +7,12 @@ declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1)
 
 define void @memcpy(i64 %len) {
 ; CHECK-LABEL: define void @memcpy(
-; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 32 %dst, ptr align 16 %src, i64 %len, i1 false)
+; CHECK-SAME: i64 [[LEN:%.*]]) {
+; CHECK-NEXT:    [[DST:%.*]] = alloca [64 x i8], align 32
+; CHECK-NEXT:    [[SRC:%.*]] = alloca [64 x i8], align 16
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 32 [[DST]], ptr align 16 [[SRC]], i64 [[LEN]], i1 false)
+; CHECK-NEXT:    ret void
+;
   %dst = alloca [64 x i8], align 32
   %src = alloca [64 x i8], align 16
   call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %len, i1 false)
@@ -15,7 +21,12 @@ define void @memcpy(i64 %len) {
 
 define void @memmove(i64 %len) {
 ; CHECK-LABEL: define void @memmove(
-; CHECK: call void @llvm.memmove.p0.p0.i64(ptr align 64 %dst, ptr align 8 %src, i64 %len, i1 false)
+; CHECK-SAME: i64 [[LEN:%.*]]) {
+; CHECK-NEXT:    [[DST:%.*]] = alloca [64 x i8], align 64
+; CHECK-NEXT:    [[SRC:%.*]] = alloca [64 x i8], align 8
+; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr align 64 [[DST]], ptr align 8 [[SRC]], i64 [[LEN]], i1 false)
+; CHECK-NEXT:    ret void
+;
   %dst = alloca [64 x i8], align 64
   %src = alloca [64 x i8], align 8
   call void @llvm.memmove.p0.p0.i64(ptr %dst, ptr %src, i64 %len, i1 false)
@@ -24,7 +35,11 @@ define void @memmove(i64 %len) {
 
 define void @memset(i64 %len) {
 ; CHECK-LABEL: define void @memset(
-; CHECK: call void @llvm.memset.p0.i64(ptr align 32 %dst, i8 0, i64 %len, i1 false)
+; CHECK-SAME: i64 [[LEN:%.*]]) {
+; CHECK-NEXT:    [[DST:%.*]] = alloca [64 x i8], align 32
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 32 [[DST]], i8 0, i64 [[LEN]], i1 false)
+; CHECK-NEXT:    ret void
+;
   %dst = alloca [64 x i8], align 32
   call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 %len, i1 false)
   ret void



More information about the llvm-commits mailing list