Index: lib/Analysis/InstructionSimplify.cpp =================================================================== --- lib/Analysis/InstructionSimplify.cpp +++ lib/Analysis/InstructionSimplify.cpp @@ -4890,6 +4890,35 @@ if (match(Op0, m_Undef()) || match(Op1, m_Undef())) return Constant::getNullValue(ReturnType); break; + case Intrinsic::uadd_sat: + // sat(X + MAX) -> MAX + if (match(Op1, m_AllOnes())) + return Constant::getAllOnesValue(ReturnType); + // sat(MAX + X) -> MAX + if (match(Op0, m_AllOnes())) + return Constant::getAllOnesValue(ReturnType); + LLVM_FALLTHROUGH; + case Intrinsic::sadd_sat: + // X + 0 -> X, X + undef -> X + if (match(Op1, m_Zero()) || match(Op1, m_Undef())) + return Op0; + // 0 + X -> X, undef + X -> X + if (match(Op0, m_Zero()) || match(Op0, m_Undef())) + return Op1; + break; + case Intrinsic::usub_sat: + // sat(0 - X) -> 0, sat(X - MAX) -> 0 + if (match(Op0, m_Zero()) || match(Op1, m_AllOnes())) + return Constant::getNullValue(ReturnType); + LLVM_FALLTHROUGH; + case Intrinsic::ssub_sat: + // X - X -> 0, X - undef -> 0, undef - X -> 0 + if (Op0 == Op1 || match(Op0, m_Undef()) || match(Op1, m_Undef())) + return Constant::getNullValue(ReturnType); + // X - 0 -> X + if (match(Op1, m_Zero())) + return Op0; + break; case Intrinsic::load_relative: if (auto *C0 = dyn_cast(Op0)) if (auto *C1 = dyn_cast(Op1)) Index: test/Transforms/InstSimplify/saturating-add-sub.ll =================================================================== --- /dev/null +++ test/Transforms/InstSimplify/saturating-add-sub.ll @@ -0,0 +1,182 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instsimplify -S | FileCheck %s + +declare void @dummy(i8) +declare void @dummy_vec(<2 x i8>) + +declare i8 @llvm.uadd.sat.i8(i8, i8) +declare i8 @llvm.sadd.sat.i8(i8, i8) +declare <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8>, <2 x i8>) +declare <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8>, <2 x i8>) + +declare i8 @llvm.usub.sat.i8(i8, i8) +declare i8 @llvm.ssub.sat.i8(i8, i8) +declare <2 x i8> @llvm.usub.sat.v2i8(<2 x i8>, <2 x i8>) +declare <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8>, <2 x i8>) + +define void @test_add_scalar(i8 %a) { +; CHECK-LABEL: @test_add_scalar( +; CHECK-NEXT: call void @dummy(i8 [[A:%.*]]) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: call void @dummy(i8 -1) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: [[Y3:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[A]], i8 127) +; CHECK-NEXT: call void @dummy(i8 [[Y3]]) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: ret void +; + %x1 = call i8 @llvm.uadd.sat.i8(i8 %a, i8 0) + call void @dummy(i8 %x1) + %x2 = call i8 @llvm.uadd.sat.i8(i8 0, i8 %a) + call void @dummy(i8 %x2) + %x3 = call i8 @llvm.uadd.sat.i8(i8 %a, i8 255) + call void @dummy(i8 %x3) + %x4 = call i8 @llvm.uadd.sat.i8(i8 %a, i8 undef) + call void @dummy(i8 %x4) + %x5 = call i8 @llvm.uadd.sat.i8(i8 undef, i8 %a) + call void @dummy(i8 %x5) + + %y1 = call i8 @llvm.sadd.sat.i8(i8 %a, i8 0) + call void @dummy(i8 %y1) + %y2 = call i8 @llvm.sadd.sat.i8(i8 0, i8 %a) + call void @dummy(i8 %y2) + %y3 = call i8 @llvm.sadd.sat.i8(i8 %a, i8 127) + call void @dummy(i8 %y3) + %y4 = call i8 @llvm.sadd.sat.i8(i8 %a, i8 undef) + call void @dummy(i8 %y4) + %y5 = call i8 @llvm.sadd.sat.i8(i8 undef, i8 %a) + call void @dummy(i8 %y5) + + ret void +} + +define void @test_add_vector(<2 x i8> %a) { +; CHECK-LABEL: @test_add_vector( +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A:%.*]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> ) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> ) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: [[Y2:%.*]] = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> [[A]], <2 x i8> ) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[Y2]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: ret void +; + %x1 = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x1) + %x2 = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x2) + %x3 = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x3) + %x4 = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x4) + + %y1 = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y1) + %y2 = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y2) + %y3 = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y3) + %y4 = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y4) + + ret void +} + +define void @test_sub_scalar(i8 %a) { +; CHECK-LABEL: @test_sub_scalar( +; CHECK-NEXT: call void @dummy(i8 [[A:%.*]]) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 [[A]]) +; CHECK-NEXT: [[Y2:%.*]] = call i8 @llvm.ssub.sat.i8(i8 0, i8 [[A]]) +; CHECK-NEXT: call void @dummy(i8 [[Y2]]) +; CHECK-NEXT: [[Y3:%.*]] = call i8 @llvm.ssub.sat.i8(i8 [[A]], i8 127) +; CHECK-NEXT: call void @dummy(i8 [[Y3]]) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: call void @dummy(i8 0) +; CHECK-NEXT: ret void +; + %x1 = call i8 @llvm.usub.sat.i8(i8 %a, i8 0) + call void @dummy(i8 %x1) + %x2 = call i8 @llvm.usub.sat.i8(i8 0, i8 %a) + call void @dummy(i8 %x2) + %x3 = call i8 @llvm.usub.sat.i8(i8 %a, i8 255) + call void @dummy(i8 %x3) + %x4 = call i8 @llvm.usub.sat.i8(i8 %a, i8 undef) + call void @dummy(i8 %x4) + %x5 = call i8 @llvm.usub.sat.i8(i8 undef, i8 %a) + call void @dummy(i8 %x5) + %x6 = call i8 @llvm.usub.sat.i8(i8 %a, i8 %a) + call void @dummy(i8 %x6) + + %y1 = call i8 @llvm.ssub.sat.i8(i8 %a, i8 0) + call void @dummy(i8 %y1) + %y2 = call i8 @llvm.ssub.sat.i8(i8 0, i8 %a) + call void @dummy(i8 %y2) + %y3 = call i8 @llvm.ssub.sat.i8(i8 %a, i8 127) + call void @dummy(i8 %y3) + %y4 = call i8 @llvm.ssub.sat.i8(i8 %a, i8 undef) + call void @dummy(i8 %y4) + %y5 = call i8 @llvm.ssub.sat.i8(i8 undef, i8 %a) + call void @dummy(i8 %y5) + %y6 = call i8 @llvm.ssub.sat.i8(i8 %a, i8 %a) + call void @dummy(i8 %y6) + + ret void +} + +define void @test_sub_vector(<2 x i8> %a) { +; CHECK-LABEL: @test_sub_vector( +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A:%.*]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: [[Y3:%.*]] = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> [[A]], <2 x i8> ) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[Y3]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> [[A]]) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: call void @dummy_vec(<2 x i8> zeroinitializer) +; CHECK-NEXT: ret void +; + %x1 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x1) + %x2 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> , <2 x i8> %a) + call void @dummy_vec(<2 x i8> %x2) + %x3 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x3) + %x4 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x4) + %x5 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %x5) + %x6 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %a, <2 x i8> %a) + call void @dummy_vec(<2 x i8> %x6) + + %y1 = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y1) + %y2 = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> , <2 x i8> %a) + call void @dummy_vec(<2 x i8> %y2) + %y3 = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y3) + %y4 = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y4) + %y5 = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %a, <2 x i8> ) + call void @dummy_vec(<2 x i8> %y5) + %y6 = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %a, <2 x i8> %a) + call void @dummy_vec(<2 x i8> %y6) + + ret void +}