; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=instcombine -S | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" target triple = "armv8-arm-none-eabi" ; We can turn a vtbl/vtbx intrinsic into a shufflevector instruction if the mask ; is constant and references 2 or fewer operands. ; Basic vtbl1 with all in-bounds indices should optimize to shufflevector. define <8 x i8> @vtbl1_basic(<8 x i8> %a) { ; CHECK-LABEL: @vtbl1_basic( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl2 with both operands the same should be optimized (1 unique source). define <8 x i8> @vtbl2_duplicate_operands(<8 x i8> %a) { ; CHECK-LABEL: @vtbl2_duplicate_operands( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl2(<8 x i8> %a, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl3 referencing 2 unique operands should optimize. define <8 x i8> @vtbl3_two_sources(<8 x i8> %a, <8 x i8> %b) { ; CHECK-LABEL: @vtbl3_two_sources( ; CHECK-NEXT: [[TBL:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TBL]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl3(<8 x i8> %a, <8 x i8> %b, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl4 with alternating duplicate operands should optimize (2 unique sources). define <8 x i8> @vtbl4_duplicate_operands(<8 x i8> %a, <8 x i8> %b) { ; CHECK-LABEL: @vtbl4_duplicate_operands( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> %a, <8 x i8> %b, <8 x i8> %a, <8 x i8> %b, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl4 where mask only references first two operands should optimize. define <8 x i8> @vtbl4_unused_operands(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %d) { ; CHECK-LABEL: @vtbl4_unused_operands( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %d, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl4 where mask only references one operand should optimize. define <8 x i8> @vtbl4_single_operand_used(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %d) { ; CHECK-LABEL: @vtbl4_single_operand_used( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %d, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl1 with some OOB indices should optimize (1 source + zero vector = 2 sources). define <8 x i8> @vtbl1_with_oob(<8 x i8> %a) { ; CHECK-LABEL: @vtbl1_with_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> , <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl2 with duplicate operands and OOB should optimize (1 unique source + zero vector = 2 sources). define <8 x i8> @vtbl2_duplicate_with_oob(<8 x i8> %a) { ; CHECK-LABEL: @vtbl2_duplicate_with_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> , <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl2(<8 x i8> %a, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl2 with OOB indices should NOT optimize (2 sources + zero vector = 3 sources). define <8 x i8> @vtbl2_with_oob_bail(<8 x i8> %a, <8 x i8> %b) { ; CHECK-LABEL: @vtbl2_with_oob_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <8 x i8> @llvm.arm.neon.vtbl2(<8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i8> ) ; CHECK-NEXT: ret <8 x i8> [[TBL]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl2(<8 x i8> %a, <8 x i8> %b, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl1 with all OOB indices should optimize to zero vector. define <8 x i8> @vtbl1_all_oob(<8 x i8> %a) { ; CHECK-LABEL: @vtbl1_all_oob( ; CHECK-NEXT: ret <8 x i8> zeroinitializer ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl3 referencing all 3 operands should NOT optimize. define <8 x i8> @vtbl3_three_sources_bail(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c) { ; CHECK-LABEL: @vtbl3_three_sources_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <8 x i8> @llvm.arm.neon.vtbl3(<8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i8> [[C:%.*]], <8 x i8> ) ; CHECK-NEXT: ret <8 x i8> [[TBL]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl3(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl4 referencing 3 unique operands should NOT optimize. define <8 x i8> @vtbl4_three_sources_bail(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c) { ; CHECK-LABEL: @vtbl4_three_sources_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i8> [[C:%.*]], <8 x i8> [[A]], <8 x i8> ) ; CHECK-NEXT: ret <8 x i8> [[TBL]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; vtbl4 referencing all 4 unique operands should NOT optimize. define <8 x i8> @vtbl4_four_sources_bail(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %d) { ; CHECK-LABEL: @vtbl4_four_sources_bail( ; CHECK-NEXT: [[TBL:%.*]] = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i8> [[C:%.*]], <8 x i8> [[D:%.*]], <8 x i8> ) ; CHECK-NEXT: ret <8 x i8> [[TBL]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl4(<8 x i8> %a, <8 x i8> %b, <8 x i8> %c, <8 x i8> %d, <8 x i8> ) ret <8 x i8> %tbl } ; vtbx1 with no OOB should optimize. define <8 x i8> @vtbx1_no_oob(<8 x i8> %fallback, <8 x i8> %a) { ; CHECK-LABEL: @vtbx1_no_oob( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbx = call <8 x i8> @llvm.arm.neon.vtbx1(<8 x i8> %fallback, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbx } ; vtbx2 where fallback == second source operand should optimize (deduplicated). define <8 x i8> @vtbx2_fallback_equals_second_source(<8 x i8> %a, <8 x i8> %b) { ; CHECK-LABEL: @vtbx2_fallback_equals_second_source( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbx = call <8 x i8> @llvm.arm.neon.vtbx2(<8 x i8> %b, <8 x i8> %a, <8 x i8> %b, <8 x i8> ) ret <8 x i8> %tbx } ; vtbx1 with OOB where fallback == source should optimize (deduplicated). define <8 x i8> @vtbx1_oob_fallback_same_as_source(<8 x i8> %a) { ; CHECK-LABEL: @vtbx1_oob_fallback_same_as_source( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbx = call <8 x i8> @llvm.arm.neon.vtbx1(<8 x i8> %a, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbx } ; vtbx2 with OOB should NOT optimize (2 sources + fallback = 3 sources). define <8 x i8> @vtbx2_with_oob_bail(<8 x i8> %fallback, <8 x i8> %a, <8 x i8> %b) { ; CHECK-LABEL: @vtbx2_with_oob_bail( ; CHECK-NEXT: [[TBX:%.*]] = call <8 x i8> @llvm.arm.neon.vtbx2(<8 x i8> [[FALLBACK:%.*]], <8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]], <8 x i8> ) ; CHECK-NEXT: ret <8 x i8> [[TBX]] ; %tbx = call <8 x i8> @llvm.arm.neon.vtbx2(<8 x i8> %fallback, <8 x i8> %a, <8 x i8> %b, <8 x i8> ) ret <8 x i8> %tbx } ; vtbx1 with all OOB indices should optimize to fallback. define <8 x i8> @vtbx1_all_oob(<8 x i8> %fallback, <8 x i8> %a) { ; CHECK-LABEL: @vtbx1_all_oob( ; CHECK-NEXT: ret <8 x i8> [[FALLBACK:%.*]] ; %tbx = call <8 x i8> @llvm.arm.neon.vtbx1(<8 x i8> %fallback, <8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbx } ; Non-constant mask should NOT optimize. define <8 x i8> @vtbl1_non_constant_mask(<8 x i8> %a, <8 x i8> %mask) { ; CHECK-LABEL: @vtbl1_non_constant_mask( ; CHECK-NEXT: [[TBL:%.*]] = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> [[A:%.*]], <8 x i8> [[MASK:%.*]]) ; CHECK-NEXT: ret <8 x i8> [[TBL]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> %a, <8 x i8> %mask) ret <8 x i8> %tbl } ; Mask with some poison elements should optimize, with poison propagating to output. define <8 x i8> @vtbl1_poison_mask_elements(<8 x i8> %a) { ; CHECK-LABEL: @vtbl1_poison_mask_elements( ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[A:%.*]], <8 x i8> poison, <8 x i32> ; CHECK-NEXT: ret <8 x i8> [[TMP1]] ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> %a, <8 x i8> ) ret <8 x i8> %tbl } ; Mask with all poison elements should optimize to poison. define <8 x i8> @vtbl1_all_poison_mask(<8 x i8> %a) { ; CHECK-LABEL: @vtbl1_all_poison_mask( ; CHECK-NEXT: ret <8 x i8> poison ; %tbl = call <8 x i8> @llvm.arm.neon.vtbl1(<8 x i8> %a, <8 x i8> poison) ret <8 x i8> %tbl }