CodeGen: Remove TargetOptions::LoopAlignment Delete the module-wide LoopAlignment override and llc's -align-loops flag that fed it. Loop alignment is now expressed solely through per-loop llvm.loop.align metadata. Co-authored-by: Claude (Opus 4.8) <noreply@anthropic.com>
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 5a01779..c062401 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -439,7 +439,6 @@ Options.EmitCallGraphSection = CodeGenOpts.CallGraphSection; Options.EmitCallSiteInfo = CodeGenOpts.EmitCallSiteInfo; Options.XRayFunctionIndex = CodeGenOpts.XRayFunctionIndex; - Options.LoopAlignment = CodeGenOpts.LoopAlignment; Options.DebugStrictDwarf = CodeGenOpts.DebugStrictDwarf; Options.ObjectFilenameForDebug = CodeGenOpts.remapDebugPathPrefix(CodeGenOpts.ObjectFilenameForDebug);
diff --git a/llvm/include/llvm/CodeGen/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h index 1a5d445..f01df5e 100644 --- a/llvm/include/llvm/CodeGen/CommandFlags.h +++ b/llvm/include/llvm/CodeGen/CommandFlags.h
@@ -129,8 +129,6 @@ LLVM_ABI bool getDebugStrictDwarf(); -LLVM_ABI unsigned getAlignLoops(); - LLVM_ABI bool getJMCInstrument(); LLVM_ABI bool getXCOFFReadOnlyPointers();
diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h index 7ebc0e0..88165798 100644 --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h
@@ -297,9 +297,6 @@ /// passed on the command line. std::string StackUsageFile; - /// If greater than 0, override TargetLoweringBase::PrefLoopAlignment. - unsigned LoopAlignment = 0; - /// Which debugger to tune for. DebuggerKind DebuggerTuning = DebuggerKind::Default;
diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp index 1bebecd..7950729 100644 --- a/llvm/lib/CodeGen/CommandFlags.cpp +++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -110,7 +110,6 @@ CGOPT(bool, ForceDwarfFrameSection) CGOPT(bool, XRayFunctionIndex) CGOPT(bool, DebugStrictDwarf) -CGOPT(unsigned, AlignLoops) CGOPT(bool, JMCInstrument) CGOPT(bool, XCOFFReadOnlyPointers) CGOPT(codegen::SaveStatsMode, SaveStats) @@ -459,10 +458,6 @@ "strict-dwarf", cl::desc("use strict dwarf"), cl::init(false)); CGBINDOPT(DebugStrictDwarf); - static cl::opt<unsigned> AlignLoops("align-loops", - cl::desc("Default alignment for loops")); - CGBINDOPT(AlignLoops); - static cl::opt<bool> JMCInstrument( "enable-jmc-instrument", cl::desc("Instrument functions with a call to __CheckForDebuggerJustMyCode"), @@ -559,7 +554,6 @@ Options.ForceDwarfFrameSection = getForceDwarfFrameSection(); Options.XRayFunctionIndex = getXRayFunctionIndex(); Options.DebugStrictDwarf = getDebugStrictDwarf(); - Options.LoopAlignment = getAlignLoops(); Options.JMCInstrument = getJMCInstrument(); Options.XCOFFReadOnlyPointers = getXCOFFReadOnlyPointers();
diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index 79badc6..def33ed 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -2282,8 +2282,6 @@ } Align TargetLoweringBase::getPrefLoopAlignment(MachineLoop *ML) const { - if (TM.Options.LoopAlignment) - return Align(TM.Options.LoopAlignment); return PrefLoopAlignment; }
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index e2f3b5f..2e777a9 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -2946,7 +2946,8 @@ MaxLoadsPerMemcmp = 2; MaxLoadsPerMemcmpOptSize = 2; - // Default loop alignment, which can be overridden by -align-loops. + // Default loop alignment, which can be overridden by llvm.loop.align + // metadata. setPrefLoopAlignment(Align(16)); // An out-of-order CPU can speculatively execute past a predictable branch,
diff --git a/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes-neoverse.ll b/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes-neoverse.ll index 8df4b26..82e8ad9 100644 --- a/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes-neoverse.ll +++ b/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes-neoverse.ll
@@ -1,4 +1,3 @@ -; RUN: llc -mtriple=aarch64-none-linux-gnu -align-loops=32 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-DEFAULT ; RUN: llc -mtriple=aarch64-none-linux-gnu -mcpu=neoverse-n1 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-16 ; RUN: llc -mtriple=aarch64-none-linux-gnu -mcpu=neoverse-n2 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-16 ; RUN: llc -mtriple=aarch64-none-linux-gnu -mcpu=neoverse-v1 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-16 @@ -13,11 +12,9 @@ ; RUN: llc -mtriple=aarch64-none-linux-gnu -mcpu=cortex-a710 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-16 define i32 @a(i32 %x, ptr nocapture readonly %y, ptr nocapture readonly %z) { -; CHECK-DEFAULT: .p2align 5 ; CHECK-8: .p2align 4, , 8 ; CHECK-16: .p2align 5, , 16 ; CHECK-NEXT: .LBB0_5: // %vector.body -; CHECK-DEFAULT: .p2align 5 ; CHECK-8: .p2align 4, , 8 ; CHECK-16: .p2align 5, , 16 ; CHECK-NEXT: .LBB0_8: // %for.body
diff --git a/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes.ll b/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes.ll index 99e0d06..d7fed00 100644 --- a/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes.ll +++ b/llvm/test/CodeGen/AArch64/aarch64-p2align-max-bytes.ll
@@ -1,7 +1,7 @@ -; RUN: llc -mtriple=aarch64-none-linux-gnu -max-bytes-for-alignment=8 --align-loops=32 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-EXPLICIT -; RUN: llc -mtriple=aarch64-none-linux-gnu --align-loops=32 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-IMPLICIT -; RUN: llc -mtriple=aarch64-none-linux-gnu --align-loops=32 < %s -o - --filetype=obj | llvm-objdump --arch=aarch64 -d -| FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-IMPLICIT -; RUN: llc -mtriple=aarch64-none-linux-gnu -max-bytes-for-alignment=8 --align-loops=32 < %s -o - --filetype=obj | llvm-objdump --arch=aarch64 -d -| FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-EXPLICIT +; RUN: llc -mtriple=aarch64-none-linux-gnu -max-bytes-for-alignment=8 < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-EXPLICIT +; RUN: llc -mtriple=aarch64-none-linux-gnu < %s -o -| FileCheck %s --check-prefixes=CHECK,CHECK-IMPLICIT +; RUN: llc -mtriple=aarch64-none-linux-gnu < %s -o - --filetype=obj | llvm-objdump --arch=aarch64 -d -| FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-IMPLICIT +; RUN: llc -mtriple=aarch64-none-linux-gnu -max-bytes-for-alignment=8 < %s -o - --filetype=obj | llvm-objdump --arch=aarch64 -d -| FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-EXPLICIT ; This test is checking that the correct operands to the .p2align are emitted correctly, and that the resulting obj ; is padded as expected. The key interest in the CHECK-OBJ-* sections is the size of the padding region (the nops), @@ -59,7 +59,7 @@ %11 = add <4 x i32> %9, %wide.load16 %index.next = add nuw i64 %index, 8 %12 = icmp eq i64 %index.next, %n.vec - br i1 %12, label %middle.block, label %vector.body + br i1 %12, label %middle.block, label %vector.body, !llvm.loop !0 middle.block: ; preds = %vector.body %bin.rdx = add <4 x i32> %11, %10 @@ -87,7 +87,10 @@ %add3 = add i32 %add, %15 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count - br i1 %exitcond.not, label %for.cond.cleanup, label %for.body + br i1 %exitcond.not, label %for.cond.cleanup, label %for.body, !llvm.loop !0 } declare i32 @llvm.vector.reduce.add.v4i32(<4 x i32>) + +!0 = distinct !{!0, !1} +!1 = !{!"llvm.loop.align", i32 32}
diff --git a/llvm/test/CodeGen/RISCV/align-loops.ll b/llvm/test/CodeGen/RISCV/align-loops.ll index efa0399..9834235 100644 --- a/llvm/test/CodeGen/RISCV/align-loops.ll +++ b/llvm/test/CodeGen/RISCV/align-loops.ll
@@ -1,25 +1,13 @@ ; RUN: llc < %s -mtriple=riscv64 | FileCheck %s -; RUN: llc < %s -mtriple=riscv64 -align-loops=16 | FileCheck %s -check-prefix=ALIGN_16 -; RUN: llc < %s -mtriple=riscv64 -align-loops=32 | FileCheck %s -check-prefix=ALIGN_32 declare void @foo() -define void @test(i32 %n, i32 %m) nounwind { -; CHECK-LABEL: test: +; Reference case: without llvm.loop.align metadata, RISCV emits no loop +; alignment. +define void @test_noalign(i32 %n, i32 %m) nounwind { +; CHECK-LABEL: test_noalign: ; CHECK-NOT: .p2align ; CHECK: ret - -; ALIGN_16-LABEL: test: -; ALIGN_16: .p2align 4{{$}} -; ALIGN_16-NEXT: .LBB0_1: # %outer -; ALIGN_16: .p2align 4{{$}} -; ALIGN_16-NEXT: .LBB0_2: # %inner - -; ALIGN_32-LABEL: test: -; ALIGN_32: .p2align 5{{$}} -; ALIGN_32-NEXT: .LBB0_1: # %outer -; ALIGN_32: .p2align 5{{$}} -; ALIGN_32-NEXT: .LBB0_2: # %inner entry: br label %outer @@ -42,3 +30,39 @@ exit: ret void } + +; Each loop is aligned independently via its own llvm.loop.align metadata: +; the outer loop to 16 and the inner loop to 32. +define void @test_peralign(i32 %n, i32 %m) nounwind { +; CHECK-LABEL: test_peralign: +; CHECK: .p2align 4{{$}} +; CHECK-NEXT: .LBB1_1: # %outer +; CHECK: .p2align 5{{$}} +; CHECK-NEXT: .LBB1_2: # %inner +entry: + br label %outer + +outer: + %outer.iv = phi i32 [0, %entry], [%outer.iv.next, %outer_bb] + br label %inner + +inner: + %inner.iv = phi i32 [0, %outer], [%inner.iv.next, %inner] + call void @foo() + %inner.iv.next = add i32 %inner.iv, 1 + %inner.cond = icmp ne i32 %inner.iv.next, %m + br i1 %inner.cond, label %inner, label %outer_bb, !llvm.loop !2 + +outer_bb: + %outer.iv.next = add i32 %outer.iv, 1 + %outer.cond = icmp ne i32 %outer.iv.next, %n + br i1 %outer.cond, label %outer, label %exit, !llvm.loop !0 + +exit: + ret void +} + +!0 = distinct !{!0, !1} +!1 = !{!"llvm.loop.align", i32 16} +!2 = distinct !{!2, !3} +!3 = !{!"llvm.loop.align", i32 32}
diff --git a/llvm/test/CodeGen/X86/code-align-loops.ll b/llvm/test/CodeGen/X86/code-align-loops.ll index cd2bac5..1424950 100644 --- a/llvm/test/CodeGen/X86/code-align-loops.ll +++ b/llvm/test/CodeGen/X86/code-align-loops.ll
@@ -1,11 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=x86_64-pc-linux-gnu | FileCheck %s -check-prefixes=CHECK,ALIGN -; RUN: llc < %s -mtriple=x86_64-pc-linux-gnu -align-loops=32 | FileCheck %s -check-prefixes=CHECK,ALIGN32 -; RUN: llc < %s -mtriple=x86_64-pc-linux-gnu -align-loops=256 | FileCheck %s -check-prefixes=CHECK,ALIGN256 -; This test is to check if .p2align can be correctly generated by considerring -; 1. -align-loops=N from llc option -; 2. loop metadata node !{!"llvm.loop.align", i32 64} +; This test is to check if .p2align can be correctly generated from the loop +; metadata node !{!"llvm.loop.align", i32 64}. ; The test IR is generated from below simple C file: ; $ clang -S -emit-llvm loop.c ; $ cat loop.c @@ -25,16 +22,6 @@ ; ALIGN: .p2align 9 ; ALIGN-NEXT: .LBB0_3: # %for.body -; ALIGN32: .p2align 6 -; ALIGN32-NEXT: .LBB0_2: # %for.body -; ALIGN32: .p2align 9 -; ALIGN32-NEXT: .LBB0_3: # %for.body - -; ALIGN256: .p2align 8 -; ALIGN256-NEXT: .LBB0_2: # %for.body -; ALIGN256: .p2align 9 -; ALIGN256-NEXT: .LBB0_3: # %for.body - define void @test1(i32 %a) nounwind { entry: %cmp12 = icmp sgt i32 %a, 0 @@ -63,16 +50,6 @@ ; ALIGN-NEXT: .LBB1_2: # %for.body ; ALIGN: .p2align 9 ; ALIGN-NEXT: .LBB1_3: # %for.body - -; ALIGN32: .p2align 5 -; ALIGN32-NEXT: .LBB1_2: # %for.body -; ALIGN32: .p2align 9 -; ALIGN32-NEXT: .LBB1_3: # %for.body - -; ALIGN256: .p2align 8 -; ALIGN256-NEXT: .LBB1_2: # %for.body -; ALIGN256: .p2align 9 -; ALIGN256-NEXT: .LBB1_3: # %for.body define void @test2(i32 %a) nounwind { entry: %cmp12 = icmp sgt i32 %a, 0
diff --git a/llvm/test/CodeGen/X86/innermost-loop-alignment.ll b/llvm/test/CodeGen/X86/innermost-loop-alignment.ll index 482c814..be1045b 100644 --- a/llvm/test/CodeGen/X86/innermost-loop-alignment.ll +++ b/llvm/test/CodeGen/X86/innermost-loop-alignment.ll
@@ -1,7 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=i686-pc-linux-gnu | FileCheck %s -check-prefix=DEFAULT ; RUN: llc < %s -mtriple=i686-pc-linux-gnu -x86-experimental-pref-innermost-loop-alignment=5 | FileCheck %s -check-prefix=ALIGN32 -; RUN: llc < %s -mtriple=i686-pc-linux-gnu -align-loops=32 -x86-experimental-pref-innermost-loop-alignment=6 | FileCheck %s -check-prefix=ALIGN64 +; RUN: llc < %s -mtriple=i686-pc-linux-gnu -x86-experimental-pref-innermost-loop-alignment=6 | FileCheck %s -check-prefix=ALIGN64 declare void @foo() @@ -123,7 +123,7 @@ ; ALIGN64-NEXT: xorl %esi, %esi ; ALIGN64-NEXT: movl {{[0-9]+}}(%esp), %edi ; ALIGN64-NEXT: movl {{[0-9]+}}(%esp), %ebx -; ALIGN64-NEXT: .p2align 5 +; ALIGN64-NEXT: .p2align 4 ; ALIGN64-NEXT: .LBB0_1: # %outer ; ALIGN64-NEXT: # =>This Loop Header: Depth=1 ; ALIGN64-NEXT: # Child Loop BB0_2 Depth 2 @@ -152,9 +152,6 @@ ; ALIGN64-NEXT: popl %ebp ; ALIGN64-NEXT: .cfi_def_cfa_offset 4 ; ALIGN64-NEXT: retl - - - entry: br label %outer @@ -177,3 +174,136 @@ exit: ret void } + +; The outer loop carries llvm.loop.align metadata (32 bytes), which composes +; with the innermost-loop preferred alignment applied to the inner loop. +define void @test_outer_loop_align(i32 %n, i32 %m) nounwind { +; DEFAULT-LABEL: test_outer_loop_align: +; DEFAULT: # %bb.0: # %entry +; DEFAULT-NEXT: pushl %ebp +; DEFAULT-NEXT: pushl %ebx +; DEFAULT-NEXT: pushl %edi +; DEFAULT-NEXT: pushl %esi +; DEFAULT-NEXT: subl $12, %esp +; DEFAULT-NEXT: xorl %esi, %esi +; DEFAULT-NEXT: movl {{[0-9]+}}(%esp), %edi +; DEFAULT-NEXT: movl {{[0-9]+}}(%esp), %ebx +; DEFAULT-NEXT: .p2align 5 +; DEFAULT-NEXT: .LBB1_1: # %outer +; DEFAULT-NEXT: # =>This Loop Header: Depth=1 +; DEFAULT-NEXT: # Child Loop BB1_2 Depth 2 +; DEFAULT-NEXT: movl %edi, %ebp +; DEFAULT-NEXT: .p2align 4 +; DEFAULT-NEXT: .LBB1_2: # %inner +; DEFAULT-NEXT: # Parent Loop BB1_1 Depth=1 +; DEFAULT-NEXT: # => This Inner Loop Header: Depth=2 +; DEFAULT-NEXT: calll foo@PLT +; DEFAULT-NEXT: decl %ebp +; DEFAULT-NEXT: jne .LBB1_2 +; DEFAULT-NEXT: # %bb.3: # %outer_bb +; DEFAULT-NEXT: # in Loop: Header=BB1_1 Depth=1 +; DEFAULT-NEXT: incl %esi +; DEFAULT-NEXT: cmpl %ebx, %esi +; DEFAULT-NEXT: jne .LBB1_1 +; DEFAULT-NEXT: # %bb.4: # %exit +; DEFAULT-NEXT: addl $12, %esp +; DEFAULT-NEXT: popl %esi +; DEFAULT-NEXT: popl %edi +; DEFAULT-NEXT: popl %ebx +; DEFAULT-NEXT: popl %ebp +; DEFAULT-NEXT: retl +; +; ALIGN32-LABEL: test_outer_loop_align: +; ALIGN32: # %bb.0: # %entry +; ALIGN32-NEXT: pushl %ebp +; ALIGN32-NEXT: pushl %ebx +; ALIGN32-NEXT: pushl %edi +; ALIGN32-NEXT: pushl %esi +; ALIGN32-NEXT: subl $12, %esp +; ALIGN32-NEXT: xorl %esi, %esi +; ALIGN32-NEXT: movl {{[0-9]+}}(%esp), %edi +; ALIGN32-NEXT: movl {{[0-9]+}}(%esp), %ebx +; ALIGN32-NEXT: .p2align 5 +; ALIGN32-NEXT: .LBB1_1: # %outer +; ALIGN32-NEXT: # =>This Loop Header: Depth=1 +; ALIGN32-NEXT: # Child Loop BB1_2 Depth 2 +; ALIGN32-NEXT: movl %edi, %ebp +; ALIGN32-NEXT: .p2align 5 +; ALIGN32-NEXT: .LBB1_2: # %inner +; ALIGN32-NEXT: # Parent Loop BB1_1 Depth=1 +; ALIGN32-NEXT: # => This Inner Loop Header: Depth=2 +; ALIGN32-NEXT: calll foo@PLT +; ALIGN32-NEXT: decl %ebp +; ALIGN32-NEXT: jne .LBB1_2 +; ALIGN32-NEXT: # %bb.3: # %outer_bb +; ALIGN32-NEXT: # in Loop: Header=BB1_1 Depth=1 +; ALIGN32-NEXT: incl %esi +; ALIGN32-NEXT: cmpl %ebx, %esi +; ALIGN32-NEXT: jne .LBB1_1 +; ALIGN32-NEXT: # %bb.4: # %exit +; ALIGN32-NEXT: addl $12, %esp +; ALIGN32-NEXT: popl %esi +; ALIGN32-NEXT: popl %edi +; ALIGN32-NEXT: popl %ebx +; ALIGN32-NEXT: popl %ebp +; ALIGN32-NEXT: retl +; +; ALIGN64-LABEL: test_outer_loop_align: +; ALIGN64: # %bb.0: # %entry +; ALIGN64-NEXT: pushl %ebp +; ALIGN64-NEXT: pushl %ebx +; ALIGN64-NEXT: pushl %edi +; ALIGN64-NEXT: pushl %esi +; ALIGN64-NEXT: subl $12, %esp +; ALIGN64-NEXT: xorl %esi, %esi +; ALIGN64-NEXT: movl {{[0-9]+}}(%esp), %edi +; ALIGN64-NEXT: movl {{[0-9]+}}(%esp), %ebx +; ALIGN64-NEXT: .p2align 5 +; ALIGN64-NEXT: .LBB1_1: # %outer +; ALIGN64-NEXT: # =>This Loop Header: Depth=1 +; ALIGN64-NEXT: # Child Loop BB1_2 Depth 2 +; ALIGN64-NEXT: movl %edi, %ebp +; ALIGN64-NEXT: .p2align 6 +; ALIGN64-NEXT: .LBB1_2: # %inner +; ALIGN64-NEXT: # Parent Loop BB1_1 Depth=1 +; ALIGN64-NEXT: # => This Inner Loop Header: Depth=2 +; ALIGN64-NEXT: calll foo@PLT +; ALIGN64-NEXT: decl %ebp +; ALIGN64-NEXT: jne .LBB1_2 +; ALIGN64-NEXT: # %bb.3: # %outer_bb +; ALIGN64-NEXT: # in Loop: Header=BB1_1 Depth=1 +; ALIGN64-NEXT: incl %esi +; ALIGN64-NEXT: cmpl %ebx, %esi +; ALIGN64-NEXT: jne .LBB1_1 +; ALIGN64-NEXT: # %bb.4: # %exit +; ALIGN64-NEXT: addl $12, %esp +; ALIGN64-NEXT: popl %esi +; ALIGN64-NEXT: popl %edi +; ALIGN64-NEXT: popl %ebx +; ALIGN64-NEXT: popl %ebp +; ALIGN64-NEXT: retl +entry: + br label %outer + +outer: + %outer.iv = phi i32 [0, %entry], [%outer.iv.next, %outer_bb] + br label %inner + +inner: + %inner.iv = phi i32 [0, %outer], [%inner.iv.next, %inner] + call void @foo() + %inner.iv.next = add i32 %inner.iv, 1 + %inner.cond = icmp ne i32 %inner.iv.next, %m + br i1 %inner.cond, label %inner, label %outer_bb + +outer_bb: + %outer.iv.next = add i32 %outer.iv, 1 + %outer.cond = icmp ne i32 %outer.iv.next, %n + br i1 %outer.cond, label %outer, label %exit, !llvm.loop !0 + +exit: + ret void +} + +!0 = distinct !{!0, !1} +!1 = !{!"llvm.loop.align", i32 32}