[llvm] IR: Add verifier checks and LangRef for llvm.loop.align (PR #221933)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 02:20:22 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

Author: Matt Arsenault (arsenm)

<details>
<summary>Changes</summary>

Verify the nested !{!"llvm.loop.align", i32 N} tag.
Require exactly two operands, an integer constant of type i32 or smaller,
and a positive power-of-two value

Co-authored-by: Claude (Claude-Opus-4.8)

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


5 Files Affected:

- (modified) llvm/docs/LangRef.md (+15) 
- (modified) llvm/lib/CodeGen/MachineBlockPlacement.cpp (-3) 
- (modified) llvm/lib/IR/Verifier.cpp (+27) 
- (added) llvm/test/Assembler/llvm.loop.align.ll (+28) 
- (added) llvm/test/Verifier/llvm.loop.align.ll (+121) 


``````````diff
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 14caff88243c1..1dd342e256b43 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -8631,6 +8631,21 @@ via a volatile memory access, I/O, or other synchronization. If such a loop is
 not found to interact with the environment in an observable way, the loop may
 be removed. This corresponds to the `mustprogress` function attribute.
 
+#### '`llvm.loop.align`' Metadata
+
+This metadata suggests an alignment (in bytes) for the loop to the backend. The
+first operand is the string `llvm.loop.align` and the second operand is a
+positive power-of-two integer constant of type `i32` specifying the alignment.
+For example:
+
+```llvm
+!0 = !{!"llvm.loop.align", i32 64}
+```
+
+The backend aligns the loop to the maximum of this value and the target's
+preferred loop alignment. This corresponds to the Clang `[[clang::code_align(N)]]`
+statement attribute.
+
 #### '`irr_loop`' Metadata
 
 `irr_loop` metadata may be attached to the terminator instruction of a basic
diff --git a/llvm/lib/CodeGen/MachineBlockPlacement.cpp b/llvm/lib/CodeGen/MachineBlockPlacement.cpp
index 1378aaa67ba39..a748150d98c64 100644
--- a/llvm/lib/CodeGen/MachineBlockPlacement.cpp
+++ b/llvm/lib/CodeGen/MachineBlockPlacement.cpp
@@ -3044,11 +3044,8 @@ void MachineBlockPlacement::alignBlocks() {
         if (S == nullptr)
           continue;
         if (S->getString() == "llvm.loop.align") {
-          assert(MD->getNumOperands() == 2 &&
-                 "per-loop align metadata should have two operands.");
           MDAlign =
               mdconst::extract<ConstantInt>(MD->getOperand(1))->getZExtValue();
-          assert(MDAlign >= 1 && "per-loop align value must be positive.");
         }
       }
     }
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 20df60ed61da3..6faaef7ba92a6 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1062,6 +1062,13 @@ void Verifier::visitMDNode(const MDNode &BaseMD,
       }
     }
 
+    // FIXME: The nested llvm.loop.* property tags (llvm.loop.align,
+    // llvm.loop.estimated_trip_count, the boolean enable/disable tags below)
+    // are only meaningful as operands of an llvm.loop node. Neither llvm.loop's
+    // structure nor the requirement that these tags appear only within it is
+    // validated here; the checks below fire on any matching tuple regardless of
+    // where it appears.
+
     // Check llvm.loop.estimated_trip_count.
     if (CurrentMD->getNumOperands() > 0 &&
         CurrentMD->getOperand(0).equalsStr(LLVMLoopEstimatedTripCount)) {
@@ -1076,6 +1083,26 @@ void Verifier::visitMDNode(const MDNode &BaseMD,
             CurrentMD);
     }
 
+    // Check llvm.loop.align.
+    if (CurrentMD->getNumOperands() > 0 &&
+        CurrentMD->getOperand(0).equalsStr("llvm.loop.align")) {
+      Check(CurrentMD->getNumOperands() == 2, "Expected two operands",
+            CurrentMD);
+      auto *AlignMD =
+          mdconst::dyn_extract_or_null<ConstantInt>(CurrentMD->getOperand(1));
+      Check(AlignMD && AlignMD->getType()->isIntegerTy(32),
+            "Expected the alignment to be an integer constant of type i32",
+            CurrentMD);
+      if (AlignMD) {
+        uint64_t Align = AlignMD->getValue().getZExtValue();
+        Check(isPowerOf2_64(Align),
+              "Expected the alignment to be a power of two", CurrentMD);
+        Check(Align <= Value::MaximumAlignment,
+              "Alignment is larger than the implementation defined limit",
+              CurrentMD);
+      }
+    }
+
     // Enforce the single-operand form of the loop enable/disable pairs.
     if (CurrentMD->getNumOperands() > 0 &&
         any_of(OldBooleanLoopTags, [CurrentMD](const BooleanLoopTags &Tags) {
diff --git a/llvm/test/Assembler/llvm.loop.align.ll b/llvm/test/Assembler/llvm.loop.align.ll
new file mode 100644
index 0000000000000..fab172dab47ed
--- /dev/null
+++ b/llvm/test/Assembler/llvm.loop.align.ll
@@ -0,0 +1,28 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+; Valid "llvm.loop.align" metadata round-trips through the assembler.
+
+define void @pow2() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i32 64}
+
+define void @one() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !2
+exit:
+  ret void
+}
+!2 = distinct !{!2, !3}
+!3 = !{!"llvm.loop.align", i32 1}
+
+; CHECK: [[LOOP0:![0-9]+]] = distinct !{[[LOOP0]], [[ALIGN0:![0-9]+]]}
+; CHECK: [[ALIGN0]] = !{!"llvm.loop.align", i32 64}
+; CHECK: [[LOOP2:![0-9]+]] = distinct !{[[LOOP2]], [[ALIGN2:![0-9]+]]}
+; CHECK: [[ALIGN2]] = !{!"llvm.loop.align", i32 1}
diff --git a/llvm/test/Verifier/llvm.loop.align.ll b/llvm/test/Verifier/llvm.loop.align.ll
new file mode 100644
index 0000000000000..5cd7d2849f8e5
--- /dev/null
+++ b/llvm/test/Verifier/llvm.loop.align.ll
@@ -0,0 +1,121 @@
+; Test "llvm.loop.align" validation
+
+; RUN: split-file %s %t
+
+; RUN: not llvm-as < %t/too-few.ll 2>&1 | FileCheck %s --check-prefix=TOO-FEW
+; RUN: not llvm-as < %t/too-many.ll 2>&1 | FileCheck %s --check-prefix=TOO-MANY
+
+; RUN: not llvm-as < %t/i16.ll 2>&1 | FileCheck %s --check-prefix=BAD-VALUE
+; RUN: not llvm-as < %t/i64.ll 2>&1 | FileCheck %s --check-prefix=BAD-VALUE
+; RUN: not llvm-as < %t/mdstring.ll 2>&1 | FileCheck %s --check-prefix=BAD-VALUE
+; RUN: not llvm-as < %t/mdnode.ll 2>&1 | FileCheck %s --check-prefix=BAD-VALUE
+
+; RUN: not llvm-as < %t/zero.ll 2>&1 | FileCheck %s --check-prefix=BAD-ALIGN
+; RUN: not llvm-as < %t/not-pow2.ll 2>&1 | FileCheck %s --check-prefix=BAD-ALIGN
+; RUN: not llvm-as < %t/negative.ll 2>&1 | FileCheck %s --check-prefix=BAD-ALIGN
+
+;--- too-few.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align"}
+; TOO-FEW: Expected two operands
+; TOO-FEW: !{!"llvm.loop.align"}
+
+;--- too-many.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i32 64, i32 64}
+; TOO-MANY: Expected two operands
+; TOO-MANY: !{!"llvm.loop.align", i32 64, i32 64}
+
+;--- i16.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i16 16}
+; BAD-VALUE: Expected the alignment to be an integer constant of type i32
+
+;--- i64.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i64 64}
+
+;--- mdstring.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", !"64"}
+
+;--- mdnode.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", !2}
+!2 = !{i32 64}
+
+;--- zero.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i32 0}
+; BAD-ALIGN: Expected the alignment to be a power of two
+
+;--- not-pow2.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i32 3}
+
+;--- negative.ll
+define void @test() {
+  br label %body
+body:
+  br i1 0, label %body, label %exit, !llvm.loop !0
+exit:
+  ret void
+}
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.align", i32 -8}

``````````

</details>


https://github.com/llvm/llvm-project/pull/221933


More information about the llvm-commits mailing list