Add bit counting nodes (JIT code generation is not supported yet)

This commit is contained in:
Dmitry Stogov 2023-11-16 03:05:10 +03:00
parent 90b6f34db2
commit ea2de69592
5 changed files with 89 additions and 7 deletions

3
ir.h
View File

@ -264,6 +264,9 @@ typedef enum _ir_type {
_(ROL, d2, def, def, ___) /* rotate left */ \
_(ROR, d2, def, def, ___) /* rotate right */ \
_(BSWAP, d1, def, ___, ___) /* byte swap */ \
_(CTPOP, d1, def, ___, ___) /* count population */ \
_(CTLZ, d1, def, ___, ___) /* count leading zeros */ \
_(CTTZ, d1, def, ___, ___) /* count trailing zeros */ \
\
/* branch-less conditional ops */ \
_(MIN, d2C, def, def, ___) /* min(op1, op2) */ \

View File

@ -389,6 +389,42 @@ extern "C" {
#define ir_BSWAP_I32(_op1) ir_UNARY_OP_I32(IR_BSWAP, (_op1))
#define ir_BSWAP_I64(_op1) ir_UNARY_OP_I64(IR_BSWAP, (_op1))
#define ir_CTPOP(_type, _op1) ir_UNARY_OP(IR_CTPOP, (_type), (_op1))
#define ir_CTPOP_8(_op1) ir_UNARY_OP_U8(IR_CTPOP, (_op1))
#define ir_CTPOP_U16(_op1) ir_UNARY_OP_U16(IR_CTPOP, (_op1))
#define ir_CTPOP_U32(_op1) ir_UNARY_OP_U32(IR_CTPOP, (_op1))
#define ir_CTPOP_U64(_op1) ir_UNARY_OP_U64(IR_CTPOP, (_op1))
#define ir_CTPOP_A(_op1) ir_UNARY_OP_A(IR_CTPOP, (_op1))
#define ir_CTPOP_C(_op1) ir_UNARY_OP_C(IR_CTPOP, (_op1))
#define ir_CTPOP_I8(_op1) ir_UNARY_OP_I8(IR_CTPOP, (_op1))
#define ir_CTPOP_I16(_op1) ir_UNARY_OP_I16(IR_CTPOP, (_op1))
#define ir_CTPOP_I32(_op1) ir_UNARY_OP_I32(IR_CTPOP, (_op1))
#define ir_CTPOP_I64(_op1) ir_UNARY_OP_I64(IR_CTPOP, (_op1))
#define ir_CTLZ(_type, _op1) ir_UNARY_OP(IR_CTLZ, (_type), (_op1))
#define ir_CTLZ_8(_op1) ir_UNARY_OP_U8(IR_CTLZ, (_op1))
#define ir_CTLZ_U16(_op1) ir_UNARY_OP_U16(IR_CTLZ, (_op1))
#define ir_CTLZ_U32(_op1) ir_UNARY_OP_U32(IR_CTLZ, (_op1))
#define ir_CTLZ_U64(_op1) ir_UNARY_OP_U64(IR_CTLZ, (_op1))
#define ir_CTLZ_A(_op1) ir_UNARY_OP_A(IR_CTLZ, (_op1))
#define ir_CTLZ_C(_op1) ir_UNARY_OP_C(IR_CTLZ, (_op1))
#define ir_CTLZ_I8(_op1) ir_UNARY_OP_I8(IR_CTLZ, (_op1))
#define ir_CTLZ_I16(_op1) ir_UNARY_OP_I16(IR_CTLZ, (_op1))
#define ir_CTLZ_I32(_op1) ir_UNARY_OP_I32(IR_CTLZ, (_op1))
#define ir_CTLZ_I64(_op1) ir_UNARY_OP_I64(IR_CTLZ, (_op1))
#define ir_CTTZ(_type, _op1) ir_UNARY_OP(IR_CTTZ, (_type), (_op1))
#define ir_CTTZ_8(_op1) ir_UNARY_OP_U8(IR_CTTZ, (_op1))
#define ir_CTTZ_U16(_op1) ir_UNARY_OP_U16(IR_CTTZ, (_op1))
#define ir_CTTZ_U32(_op1) ir_UNARY_OP_U32(IR_CTTZ, (_op1))
#define ir_CTTZ_U64(_op1) ir_UNARY_OP_U64(IR_CTTZ, (_op1))
#define ir_CTTZ_A(_op1) ir_UNARY_OP_A(IR_CTTZ, (_op1))
#define ir_CTTZ_C(_op1) ir_UNARY_OP_C(IR_CTTZ, (_op1))
#define ir_CTTZ_I8(_op1) ir_UNARY_OP_I8(IR_CTTZ, (_op1))
#define ir_CTTZ_I16(_op1) ir_UNARY_OP_I16(IR_CTTZ, (_op1))
#define ir_CTTZ_I32(_op1) ir_UNARY_OP_I32(IR_CTTZ, (_op1))
#define ir_CTTZ_I64(_op1) ir_UNARY_OP_I64(IR_CTTZ, (_op1))
#define ir_MIN(_type, _op1, _op2) ir_BINARY_OP(IR_MIN, (_type), (_op1), (_op2))
#define ir_MIN_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MIN, (_op1), (_op2))
#define ir_MIN_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MIN, (_op1), (_op2))

View File

@ -223,6 +223,14 @@ static void ir_emit_bswap(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
fprintf(f, ");\n");
}
static void ir_emit_count(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *name)
{
ir_emit_def_ref(ctx, f, def);
fprintf(f, "__builtin_%s%s(", name, ir_type_size[insn->type] == 8 ? "ll" : "");
ir_emit_ref(ctx, f, insn->op1);
fprintf(f, ");\n");
}
static void ir_emit_sext(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
{
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
@ -895,6 +903,15 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
case IR_BSWAP:
ir_emit_bswap(ctx, f, i, insn);
break;
case IR_CTPOP:
ir_emit_count(ctx, f, i, insn, "popcount");
break;
case IR_CTLZ:
ir_emit_count(ctx, f, i, insn, "clz");
break;
case IR_CTTZ:
ir_emit_count(ctx, f, i, insn, "ctz");
break;
case IR_SEXT:
ir_emit_sext(ctx, f, i, insn);
break;
@ -1006,6 +1023,9 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ", %s);\n", ir_type_cname[insn->type]);
break;
case IR_TRAP:
fprintf(f, "\t__builtin_debugtrap();\n");
break;
default:
IR_ASSERT(0 && "NIY instruction");
ctx->status = IR_ERROR_UNSUPPORTED_CODE_RULE;

View File

@ -235,14 +235,17 @@ static void ir_emit_rol_ror(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const
fprintf(f, ")\n");
}
static void ir_emit_bswap(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
static void ir_emit_bitop(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *name, bool poison)
{
ir_type type = insn->type;
ir_emit_def_ref(ctx, f, def);
fprintf(f, "call %s @llvm.bswap.%s(%s ",
ir_type_llvm_name[type], ir_type_llvm_name[type], ir_type_llvm_name[type]);
fprintf(f, "call %s @llvm.%s.%s(%s ",
ir_type_llvm_name[type], name, ir_type_llvm_name[type], ir_type_llvm_name[type]);
ir_emit_ref(ctx, f, insn->op1);
if (poison) {
fprintf(f, ", 0");
}
fprintf(f, ")\n");
}
@ -795,7 +798,16 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
ir_emit_rol_ror(ctx, f, i, insn, "fshr");
break;
case IR_BSWAP:
ir_emit_bswap(ctx, f, i, insn);
ir_emit_bitop(ctx, f, i, insn, "bswap", 0);
break;
case IR_CTPOP:
ir_emit_bitop(ctx, f, i, insn, "ctpop", 0);
break;
case IR_CTLZ:
ir_emit_bitop(ctx, f, i, insn, "ctlz", 1);
break;
case IR_CTTZ:
ir_emit_bitop(ctx, f, i, insn, "cttz", 1);
break;
case IR_SEXT:
IR_ASSERT(IR_IS_TYPE_INT(insn->type));

View File

@ -772,11 +772,22 @@ static ir_ref llvm2ir_intrinsic(ir_ctx *ctx, LLVMValueRef insn, LLVMTypeRef ftyp
llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_ADDR));
return IR_NULL;
} else if (STR_START(name, name_len, "llvm.ctpop.")) {
// TODO:
LLVMValueRef op0 = LLVMGetOperand(insn, 0);
ir_type type = llvm2ir_type(LLVMTypeOf(op0));
return ir_CTPOP(type, llvm2ir_op(ctx, op0, type));
} else if (STR_START(name, name_len, "llvm.ctlz.")) {
// TODO:
LLVMValueRef op0 = LLVMGetOperand(insn, 0);
ir_type type = llvm2ir_type(LLVMTypeOf(op0));
// TODO: support for the second argument
return ir_CTLZ(type, llvm2ir_op(ctx, op0, type));
} else if (STR_START(name, name_len, "llvm.cttz.")) {
// TODO:
LLVMValueRef op0 = LLVMGetOperand(insn, 0);
ir_type type = llvm2ir_type(LLVMTypeOf(op0));
// TODO: support for the second argument
return ir_CTTZ(type, llvm2ir_op(ctx, op0, type));
} else if (STR_START(name, name_len, "llvm.memset.")) {
IR_ASSERT(count == 3 || count == 4);
func = BUILTIN_FUNC("memset");