Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement llvm.*.sat.* intrinsics #210

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions lib/Target/CBackend/CBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<StructType>(retT)->getElementType(0) == elemT);
Out << " r.field0 = a - b;\n";
Out << " r.field1 = (b <= 0 ? a > ";
Expand All @@ -4637,6 +4648,87 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode,
cwriter_assert(cast<StructType>(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);
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
86 changes: 86 additions & 0 deletions test/ll_tests/test_intrinsic_sadd_sat.ll
Original file line number Diff line number Diff line change
@@ -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
}
86 changes: 86 additions & 0 deletions test/ll_tests/test_intrinsic_sshl_sat.ll
Original file line number Diff line number Diff line change
@@ -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
}
62 changes: 62 additions & 0 deletions test/ll_tests/test_intrinsic_uadd_sat.ll
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading