From ea2de69592646ce9126b4a3dda2ed6114a98cbbe Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 16 Nov 2023 03:05:10 +0300 Subject: [PATCH] Add bit counting nodes (JIT code generation is not supported yet) --- ir.h | 3 +++ ir_builder.h | 36 ++++++++++++++++++++++++++++++++++++ ir_emit_c.c | 20 ++++++++++++++++++++ ir_emit_llvm.c | 20 ++++++++++++++++---- ir_load_llvm.c | 17 ++++++++++++++--- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/ir.h b/ir.h index 315ca6b..04d7123 100644 --- a/ir.h +++ b/ir.h @@ -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) */ \ diff --git a/ir_builder.h b/ir_builder.h index 51a8a1e..bba207f 100644 --- a/ir_builder.h +++ b/ir_builder.h @@ -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)) diff --git a/ir_emit_c.c b/ir_emit_c.c index ea6f3e1..6e5683c 100644 --- a/ir_emit_c.c +++ b/ir_emit_c.c @@ -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; diff --git a/ir_emit_llvm.c b/ir_emit_llvm.c index 117ca31..87ab107 100644 --- a/ir_emit_llvm.c +++ b/ir_emit_llvm.c @@ -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)); diff --git a/ir_load_llvm.c b/ir_load_llvm.c index a0aef52..6fd3987 100644 --- a/ir_load_llvm.c +++ b/ir_load_llvm.c @@ -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");