diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3307,6 +3307,39 @@ } break; } + case Intrinsic::arm_mve_minv_u: + case Intrinsic::arm_mve_minv_s: + case Intrinsic::arm_mve_maxv_u: + case Intrinsic::arm_mve_maxv_s: { + unsigned ScalarWidth = II->getArgOperand(1) + ->getType() + ->getVectorElementType() + ->getScalarSizeInBits(); + + bool Modified = false; + + KnownBits ScalarKnown(32); + if (SimplifyDemandedBits(II, 0, APInt::getLowBitsSet(32, ScalarWidth), + ScalarKnown, 0)) + Modified = true; + if (ScalarWidth < 32 && !II->getMetadata(LLVMContext::MD_range)) { + uint32_t Lo = 0, Hi = (uint32_t)1 << ScalarWidth; + if (IID == Intrinsic::arm_mve_minv_s || + IID == Intrinsic::arm_mve_maxv_s) { + uint32_t Offset = Hi >> 1; + Lo -= Offset; + Hi -= Offset; + } + Type *IntTy32 = Type::getInt32Ty(II->getContext()); + Metadata *M[] = {ConstantAsMetadata::get(ConstantInt::get(IntTy32, Lo)), + ConstantAsMetadata::get(ConstantInt::get(IntTy32, Hi))}; + II->setMetadata(LLVMContext::MD_range, MDNode::get(II->getContext(), M)); + Modified = true; + } + if (Modified) + return II; + break; + } case Intrinsic::arm_mve_vadc: case Intrinsic::arm_mve_vadc_predicated: { unsigned CarryOp = diff --git a/llvm/test/CodeGen/Thumb2/mve-intrinsics/vmin-instcombine.ll b/llvm/test/CodeGen/Thumb2/mve-intrinsics/vmin-instcombine.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Thumb2/mve-intrinsics/vmin-instcombine.ll @@ -0,0 +1,38 @@ +; RUN: opt -instcombine -S -o - %s | FileCheck --check-prefix=IR %s +; RUN: opt -instcombine %s | llc -mtriple=thumbv8.1m.main -mattr=+mve.fp -verify-machineinstrs -o - | FileCheck --check-prefix=ASM %s + +define arm_aapcs_vfpcc i32 @test_vmaxvq_u8(i32 %a, <16 x i8> %b) { +; ASM-LABEL: test_vmaxvq_u8: +; ASM: @ %bb.0: @ %entry +; ASM-NEXT: vmaxv.u8 r0, q0 +; ASM-NEXT: bx lr +entry: + %0 = tail call i32 @llvm.arm.mve.maxv.u.v16i8(i32 %a, <16 x i8> %b) + %1 = trunc i32 %0 to i8 + %2 = zext i8 %1 to i32 + ret i32 %2 +} + +define arm_aapcs_vfpcc i16 @test_vminvq_s16(i32 %a, <8 x i16> %b) { +; ASM-LABEL: test_vminvq_s16: +; ASM: @ %bb.0: @ %entry +; ASM-NEXT: vminv.s16 r0, q0 +; Ideally the next line should be ASM-NEXT, enforcing that there's +; no sxth instruction between the vminv and the return. But in fact +; there is, because signed range metadata doesn't generate an +; AssertSext. +; ASM: bx lr +entry: + %0 = tail call i32 @llvm.arm.mve.minv.s.v8i16(i32 %a, <8 x i16> %b) + %1 = trunc i32 %0 to i16 + %2 = sext i16 %1 to i32 + ret i16 %1 +} + +declare i32 @llvm.arm.mve.maxv.u.v16i8(i32, <16 x i8>) +declare i32 @llvm.arm.mve.minv.s.v8i16(i32, <8 x i16>) + +; IR: tail call i32 @llvm.arm.mve.maxv.u.v16i8(i32 %a, <16 x i8> %b), !range !0 +; IR: tail call i32 @llvm.arm.mve.minv.s.v8i16(i32 %a, <8 x i16> %b), !range ! +; IR: !0 = !{i32 0, i32 256} +; IR: !1 = !{i32 -32768, i32 32768}