[llvm] [VectorCombine] Fold contiguous loads into a single vector load (PR #185736)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 07:11:25 PDT 2026


================
@@ -0,0 +1,676 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=vector-combine -S -mtriple=x86_64-- | FileCheck %s --check-prefixes=CHECK
+
+; 1. Basic behavior: Extract subvector from a wide load with offset (Success case)
+define <2 x float> @extract_subvector_with_offset(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @extract_subvector_with_offset(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[ARG0]], i64 48
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x float>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x float> [[TMP2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %v2
+}
+
+define <2 x float> @large_offset_without_signed_overflow(ptr %p) {
+; CHECK-LABEL: define <2 x float> @large_offset_without_signed_overflow(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 -9223372036854775808
+; CHECK-NEXT:    [[R:%.*]] = load <2 x float>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %q = getelementptr i8, ptr %p, i64 9223372036854775800
+  %v = load <4 x float>, ptr %q, align 8
+  %r = shufflevector <4 x float> %v, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %r
+}
+
+; GEP base type i32: 10 * sizeof(i32) = 40 bytes.
+define <2 x float> @extract_subvector_with_offset_gep_i32(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @extract_subvector_with_offset_gep_i32(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[ARG0]], i64 48
+; CHECK-NEXT:    [[V2:%.*]] = load <2 x float>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i32, ptr %arg0, i64 10
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %v2
+}
+
+; GEP base type i64: 5 * sizeof(i64) = 40 bytes.
+define <2 x float> @extract_subvector_with_offset_gep_i64(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @extract_subvector_with_offset_gep_i64(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[ARG0]], i64 48
+; CHECK-NEXT:    [[V2:%.*]] = load <2 x float>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i64, ptr %arg0, i64 5
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %v2
+}
+
+; 2. Basic behavior: Extract middle subvector with different type (Success case)
+define <2 x double> @extract_subvector_middle(ptr %arg0) {
+; CHECK-LABEL: define <2 x double> @extract_subvector_middle(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[ARG0]], i64 48
+; CHECK-NEXT:    [[V2:%.*]] = load <2 x double>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x double> [[V2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load <4 x double>, ptr %v0, align 16
+  %v2 = shufflevector <4 x double> %v1, <4 x double> poison, <2 x i32> <i32 1, i32 2>
+  ret <2 x double> %v2
+}
+
+define <2 x double> @extract_subvector_middle_gep_i32(ptr %arg0) {
+; CHECK-LABEL: define <2 x double> @extract_subvector_middle_gep_i32(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[ARG0]], i64 48
+; CHECK-NEXT:    [[V2:%.*]] = load <2 x double>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x double> [[V2]]
+;
+  %v0 = getelementptr inbounds i32, ptr %arg0, i64 10
+  %v1 = load <4 x double>, ptr %v0, align 16
+  %v2 = shufflevector <4 x double> %v1, <4 x double> poison, <2 x i32> <i32 1, i32 2>
+  ret <2 x double> %v2
+}
+
+define <2 x double> @extract_subvector_middle_gep_i64(ptr %arg0) {
+; CHECK-LABEL: define <2 x double> @extract_subvector_middle_gep_i64(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[ARG0]], i64 48
+; CHECK-NEXT:    [[V2:%.*]] = load <2 x double>, ptr [[TMP1]], align 8
+; CHECK-NEXT:    ret <2 x double> [[V2]]
+;
+  %v0 = getelementptr inbounds i64, ptr %arg0, i64 5
+  %v1 = load <4 x double>, ptr %v0, align 16
+  %v2 = shufflevector <4 x double> %v1, <4 x double> poison, <2 x i32> <i32 1, i32 2>
+  ret <2 x double> %v2
+}
+
+define <3 x i32> @test_odd_number_elements(ptr align 16 dereferenceable(16) %p) {
+; CHECK-LABEL: define <3 x i32> @test_odd_number_elements(
+; CHECK-SAME: ptr align 16 dereferenceable(16) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <3 x i32>, ptr [[P]], align 8
+; CHECK-NEXT:    ret <3 x i32> [[RES]]
+;
+  %L0 = load <2 x i32>, ptr %p, align 8
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %L1 = load <2 x i32>, ptr %p1, align 8
+  %res = shufflevector <2 x i32> %L0, <2 x i32> %L1, <3 x i32> <i32 0, i32 1, i32 2>
+  ret <3 x i32> %res
+}
+
+define <3 x i32> @test_odd_number_elements_gep_i32(ptr align 16 dereferenceable(16) %p) {
+; CHECK-LABEL: define <3 x i32> @test_odd_number_elements_gep_i32(
+; CHECK-SAME: ptr align 16 dereferenceable(16) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <3 x i32>, ptr [[P]], align 8
+; CHECK-NEXT:    ret <3 x i32> [[RES]]
+;
+  %L0 = load <2 x i32>, ptr %p, align 8
+  %p1 = getelementptr i32, ptr %p, i64 2
+  %L1 = load <2 x i32>, ptr %p1, align 8
+  %res = shufflevector <2 x i32> %L0, <2 x i32> %L1, <3 x i32> <i32 0, i32 1, i32 2>
+  ret <3 x i32> %res
+}
+
+define <3 x i32> @test_odd_number_elements_gep_i64(ptr align 16 dereferenceable(16) %p) {
+; CHECK-LABEL: define <3 x i32> @test_odd_number_elements_gep_i64(
+; CHECK-SAME: ptr align 16 dereferenceable(16) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <3 x i32>, ptr [[P]], align 8
+; CHECK-NEXT:    ret <3 x i32> [[RES]]
+;
+  %L0 = load <2 x i32>, ptr %p, align 8
+  %p1 = getelementptr i64, ptr %p, i64 1
+  %L1 = load <2 x i32>, ptr %p1, align 8
+  %res = shufflevector <2 x i32> %L0, <2 x i32> %L1, <3 x i32> <i32 0, i32 1, i32 2>
+  ret <3 x i32> %res
+}
+
+define <4 x i32> @test_odd_number_load_to_even_svi(ptr align 16 dereferenceable(32) %p) {
+; CHECK-LABEL: define <4 x i32> @test_odd_number_load_to_even_svi(
+; CHECK-SAME: ptr align 16 dereferenceable(32) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <4 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    ret <4 x i32> [[RES]]
+;
+  %L0 = load <3 x i32>, ptr %p, align 16
+  %p1 = getelementptr i8, ptr %p, i64 12
+  %L1 = load <3 x i32>, ptr %p1, align 4
+  %res = shufflevector <3 x i32> %L0, <3 x i32> %L1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %res
+}
+
+define <4 x i32> @test_odd_number_load_to_even_svi_gep_i32(ptr align 16 dereferenceable(32) %p) {
+; CHECK-LABEL: define <4 x i32> @test_odd_number_load_to_even_svi_gep_i32(
+; CHECK-SAME: ptr align 16 dereferenceable(32) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <4 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    ret <4 x i32> [[RES]]
+;
+  %L0 = load <3 x i32>, ptr %p, align 16
+  %p1 = getelementptr i32, ptr %p, i64 3
+  %L1 = load <3 x i32>, ptr %p1, align 4
+  %res = shufflevector <3 x i32> %L0, <3 x i32> %L1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %res
+}
+
+define <4 x i32> @test_odd_number_load_to_even_svi_gep_i64(ptr align 16 dereferenceable(32) %p) {
+; CHECK-LABEL: define <4 x i32> @test_odd_number_load_to_even_svi_gep_i64(
+; CHECK-SAME: ptr align 16 dereferenceable(32) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <4 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    ret <4 x i32> [[RES]]
+;
+  %L0 = load <3 x i32>, ptr %p, align 16
+  %p1.base = getelementptr i64, ptr %p, i64 1
+  %p1 = getelementptr i8, ptr %p1.base, i64 4
+  %L1 = load <3 x i32>, ptr %p1, align 4
+  %res = shufflevector <3 x i32> %L0, <3 x i32> %L1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %res
+}
+
+define <11 x i32> @test_odd_to_odd_large_mask(ptr align 64 dereferenceable(64) %p) {
+; CHECK-LABEL: define <11 x i32> @test_odd_to_odd_large_mask(
+; CHECK-SAME: ptr align 64 dereferenceable(64) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <11 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    ret <11 x i32> [[RES]]
+;
+  %L0 = load <7 x i32>, ptr %p, align 16
+  %p1 = getelementptr i8, ptr %p, i64 28
+  %L1 = load <7 x i32>, ptr %p1, align 4
+  %res = shufflevector <7 x i32> %L0, <7 x i32> %L1, <11 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10>
+  ret <11 x i32> %res
+}
+
+define <11 x i32> @test_odd_to_odd_large_mask_gep_i32(ptr align 64 dereferenceable(64) %p) {
+; CHECK-LABEL: define <11 x i32> @test_odd_to_odd_large_mask_gep_i32(
+; CHECK-SAME: ptr align 64 dereferenceable(64) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <11 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    ret <11 x i32> [[RES]]
+;
+  %L0 = load <7 x i32>, ptr %p, align 16
+  %p1 = getelementptr i32, ptr %p, i64 7
+  %L1 = load <7 x i32>, ptr %p1, align 4
+  %res = shufflevector <7 x i32> %L0, <7 x i32> %L1, <11 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10>
+  ret <11 x i32> %res
+}
+
+define <11 x i32> @test_odd_to_odd_large_mask_gep_i64(ptr align 64 dereferenceable(64) %p) {
+; CHECK-LABEL: define <11 x i32> @test_odd_to_odd_large_mask_gep_i64(
+; CHECK-SAME: ptr align 64 dereferenceable(64) [[P:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = load <11 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    ret <11 x i32> [[RES]]
+;
+  %L0 = load <7 x i32>, ptr %p, align 16
+  %p1.base = getelementptr i64, ptr %p, i64 3
+  %p1 = getelementptr i8, ptr %p1.base, i64 4
+  %L1 = load <7 x i32>, ptr %p1, align 4
+  %res = shufflevector <7 x i32> %L0, <7 x i32> %L1, <11 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10>
+  ret <11 x i32> %res
+}
+
+define <5 x i16> @test_load_17_mask_5_mid_offset_i16(ptr align 16 dereferenceable(68) %p) {
+; CHECK-LABEL: define <5 x i16> @test_load_17_mask_5_mid_offset_i16(
+; CHECK-SAME: ptr align 16 dereferenceable(68) [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 30
+; CHECK-NEXT:    [[RES:%.*]] = load <5 x i16>, ptr [[TMP1]], align 2
+; CHECK-NEXT:    ret <5 x i16> [[RES]]
+;
+  %L0 = load <17 x i16>, ptr %p, align 16
+  %p1 = getelementptr i8, ptr %p, i64 34
+  %L1 = load <17 x i16>, ptr %p1, align 2
+  %res = shufflevector <17 x i16> %L0, <17 x i16> %L1, <5 x i32> <i32 15, i32 16, i32 17, i32 18, i32 19>
+  ret <5 x i16> %res
+}
+
+define <5 x i16> @test_load_17_mask_5_mid_offset_i16_gep_i32(ptr align 16 dereferenceable(68) %p) {
+; CHECK-LABEL: define <5 x i16> @test_load_17_mask_5_mid_offset_i16_gep_i32(
+; CHECK-SAME: ptr align 16 dereferenceable(68) [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 30
+; CHECK-NEXT:    [[RES:%.*]] = load <5 x i16>, ptr [[TMP1]], align 2
+; CHECK-NEXT:    ret <5 x i16> [[RES]]
+;
+  %L0 = load <17 x i16>, ptr %p, align 16
+  %p1.base = getelementptr i32, ptr %p, i64 8
+  %p1 = getelementptr i8, ptr %p1.base, i64 2
+  %L1 = load <17 x i16>, ptr %p1, align 2
+  %res = shufflevector <17 x i16> %L0, <17 x i16> %L1, <5 x i32> <i32 15, i32 16, i32 17, i32 18, i32 19>
+  ret <5 x i16> %res
+}
+
+define <5 x i16> @test_load_17_mask_5_mid_offset_i16_gep_i64(ptr align 16 dereferenceable(68) %p) {
+; CHECK-LABEL: define <5 x i16> @test_load_17_mask_5_mid_offset_i16_gep_i64(
+; CHECK-SAME: ptr align 16 dereferenceable(68) [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 30
+; CHECK-NEXT:    [[RES:%.*]] = load <5 x i16>, ptr [[TMP1]], align 2
+; CHECK-NEXT:    ret <5 x i16> [[RES]]
+;
+  %L0 = load <17 x i16>, ptr %p, align 16
+  %p1.base = getelementptr i64, ptr %p, i64 4
+  %p1 = getelementptr i8, ptr %p1.base, i64 2
+  %L1 = load <17 x i16>, ptr %p1, align 2
+  %res = shufflevector <17 x i16> %L0, <17 x i16> %L1, <5 x i32> <i32 15, i32 16, i32 17, i32 18, i32 19>
+  ret <5 x i16> %res
+}
+
+; 3. Negative test: Load instruction has multiple uses (!hasOneUse)
+; Added a store instruction using the load result to the success case.
+define <2 x float> @negative_multi_use(ptr %arg0, ptr %out) {
+; CHECK-LABEL: define <2 x float> @negative_multi_use(
+; CHECK-SAME: ptr [[ARG0:%.*]], ptr [[OUT:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i8, ptr [[ARG0]], i64 40
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    store <4 x float> [[V1]], ptr [[OUT]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load <4 x float>, ptr %v0, align 8
+  store <4 x float> %v1, ptr %out, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %v2
+}
+
+; 4. Negative test: Non-contiguous memory access (Reverse mask)
+; Tweaked the mask to <3, 2> from the success case.
+define <2 x float> @negative_non_contiguous(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @negative_non_contiguous(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i8, ptr [[ARG0]], i64 40
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 3, i32 2>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 3, i32 2>
+  ret <2 x float> %v2
+}
+
+define <2 x float> @negative_non_contiguous_gep_i32(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @negative_non_contiguous_gep_i32(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i32, ptr [[ARG0]], i64 10
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 3, i32 2>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i32, ptr %arg0, i64 10
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 3, i32 2>
+  ret <2 x float> %v2
+}
+
+; 5. Negative test: Volatile load (!isSimple)
+; Added the volatile attribute to the load from the success case.
+define <2 x float> @negative_volatile_load(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @negative_volatile_load(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i8, ptr [[ARG0]], i64 40
+; CHECK-NEXT:    [[V1:%.*]] = load volatile <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load volatile <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %v2
+}
+
+; 6. Negative test: Different pointer bases (Base != CommonBase)
+; Kept the success case structure but mixed in a second pointer base to induce failure.
+define <2 x float> @negative_different_bases(ptr %arg0, ptr %arg1) {
+; CHECK-LABEL: define <2 x float> @negative_different_bases(
+; CHECK-SAME: ptr [[ARG0:%.*]], ptr [[ARG1:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i8, ptr [[ARG0]], i64 40
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V0_OTHER:%.*]] = getelementptr inbounds i8, ptr [[ARG1]], i64 40
+; CHECK-NEXT:    [[V1_OTHER:%.*]] = load <4 x float>, ptr [[V0_OTHER]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> [[V1_OTHER]], <2 x i32> <i32 2, i32 7>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v0_other = getelementptr inbounds i8, ptr %arg1, i64 40
+  %v1_other = load <4 x float>, ptr %v0_other, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> %v1_other, <2 x i32> <i32 2, i32 7>
+  ret <2 x float> %v2
+}
+
+define <2 x float> @negative_different_bases_gep_i32(ptr %arg0, ptr %arg1) {
+; CHECK-LABEL: define <2 x float> @negative_different_bases_gep_i32(
+; CHECK-SAME: ptr [[ARG0:%.*]], ptr [[ARG1:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i32, ptr [[ARG0]], i64 10
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V0_OTHER:%.*]] = getelementptr inbounds i32, ptr [[ARG1]], i64 10
+; CHECK-NEXT:    [[V1_OTHER:%.*]] = load <4 x float>, ptr [[V0_OTHER]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> [[V1_OTHER]], <2 x i32> <i32 2, i32 7>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i32, ptr %arg0, i64 10
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v0_other = getelementptr inbounds i32, ptr %arg1, i64 10
+  %v1_other = load <4 x float>, ptr %v0_other, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> %v1_other, <2 x i32> <i32 2, i32 7>
+  ret <2 x float> %v2
+}
+
+define <2 x float> @negative_different_bases_gep_i64(ptr %arg0, ptr %arg1) {
+; CHECK-LABEL: define <2 x float> @negative_different_bases_gep_i64(
+; CHECK-SAME: ptr [[ARG0:%.*]], ptr [[ARG1:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i64, ptr [[ARG0]], i64 5
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V0_OTHER:%.*]] = getelementptr inbounds i64, ptr [[ARG1]], i64 5
+; CHECK-NEXT:    [[V1_OTHER:%.*]] = load <4 x float>, ptr [[V0_OTHER]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> [[V1_OTHER]], <2 x i32> <i32 2, i32 7>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i64, ptr %arg0, i64 5
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v0_other = getelementptr inbounds i64, ptr %arg1, i64 5
+  %v1_other = load <4 x float>, ptr %v0_other, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> %v1_other, <2 x i32> <i32 2, i32 7>
+  ret <2 x float> %v2
+}
+
+; 7. Negative test: Offset continuity failure (Memory gap)
+; Tweaked the mask to <1, 3> from the success case to create a gap.
+define <2 x float> @negative_memory_gap(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @negative_memory_gap(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i8, ptr [[ARG0]], i64 40
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 1, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i8, ptr %arg0, i64 40
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 1, i32 3>
+  ret <2 x float> %v2
+}
+
+define <2 x float> @negative_memory_gap_gep_i32(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @negative_memory_gap_gep_i32(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i32, ptr [[ARG0]], i64 10
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 1, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i32, ptr %arg0, i64 10
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 1, i32 3>
+  ret <2 x float> %v2
+}
+
+define <2 x float> @negative_memory_gap_gep_i64(ptr %arg0) {
+; CHECK-LABEL: define <2 x float> @negative_memory_gap_gep_i64(
+; CHECK-SAME: ptr [[ARG0:%.*]]) {
+; CHECK-NEXT:    [[V0:%.*]] = getelementptr inbounds i64, ptr [[ARG0]], i64 5
+; CHECK-NEXT:    [[V1:%.*]] = load <4 x float>, ptr [[V0]], align 8
+; CHECK-NEXT:    [[V2:%.*]] = shufflevector <4 x float> [[V1]], <4 x float> poison, <2 x i32> <i32 1, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[V2]]
+;
+  %v0 = getelementptr inbounds i64, ptr %arg0, i64 5
+  %v1 = load <4 x float>, ptr %v0, align 8
+  %v2 = shufflevector <4 x float> %v1, <4 x float> poison, <2 x i32> <i32 1, i32 3>
+  ret <2 x float> %v2
+}
+
+define <4 x i32> @test_element_mismatch(ptr dereferenceable(16) align 16 %p) {
+; CHECK-LABEL: define <4 x i32> @test_element_mismatch(
+; CHECK-SAME: ptr align 16 dereferenceable(16) [[P:%.*]]) {
+; CHECK-NEXT:    [[L0:%.*]] = load <2 x i32>, ptr [[P]], align 8
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr i8, ptr [[P]], i64 8
+; CHECK-NEXT:    [[L1:%.*]] = load <2 x float>, ptr [[P1]], align 8
+; CHECK-NEXT:    [[V1:%.*]] = bitcast <2 x float> [[L1]] to <2 x i32>
+; CHECK-NEXT:    [[RES:%.*]] = shufflevector <2 x i32> [[L0]], <2 x i32> [[V1]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    ret <4 x i32> [[RES]]
+;
+  %L0 = load <2 x i32>, ptr %p, align 8
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %L1 = load <2 x float>, ptr %p1, align 8
+  %v1 = bitcast <2 x float> %L1 to <2 x i32>
+  %res = shufflevector <2 x i32> %L0, <2 x i32> %v1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %res
+}
+
+define <4 x i64> @test_element_mismatch_double(ptr dereferenceable(32) align 32 %p) {
+; CHECK-LABEL: define <4 x i64> @test_element_mismatch_double(
+; CHECK-SAME: ptr align 32 dereferenceable(32) [[P:%.*]]) {
+; CHECK-NEXT:    [[L0:%.*]] = load <2 x i64>, ptr [[P]], align 16
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr i8, ptr [[P]], i64 16
+; CHECK-NEXT:    [[L1:%.*]] = load <2 x double>, ptr [[P1]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = bitcast <2 x double> [[L1]] to <2 x i64>
+; CHECK-NEXT:    [[RES:%.*]] = shufflevector <2 x i64> [[L0]], <2 x i64> [[V1]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    ret <4 x i64> [[RES]]
+;
+  %L0 = load <2 x i64>, ptr %p, align 16
+  %p1 = getelementptr i8, ptr %p, i64 16
+  %L1 = load <2 x double>, ptr %p1, align 16
+  %v1 = bitcast <2 x double> %L1 to <2 x i64>
+  %res = shufflevector <2 x i64> %L0, <2 x i64> %v1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i64> %res
+}
+
+define <4 x i32> @test_element_mismatch_i16_i32(ptr dereferenceable(16) align 16 %p) {
+; CHECK-LABEL: define <4 x i32> @test_element_mismatch_i16_i32(
+; CHECK-SAME: ptr align 16 dereferenceable(16) [[P:%.*]]) {
+; CHECK-NEXT:    [[L0:%.*]] = load <4 x i16>, ptr [[P]], align 8
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr i8, ptr [[P]], i64 8
+; CHECK-NEXT:    [[L1:%.*]] = load <2 x i32>, ptr [[P1]], align 8
+; CHECK-NEXT:    [[V0:%.*]] = bitcast <4 x i16> [[L0]] to <2 x i32>
+; CHECK-NEXT:    [[RES:%.*]] = shufflevector <2 x i32> [[V0]], <2 x i32> [[L1]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    ret <4 x i32> [[RES]]
+;
+  %L0 = load <4 x i16>, ptr %p, align 8
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %L1 = load <2 x i32>, ptr %p1, align 8
+  %v0 = bitcast <4 x i16> %L0 to <2 x i32>
+  %res = shufflevector <2 x i32> %v0, <2 x i32> %L1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %res
+}
+
+define <4 x i64> @test_element_mismatch_i32_i64(ptr dereferenceable(32) align 32 %p) {
+; CHECK-LABEL: define <4 x i64> @test_element_mismatch_i32_i64(
+; CHECK-SAME: ptr align 32 dereferenceable(32) [[P:%.*]]) {
+; CHECK-NEXT:    [[L0:%.*]] = load <4 x i32>, ptr [[P]], align 16
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr i8, ptr [[P]], i64 16
+; CHECK-NEXT:    [[L1:%.*]] = load <2 x i64>, ptr [[P1]], align 16
+; CHECK-NEXT:    [[V0:%.*]] = bitcast <4 x i32> [[L0]] to <2 x i64>
+; CHECK-NEXT:    [[RES:%.*]] = shufflevector <2 x i64> [[V0]], <2 x i64> [[L1]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    ret <4 x i64> [[RES]]
+;
+  %L0 = load <4 x i32>, ptr %p, align 16
+  %p1 = getelementptr i8, ptr %p, i64 16
+  %L1 = load <2 x i64>, ptr %p1, align 16
+  %v0 = bitcast <4 x i32> %L0 to <2 x i64>
+  %res = shufflevector <2 x i64> %v0, <2 x i64> %L1, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i64> %res
+}
+
+define <4 x float> @test_shuffle_poison(ptr align 16 dereferenceable(16) %p) {
+; CHECK-LABEL: define <4 x float> @test_shuffle_poison(
+; CHECK-SAME: ptr align 16 dereferenceable(16) [[P:%.*]]) {
+; CHECK-NEXT:    [[L0:%.*]] = load <2 x float>, ptr [[P]], align 4
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr i8, ptr [[P]], i64 8
+; CHECK-NEXT:    [[L1:%.*]] = load <2 x float>, ptr [[P1]], align 4
+; CHECK-NEXT:    [[RES:%.*]] = shufflevector <2 x float> [[L0]], <2 x float> [[L1]], <4 x i32> <i32 0, i32 poison, i32 2, i32 3>
+; CHECK-NEXT:    ret <4 x float> [[RES]]
+;
+  %L0 = load <2 x float>, ptr %p, align 4
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %L1 = load <2 x float>, ptr %p1, align 4
+  %res = shufflevector <2 x float> %L0, <2 x float> %L1, <4 x i32> <i32 0, i32 poison, i32 2, i32 3>
+  ret <4 x float> %res
+}
+
+define <2 x float> @negative_store_between_load_and_shuffle(ptr %p, float %x) {
+; CHECK-LABEL: define <2 x float> @negative_store_between_load_and_shuffle(
+; CHECK-SAME: ptr [[P:%.*]], float [[X:%.*]]) {
+; CHECK-NEXT:    [[V:%.*]] = load <4 x float>, ptr [[P]], align 4
+; CHECK-NEXT:    [[Q:%.*]] = getelementptr float, ptr [[P]], i64 2
+; CHECK-NEXT:    store float [[X]], ptr [[Q]], align 4
+; CHECK-NEXT:    [[R:%.*]] = shufflevector <4 x float> [[V]], <4 x float> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %v = load <4 x float>, ptr %p, align 4
+  %q = getelementptr float, ptr %p, i64 2
+  store float %x, ptr %q, align 4
+  %r = shufflevector <4 x float> %v, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %r
+}
+
+declare void @clobber()
+define <2 x float> @negative_call_between_load_and_shuffle(ptr %p) {
+; CHECK-LABEL: define <2 x float> @negative_call_between_load_and_shuffle(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[V:%.*]] = load <4 x float>, ptr [[P]], align 4
+; CHECK-NEXT:    call void @clobber()
+; CHECK-NEXT:    [[R:%.*]] = shufflevector <4 x float> [[V]], <4 x float> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %v = load <4 x float>, ptr %p, align 4
+  call void @clobber()
+  %r = shufflevector <4 x float> %v, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %r
+}
+
+define <2 x float> @preserve_load_metadata_for_single_attributed_load(ptr %p) {
+; CHECK-LABEL: define <2 x float> @preserve_load_metadata_for_single_attributed_load(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 48
+; CHECK-NEXT:    [[R:%.*]] = load <2 x float>, ptr [[TMP1]], align 8, !tbaa [[FLOAT_TBAA0:![0-9]+]], !alias.scope [[META3:![0-9]+]], !noalias [[META3]]
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %q = getelementptr inbounds i8, ptr %p, i64 40
+  %v = load <4 x float>, ptr %q, align 8, !tbaa !0, !alias.scope !3, !noalias !3
+  %r = shufflevector <4 x float> %v, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %r
+}
+
+define <3 x i32> @drop_load_metadata_for_multiple_loads(ptr %p) {
+; CHECK-LABEL: define <3 x i32> @drop_load_metadata_for_multiple_loads(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = load <3 x i32>, ptr [[P]], align 8
+; CHECK-NEXT:    ret <3 x i32> [[R]]
+;
+  %l0 = load <2 x i32>, ptr %p, align 8, !invariant.load !6
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %l1 = load <2 x i32>, ptr %p1, align 8
+  %r = shufflevector <2 x i32> %l0, <2 x i32> %l1, <3 x i32> <i32 0, i32 1, i32 2>
+  ret <3 x i32> %r
+}
+
+define <2 x i1> @merge_range_and_noundef_metadata(ptr %p) {
+; CHECK-LABEL: define <2 x i1> @merge_range_and_noundef_metadata(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 1
+; CHECK-NEXT:    [[R:%.*]] = load <2 x i8>, ptr [[TMP1]], align 1, !range [[RNG6:![0-9]+]], !noundef [[META7:![0-9]+]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult <2 x i8> [[R]], splat (i8 2)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %l0 = load <2 x i8>, ptr %p, align 2, !range !7, !noundef !6
+  %p1 = getelementptr i8, ptr %p, i64 2
+  %l1 = load <2 x i8>, ptr %p1, align 2, !range !8, !noundef !6
+  %r = shufflevector <2 x i8> %l0, <2 x i8> %l1, <2 x i32> <i32 1, i32 2>
+  %cmp = icmp ult <2 x i8> %r, <i8 2, i8 2>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @drop_noncommon_range_and_noundef_metadata(ptr %p) {
+; CHECK-LABEL: define <2 x i1> @drop_noncommon_range_and_noundef_metadata(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[P]], i64 1
+; CHECK-NEXT:    [[R:%.*]] = load <2 x i8>, ptr [[TMP1]], align 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult <2 x i8> [[R]], splat (i8 2)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %l0 = load <2 x i8>, ptr %p, align 2, !range !7, !noundef !6
+  %p1 = getelementptr i8, ptr %p, i64 2
+  %l1 = load <2 x i8>, ptr %p1, align 2, !range !9
+  %r = shufflevector <2 x i8> %l0, <2 x i8> %l1, <2 x i32> <i32 1, i32 2>
+  %cmp = icmp ult <2 x i8> %r, <i8 2, i8 2>
+  ret <2 x i1> %cmp
+}
+
+define <3 x float> @intersect_nofpclass_metadata(ptr %p) {
+; CHECK-LABEL: define <3 x float> @intersect_nofpclass_metadata(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = load <3 x float>, ptr [[P]], align 8, !nofpclass [[META8:![0-9]+]]
+; CHECK-NEXT:    ret <3 x float> [[R]]
+;
+  %l0 = load <2 x float>, ptr %p, align 8, !nofpclass !10
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %l1 = load <2 x float>, ptr %p1, align 8, !nofpclass !11
+  %r = shufflevector <2 x float> %l0, <2 x float> %l1, <3 x i32> <i32 0, i32 1, i32 2>
+  ret <3 x float> %r
+}
+
+define <3 x float> @drop_noncommon_nofpclass_metadata(ptr %p) {
+; CHECK-LABEL: define <3 x float> @drop_noncommon_nofpclass_metadata(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = load <3 x float>, ptr [[P]], align 8
+; CHECK-NEXT:    ret <3 x float> [[R]]
+;
+  %l0 = load <2 x float>, ptr %p, align 8, !nofpclass !11
+  %p1 = getelementptr i8, ptr %p, i64 8
+  %l1 = load <2 x float>, ptr %p1, align 8, !nofpclass !12
+  %r = shufflevector <2 x float> %l0, <2 x float> %l1, <3 x i32> <i32 0, i32 1, i32 2>
+  ret <3 x float> %r
+}
+
+define <2 x float> @negative_load_in_different_block(ptr %p, i1 %c) {
+; CHECK-LABEL: define <2 x float> @negative_load_in_different_block(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[V:%.*]] = load <4 x float>, ptr [[P]], align 4
+; CHECK-NEXT:    br i1 [[C]], label %[[USE:.*]], label %[[OTHER:.*]]
+; CHECK:       [[USE]]:
+; CHECK-NEXT:    [[R:%.*]] = shufflevector <4 x float> [[V]], <4 x float> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    ret <2 x float> [[R]]
+; CHECK:       [[OTHER]]:
+; CHECK-NEXT:    ret <2 x float> zeroinitializer
+;
+entry:
+  %v = load <4 x float>, ptr %p, align 4
+  br i1 %c, label %use, label %other
+
+use:
+  %r = shufflevector <4 x float> %v, <4 x float> poison, <2 x i32> <i32 2, i32 3>
+  ret <2 x float> %r
+
+other:
+  ret <2 x float> zeroinitializer
+}
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"float", !2, i64 0}
+!2 = !{!"Simple C/C++ TBAA"}
+!3 = !{!4}
+!4 = distinct !{!4, !5}
+!5 = distinct !{!5}
+!6 = !{}
+!7 = !{i8 0, i8 2}
+!8 = !{i8 2, i8 4}
+!9 = !{i8 2, i8 0}
+!10 = !{i32 519}
+!11 = !{i32 3}
+!12 = !{i32 516}
+;.
----------------
arsenm wrote:

Test the really wide index edge case 

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


More information about the llvm-commits mailing list