Skip to content

Commit

Permalink
Add/fix more intrinsics: smin, smax, trap, bswap, popcnt, ctlz, and c…
Browse files Browse the repository at this point in the history
…ttz (#188)

add / fix support for some intrisics which either weren't implemented
at all (smin, smax, and trap), or weren't actually working (bswap,
popcnt, ctlz, and cttz).
  • Loading branch information
bcoppens authored Jan 13, 2024
1 parent 1bc2950 commit c019b06
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 23 deletions.
84 changes: 61 additions & 23 deletions lib/Target/CBackend/CBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2232,6 +2232,15 @@ static void defineThreadFence(raw_ostream &Out) {
<< "#endif\n\n";
}

static void defineTrap(raw_ostream &Out) {
Out << "extern void abort(void);\n"
<< "#if defined(__GNUC__)\n"
<< "extern void __builtin_trap(void);\n"
<< "#else\n"
<< "#define __builtin_trap() abort()\n"
<< "#endif\n\n";
}

/// FindStaticTors - Given a static ctor/dtor list, unpack its contents into
/// the StaticTors set.
static void FindStaticTors(GlobalVariable *GV,
Expand Down Expand Up @@ -2348,6 +2357,8 @@ void CWriter::generateCompilerSpecificCode(raw_ostream &Out,
defineThreadFence(Out);
if (headerIncStackSaveRestore())
defineStackSaveRestore(Out);
if (headerIncTrap())
defineTrap(Out);
}

bool CWriter::doInitialization(Module &M) {
Expand Down Expand Up @@ -2573,6 +2584,8 @@ void CWriter::generateHeader(Module &M) {
case Intrinsic::trunc:
case Intrinsic::umax:
case Intrinsic::umin:
case Intrinsic::smin:
case Intrinsic::smax:
case Intrinsic::is_constant:
intrinsicsToDefine.push_back(&*I);
continue;
Expand Down Expand Up @@ -4485,6 +4498,8 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode,
case Intrinsic::sadd_with_overflow:
case Intrinsic::ssub_with_overflow:
case Intrinsic::smul_with_overflow:
case Intrinsic::smin:
case Intrinsic::smax:
isSigned = true;
break;
}
Expand Down Expand Up @@ -4601,47 +4616,63 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode,

case Intrinsic::bswap:
cwriter_assert(retT == elemT);
Out << " LLVMFlipAllBits(8 * sizeof(a), &a, &r);\n";
cwriter_assert(!isa<VectorType>(retT));
cwriter_assert(elemT->getIntegerBitWidth() <= 64);
Out << " int i;\n";
Out << " r = 0;\n";
Out << " int bitwidth = " << elemT->getIntegerBitWidth() << ";\n";
Out << " for (i = 0; i < bitwidth/8; i++)\n";
Out << " r |= ((a >> (i*8)) & 0xff) << (bitwidth - (i+1)*8);\n";
break;

case Intrinsic::ctpop:
cwriter_assert(retT == elemT);
Out << " r = ";
if (retT->getPrimitiveSizeInBits() > 64)
Out << "llvm_ctor_u128(0, ";
Out << "LLVMCountPopulation(8 * sizeof(a), &a)";
if (retT->getPrimitiveSizeInBits() > 64)
Out << ")";
Out << ";\n";
cwriter_assert(!isa<VectorType>(retT));
cwriter_assert(elemT->getIntegerBitWidth() <= 64);
Out << " int i;\n";
Out << " r = 0;\n";
Out << " int bitwidth = " << elemT->getIntegerBitWidth() << ";\n";
Out << " for (i = 0; i < bitwidth; i++)\n";
Out << " if ( (a >> i ) & 1 )\n";
Out << " r++;\n";

break;

case Intrinsic::ctlz:
cwriter_assert(retT == elemT);
Out << " (void)b;\n r = ";
if (retT->getPrimitiveSizeInBits() > 64)
Out << "llvm_ctor_u128(0, ";
Out << "LLVMCountLeadingZeros(8 * sizeof(a), &a)";
if (retT->getPrimitiveSizeInBits() > 64)
Out << ")";
Out << ";\n";
cwriter_assert(!isa<VectorType>(retT));
cwriter_assert(elemT->getIntegerBitWidth() <= 64);
Out << " int i;\n";
Out << " r = 0;\n";
Out << " int bitwidth = " << elemT->getIntegerBitWidth() << ";\n";
Out << " for (i = bitwidth - 1; i >= 0; i--)\n";
Out << " if ( ((a >> i ) & 1) == 0 )\n";
Out << " r++;\n";
Out << " else\n";
Out << " break;\n";
break;

case Intrinsic::cttz:
cwriter_assert(retT == elemT);
Out << " (void)b;\n r = ";
if (retT->getPrimitiveSizeInBits() > 64)
Out << "llvm_ctor_u128(0, ";
Out << "LLVMCountTrailingZeros(8 * sizeof(a), &a)";
if (retT->getPrimitiveSizeInBits() > 64)
Out << ")";
Out << ";\n";
cwriter_assert(!isa<VectorType>(retT));
cwriter_assert(elemT->getIntegerBitWidth() <= 64);
Out << " int i;\n";
Out << " r = 0;\n";
Out << " int bitwidth = " << elemT->getIntegerBitWidth() << ";\n";
Out << " for (i = 0; i < bitwidth; i++)\n";
Out << " if ( ((a >> i) & 1) == 0 )\n";
Out << " r++;\n";
Out << " else\n";
Out << " break;\n";
break;

case Intrinsic::umax:
case Intrinsic::smax:
Out << " r = a > b ? a : b;\n";
break;

case Intrinsic::umin:
case Intrinsic::smin:
Out << " r = a < b ? a : b;\n";
break;
case Intrinsic::is_constant:
Expand Down Expand Up @@ -4768,6 +4799,8 @@ bool CWriter::lowerIntrinsics(Function &F) {
case Intrinsic::dbg_declare:
case Intrinsic::umax:
case Intrinsic::umin:
case Intrinsic::smin:
case Intrinsic::smax:
case Intrinsic::is_constant:
// We directly implement these intrinsics
break;
Expand Down Expand Up @@ -5054,6 +5087,10 @@ bool CWriter::visitBuiltinCall(CallInst &I, Intrinsic::ID ID) {
Out << " = ";
writeOperand(I.getArgOperand(0), ContextCasted);
return true;
case Intrinsic::trap:
headerUseTrap();
Out << "__builtin_trap()";
return true;

// these use the normal function call emission
case Intrinsic::sadd_with_overflow:
Expand All @@ -5080,9 +5117,10 @@ bool CWriter::visitBuiltinCall(CallInst &I, Intrinsic::ID ID) {
case Intrinsic::ctpop:
case Intrinsic::cttz:
case Intrinsic::fmuladd:
case Intrinsic::trap:
case Intrinsic::umax:
case Intrinsic::umin:
case Intrinsic::smax:
case Intrinsic::smin:
case Intrinsic::is_constant:
return false; // these use the normal function call emission
}
Expand Down
2 changes: 2 additions & 0 deletions lib/Target/CBackend/CBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class CWriter : public FunctionPass, public InstVisitor<CWriter> {
bool ConstantFP80Ty : 1;
bool ConstantFP128Ty : 1;
bool ForceInline : 1;
bool Trap : 1;
} UsedHeaders;

#define USED_HEADERS_FLAG(Name) \
Expand Down Expand Up @@ -155,6 +156,7 @@ class CWriter : public FunctionPass, public InstVisitor<CWriter> {
USED_HEADERS_FLAG(ConstantFP80Ty)
USED_HEADERS_FLAG(ConstantFP128Ty)
USED_HEADERS_FLAG(ForceInline)
USED_HEADERS_FLAG(Trap)

llvm::SmallSet<CmpInst::Predicate, 26> FCmpOps;
void headerUseFCmpOp(CmpInst::Predicate P);
Expand Down
69 changes: 69 additions & 0 deletions test/ll_tests/test_bitcounts.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
declare i64 @llvm.ctpop.i64(i64)
declare i64 @llvm.ctlz.i64(i64, i1) ; i1: is_zero_poison
declare i16 @llvm.ctlz.i16(i16, i1) ; i1: is_zero_poison
declare i64 @llvm.cttz.i64(i64, i1); i1: is_zero_poison>
declare i16 @llvm.cttz.i16(i16, i1); i1: is_zero_poison>


define dso_local i32 @main() #0 {
ctpop.1:
%ctpop.1.val = call i64 @llvm.ctpop.i64(i64 18446744073709551615) ; 0xffffffffffffffff
%ctpop.1.ok = icmp eq i64 %ctpop.1.val, 64
br i1 %ctpop.1.ok, label %ctpop.2, label %error

ctpop.2:
%ctpop.2.val = call i64 @llvm.ctpop.i64(i64 0)
%ctpop.2.ok = icmp eq i64 %ctpop.2.val, 0
br i1 %ctpop.2.ok, label %ctpop.3, label %error

ctpop.3:
%ctpop.3.val = call i64 @llvm.ctpop.i64(i64 181) ; 0b10110101
%ctpop.3.ok = icmp eq i64 %ctpop.3.val, 5
br i1 %ctpop.3.ok, label %ctlz.1, label %error

ctlz.1:
%ctlz.1.val = call i64 @llvm.ctlz.i64(i64 18446744073709551615, i1 0) ; 0xffffffffffffffff
%ctlz.1.ok = icmp eq i64 %ctlz.1.val, 0
br i1 %ctlz.1.ok, label %ctlz.2, label %error

ctlz.2:
%ctlz.2.val = call i64 @llvm.ctlz.i64(i64 1, i1 0)
%ctlz.2.ok = icmp eq i64 %ctlz.2.val, 63
br i1 %ctlz.2.ok, label %ctlz.3, label %error

ctlz.3:
%ctlz.3.val = call i16 @llvm.ctlz.i16(i16 181, i1 0) ; 0b10110101
%ctlz.3.ok = icmp eq i16 %ctlz.3.val, 8
br i1 %ctlz.3.ok, label %cttz.1, label %error

cttz.1:
%cttz.1.val = call i64 @llvm.cttz.i64(i64 18446744073709551615, i1 0) ; 0xffffffffffffffff
%cttz.1.ok = icmp eq i64 %cttz.1.val, 0
br i1 %cttz.1.ok, label %cttz.2, label %error

cttz.2:
%cttz.2.val = call i64 @llvm.cttz.i64(i64 1, i1 0)
%cttz.2.ok = icmp eq i64 %cttz.2.val, 0
br i1 %cttz.2.ok, label %cttz.3, label %error

cttz.3:
%cttz.3.val = call i16 @llvm.cttz.i16(i16 180, i1 0) ; 0b10110100
%cttz.3.ok = icmp eq i16 %cttz.3.val, 2
br i1 %cttz.3.ok, label %ok, label %error

ok:
ret i32 6

error:
%retVal = phi i32
[ 20, %ctpop.1 ],
[ 21, %ctpop.2 ],
[ 22, %ctpop.3 ],
[ 23, %ctlz.1 ],
[ 24, %ctlz.2 ],
[ 25, %ctlz.3 ],
[ 26, %cttz.1 ],
[ 27, %cttz.2 ],
[ 28, %cttz.3 ]
ret i32 %retVal
}
30 changes: 30 additions & 0 deletions test/ll_tests/test_bswap.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
declare i16 @llvm.bswap.i16(i16)
declare i32 @llvm.bswap.i32(i32)
declare i64 @llvm.bswap.i64(i64)

define dso_local i32 @main() #0 {
bswap.i16:
%bswap.i16.val = call i16 @llvm.bswap.i16(i16 4660) ; 0x1234
%bswap.i16.ok = icmp eq i16 %bswap.i16.val, 13330 ; 0x3412
br i1 %bswap.i16.ok, label %bswap.i32, label %error

bswap.i32:
%bswap.i32.val = call i32 @llvm.bswap.i32(i32 305419896) ; 0x12345678
%bswap.i32.ok = icmp eq i32 %bswap.i32.val, 2018915346 ; 0x78563412
br i1 %bswap.i32.ok, label %bswap.i64, label %error

bswap.i64:
%bswap.i64.val = call i64 @llvm.bswap.i64(i64 1311768467463790320) ; 0x123456789abcdef0
%bswap.i64.ok = icmp eq i64 %bswap.i64.val, 17356517385562371090 ; 0xf0debc9a78563412
br i1 %bswap.i64.ok, label %ok, label %error

ok:
ret i32 6

error:
%retVal = phi i32
[ 20, %bswap.i16 ],
[ 21, %bswap.i32 ],
[ 22, %bswap.i64 ]
ret i32 %retVal
}
38 changes: 38 additions & 0 deletions test/ll_tests/test_min_max_intrinsics.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
declare i64 @llvm.smin.i64(i64, i64)
declare i64 @llvm.smax.i64(i64, i64)
declare i64 @llvm.umin.i64(i64, i64)
declare i64 @llvm.umax.i64(i64, i64)

define dso_local i32 @main() #0 {
smin.check:
%smin.res = call i64 @llvm.smin.i64(i64 6, i64 -6)
%smin.ok = icmp eq i64 %smin.res, -6
br i1 %smin.ok, label %smax.check, label %error

smax.check:
%smax.res = call i64 @llvm.smax.i64(i64 6, i64 -6)
%smax.ok = icmp eq i64 %smax.res, 6
br i1 %smax.ok, label %umin.check, label %error

umin.check:
%umin.res = call i64 @llvm.umin.i64(i64 6, i64 -6)
%umin.ok = icmp eq i64 %umin.res, 6
br i1 %umin.ok, label %umax.check, label %error

umax.check:
%umax.res = call i64 @llvm.umax.i64(i64 6, i64 -6)
%umax.ok = icmp eq i64 %umax.res, -6
br i1 %umax.ok, label %ok, label %error


ok:
ret i32 6

error:
%retVal = phi i32
[ 20, %smin.check ],
[ 21, %smax.check ],
[ 22, %umin.check ],
[ 23, %umax.check ]
ret i32 %retVal
}
10 changes: 10 additions & 0 deletions test/ll_tests/test_trap.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
declare void @llvm.trap() cold noreturn nounwind

define dso_local i32 @main() #0 {
ret i32 6
}

define dso_local void @this_should_not_happen() {
call void @llvm.trap()
unreachable
}

0 comments on commit c019b06

Please sign in to comment.