; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=instcombine -S | FileCheck %s target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" target triple = "aarch64" ; We can turn a tbl/tbx intrinsic into a shufflevector instruction if the mask ; is constant and references 2 or fewer operands. ; Basic tbl1 with all in-bounds indices should optimize to shufflevector. define <16 x i8> @tbl1_basic(<16 x i8> %a) { ; CHECK-LABEL: @tbl1_basic( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> poison, <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl2 with both operands the same should optimize (1 unique source). define <16 x i8> @tbl2_duplicate_operands(<16 x i8> %a) { ; CHECK-LABEL: @tbl2_duplicate_operands( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> poison, <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl2.v16i8(<16 x i8> %a, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl3 referencing 2 unique operands should optimize. define <16 x i8> @tbl3_two_sources(<16 x i8> %a, <16 x i8> %b) { ; CHECK-LABEL: @tbl3_two_sources( ; CHECK-NEXT: [[TBL:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TBL]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl3.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl4 with alternating duplicate operands should optimize (2 unique sources). define <16 x i8> @tbl4_duplicate_operands(<16 x i8> %a, <16 x i8> %b) { ; CHECK-LABEL: @tbl4_duplicate_operands( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %a, <16 x i8> %b, <16 x i8> ) ret <16 x i8> %tbl } ; tbl4 where mask only references first two operands should optimize. define <16 x i8> @tbl4_unused_operands(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d) { ; CHECK-LABEL: @tbl4_unused_operands( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d, <16 x i8> ) ret <16 x i8> %tbl } ; tbl4 where mask only references one operand should optimize. define <16 x i8> @tbl4_single_operand_used(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d) { ; CHECK-LABEL: @tbl4_single_operand_used( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> poison, <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d, <16 x i8> ) ret <16 x i8> %tbl } ; tbl1 with some OOB indices should optimize (1 source + zero vector = 2 sources). define <16 x i8> @tbl1_with_oob(<16 x i8> %a) { ; CHECK-LABEL: @tbl1_with_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> , <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl2 with duplicate operands and OOB should optimize (1 unique source + zero vector = 2 sources). define <16 x i8> @tbl2_duplicate_with_oob(<16 x i8> %a) { ; CHECK-LABEL: @tbl2_duplicate_with_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> , <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl2.v16i8(<16 x i8> %a, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl2 with OOB indices should NOT optimize (2 sources + zero vector = 3 sources). define <16 x i8> @tbl2_with_oob_bail(<16 x i8> %a, <16 x i8> %b) { ; CHECK-LABEL: @tbl2_with_oob_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <16 x i8> @llvm.aarch64.neon.tbl2.v16i8(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i8> ) ; CHECK-NEXT: ret <16 x i8> [[TBL]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl2.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> ) ret <16 x i8> %tbl } ; tbl1 with all OOB indices should optimize to zero vector. define <16 x i8> @tbl1_all_oob(<16 x i8> %a) { ; CHECK-LABEL: @tbl1_all_oob( ; CHECK-NEXT: ret <16 x i8> zeroinitializer ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl3 referencing all 3 operands should NOT optimize. define <16 x i8> @tbl3_three_sources_bail(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c) { ; CHECK-LABEL: @tbl3_three_sources_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <16 x i8> @llvm.aarch64.neon.tbl3.v16i8(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i8> [[C:%.*]], <16 x i8> ) ; CHECK-NEXT: ret <16 x i8> [[TBL]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl3.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> ) ret <16 x i8> %tbl } ; tbl4 referencing 3 unique operands should NOT optimize. define <16 x i8> @tbl4_three_sources_bail(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c) { ; CHECK-LABEL: @tbl4_three_sources_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i8> [[C:%.*]], <16 x i8> [[A]], <16 x i8> ) ; CHECK-NEXT: ret <16 x i8> [[TBL]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; tbl4 referencing all 4 unique operands should NOT optimize. define <16 x i8> @tbl4_four_sources_bail(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d) { ; CHECK-LABEL: @tbl4_four_sources_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i8> [[C:%.*]], <16 x i8> [[D:%.*]], <16 x i8> ) ; CHECK-NEXT: ret <16 x i8> [[TBL]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl4.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d, <16 x i8> ) ret <16 x i8> %tbl } ; tbx1 with no OOB should optimize. define <16 x i8> @tbx1_no_oob(<16 x i8> %fallback, <16 x i8> %a) { ; CHECK-LABEL: @tbx1_no_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> poison, <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbx = call <16 x i8> @llvm.aarch64.neon.tbx1.v16i8(<16 x i8> %fallback, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbx } ; tbx2 where fallback == second source operand should optimize (deduplicated). define <16 x i8> @tbx2_fallback_equals_second_source(<16 x i8> %a, <16 x i8> %b) { ; CHECK-LABEL: @tbx2_fallback_equals_second_source( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbx = call <16 x i8> @llvm.aarch64.neon.tbx2.v16i8(<16 x i8> %b, <16 x i8> %a, <16 x i8> %b, <16 x i8> ) ret <16 x i8> %tbx } ; tbx1 with OOB where fallback == source should optimize (deduplicated). define <16 x i8> @tbx1_oob_fallback_same_as_source(<16 x i8> %a) { ; CHECK-LABEL: @tbx1_oob_fallback_same_as_source( ; CHECK-NEXT: [[A:%.*]] = shufflevector <16 x i8> [[A1:%.*]], <16 x i8> poison, <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[A]] ; %tbx = call <16 x i8> @llvm.aarch64.neon.tbx1.v16i8(<16 x i8> %a, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbx } ; tbx2 with OOB should NOT optimize (2 sources + fallback = 3 sources). define <16 x i8> @tbx2_with_oob_bail(<16 x i8> %fallback, <16 x i8> %a, <16 x i8> %b) { ; CHECK-LABEL: @tbx2_with_oob_bail( ; CHECK-NEXT: [[TBX:%.*]] = call <16 x i8> @llvm.aarch64.neon.tbx2.v16i8(<16 x i8> [[FALLBACK:%.*]], <16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i8> ) ; CHECK-NEXT: ret <16 x i8> [[TBX]] ; %tbx = call <16 x i8> @llvm.aarch64.neon.tbx2.v16i8(<16 x i8> %fallback, <16 x i8> %a, <16 x i8> %b, <16 x i8> ) ret <16 x i8> %tbx } ; tbx1 with all OOB indices should optimize to fallback. define <16 x i8> @tbx1_all_oob(<16 x i8> %fallback, <16 x i8> %a) { ; CHECK-LABEL: @tbx1_all_oob( ; CHECK-NEXT: ret <16 x i8> [[FALLBACK:%.*]] ; %tbx = call <16 x i8> @llvm.aarch64.neon.tbx1.v16i8(<16 x i8> %fallback, <16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbx } ; tbx1 with OOB and mismatched fallback/source sizes should NOT optimize. define <8 x i8> @tbx1_fallback_size_mismatch(<8 x i8> %fallback, <16 x i8> %a) { ; CHECK-LABEL: @tbx1_fallback_size_mismatch( ; CHECK-NEXT: [[TBX:%.*]] = call <8 x i8> @llvm.aarch64.neon.tbx1.v8i8(<8 x i8> [[FALLBACK:%.*]], <16 x i8> [[A:%.*]], <8 x i8> ) ; CHECK-NEXT: ret <8 x i8> [[TBX]] ; %tbx = call <8 x i8> @llvm.aarch64.neon.tbx1.v8i8(<8 x i8> %fallback, <16 x i8> %a, <8 x i8> ) ret <8 x i8> %tbx } ; tbx1 with no OOB and mismatched fallback/source sizes should optimize. define <8 x i8> @tbx1_fallback_size_mismatch_no_oob(<8 x i8> %fallback, <16 x i8> %a) { ; CHECK-LABEL: @tbx1_fallback_size_mismatch_no_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbx = call <8 x i8> @llvm.aarch64.neon.tbx1.v8i8(<8 x i8> %fallback, <16 x i8> %a, <8 x i8> ) ret <8 x i8> %tbx } ; tbl1 with non-i8 element type should NOT optimize. define <8 x i16> @tbl1_8x16(<16 x i8> %vec) { ; CHECK-LABEL: @tbl1_8x16( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TBL1:%.*]] = call <8 x i16> @llvm.aarch64.neon.tbl1.v8i16(<16 x i8> [[VEC:%.*]], <8 x i16> ) ; CHECK-NEXT: ret <8 x i16> [[TBL1]] ; entry: ; `tbl1.v8i16` is not really a thing, but it's good to check. %tbl1 = call <8 x i16> @llvm.aarch64.neon.tbl1.v8i16(<16 x i8> %vec, <8 x i16> ) ret <8 x i16> %tbl1 } ; tbl1 with non-8/16 element count should NOT optimize. define <12 x i8> @tbl1_16x8(<16 x i8> %vec) { ; CHECK-LABEL: @tbl1_16x8( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TBL1:%.*]] = call <12 x i8> @llvm.aarch64.neon.tbl1.v12i8(<16 x i8> [[VEC:%.*]], <12 x i8> ) ; CHECK-NEXT: ret <12 x i8> [[TBL1]] ; entry: ; `tbl1.v12i8` is not really a thing, but it's good to check. %tbl1 = call <12 x i8> @llvm.aarch64.neon.tbl1.v12i8(<16 x i8> %vec, <12 x i8> ) ret <12 x i8> %tbl1 } ; Non-constant mask should NOT optimize. define <16 x i8> @tbl1_non_constant_mask(<16 x i8> %a, <16 x i8> %mask) { ; CHECK-LABEL: @tbl1_non_constant_mask( ; CHECK-NEXT: [[TBL:%.*]] = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> [[A:%.*]], <16 x i8> [[MASK:%.*]]) ; CHECK-NEXT: ret <16 x i8> [[TBL]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> %a, <16 x i8> %mask) ret <16 x i8> %tbl } ; Mask with some poison elements should optimize, with poison propagating to output. define <16 x i8> @tbl1_poison_mask_elements(<16 x i8> %a) { ; CHECK-LABEL: @tbl1_poison_mask_elements( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <16 x i8> [[A:%.*]], <16 x i8> poison, <16 x i32> ; CHECK-NEXT: ret <16 x i8> [[TMP1]] ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> %a, <16 x i8> ) ret <16 x i8> %tbl } ; Mask with all poison elements should optimize to poison. define <16 x i8> @tbl1_all_poison_mask(<16 x i8> %a) { ; CHECK-LABEL: @tbl1_all_poison_mask( ; CHECK-NEXT: ret <16 x i8> poison ; %tbl = call <16 x i8> @llvm.aarch64.neon.tbl1.v16i8(<16 x i8> %a, <16 x i8> poison) ret <16 x i8> %tbl }