[llvm] [LICM] Allow hoisting of InsertElementInst's past non-hoistable InsertElementInsts (PR #200532)
Ryan Buchner via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 10:55:55 PDT 2026
https://github.com/bababuck updated https://github.com/llvm/llvm-project/pull/200532
>From 811b05718184ed1058762d9498029a6e654984e5 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Thu, 28 May 2026 14:45:12 -0700
Subject: [PATCH 01/15] [LICM] Add new test for hoisting InsertElementInst's
past other InsertElementInst's
---
llvm/test/Transforms/LICM/vector-insert.ll | 536 +++++++++++++++++++++
1 file changed, 536 insertions(+)
create mode 100644 llvm/test/Transforms/LICM/vector-insert.ll
diff --git a/llvm/test/Transforms/LICM/vector-insert.ll b/llvm/test/Transforms/LICM/vector-insert.ll
new file mode 100644
index 0000000000000..7bf1ef7aba4ab
--- /dev/null
+++ b/llvm/test/Transforms/LICM/vector-insert.ll
@@ -0,0 +1,536 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=licm -S < %s | FileCheck %s
+
+define <16 x i1> @iss195497(ptr %1, <16 x ptr> %2, ptr %store, ptr %scevgep90, <2 x ptr> %17, <4 x ptr> %18, <8 x ptr> %19, <2 x ptr> %25, <4 x ptr> %26, <8 x ptr> %27) {
+; CHECK-LABEL: define <16 x i1> @iss195497(
+; CHECK-SAME: ptr [[TMP0:%.*]], <16 x ptr> [[TMP1:%.*]], ptr [[STORE:%.*]], ptr [[SCEVGEP90:%.*]], <2 x ptr> [[TMP2:%.*]], <4 x ptr> [[TMP3:%.*]], <8 x ptr> [[TMP4:%.*]], <2 x ptr> [[TMP5:%.*]], <4 x ptr> [[TMP6:%.*]], <8 x ptr> [[TMP7:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <8 x ptr> [[TMP7]], <8 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <4 x ptr> [[TMP6]], <4 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP10:%.*]] = shufflevector <2 x ptr> [[TMP5]], <2 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP11:%.*]] = shufflevector <2 x ptr> [[TMP2]], <2 x ptr> poison, <16 x i32> <i32 poison, i32 poison, i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP12:%.*]] = shufflevector <8 x ptr> [[TMP4]], <8 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP13:%.*]] = shufflevector <4 x ptr> [[TMP3]], <4 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[CNT:%.*]] = phi i64 [ [[CNT_NEW:%.*]], %[[LOOP]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: [[SCEVGEP89:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[CNT]]
+; CHECK-NEXT: [[TMP14:%.*]] = insertelement <16 x ptr> poison, ptr [[SCEVGEP89]], i64 0
+; CHECK-NEXT: [[TMP15:%.*]] = insertelement <16 x ptr> [[TMP14]], ptr [[SCEVGEP90]], i64 1
+; CHECK-NEXT: [[TMP16:%.*]] = shufflevector <16 x ptr> [[TMP15]], <16 x ptr> [[TMP8]], <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 16, i32 17, i32 18, i32 19, i32 20, i32 21, i32 22, i32 23>
+; CHECK-NEXT: [[TMP17:%.*]] = shufflevector <16 x ptr> [[TMP16]], <16 x ptr> [[TMP9]], <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 16, i32 17, i32 18, i32 19, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP18:%.*]] = shufflevector <16 x ptr> [[TMP17]], <16 x ptr> [[TMP10]], <16 x i32> <i32 0, i32 1, i32 16, i32 17, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP19:%.*]] = icmp ult <16 x ptr> [[TMP1]], [[TMP18]]
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr <16 x i1>, ptr [[STORE]], i64 [[CNT]]
+; CHECK-NEXT: store volatile <16 x i1> [[TMP19]], ptr [[STORE_GEP]], align 2
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i64 [[CNT]], 100
+; CHECK-NEXT: [[CNT_NEW]] = add i64 [[CNT]], 1
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[DOTLCSSA:%.*]] = phi <16 x i1> [ [[TMP19]], %[[LOOP]] ]
+; CHECK-NEXT: ret <16 x i1> [[DOTLCSSA]]
+;
+entry:
+ %30 = shufflevector <8 x ptr> %27, <8 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %31 = shufflevector <4 x ptr> %26, <4 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %32 = shufflevector <2 x ptr> %25, <2 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %33 = shufflevector <2 x ptr> %17, <2 x ptr> poison, <16 x i32> <i32 poison, i32 poison, i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %34 = shufflevector <8 x ptr> %19, <8 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %35 = shufflevector <4 x ptr> %18, <4 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ br label %loop
+
+loop:
+ %cnt = phi i64 [ %cnt_new, %loop ], [ 0, %entry ]
+ %scevgep89 = getelementptr i8, ptr %1, i64 %cnt
+ %92 = insertelement <16 x ptr> poison, ptr %scevgep89, i64 0
+ %93 = insertelement <16 x ptr> %92, ptr %scevgep90, i64 1
+ %94 = shufflevector <16 x ptr> %93, <16 x ptr> %30, <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 16, i32 17, i32 18, i32 19, i32 20, i32 21, i32 22, i32 23>
+ %95 = shufflevector <16 x ptr> %94, <16 x ptr> %31, <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 16, i32 17, i32 18, i32 19, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+ %96 = shufflevector <16 x ptr> %95, <16 x ptr> %32, <16 x i32> <i32 0, i32 1, i32 16, i32 17, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+ %97 = icmp ult <16 x ptr> %2, %96
+ %store.gep = getelementptr <16 x i1>, ptr %store, i64 %cnt
+ store volatile <16 x i1> %97, ptr %store.gep
+ %done = icmp eq i64 %cnt, 100
+ %cnt_new = add i64 %cnt, 1
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <16 x i1> %97
+}
+
+; Basic positive case: simple 4xi32, invariant inserted at lane 1 over variant at lane 0.
+; The inner insertelement (lane 0, variant) should be hoisted.
+define <4 x i32> @hoist_simple_swap(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @hoist_simple_swap(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 1
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Positive: wide constant index type should not crash and should still hoist/swap.
+define <4 x i32> @hoist_simple_swap_wide_index_ty(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @hoist_simple_swap_wide_index_ty(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV]]
+; CHECK-NEXT: [[VARIANT:%.*]] = load i32, ptr [[IDX]], align 4
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT]], i128 0
+; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[INV]], i128 1
+; CHECK-NEXT: store <4 x i32> [[OUTER]], ptr [[IDX]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i128 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i128 1
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Positive: The initial vector is invariant
+define <4 x i32> @hoist_simple_swap_basevec(ptr %base, i32 %inv, i32 %n, <4 x i32> %basevec) {
+; CHECK-LABEL: define <4 x i32> @hoist_simple_swap_basevec(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]], <4 x i32> [[BASEVEC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> [[BASEVEC]], i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> %basevec, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 1
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Positive: Have to jump through multiple insert vectors
+define <4 x i32> @hoist_multiple_variants(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @hoist_multiple_variants(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[INNER2_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[VARIANT_LE]], i32 1
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER2_LE]], i32 [[INV]], i32 2
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %inner2 = insertelement <4 x i32> %inner, i32 %variant, i32 1
+ %outer = insertelement <4 x i32> %inner2, i32 %inv, i32 2
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Positive: Multiple invariant inserts
+define <4 x i32> @hoist_multiple_invariants(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @hoist_multiple_invariants(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: [[OUTER2_LE:%.*]] = insertelement <4 x i32> [[OUTER_LE]], i32 [[INV]], i32 2
+; CHECK-NEXT: store <4 x i32> [[OUTER2_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER2_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER2_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER2_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 1
+ %outer2 = insertelement <4 x i32> %outer, i32 %inv, i32 2
+ store <4 x i32> %outer2, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer2
+}
+
+define <4 x i32> @no_hoist_variant_basevec(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_variant_basevec(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[BASEVEC_LE:%.*]] = load <4 x i32>, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> [[BASEVEC_LE]], i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %basevec = load <4 x i32>, ptr %idx, align 4
+ %inner = insertelement <4 x i32> %basevec, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 1
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: outer index is not a constant — no hoist.
+define <4 x i32> @no_hoist_nonconstant_outer_idx(ptr %base, i32 %inv, i32 %vidx, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_nonconstant_outer_idx(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[VIDX:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 [[VIDX]]
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 %vidx
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: inner index is not a constant — no hoist.
+define <4 x i32> @no_hoist_nonconstant_inner_idx(ptr %base, i32 %inv, i32 %vidx, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_nonconstant_inner_idx(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[VIDX:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 [[VIDX]]
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 %vidx
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 1
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: outer element is loop-variant — no hoist.
+define <4 x i32> @no_hoist_variant_outer_elt(ptr %base, ptr %base2, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_variant_outer_elt(
+; CHECK-SAME: ptr [[BASE:%.*]], ptr [[BASE2:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA1:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA1]]
+; CHECK-NEXT: [[IDX2_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE2]], i32 [[IV_LCSSA1]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[VARIANT2_LE:%.*]] = load i32, ptr [[IDX2_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[VARIANT2_LE]], i32 1
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA1]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA1]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %idx2 = getelementptr inbounds i32, ptr %base2, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %variant2 = load i32, ptr %idx2, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %variant2, i32 1
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: inner has multiple uses — no hoist.
+define <4 x i32> @no_hoist_inner_multiuse(ptr %base, i32 %inv, ptr %store, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_inner_multiuse(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], ptr [[STORE:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV]]
+; CHECK-NEXT: [[VARIANT:%.*]] = load i32, ptr [[IDX]], align 4
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT]], i32 0
+; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[INV]], i32 1
+; CHECK-NEXT: store volatile <4 x i32> [[INNER]], ptr [[STORE]], align 16
+; CHECK-NEXT: store <4 x i32> [[OUTER]], ptr [[IDX]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LE:%.*]] = phi <4 x i32> [ [[OUTER]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LE]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 1
+ store volatile <4 x i32> %inner, ptr %store
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: inner and outer use the same index — no hoist.
+define <4 x i32> @no_hoist_same_index(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_same_index(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
+; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 0
+; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER_LE]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 0
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: outer index is out-of-range constant — no hoist/swap.
+define <4 x i32> @no_hoist_out_of_range_outer_idx(ptr %base, i32 %inv, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_out_of_range_outer_idx(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV]]
+; CHECK-NEXT: [[VARIANT:%.*]] = load i32, ptr [[IDX]], align 4
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT]], i32 0
+; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[INV]], i32 7
+; CHECK-NEXT: store <4 x i32> [[OUTER]], ptr [[IDX]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LCSSA:%.*]] = phi <4 x i32> [ [[OUTER]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LCSSA]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ %variant = load i32, ptr %idx, align 4
+ %inner = insertelement <4 x i32> poison, i32 %variant, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv, i32 7
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
+
+; Negative: inner element is loop-invariant — nothing to swap, no hoist.
+define <4 x i32> @no_hoist_inner_invariant_elt(ptr %base, i32 %inv1, i32 %inv2, i32 %n) {
+; CHECK-LABEL: define <4 x i32> @no_hoist_inner_invariant_elt(
+; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV1:%.*]], i32 [[INV2:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[INV1]], i32 0
+; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[INV2]], i32 1
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[IDX:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV]]
+; CHECK-NEXT: store <4 x i32> [[OUTER]], ptr [[IDX]], align 16
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV]], [[N]]
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[OUTER_LE:%.*]] = phi <4 x i32> [ [[OUTER]], %[[LOOP]] ]
+; CHECK-NEXT: ret <4 x i32> [[OUTER_LE]]
+;
+entry:
+ br label %loop
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %inner = insertelement <4 x i32> poison, i32 %inv1, i32 0
+ %outer = insertelement <4 x i32> %inner, i32 %inv2, i32 1
+ %idx = getelementptr inbounds i32, ptr %base, i32 %iv
+ store <4 x i32> %outer, ptr %idx
+ %iv.next = add i32 %iv, 1
+ %done = icmp eq i32 %iv, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ ret <4 x i32> %outer
+}
>From 2f920a44f3732db893ca0a6f470c502fbe104ece Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 29 May 2026 11:52:55 -0700
Subject: [PATCH 02/15] [LICM] Allow hoisting of InsertElementInst's past
non-hoistable InsertElementInsts
---
llvm/lib/Transforms/Scalar/LICM.cpp | 97 ++++++++++++++++++++++
llvm/test/Transforms/LICM/vector-insert.ll | 26 +++---
2 files changed, 110 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index be92339052ec5..7774149a0430a 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -204,6 +204,13 @@ static bool hoistArithmetics(Instruction &I, Loop &L,
ICFLoopSafetyInfo &SafetyInfo,
MemorySSAUpdater &MSSAU, AssumptionCache *AC,
DominatorTree *DT);
+static bool hoistInsertPastInsert(
+ InsertElementInst *Ins, Loop *CurLoop, AAResults *AA, DominatorTree *DT,
+ const TargetLibraryInfo *TLI, BasicBlock *Preheader, BasicBlock *HoistDest,
+ ICFLoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU, AssumptionCache *AC,
+ ScalarEvolution *SE, SinkAndHoistLICMFlags &Flags,
+ OptimizationRemarkEmitter *ORE,
+ SmallVectorImpl<Instruction *> &HoistedInstructions, bool AllowSpeculation);
static Instruction *cloneInstructionInExitBlock(
Instruction &I, BasicBlock &ExitBlock, PHINode &PN, const LoopInfo *LI,
const LoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU);
@@ -936,6 +943,15 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
continue;
}
+ if (auto *Ins = dyn_cast<InsertElementInst>(&I))
+ if (hoistInsertPastInsert(Ins, CurLoop, AA, DT, TLI, Preheader,
+ CFH.getOrCreateHoistedBlock(BB), SafetyInfo,
+ MSSAU, AC, SE, Flags, ORE,
+ HoistedInstructions, AllowSpeculation)) {
+ Changed = true;
+ continue;
+ }
+
// Attempt to remove floating point division out of the loop by
// converting it to a reciprocal multiplication.
if (I.getOpcode() == Instruction::FDiv && I.hasAllowReciprocal() &&
@@ -1058,6 +1074,87 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
return Changed;
}
+static bool hoistInsertPastInsert(
+ InsertElementInst *Ins, Loop *CurLoop, AAResults *AA, DominatorTree *DT,
+ const TargetLibraryInfo *TLI, BasicBlock *Preheader, BasicBlock *HoistDest,
+ ICFLoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU, AssumptionCache *AC,
+ ScalarEvolution *SE, SinkAndHoistLICMFlags &Flags,
+ OptimizationRemarkEmitter *ORE,
+ SmallVectorImpl<Instruction *> &HoistedInstructions,
+ bool AllowSpeculation) {
+ SmallSet<uint64_t, 4> SeenIndexes;
+ InsertElementInst *Inner;
+ auto CanBypass = [&](auto &CanBypass, InsertElementInst *CurrIns,
+ bool IsHoistedInstruction) -> bool {
+ // Instruction being hoisted past must only have one use
+ if (!IsHoistedInstruction && !CurrIns->hasOneUse())
+ return false;
+
+ // Must have constant insertion lane
+ auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
+ if (!InsertedIdxCI)
+ return false;
+ auto *VecTy = cast<VectorType>(CurrIns->getType());
+
+ // Avoid hoisting past out of bounds inserts
+ if (InsertedIdxCI->isNegative() ||
+ InsertedIdxCI->getValue().uge(
+ VecTy->getElementCount().getKnownMinValue()))
+ return false;
+
+ // Make sure not hoisting past insertions into the same lane
+ if (!SeenIndexes.insert(InsertedIdxCI->getValue().getLimitedValue()).second)
+ return false;
+
+ // The instruction we are hoisting must have invariant insertion data
+ Value *InsertedElt = CurrIns->getOperand(1);
+ if (IsHoistedInstruction && !CurLoop->isLoopInvariant(InsertedElt))
+ return false;
+
+ // Only hoist past other insertions
+ Value *InnerVal = CurrIns->getOperand(0);
+ auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
+ if (IsHoistedInstruction && !InnerIns)
+ return false;
+
+ // If the value we are inserting into is not invariant/poison, recurse
+ // if it is another insert
+ if (!CurLoop->isLoopInvariant(InnerVal) && !isa<PoisonValue>(InnerVal)) {
+ return InnerIns && InnerIns->getParent() == CurrIns->getParent() &&
+ CanBypass(CanBypass, InnerIns, /*IsHoistedInstruction*/ false);
+ }
+ Inner = CurrIns;
+ // Hoists of Insertions with fully invariant operands are handled in base
+ // logic
+ return !IsHoistedInstruction;
+ };
+
+ // Canonicalize:
+ // %inner = insertelement %base, %variant, C1
+ // %outer = insertelement %inner, %invariant, C2
+ // into:
+ // %inner' = insertelement %base, %invariant, C2
+ // %outer' = insertelement %inner', %variant, C1
+ // so we can push the variant insertelement through the shuffle.
+ if (!CanBypass(CanBypass, Ins, /*IsHoistedInstruction*/ true))
+ return false;
+ if (!isSafeToExecuteUnconditionally(*Ins, DT, TLI, CurLoop, SafetyInfo, ORE,
+ Preheader->getTerminator(), AC,
+ AllowSpeculation))
+ return false;
+
+ Value *IOp1 = Ins->getOperand(1);
+ Value *IOp2 = Ins->getOperand(2);
+ Ins->setOperand(1, Inner->getOperand(1));
+ Ins->setOperand(2, Inner->getOperand(2));
+ Inner->setOperand(1, IOp1);
+ Inner->setOperand(2, IOp2);
+
+ hoist(*Inner, DT, CurLoop, HoistDest, SafetyInfo, MSSAU, SE, ORE);
+ HoistedInstructions.push_back(Inner);
+ return true;
+}
+
// Return true if LI is invariant within scope of the loop. LI is invariant if
// CurLoop is dominated by an invariant.start representing the same memory
// location and size as the memory location LI loads from, and also the
diff --git a/llvm/test/Transforms/LICM/vector-insert.ll b/llvm/test/Transforms/LICM/vector-insert.ll
index 7bf1ef7aba4ab..a7fa31c16819a 100644
--- a/llvm/test/Transforms/LICM/vector-insert.ll
+++ b/llvm/test/Transforms/LICM/vector-insert.ll
@@ -11,12 +11,12 @@ define <16 x i1> @iss195497(ptr %1, <16 x ptr> %2, ptr %store, ptr %scevgep90, <
; CHECK-NEXT: [[TMP11:%.*]] = shufflevector <2 x ptr> [[TMP2]], <2 x ptr> poison, <16 x i32> <i32 poison, i32 poison, i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
; CHECK-NEXT: [[TMP12:%.*]] = shufflevector <8 x ptr> [[TMP4]], <8 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
; CHECK-NEXT: [[TMP13:%.*]] = shufflevector <4 x ptr> [[TMP3]], <4 x ptr> poison, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP14:%.*]] = insertelement <16 x ptr> poison, ptr [[SCEVGEP90]], i64 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[CNT:%.*]] = phi i64 [ [[CNT_NEW:%.*]], %[[LOOP]] ], [ 0, %[[ENTRY]] ]
; CHECK-NEXT: [[SCEVGEP89:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[CNT]]
-; CHECK-NEXT: [[TMP14:%.*]] = insertelement <16 x ptr> poison, ptr [[SCEVGEP89]], i64 0
-; CHECK-NEXT: [[TMP15:%.*]] = insertelement <16 x ptr> [[TMP14]], ptr [[SCEVGEP90]], i64 1
+; CHECK-NEXT: [[TMP15:%.*]] = insertelement <16 x ptr> [[TMP14]], ptr [[SCEVGEP89]], i64 0
; CHECK-NEXT: [[TMP16:%.*]] = shufflevector <16 x ptr> [[TMP15]], <16 x ptr> [[TMP8]], <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 16, i32 17, i32 18, i32 19, i32 20, i32 21, i32 22, i32 23>
; CHECK-NEXT: [[TMP17:%.*]] = shufflevector <16 x ptr> [[TMP16]], <16 x ptr> [[TMP9]], <16 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 16, i32 17, i32 18, i32 19, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
; CHECK-NEXT: [[TMP18:%.*]] = shufflevector <16 x ptr> [[TMP17]], <16 x ptr> [[TMP10]], <16 x i32> <i32 0, i32 1, i32 16, i32 17, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
@@ -63,13 +63,13 @@ define <4 x i32> @hoist_simple_swap(ptr %base, i32 %inv, i32 %n) {
; CHECK-LABEL: define <4 x i32> @hoist_simple_swap(
; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[INV]], i32 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
-; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
-; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[VARIANT_LE]], i32 0
; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
@@ -99,13 +99,13 @@ define <4 x i32> @hoist_simple_swap_wide_index_ty(ptr %base, i32 %inv, i32 %n) {
; CHECK-LABEL: define <4 x i32> @hoist_simple_swap_wide_index_ty(
; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[INV]], i128 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IDX:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV]]
; CHECK-NEXT: [[VARIANT:%.*]] = load i32, ptr [[IDX]], align 4
-; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT]], i128 0
-; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[INV]], i128 1
+; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[VARIANT]], i128 0
; CHECK-NEXT: store <4 x i32> [[OUTER]], ptr [[IDX]], align 16
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV]], [[N]]
@@ -135,13 +135,13 @@ define <4 x i32> @hoist_simple_swap_basevec(ptr %base, i32 %inv, i32 %n, <4 x i3
; CHECK-LABEL: define <4 x i32> @hoist_simple_swap_basevec(
; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]], <4 x i32> [[BASEVEC:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> [[BASEVEC]], i32 [[INV]], i32 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
-; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> [[BASEVEC]], i32 [[VARIANT_LE]], i32 0
-; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[VARIANT_LE]], i32 0
; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
@@ -171,14 +171,14 @@ define <4 x i32> @hoist_multiple_variants(ptr %base, i32 %inv, i32 %n) {
; CHECK-LABEL: define <4 x i32> @hoist_multiple_variants(
; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[INV]], i32 2
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
-; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
; CHECK-NEXT: [[INNER2_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[VARIANT_LE]], i32 1
-; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER2_LE]], i32 [[INV]], i32 2
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER2_LE]], i32 [[VARIANT_LE]], i32 0
; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
@@ -209,14 +209,14 @@ define <4 x i32> @hoist_multiple_invariants(ptr %base, i32 %inv, i32 %n) {
; CHECK-LABEL: define <4 x i32> @hoist_multiple_invariants(
; CHECK-SAME: ptr [[BASE:%.*]], i32 [[INV:%.*]], i32 [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> poison, i32 [[INV]], i32 1
+; CHECK-NEXT: [[OUTER:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[INV]], i32 2
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
-; CHECK-NEXT: [[INNER_LE:%.*]] = insertelement <4 x i32> poison, i32 [[VARIANT_LE]], i32 0
-; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[INV]], i32 1
-; CHECK-NEXT: [[OUTER2_LE:%.*]] = insertelement <4 x i32> [[OUTER_LE]], i32 [[INV]], i32 2
+; CHECK-NEXT: [[OUTER2_LE:%.*]] = insertelement <4 x i32> [[OUTER]], i32 [[VARIANT_LE]], i32 0
; CHECK-NEXT: store <4 x i32> [[OUTER2_LE]], ptr [[IDX_LE]], align 16
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
>From ceb6f03cc3764959bb5a7d6e81e79b742e882119 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 1 Jun 2026 13:05:53 -0700
Subject: [PATCH 03/15] [LICM] InsertElementInst can always execute
unconditionally
---
llvm/lib/Transforms/Scalar/LICM.cpp | 4 ----
1 file changed, 4 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 7774149a0430a..88d14ffe749f8 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1138,10 +1138,6 @@ static bool hoistInsertPastInsert(
// so we can push the variant insertelement through the shuffle.
if (!CanBypass(CanBypass, Ins, /*IsHoistedInstruction*/ true))
return false;
- if (!isSafeToExecuteUnconditionally(*Ins, DT, TLI, CurLoop, SafetyInfo, ORE,
- Preheader->getTerminator(), AC,
- AllowSpeculation))
- return false;
Value *IOp1 = Ins->getOperand(1);
Value *IOp2 = Ins->getOperand(2);
>From c7ca1f4c229ecc597d52fabc825e0600e6bc6510 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 1 Jun 2026 14:37:39 -0700
Subject: [PATCH 04/15] [LICM] Make CanBypassInsert a static function
---
llvm/lib/Transforms/Scalar/LICM.cpp | 102 +++++++++++++++-------------
1 file changed, 54 insertions(+), 48 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 88d14ffe749f8..d3454d66eddbe 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1074,6 +1074,56 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
return Changed;
}
+static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
+ Loop *CurLoop,
+ bool IsHoistedInstruction,
+ SmallSet<uint64_t, 4> &SeenIndices) {
+ // Instruction being hoisted past must only have one use
+ if (!IsHoistedInstruction && !CurrIns->hasOneUse())
+ return nullptr;
+
+ // Must have constant insertion lane
+ auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
+ if (!InsertedIdxCI)
+ return nullptr;
+ auto *VecTy = cast<VectorType>(CurrIns->getType());
+
+ // Avoid hoisting past out of bounds inserts
+ if (InsertedIdxCI->isNegative() ||
+ InsertedIdxCI->getValue().uge(
+ VecTy->getElementCount().getKnownMinValue()))
+ return nullptr;
+
+ // Make sure not hoisting past insertions into the same lane
+ if (!SeenIndices.insert(InsertedIdxCI->getValue().getLimitedValue()).second)
+ return nullptr;
+
+ // The instruction we are hoisting must have invariant insertion data
+ Value *InsertedElt = CurrIns->getOperand(1);
+ if (IsHoistedInstruction && !CurLoop->isLoopInvariant(InsertedElt))
+ return nullptr;
+
+ // Only hoist past other insertions
+ Value *InnerVal = CurrIns->getOperand(0);
+ auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
+ if (IsHoistedInstruction && !InnerIns)
+ return nullptr;
+
+ // If the value we are inserting into is not invariant/poison, recurse
+ // if it is another insert
+ if (!CurLoop->isLoopInvariant(InnerVal) && !isa<PoisonValue>(InnerVal)) {
+ if (InnerIns && InnerIns->getParent() == CurrIns->getParent())
+ return canBypassInsert(InnerIns, CurLoop, /*IsHoistedInstruction*/ false,
+ SeenIndices);
+ return nullptr;
+ }
+ // Hoists of Insertions with fully invariant operands are handled in base
+ // logic
+ if (!IsHoistedInstruction)
+ return CurrIns;
+ return nullptr;
+}
+
static bool hoistInsertPastInsert(
InsertElementInst *Ins, Loop *CurLoop, AAResults *AA, DominatorTree *DT,
const TargetLibraryInfo *TLI, BasicBlock *Preheader, BasicBlock *HoistDest,
@@ -1082,53 +1132,6 @@ static bool hoistInsertPastInsert(
OptimizationRemarkEmitter *ORE,
SmallVectorImpl<Instruction *> &HoistedInstructions,
bool AllowSpeculation) {
- SmallSet<uint64_t, 4> SeenIndexes;
- InsertElementInst *Inner;
- auto CanBypass = [&](auto &CanBypass, InsertElementInst *CurrIns,
- bool IsHoistedInstruction) -> bool {
- // Instruction being hoisted past must only have one use
- if (!IsHoistedInstruction && !CurrIns->hasOneUse())
- return false;
-
- // Must have constant insertion lane
- auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
- if (!InsertedIdxCI)
- return false;
- auto *VecTy = cast<VectorType>(CurrIns->getType());
-
- // Avoid hoisting past out of bounds inserts
- if (InsertedIdxCI->isNegative() ||
- InsertedIdxCI->getValue().uge(
- VecTy->getElementCount().getKnownMinValue()))
- return false;
-
- // Make sure not hoisting past insertions into the same lane
- if (!SeenIndexes.insert(InsertedIdxCI->getValue().getLimitedValue()).second)
- return false;
-
- // The instruction we are hoisting must have invariant insertion data
- Value *InsertedElt = CurrIns->getOperand(1);
- if (IsHoistedInstruction && !CurLoop->isLoopInvariant(InsertedElt))
- return false;
-
- // Only hoist past other insertions
- Value *InnerVal = CurrIns->getOperand(0);
- auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
- if (IsHoistedInstruction && !InnerIns)
- return false;
-
- // If the value we are inserting into is not invariant/poison, recurse
- // if it is another insert
- if (!CurLoop->isLoopInvariant(InnerVal) && !isa<PoisonValue>(InnerVal)) {
- return InnerIns && InnerIns->getParent() == CurrIns->getParent() &&
- CanBypass(CanBypass, InnerIns, /*IsHoistedInstruction*/ false);
- }
- Inner = CurrIns;
- // Hoists of Insertions with fully invariant operands are handled in base
- // logic
- return !IsHoistedInstruction;
- };
-
// Canonicalize:
// %inner = insertelement %base, %variant, C1
// %outer = insertelement %inner, %invariant, C2
@@ -1136,7 +1139,10 @@ static bool hoistInsertPastInsert(
// %inner' = insertelement %base, %invariant, C2
// %outer' = insertelement %inner', %variant, C1
// so we can push the variant insertelement through the shuffle.
- if (!CanBypass(CanBypass, Ins, /*IsHoistedInstruction*/ true))
+ SmallSet<uint64_t, 4> SeenIndices;
+ InsertElementInst *Inner =
+ canBypassInsert(Ins, CurLoop, /*IsHoistedInstruction*/ true, SeenIndices);
+ if (!Inner)
return false;
Value *IOp1 = Ins->getOperand(1);
>From 59ac7526ac3a8da11ddc06e8c1271e07a8596479 Mon Sep 17 00:00:00 2001
From: Ryan Buchner <buchner.ryan at gmail.com>
Date: Tue, 2 Jun 2026 09:01:28 -0700
Subject: [PATCH 05/15] Update llvm/lib/Transforms/Scalar/LICM.cpp
Co-authored-by: Luke Lau <luke_lau at icloud.com>
---
llvm/lib/Transforms/Scalar/LICM.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index d3454d66eddbe..7239aa02dda6a 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1142,7 +1142,7 @@ static bool hoistInsertPastInsert(
SmallSet<uint64_t, 4> SeenIndices;
InsertElementInst *Inner =
canBypassInsert(Ins, CurLoop, /*IsHoistedInstruction*/ true, SeenIndices);
- if (!Inner)
+ if (!Inner && Inner != Ins)
return false;
Value *IOp1 = Ins->getOperand(1);
>From d2fab8a8e8f91a8303d2c6d1938dfb6d5b945d68 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 2 Jun 2026 10:13:05 -0700
Subject: [PATCH 06/15] [LICM] Refactor to remove IsHoistedINstruction
parameter from canBypassInsert
---
llvm/lib/Transforms/Scalar/LICM.cpp | 47 ++++++++++++-----------------
1 file changed, 20 insertions(+), 27 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 7239aa02dda6a..9e661d24c48fb 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1076,12 +1076,7 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
Loop *CurLoop,
- bool IsHoistedInstruction,
SmallSet<uint64_t, 4> &SeenIndices) {
- // Instruction being hoisted past must only have one use
- if (!IsHoistedInstruction && !CurrIns->hasOneUse())
- return nullptr;
-
// Must have constant insertion lane
auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
if (!InsertedIdxCI)
@@ -1098,30 +1093,23 @@ static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
if (!SeenIndices.insert(InsertedIdxCI->getValue().getLimitedValue()).second)
return nullptr;
- // The instruction we are hoisting must have invariant insertion data
- Value *InsertedElt = CurrIns->getOperand(1);
- if (IsHoistedInstruction && !CurLoop->isLoopInvariant(InsertedElt))
- return nullptr;
-
- // Only hoist past other insertions
Value *InnerVal = CurrIns->getOperand(0);
- auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
- if (IsHoistedInstruction && !InnerIns)
- return nullptr;
-
// If the value we are inserting into is not invariant/poison, recurse
// if it is another insert
if (!CurLoop->isLoopInvariant(InnerVal) && !isa<PoisonValue>(InnerVal)) {
- if (InnerIns && InnerIns->getParent() == CurrIns->getParent())
- return canBypassInsert(InnerIns, CurLoop, /*IsHoistedInstruction*/ false,
- SeenIndices);
- return nullptr;
+ // Only hoist past other insertions
+ // All instructions in the chain must be in the same basic block
+ auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
+ if (!InnerIns || InnerIns->getParent() != CurrIns->getParent())
+ return nullptr;
+
+ // Instruction being hoisted past must only have one use
+ if (!InnerIns->hasOneUse())
+ return nullptr;
+
+ return canBypassInsert(InnerIns, CurLoop, SeenIndices);
}
- // Hoists of Insertions with fully invariant operands are handled in base
- // logic
- if (!IsHoistedInstruction)
- return CurrIns;
- return nullptr;
+ return CurrIns;
}
static bool hoistInsertPastInsert(
@@ -1139,10 +1127,15 @@ static bool hoistInsertPastInsert(
// %inner' = insertelement %base, %invariant, C2
// %outer' = insertelement %inner', %variant, C1
// so we can push the variant insertelement through the shuffle.
+
+ // The instruction we are hoisting must have invariant insertion data
+ Value *InsertedElt = Ins->getOperand(1);
+ if (!CurLoop->isLoopInvariant(InsertedElt))
+ return false;
+
SmallSet<uint64_t, 4> SeenIndices;
- InsertElementInst *Inner =
- canBypassInsert(Ins, CurLoop, /*IsHoistedInstruction*/ true, SeenIndices);
- if (!Inner && Inner != Ins)
+ InsertElementInst *Inner = canBypassInsert(Ins, CurLoop, SeenIndices);
+ if (!Inner || Inner == Ins)
return false;
Value *IOp1 = Ins->getOperand(1);
>From 1d900e9fddfdcf47c1d9912c4d1d2feea92e2e01 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Sat, 6 Jun 2026 12:36:22 -0700
Subject: [PATCH 07/15] Hoist shuffle fix index tracking
---
llvm/lib/Transforms/Scalar/LICM.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 885c70846ee15..2a31d8965349d 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1076,7 +1076,7 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
Loop *CurLoop,
- SmallSet<uint64_t, 4> &SeenIndices) {
+ std::optional<uint64_t> HoistIndex) {
// Must have constant insertion lane
auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
if (!InsertedIdxCI)
@@ -1090,7 +1090,9 @@ static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
return nullptr;
// Make sure not hoisting past insertions into the same lane
- if (!SeenIndices.insert(InsertedIdxCI->getValue().getLimitedValue()).second)
+ if (!HoistIndex)
+ HoistIndex = InsertedIdxCI->getValue().getLimitedValue();
+ else if (*HoistIndex == InsertedIdxCI->getValue().getLimitedValue())
return nullptr;
Value *InnerVal = CurrIns->getOperand(0);
@@ -1107,7 +1109,7 @@ static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
if (!InnerIns->hasOneUse())
return nullptr;
- return canBypassInsert(InnerIns, CurLoop, SeenIndices);
+ return canBypassInsert(InnerIns, CurLoop, HoistIndex);
}
return CurrIns;
}
@@ -1133,8 +1135,8 @@ static bool hoistInsertPastInsert(
if (!CurLoop->isLoopInvariant(InsertedElt))
return false;
- SmallSet<uint64_t, 4> SeenIndices;
- InsertElementInst *Inner = canBypassInsert(Ins, CurLoop, SeenIndices);
+ InsertElementInst *Inner =
+ canBypassInsert(Ins, CurLoop, /*HoistIndex*/ std::nullopt);
if (!Inner || Inner == Ins)
return false;
>From 090ebe789e5ced88ba637c635af92b56bbe7b7a4 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Sat, 6 Jun 2026 12:39:28 -0700
Subject: [PATCH 08/15] Remove extra arguments
---
llvm/lib/Transforms/Scalar/LICM.cpp | 32 +++++++++++++----------------
1 file changed, 14 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 2a31d8965349d..fe4a7a6525198 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -204,13 +204,12 @@ static bool hoistArithmetics(Instruction &I, Loop &L,
ICFLoopSafetyInfo &SafetyInfo,
MemorySSAUpdater &MSSAU, AssumptionCache *AC,
DominatorTree *DT);
-static bool hoistInsertPastInsert(
- InsertElementInst *Ins, Loop *CurLoop, AAResults *AA, DominatorTree *DT,
- const TargetLibraryInfo *TLI, BasicBlock *Preheader, BasicBlock *HoistDest,
- ICFLoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU, AssumptionCache *AC,
- ScalarEvolution *SE, SinkAndHoistLICMFlags &Flags,
- OptimizationRemarkEmitter *ORE,
- SmallVectorImpl<Instruction *> &HoistedInstructions, bool AllowSpeculation);
+static bool
+hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
+ BasicBlock *HoistDest, ICFLoopSafetyInfo *SafetyInfo,
+ MemorySSAUpdater &MSSAU, ScalarEvolution *SE,
+ OptimizationRemarkEmitter *ORE,
+ SmallVectorImpl<Instruction *> &HoistedInstructions);
static Instruction *cloneInstructionInExitBlock(
Instruction &I, BasicBlock &ExitBlock, PHINode &PN, const LoopInfo *LI,
const LoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU);
@@ -944,10 +943,9 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
}
if (auto *Ins = dyn_cast<InsertElementInst>(&I))
- if (hoistInsertPastInsert(Ins, CurLoop, AA, DT, TLI, Preheader,
+ if (hoistInsertPastInsert(Ins, CurLoop, DT,
CFH.getOrCreateHoistedBlock(BB), SafetyInfo,
- MSSAU, AC, SE, Flags, ORE,
- HoistedInstructions, AllowSpeculation)) {
+ MSSAU, SE, ORE, HoistedInstructions)) {
Changed = true;
continue;
}
@@ -1114,14 +1112,12 @@ static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
return CurrIns;
}
-static bool hoistInsertPastInsert(
- InsertElementInst *Ins, Loop *CurLoop, AAResults *AA, DominatorTree *DT,
- const TargetLibraryInfo *TLI, BasicBlock *Preheader, BasicBlock *HoistDest,
- ICFLoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU, AssumptionCache *AC,
- ScalarEvolution *SE, SinkAndHoistLICMFlags &Flags,
- OptimizationRemarkEmitter *ORE,
- SmallVectorImpl<Instruction *> &HoistedInstructions,
- bool AllowSpeculation) {
+static bool
+hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
+ BasicBlock *HoistDest, ICFLoopSafetyInfo *SafetyInfo,
+ MemorySSAUpdater &MSSAU, ScalarEvolution *SE,
+ OptimizationRemarkEmitter *ORE,
+ SmallVectorImpl<Instruction *> &HoistedInstructions) {
// Canonicalize:
// %inner = insertelement %base, %variant, C1
// %outer = insertelement %inner, %invariant, C2
>From 037d277e2d521f38e8c054068ff726c050b653ed Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 08:29:13 -0700
Subject: [PATCH 09/15] Fix comment
---
llvm/lib/Transforms/Scalar/LICM.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index fe4a7a6525198..4ace70c4b11f3 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1075,7 +1075,7 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
Loop *CurLoop,
std::optional<uint64_t> HoistIndex) {
- // Must have constant insertion lane
+ // Must have constant insertion lane.
auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
if (!InsertedIdxCI)
return nullptr;
>From 119b4662aa75352489020de82ad688db9fa47e53 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 08:50:29 -0700
Subject: [PATCH 10/15] Don't need explicit poison check
---
llvm/lib/Transforms/Scalar/LICM.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 4ace70c4b11f3..8c12b8e8fc4fc 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1096,7 +1096,7 @@ static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
Value *InnerVal = CurrIns->getOperand(0);
// If the value we are inserting into is not invariant/poison, recurse
// if it is another insert
- if (!CurLoop->isLoopInvariant(InnerVal) && !isa<PoisonValue>(InnerVal)) {
+ if (!CurLoop->isLoopInvariant(InnerVal)) {
// Only hoist past other insertions
// All instructions in the chain must be in the same basic block
auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
>From 80e781c68fae3fb74fb8bae28c9a8c47cb9c690d Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 09:13:29 -0700
Subject: [PATCH 11/15] Pull out getConstantInsertionIndex()
---
llvm/lib/Transforms/Scalar/LICM.cpp | 25 ++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 8c12b8e8fc4fc..9bfa6e23c63cf 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1072,25 +1072,32 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
return Changed;
}
-static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
- Loop *CurLoop,
- std::optional<uint64_t> HoistIndex) {
+static std::optional<uint64_t>
+getConstantInsertionIndex(InsertElementInst *Ins) {
// Must have constant insertion lane.
- auto *InsertedIdxCI = dyn_cast<ConstantInt>(CurrIns->getOperand(2));
+ auto *InsertedIdxCI = dyn_cast<ConstantInt>(Ins->getOperand(2));
if (!InsertedIdxCI)
- return nullptr;
- auto *VecTy = cast<VectorType>(CurrIns->getType());
+ return std::nullopt;
+ auto *VecTy = cast<VectorType>(Ins->getType());
// Avoid hoisting past out of bounds inserts
if (InsertedIdxCI->isNegative() ||
InsertedIdxCI->getValue().uge(
VecTy->getElementCount().getKnownMinValue()))
- return nullptr;
+ return std::nullopt;
+ return InsertedIdxCI->getValue().getLimitedValue();
+}
+static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
+ Loop *CurLoop,
+ std::optional<uint64_t> HoistIndex) {
// Make sure not hoisting past insertions into the same lane
+ std::optional<uint64_t> InsertIdx = getConstantInsertionIndex(CurrIns);
+ if (!InsertIdx)
+ return nullptr;
if (!HoistIndex)
- HoistIndex = InsertedIdxCI->getValue().getLimitedValue();
- else if (*HoistIndex == InsertedIdxCI->getValue().getLimitedValue())
+ HoistIndex = InsertIdx;
+ else if (*HoistIndex == *InsertIdx)
return nullptr;
Value *InnerVal = CurrIns->getOperand(0);
>From 4967345b9bb24c2f607d5399d5174313bcb6c36c Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 09:46:53 -0700
Subject: [PATCH 12/15] [LICM] Make iterative
---
llvm/lib/Transforms/Scalar/LICM.cpp | 66 ++++++++++++++---------------
1 file changed, 32 insertions(+), 34 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 9bfa6e23c63cf..9c04fce846b17 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1088,37 +1088,6 @@ getConstantInsertionIndex(InsertElementInst *Ins) {
return InsertedIdxCI->getValue().getLimitedValue();
}
-static InsertElementInst *canBypassInsert(InsertElementInst *CurrIns,
- Loop *CurLoop,
- std::optional<uint64_t> HoistIndex) {
- // Make sure not hoisting past insertions into the same lane
- std::optional<uint64_t> InsertIdx = getConstantInsertionIndex(CurrIns);
- if (!InsertIdx)
- return nullptr;
- if (!HoistIndex)
- HoistIndex = InsertIdx;
- else if (*HoistIndex == *InsertIdx)
- return nullptr;
-
- Value *InnerVal = CurrIns->getOperand(0);
- // If the value we are inserting into is not invariant/poison, recurse
- // if it is another insert
- if (!CurLoop->isLoopInvariant(InnerVal)) {
- // Only hoist past other insertions
- // All instructions in the chain must be in the same basic block
- auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
- if (!InnerIns || InnerIns->getParent() != CurrIns->getParent())
- return nullptr;
-
- // Instruction being hoisted past must only have one use
- if (!InnerIns->hasOneUse())
- return nullptr;
-
- return canBypassInsert(InnerIns, CurLoop, HoistIndex);
- }
- return CurrIns;
-}
-
static bool
hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
BasicBlock *HoistDest, ICFLoopSafetyInfo *SafetyInfo,
@@ -1138,9 +1107,38 @@ hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
if (!CurLoop->isLoopInvariant(InsertedElt))
return false;
- InsertElementInst *Inner =
- canBypassInsert(Ins, CurLoop, /*HoistIndex*/ std::nullopt);
- if (!Inner || Inner == Ins)
+ std::optional<uint64_t> InsertIdx = getConstantInsertionIndex(Ins);
+ if (!InsertIdx)
+ return false;
+ uint64_t HoistIdx = *InsertIdx;
+
+ InsertElementInst *Inner = Ins;
+ while (true) {
+ Value *InnerVal = Inner->getOperand(0);
+ if (CurLoop->isLoopInvariant(InnerVal))
+ break;
+
+ // If the inner value isn't invariant, check to see if it is another insert
+ // All instructions in the chain must be in the same basic block
+ auto *InnerIns = dyn_cast<InsertElementInst>(InnerVal);
+ if (!InnerIns || InnerIns->getParent() != Ins->getParent())
+ return false;
+
+ // Make sure not hoisting past insertions into the same lane
+ std::optional<uint64_t> InsertIdx = getConstantInsertionIndex(InnerIns);
+ if (!InsertIdx || *InsertIdx == HoistIdx)
+ return false;
+
+ // Instruction being hoisted past must only have one use
+ if (!InnerIns->hasOneUse())
+ return false;
+
+ Inner = InnerIns;
+ }
+
+ // Base case of `insertelement <4 x i8> %invar0, i8 %invar1, i32 2` handled in
+ // base LICM logic
+ if (Inner == Ins)
return false;
Value *IOp1 = Ins->getOperand(1);
>From 970e2220a34cade4a9152104434b3fb58117881f Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 10:04:04 -0700
Subject: [PATCH 13/15] Fix hoisting logic to be robust to reused indices
---
llvm/lib/Transforms/Scalar/LICM.cpp | 15 ++++++---------
llvm/test/Transforms/LICM/vector-insert.ll | 4 ++--
2 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 9c04fce846b17..935d1a1ebc52b 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1141,15 +1141,12 @@ hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
if (Inner == Ins)
return false;
- Value *IOp1 = Ins->getOperand(1);
- Value *IOp2 = Ins->getOperand(2);
- Ins->setOperand(1, Inner->getOperand(1));
- Ins->setOperand(2, Inner->getOperand(2));
- Inner->setOperand(1, IOp1);
- Inner->setOperand(2, IOp2);
-
- hoist(*Inner, DT, CurLoop, HoistDest, SafetyInfo, MSSAU, SE, ORE);
- HoistedInstructions.push_back(Inner);
+ Ins->replaceAllUsesWith(Ins->getOperand(0));
+ Ins->moveBefore(Inner->getIterator());
+ Ins->setOperand(0, Inner->getOperand(0));
+ Inner->setOperand(0, Ins);
+ hoist(*Ins, DT, CurLoop, HoistDest, SafetyInfo, MSSAU, SE, ORE);
+ HoistedInstructions.push_back(Ins);
return true;
}
diff --git a/llvm/test/Transforms/LICM/vector-insert.ll b/llvm/test/Transforms/LICM/vector-insert.ll
index a7fa31c16819a..c974721170e62 100644
--- a/llvm/test/Transforms/LICM/vector-insert.ll
+++ b/llvm/test/Transforms/LICM/vector-insert.ll
@@ -177,8 +177,8 @@ define <4 x i32> @hoist_multiple_variants(ptr %base, i32 %inv, i32 %n) {
; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IDX_LE:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i32 [[IV_LCSSA]]
; CHECK-NEXT: [[VARIANT_LE:%.*]] = load i32, ptr [[IDX_LE]], align 4
-; CHECK-NEXT: [[INNER2_LE:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[VARIANT_LE]], i32 1
-; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER2_LE]], i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[INNER:%.*]] = insertelement <4 x i32> [[INNER_LE]], i32 [[VARIANT_LE]], i32 0
+; CHECK-NEXT: [[OUTER_LE:%.*]] = insertelement <4 x i32> [[INNER]], i32 [[VARIANT_LE]], i32 1
; CHECK-NEXT: store <4 x i32> [[OUTER_LE]], ptr [[IDX_LE]], align 16
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV_LCSSA]], 1
; CHECK-NEXT: [[DONE:%.*]] = icmp eq i32 [[IV_LCSSA]], [[N]]
>From 51c3048691ac43d476172ab4624ad78b75eeb77f Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 10:33:03 -0700
Subject: [PATCH 14/15] Fix comment
---
llvm/lib/Transforms/Scalar/LICM.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 935d1a1ebc52b..7f61a160245f1 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1098,9 +1098,9 @@ hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
// %inner = insertelement %base, %variant, C1
// %outer = insertelement %inner, %invariant, C2
// into:
- // %inner' = insertelement %base, %invariant, C2
- // %outer' = insertelement %inner', %variant, C1
- // so we can push the variant insertelement through the shuffle.
+ // %outer = insertelement %base, %invariant, C2
+ // %inner = insertelement %outer, %variant, C1
+ // so we can hoist %outer
// The instruction we are hoisting must have invariant insertion data
Value *InsertedElt = Ins->getOperand(1);
>From 10c113b26645957803ec4a6cf74c690a3200e069 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 8 Jun 2026 10:43:54 -0700
Subject: [PATCH 15/15] Clarify variable names
---
llvm/lib/Transforms/Scalar/LICM.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 7f61a160245f1..c03eb9e308601 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -1107,10 +1107,9 @@ hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
if (!CurLoop->isLoopInvariant(InsertedElt))
return false;
- std::optional<uint64_t> InsertIdx = getConstantInsertionIndex(Ins);
- if (!InsertIdx)
+ std::optional<uint64_t> HoistIdx = getConstantInsertionIndex(Ins);
+ if (!HoistIdx)
return false;
- uint64_t HoistIdx = *InsertIdx;
InsertElementInst *Inner = Ins;
while (true) {
@@ -1126,7 +1125,7 @@ hoistInsertPastInsert(InsertElementInst *Ins, Loop *CurLoop, DominatorTree *DT,
// Make sure not hoisting past insertions into the same lane
std::optional<uint64_t> InsertIdx = getConstantInsertionIndex(InnerIns);
- if (!InsertIdx || *InsertIdx == HoistIdx)
+ if (!InsertIdx || *InsertIdx == *HoistIdx)
return false;
// Instruction being hoisted past must only have one use
More information about the llvm-commits
mailing list