diff --git a/lib/Target/CBackend/CBackend.cpp b/lib/Target/CBackend/CBackend.cpp index aa6c4bd2..8d570965 100644 --- a/lib/Target/CBackend/CBackend.cpp +++ b/lib/Target/CBackend/CBackend.cpp @@ -2588,6 +2588,12 @@ void CWriter::generateHeader(Module &M) { case Intrinsic::ssub_with_overflow: case Intrinsic::umul_with_overflow: case Intrinsic::smul_with_overflow: + case Intrinsic::uadd_sat: + case Intrinsic::sadd_sat: + case Intrinsic::usub_sat: + case Intrinsic::ssub_sat: + case Intrinsic::sshl_sat: + case Intrinsic::ushl_sat: case Intrinsic::bswap: case Intrinsic::ceil: case Intrinsic::ctlz: @@ -4521,6 +4527,9 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode, case Intrinsic::sadd_with_overflow: case Intrinsic::ssub_with_overflow: case Intrinsic::smul_with_overflow: + case Intrinsic::sadd_sat: + case Intrinsic::ssub_sat: + case Intrinsic::sshl_sat: case Intrinsic::smin: case Intrinsic::smax: isSigned = true; @@ -4619,6 +4628,8 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode, break; case Intrinsic::ssub_with_overflow: + // r.field0 = a - b; + // r.field1 = (b <= 0 ? a > XX_MAX + b : a < XX_MIN + b); cwriter_assert(cast(retT)->getElementType(0) == elemT); Out << " r.field0 = a - b;\n"; Out << " r.field1 = (b <= 0 ? a > "; @@ -4637,6 +4648,87 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode, cwriter_assert(cast(retT)->getElementType(0) == elemT); Out << " r.field1 = LLVMMul_sov(8 * sizeof(a), &a, &b, &r.field0);\n"; break; + + case Intrinsic::uadd_sat: + // r = (a > XX_MAX - b) ? XX_MAX : a + b + cwriter_assert(retT == elemT); + Out << " r = (a > "; + printLimitValue(*elemIntT, false, true, Out); + Out << " -b ) ? "; + printLimitValue(*elemIntT, false, true, Out); + Out << " : a + b;\n"; + break; + + case Intrinsic::sadd_sat: + // r = (b > 0 && a > XX_MAX - b) ? XX_MAX : a + b; + // r = (b < 0 && a < XX_MIN - b) ? XX_MIN : r; + cwriter_assert(retT == elemT); + Out << " r = (b > 0 && a > "; + printLimitValue(*elemIntT, true, true, Out); + Out << " - b) ? "; + printLimitValue(*elemIntT, true, true, Out); + Out << " : a + b;\n"; + Out << " r = (b < 0 && a < "; + printLimitValue(*elemIntT, true, false, Out); + Out << " - b) ? "; + printLimitValue(*elemIntT, true, false, Out); + Out << " : r;\n"; + break; + + case Intrinsic::usub_sat: + // r = (a < b) ? XX_MIN : a - b; + cwriter_assert(retT == elemT); + Out << " r = (a < b) ? "; + printLimitValue(*elemIntT, false, false, Out); + Out << " : a - b;\n"; + break; + + case Intrinsic::ssub_sat: + // r = (b > 0 && a < XX_MIN + b) ? XX_MIN : a - b; + // r = (b < 0 && a > XX_MAX + b) ? XX_MAX : r; + cwriter_assert(retT == elemT); + Out << " r = (b > 0 && a < "; + printLimitValue(*elemIntT, true, false, Out); + Out << " + b) ? "; + printLimitValue(*elemIntT, true, false, Out); + Out << " : a - b;\n"; + Out << " r = (b < 0 && a > "; + printLimitValue(*elemIntT, true, true, Out); + Out << " + b) ? "; + printLimitValue(*elemIntT, true, true, Out); + Out << " : r;\n"; + break; + + case Intrinsic::ushl_sat: + // There's no poison value handler in llvm-cbe yet, so this code don't consider that. + // r = (a > (XX_MAX >> b)) ? XX_MAX : a << b; + cwriter_assert(retT == elemT); + Out << " r = (a > ("; + printLimitValue(*elemIntT, false, true, Out); + Out << " >> b)) ? "; + printLimitValue(*elemIntT, false, true, Out); + Out << " : a << b;\n"; + break; + + case Intrinsic::sshl_sat: + // (XX_MAX) = 0111... Therfore, shifting this value by b to the right yields the + // maximum/minimum value that can be shifted without overflow. + // r = (a >= 0 && a > (XX_MAX >> b)) ? XX_MAX : a << b; + // r = (a < 0 && a < ((XX_MAX >> b) | XX_MIN))) ? XX_MIN : r; + cwriter_assert(retT == elemT); + Out << " r = (a >= 0 && a > ("; + printLimitValue(*elemIntT, true, true, Out); + Out << " >> b)) ? "; + printLimitValue(*elemIntT, true, true, Out); + Out << " : a << b;\n"; + Out << " r = (a < 0 && a < (("; + printLimitValue(*elemIntT, true, true, Out); + Out << " >> b | "; + printLimitValue(*elemIntT, true, false, Out); + Out << "))) ? "; + printLimitValue(*elemIntT, true, false, Out); + Out << " : r;\n"; + break; case Intrinsic::bswap: cwriter_assert(retT == elemT); @@ -4805,6 +4897,12 @@ bool CWriter::lowerIntrinsics(Function &F) { case Intrinsic::ssub_with_overflow: case Intrinsic::umul_with_overflow: case Intrinsic::smul_with_overflow: + case Intrinsic::uadd_sat: + case Intrinsic::sadd_sat: + case Intrinsic::usub_sat: + case Intrinsic::ssub_sat: + case Intrinsic::ushl_sat: + case Intrinsic::sshl_sat: case Intrinsic::bswap: case Intrinsic::ceil: case Intrinsic::ctlz: @@ -5124,6 +5222,12 @@ bool CWriter::visitBuiltinCall(CallInst &I, Intrinsic::ID ID) { // these use the normal function call emission case Intrinsic::sadd_with_overflow: case Intrinsic::ssub_with_overflow: + case Intrinsic::uadd_sat: + case Intrinsic::sadd_sat: + case Intrinsic::usub_sat: + case Intrinsic::ssub_sat: + case Intrinsic::ushl_sat: + case Intrinsic::sshl_sat: headerUseLimits(); return false; case Intrinsic::ceil: diff --git a/test/ll_tests/test_intrinsic_sadd_sat.ll b/test/ll_tests/test_intrinsic_sadd_sat.ll new file mode 100644 index 00000000..728df0b8 --- /dev/null +++ b/test/ll_tests/test_intrinsic_sadd_sat.ll @@ -0,0 +1,86 @@ +declare i8 @llvm.sadd.sat.i8(i8 %a, i8 %b) +declare i16 @llvm.sadd.sat.i16(i16 %a, i16 %b) +declare i32 @llvm.sadd.sat.i32(i32 %a, i32 %b) +declare i64 @llvm.sadd.sat.i64(i64 %a, i64 %b) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { +basic_8: + %basic_8_sum = call i8 @llvm.sadd.sat.i8(i8 52, i8 -10) + %basic_8_ok = icmp eq i8 %basic_8_sum, 42 + br i1 %basic_8_ok, label %overflowing_8, label %error + +overflowing_8: + %overflowing_8_sum = call i8 @llvm.sadd.sat.i8(i8 u0x7C, i8 u0x7C) + %overflowing_8_ok = icmp eq i8 %overflowing_8_sum, 127 + br i1 %overflowing_8_ok, label %underflowing_8, label %error + +underflowing_8: + %underflowing_8_sum = call i8 @llvm.sadd.sat.i8(i8 u0x8C, i8 u0x8C) + %underflowing_8_ok = icmp eq i8 %underflowing_8_sum, -128 + br i1 %underflowing_8_ok, label %basic_16, label %error + +basic_16: + %basic_16_sum = call i16 @llvm.sadd.sat.i16(i16 52, i16 -10) + %basic_16_ok = icmp eq i16 %basic_16_sum, 42 + br i1 %basic_16_ok, label %overflowing_16, label %error + +overflowing_16: + %overflowing_16_sum = call i16 @llvm.sadd.sat.i16(i16 u0x7CCC, i16 u0x7CCC) + %overflowing_16_ok = icmp eq i16 %overflowing_16_sum, 32767 + br i1 %overflowing_16_ok, label %underflowing_16, label %error + +underflowing_16: + %underflowing_16_sum = call i16 @llvm.sadd.sat.i16(i16 u0x8CCC, i16 u0x8CCC) + %underflowing_16_ok = icmp eq i16 %underflowing_16_sum, -32768 + br i1 %underflowing_16_ok, label %basic_32, label %error + +basic_32: + %basic_32_sum = call i32 @llvm.sadd.sat.i32(i32 52, i32 -10) + %basic_32_ok = icmp eq i32 %basic_32_sum, 42 + br i1 %basic_32_ok, label %overflowing_32, label %error + +overflowing_32: + %overflowing_32_sum = call i32 @llvm.sadd.sat.i32(i32 u0x7CCCCCCC, i32 u0x7CCCCCCC) + %overflowing_32_ok = icmp eq i32 %overflowing_32_sum, 2147483647 + br i1 %overflowing_32_ok, label %underflowing_32, label %error + +underflowing_32: + %underflowing_32_sum = call i32 @llvm.sadd.sat.i32(i32 u0x8CCCCCCC, i32 u0x8CCCCCCC) + %underflowing_32_ok = icmp eq i32 %underflowing_32_sum, -2147483648 + br i1 %underflowing_32_ok, label %basic_64, label %error + +basic_64: + %basic_64_sum = call i64 @llvm.sadd.sat.i64(i64 52, i64 -10) + %basic_64_ok = icmp eq i64 %basic_64_sum, 42 + br i1 %basic_64_ok, label %overflowing_64, label %error + +overflowing_64: + %overflowing_64_sum = call i64 @llvm.sadd.sat.i64(i64 u0x7CCCCCCCCCCCCCCC, i64 u0x7CCCCCCCCCCCCCCC) + %overflowing_64_ok = icmp eq i64 %overflowing_64_sum, 9223372036854775807 + br i1 %overflowing_64_ok, label %underflowing_64, label %error + +underflowing_64: + %underflowing_64_sum = call i64 @llvm.sadd.sat.i64(i64 u0x8CCCCCCCCCCCCCCC, i64 u0x8CCCCCCCCCCCCCCC) + %underflowing_64_ok = icmp eq i64 %underflowing_64_sum, -9223372036854775808 + br i1 %underflowing_64_ok, label %ok, label %error + +ok: + ret i32 6 + +error: + %retVal = phi i32 + [ 81, %basic_8 ], + [ 82, %overflowing_8 ], + [ 83, %underflowing_8 ], + [ 161, %basic_16 ], + [ 162, %overflowing_16 ], + [ 163, %underflowing_16 ], + [ 321, %basic_32 ], + [ 322, %overflowing_32 ], + [ 323, %underflowing_32 ], + [ 641, %basic_64 ], + [ 642, %overflowing_64 ], + [ 643, %underflowing_64 ] + ret i32 %retVal +} diff --git a/test/ll_tests/test_intrinsic_sshl_sat.ll b/test/ll_tests/test_intrinsic_sshl_sat.ll new file mode 100644 index 00000000..6d4799a1 --- /dev/null +++ b/test/ll_tests/test_intrinsic_sshl_sat.ll @@ -0,0 +1,86 @@ +declare i8 @llvm.sshl.sat.i8(i8 %a, i8 %b) +declare i16 @llvm.sshl.sat.i16(i16 %a, i16 %b) +declare i32 @llvm.sshl.sat.i32(i32 %a, i32 %b) +declare i64 @llvm.sshl.sat.i64(i64 %a, i64 %b) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { +basic_8: + %basic_8_shl = call i8 @llvm.sshl.sat.i8(i8 21, i8 1) + %basic_8_ok = icmp eq i8 %basic_8_shl, 42 + br i1 %basic_8_ok, label %overflowing_8, label %error + +overflowing_8: + %overflowing_8_shl = call i8 @llvm.sshl.sat.i8(i8 1, i8 7) + %overflowing_8_ok = icmp eq i8 %overflowing_8_shl, 127 + br i1 %overflowing_8_ok, label %underflowing_8, label %error + +underflowing_8: + %underflowing_8_shl = call i8 @llvm.sshl.sat.i8(i8 -1, i8 7) + %underflowing_8_ok = icmp eq i8 %underflowing_8_shl, -128 + br i1 %underflowing_8_ok, label %basic_16, label %error + +basic_16: + %basic_16_shl = call i16 @llvm.sshl.sat.i16(i16 21, i16 1) + %basic_16_ok = icmp eq i16 %basic_16_shl, 42 + br i1 %basic_16_ok, label %overflowing_16, label %error + +overflowing_16: + %overflowing_16_shl = call i16 @llvm.sshl.sat.i16(i16 1, i16 15) + %overflowing_16_ok = icmp eq i16 %overflowing_16_shl, 32767 + br i1 %overflowing_16_ok, label %underflowing_16, label %error + +underflowing_16: + %underflowing_16_shl = call i16 @llvm.sshl.sat.i16(i16 -1, i16 15) + %underflowing_16_ok = icmp eq i16 %underflowing_16_shl, -32768 + br i1 %underflowing_16_ok, label %basic_32, label %error + +basic_32: + %basic_32_shl = call i32 @llvm.sshl.sat.i32(i32 21, i32 1) + %basic_32_ok = icmp eq i32 %basic_32_shl, 42 + br i1 %basic_32_ok, label %overflowing_32, label %error + +overflowing_32: + %overflowing_32_shl = call i32 @llvm.sshl.sat.i32(i32 1, i32 31) + %overflowing_32_ok = icmp eq i32 %overflowing_32_shl, 2147483647 + br i1 %overflowing_32_ok, label %underflowing_32, label %error + +underflowing_32: + %underflowing_32_shl = call i32 @llvm.sshl.sat.i32(i32 -1, i32 31) + %underflowing_32_ok = icmp eq i32 %underflowing_32_shl, -2147483648 + br i1 %underflowing_32_ok, label %basic_64, label %error + +basic_64: + %basic_64_shl = call i64 @llvm.sshl.sat.i64(i64 21, i64 1) + %basic_64_ok = icmp eq i64 %basic_64_shl, 42 + br i1 %basic_64_ok, label %overflowing_64, label %error + +overflowing_64: + %overflowing_64_shl = call i64 @llvm.sshl.sat.i64(i64 1, i64 63) + %overflowing_64_ok = icmp eq i64 %overflowing_64_shl, 9223372036854775807 + br i1 %overflowing_64_ok, label %underflowing_64, label %error + +underflowing_64: + %underflowing_64_shl = call i64 @llvm.sshl.sat.i64(i64 -1, i64 63) + %underflowing_64_ok = icmp eq i64 %underflowing_64_shl, -9223372036854775808 + br i1 %underflowing_64_ok, label %ok, label %error + +ok: + ret i32 6 + +error: + %retVal = phi i32 + [ 81, %basic_8 ], + [ 82, %overflowing_8 ], + [ 83, %underflowing_8 ], + [ 161, %basic_16 ], + [ 162, %overflowing_16 ], + [ 163, %underflowing_16 ], + [ 321, %basic_32 ], + [ 322, %overflowing_32 ], + [ 323, %underflowing_32 ], + [ 641, %basic_64 ], + [ 642, %overflowing_64 ], + [ 643, %underflowing_64 ] + ret i32 %retVal +} diff --git a/test/ll_tests/test_intrinsic_uadd_sat.ll b/test/ll_tests/test_intrinsic_uadd_sat.ll new file mode 100644 index 00000000..2f214b65 --- /dev/null +++ b/test/ll_tests/test_intrinsic_uadd_sat.ll @@ -0,0 +1,62 @@ +declare i8 @llvm.uadd.sat.i8(i8 %a, i8 %b) +declare i16 @llvm.uadd.sat.i16(i16 %a, i16 %b) +declare i32 @llvm.uadd.sat.i32(i32 %a, i32 %b) +declare i64 @llvm.uadd.sat.i64(i64 %a, i64 %b) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { +basic_8: + %basic_8_sum = call i8 @llvm.uadd.sat.i8(i8 12, i8 30) + %basic_8_ok = icmp eq i8 %basic_8_sum, 42 + br i1 %basic_8_ok, label %overflowing_8, label %error + +overflowing_8: + %overflowing_8_sum = call i8 @llvm.uadd.sat.i8(i8 u0xCC, i8 u0xCC) + %overflowing_8_ok = icmp eq i8 %overflowing_8_sum, 255 + br i1 %overflowing_8_ok, label %basic_16, label %error + +basic_16: + %basic_16_sum = call i16 @llvm.uadd.sat.i16(i16 12, i16 30) + %basic_16_ok = icmp eq i16 %basic_16_sum, 42 + br i1 %basic_16_ok, label %overflowing_16, label %error + +overflowing_16: + %overflowing_16_sum = call i16 @llvm.uadd.sat.i16(i16 u0xCCCC, i16 u0xCCCC) + %overflowing_16_ok = icmp eq i16 %overflowing_16_sum, 65535 + br i1 %overflowing_16_ok, label %basic_32, label %error + +basic_32: + %basic_32_sum = call i32 @llvm.uadd.sat.i32(i32 12, i32 30) + %basic_32_ok = icmp eq i32 %basic_32_sum, 42 + br i1 %basic_32_ok, label %overflowing_32, label %error + +overflowing_32: + %overflowing_32_sum = call i32 @llvm.uadd.sat.i32(i32 u0xCCCCCCCC, i32 u0xCCCCCCCC) + %overflowing_32_ok = icmp eq i32 %overflowing_32_sum, 4294967295 + br i1 %overflowing_32_ok, label %basic_64, label %error + +basic_64: + %basic_64_sum = call i64 @llvm.uadd.sat.i64(i64 12, i64 30) + %basic_64_ok = icmp eq i64 %basic_64_sum, 42 + br i1 %basic_64_ok, label %overflowing_64, label %error + +overflowing_64: + %overflowing_64_sum = call i64 @llvm.uadd.sat.i64(i64 u0xCCCCCCCCCCCCCCCC, i64 u0xCCCCCCCCCCCCCCCC) + %overflowing_64_ok = icmp eq i64 %overflowing_64_sum, 18446744073709551615 + br i1 %overflowing_64_ok, label %ok, label %error + +ok: + ret i32 6 + +error: + %retVal = phi i32 + [ 81, %basic_8 ], + [ 82, %overflowing_8 ], + [ 161, %basic_16 ], + [ 162, %overflowing_16 ], + [ 321, %basic_32 ], + [ 322, %overflowing_32 ], + [ 641, %basic_64 ], + [ 642, %overflowing_64 ] + ret i32 %retVal +} diff --git a/test/ll_tests/test_intrinsic_ushl_sat.ll b/test/ll_tests/test_intrinsic_ushl_sat.ll new file mode 100644 index 00000000..e3e04f98 --- /dev/null +++ b/test/ll_tests/test_intrinsic_ushl_sat.ll @@ -0,0 +1,62 @@ +declare i8 @llvm.ushl.sat.i8(i8 %a, i8 %b) +declare i16 @llvm.ushl.sat.i16(i16 %a, i16 %b) +declare i32 @llvm.ushl.sat.i32(i32 %a, i32 %b) +declare i64 @llvm.ushl.sat.i64(i64 %a, i64 %b) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { +basic_8: + %basic_8_shl = call i8 @llvm.ushl.sat.i8(i8 21, i8 1) + %basic_8_ok = icmp eq i8 %basic_8_shl, 42 + br i1 %basic_8_ok, label %overflowing_8, label %error + +overflowing_8: + %overflowing_8_shl = call i8 @llvm.ushl.sat.i8(i8 2, i8 7) + %overflowing_8_ok = icmp eq i8 %overflowing_8_shl, 255 + br i1 %overflowing_8_ok, label %basic_16, label %error + +basic_16: + %basic_16_shl = call i16 @llvm.ushl.sat.i16(i16 21, i16 1) + %basic_16_ok = icmp eq i16 %basic_16_shl, 42 + br i1 %basic_16_ok, label %overflowing_16, label %error + +overflowing_16: + %overflowing_16_shl = call i16 @llvm.ushl.sat.i16(i16 2, i16 15) + %overflowing_16_ok = icmp eq i16 %overflowing_16_shl, 65535 + br i1 %overflowing_16_ok, label %basic_32, label %error + +basic_32: + %basic_32_shl = call i32 @llvm.ushl.sat.i32(i32 21, i32 1) + %basic_32_ok = icmp eq i32 %basic_32_shl, 42 + br i1 %basic_32_ok, label %overflowing_32, label %error + +overflowing_32: + %overflowing_32_shl = call i32 @llvm.ushl.sat.i32(i32 2, i32 31) + %overflowing_32_ok = icmp eq i32 %overflowing_32_shl, 4294967295 + br i1 %overflowing_32_ok, label %basic_64, label %error + +basic_64: + %basic_64_shl = call i64 @llvm.ushl.sat.i64(i64 21, i64 1) + %basic_64_ok = icmp eq i64 %basic_64_shl, 42 + br i1 %basic_64_ok, label %overflowing_64, label %error + +overflowing_64: + %overflowing_64_shl = call i64 @llvm.ushl.sat.i64(i64 2, i64 63) + %overflowing_64_ok = icmp eq i64 %overflowing_64_shl, 18446744073709551615 + br i1 %overflowing_64_ok, label %ok, label %error + +ok: + ret i32 6 + +error: + %retVal = phi i32 + [ 81, %basic_8 ], + [ 82, %overflowing_8 ], + [ 161, %basic_16 ], + [ 162, %overflowing_16 ], + [ 321, %basic_32 ], + [ 322, %overflowing_32 ], + [ 641, %basic_64 ], + [ 642, %overflowing_64 ] + ret i32 %retVal +}