From a8e514266f25fe2cc2297a2ed7dd7a7c4890d359 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 15 Nov 2023 15:17:36 +0300 Subject: [PATCH] Add support for some LLVM intrinsic --- ir.h | 13 ++++ ir_emit_llvm.c | 102 +++++++++++++++++++++++++++- ir_load_llvm.c | 180 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 263 insertions(+), 32 deletions(-) diff --git a/ir.h b/ir.h index a93eb75..d92ae0e 100644 --- a/ir.h +++ b/ir.h @@ -136,6 +136,18 @@ typedef enum _ir_type { IR_LAST_TYPE } ir_type; +#ifdef IR_64 +# define IR_SIZE_T IR_U64 +# define IR_SSIZE_T IR_I64 +# define IR_UINTPTR_T IR_U64 +# define IR_INTPTR_T IR_I64 +#else +# define IR_SIZE_T IR_U32 +# define IR_SSIZE_T IR_I32 +# define IR_UINTPTR_T IR_U32 +# define IR_INTPTR_T IR_I32 +#endif + /* List of IR opcodes * ================== * @@ -399,6 +411,7 @@ typedef union _ir_val { #define IR_CONST_EMIT (1<<0) #define IR_CONST_FASTCALL_FUNC (1<<1) #define IR_CONST_VARARG_FUNC (1<<2) +#define IR_CONST_BUILTIN_FUNC (1<<3) /* IR Instruction */ typedef struct _ir_insn { diff --git a/ir_emit_llvm.c b/ir_emit_llvm.c index b0739a0..af1fbfd 100644 --- a/ir_emit_llvm.c +++ b/ir_emit_llvm.c @@ -422,9 +422,93 @@ static void ir_emit_switch(ir_ctx *ctx, FILE *f, uint32_t b, ir_ref def, ir_insn fprintf(f, "\t]\n"); } +static const char *ir_builtin_func_name(const char *name, ir_ref *last_arg) +{ + if (strcmp(name, "memset")) { + *last_arg = IR_FALSE; + return (IR_SIZE_T == IR_I32) ? "llvm.memset.p0.i32" : "llvm.memset.p0.i64"; + } else if (strcmp(name, "memcpy")) { + *last_arg = IR_FALSE; + return (IR_SIZE_T == IR_I32) ? "llvm.memcpy.p0.p0.i32" : "llvm.memcpy.p0.p0.i64"; + } else if (strcmp(name, "memmove")) { + *last_arg = IR_FALSE; + return (IR_SIZE_T == IR_I32) ? "llvm.memmove.p0.p0.i32" : "llvm.memmve.p0.p0.i64"; + } else if (strcmp(name, "sqrt")) { + return "llvm.sqrt.f64"; + } else if (strcmp(name, "sqrtf")) { + return "llvm.sqrt.f32"; + } else if (strcmp(name, "sin")) { + return "llvm.sin.f64"; + } else if (strcmp(name, "sinf")) { + return "llvm.sin.f32"; + } else if (strcmp(name, "cos")) { + return "llvm.cos.f64"; + } else if (strcmp(name, "cosf")) { + return "llvm.cos.f32"; + } else if (strcmp(name, "pow")) { + return "llvm.pow.f64"; + } else if (strcmp(name, "powf")) { + return "llvm.pow.f32"; + } else if (strcmp(name, "exp")) { + return "llvm.exp.f64"; + } else if (strcmp(name, "expf")) { + return "llvm.exp.f32"; + } else if (strcmp(name, "exp2")) { + return "llvm.exp2.f64"; + } else if (strcmp(name, "exp2f")) { + return "llvm.exp2.f32"; + } else if (strcmp(name, "exp10")) { + return "llvm.exp10.f64"; + } else if (strcmp(name, "exp10f")) { + return "llvm.exp10.f32"; + } else if (strcmp(name, "ldexp")) { + return "llvm.ldexp.f64.i32"; + } else if (strcmp(name, "ldexpf")) { + return "llvm.ldexp.f32.i32"; + } else if (strcmp(name, "frexp")) { + return "llvm.frexp.f64.i32"; + } else if (strcmp(name, "frexpf")) { + return "llvm.frexp.f32.i32"; + } else if (strcmp(name, "log")) { + return "llvm.log.f64"; + } else if (strcmp(name, "logf")) { + return "llvm.log.f32"; + } else if (strcmp(name, "log2")) { + return "llvm.log2.f64"; + } else if (strcmp(name, "log2f")) { + return "llvm.log2.f32"; + } else if (strcmp(name, "log10")) { + return "llvm.log10.f64"; + } else if (strcmp(name, "log10f")) { + return "llvm.log10.f32"; + } else if (strcmp(name, "copysign")) { + return "llvm.copysign.f64"; + } else if (strcmp(name, "copysignf")) { + return "llvm.copysign.f32"; + } else if (strcmp(name, "floor")) { + return "llvm.floor.f64"; + } else if (strcmp(name, "floorf")) { + return "llvm.floor.f32"; + } else if (strcmp(name, "ceil")) { + return "llvm.ceil.f64"; + } else if (strcmp(name, "ceilf")) { + return "llvm.ceil.f32"; + } else if (strcmp(name, "trunc")) { + return "llvm.trunc.f64"; + } else if (strcmp(name, "truncf")) { + return "llvm.trunc.f32"; + } else if (strcmp(name, "round")) { + return "llvm.round.f64"; + } else if (strcmp(name, "roundf")) { + return "llvm.round.f32"; + } + return name; +} + static void ir_emit_call(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn) { int j, k, n; + ir_ref last_arg = IR_UNUSED; if (insn->type != IR_VOID) { ir_emit_def_ref(ctx, f, def); @@ -446,7 +530,11 @@ static void ir_emit_call(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn) // TODO: function prototype ??? if (IR_IS_CONST_REF(insn->op2)) { - fprintf(f, "@%s", ir_get_str(ctx, ctx->ir_base[insn->op2].val.i32)); + const char *name = ir_get_str(ctx, ctx->ir_base[insn->op2].val.i32); + if (ctx->ir_base[insn->op2].const_flags & IR_CONST_BUILTIN_FUNC) { + name = ir_builtin_func_name(name, &last_arg); + } + fprintf(f, "@%s", name); } else { ir_emit_ref(ctx, f, insn->op2); } @@ -460,6 +548,11 @@ static void ir_emit_call(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn) fprintf(f, "%s ", ir_type_llvm_name[ctx->ir_base[k].type]); ir_emit_ref(ctx, f, k); } + if (last_arg) { + fprintf(f, ", "); + fprintf(f, "%s ", ir_type_llvm_name[ctx->ir_base[last_arg].type]); + ir_emit_ref(ctx, f, last_arg); + } fprintf(f, ")\n"); if (insn->op == IR_TAILCALL) { if (insn->type != IR_VOID) { @@ -833,8 +926,13 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f) for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) { if (insn->op == IR_FUNC) { + const char *name = ir_get_str(ctx, insn->val.i32); + if (insn->const_flags & IR_CONST_BUILTIN_FUNC) { + ir_ref dummy; + name = ir_builtin_func_name(name, &dummy); + } // TODO: function prototype ??? - fprintf(f, "declare void @%s()\n", ir_get_str(ctx, insn->val.i32)); + fprintf(f, "declare void @%s()\n", name); } else if (insn->op == IR_SYM) { // TODO: symbol "global" or "constant" ??? // TODO: symbol type ??? diff --git a/ir_load_llvm.c b/ir_load_llvm.c index 7b99e7e..548b0b2 100644 --- a/ir_load_llvm.c +++ b/ir_load_llvm.c @@ -16,6 +16,12 @@ #define IR_BAD_TYPE IR_LAST_TYPE +#define BUILTIN_FUNC(name) \ + ir_const_func(ctx, ir_strl(ctx, name, strlen(name)), IR_CONST_BUILTIN_FUNC) + +#define BUILTIN_FP_FUNC(type, dname, fname) \ + ((type == IR_DOUBLE) ? BUILTIN_FUNC(dname) : BUILTIN_FUNC(fname)) + static ir_ref llvm2ir_const_expr(ir_ctx *ctx, LLVMValueRef expr); static ir_ref llvm2ir_auto_cast(ir_ctx *ctx, ir_ref ref, ir_type src_type, ir_type type); @@ -303,6 +309,26 @@ static ir_ref llvm2ir_icmp_op(ir_ctx *ctx, LLVMValueRef expr) return ref; } +static ir_ref llvm2ir_fcmp_op_isnan(ir_ctx *ctx, LLVMValueRef expr, ir_type type, + LLVMRealPredicate predicate, LLVMValueRef op0, LLVMValueRef op1) +{ + ir_ref func, ref; + + func = BUILTIN_FP_FUNC(type, "isnan", "isnanf"); + if (LLVMGetValueKind(op1) == LLVMConstantIntValueKind) { + ref = ir_CALL_1(IR_BOOL, func, llvm2ir_op(ctx, op0, type)); + } else { + ref = ir_OR_B( + ir_CALL_1(IR_BOOL, func, llvm2ir_op(ctx, op0, type)), + ir_CALL_1(IR_BOOL, func, llvm2ir_op(ctx, op1, type))); + } + if (predicate == LLVMRealORD) { + ref = ir_NOT_B(ref); + } + ir_addrtab_add(ctx->binding, (uintptr_t)expr, ref); + return ref; +} + static ir_ref llvm2ir_fcmp_op(ir_ctx *ctx, LLVMValueRef expr) { LLVMValueRef op0 = LLVMGetOperand(expr, 0); @@ -310,8 +336,9 @@ static ir_ref llvm2ir_fcmp_op(ir_ctx *ctx, LLVMValueRef expr) ir_type type = llvm2ir_type(LLVMTypeOf(op0)); ir_ref ref; ir_op op; + LLVMRealPredicate predicate = LLVMGetFCmpPredicate(expr); - switch (LLVMGetFCmpPredicate(expr)) { + switch (predicate) { case LLVMRealOEQ: case LLVMRealUEQ: op = IR_EQ; break; case LLVMRealONE: @@ -324,8 +351,9 @@ static ir_ref llvm2ir_fcmp_op(ir_ctx *ctx, LLVMValueRef expr) case LLVMRealOGE: op = IR_GE; break; case LLVMRealOLT: op = IR_LT; break; case LLVMRealOLE: op = IR_LE; break; - case LLVMRealUNO: op = IR_NE; break; // TODO: isnan() upport. IR_NE is invalid ??? - case LLVMRealORD: op = IR_NE; break; // TODO: isnan() upport. IR_NE is invalid ??? + case LLVMRealUNO: + case LLVMRealORD: + return llvm2ir_fcmp_op_isnan(ctx, expr, type, predicate, op0, op1); default: IR_ASSERT(0); return 0; } ref = ir_fold2(ctx, IR_OPT(op, IR_BOOL), llvm2ir_op(ctx, op0, type), llvm2ir_op(ctx, op1, type)); @@ -449,6 +477,7 @@ static ir_type llvm2ir_overflow_type(LLVMTypeRef stype) static ir_ref llvm2ir_intrinsic(ir_ctx *ctx, LLVMValueRef insn, LLVMTypeRef ftype, uint32_t count, const char *name, size_t name_len) { ir_type type; + ir_ref func; if (STR_START(name, name_len, "llvm.lifetime.")) { /* skip */ @@ -636,56 +665,147 @@ static ir_ref llvm2ir_intrinsic(ir_ctx *ctx, LLVMValueRef insn, LLVMTypeRef ftyp } else if (STR_START(name, name_len, "llvm.cttz.")) { // TODO: } else if (STR_START(name, name_len, "llvm.memset.")) { - // TODO: + IR_ASSERT(count == 3 || count == 4); + func = BUILTIN_FUNC("memset"); + return ir_CALL_3(IR_VOID, func, + llvm2ir_op(ctx, LLVMGetOperand(insn, 0), IR_ADDR), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_I8), + llvm2ir_op(ctx, LLVMGetOperand(insn, 2), IR_SIZE_T)); } else if (STR_START(name, name_len, "llvm.memcpy.")) { - // TODO: + IR_ASSERT(count == 3 || count == 4); + func = BUILTIN_FUNC("memcpy"); + return ir_CALL_3(IR_VOID, func, + llvm2ir_op(ctx, LLVMGetOperand(insn, 0), IR_ADDR), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_ADDR), + llvm2ir_op(ctx, LLVMGetOperand(insn, 2), IR_SIZE_T)); } else if (STR_START(name, name_len, "llvm.memmove.")) { - // TODO: + IR_ASSERT(count == 3 || count == 4); + func = BUILTIN_FUNC("memmove"); + return ir_CALL_3(IR_VOID, func, + llvm2ir_op(ctx, LLVMGetOperand(insn, 0), IR_ADDR), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_ADDR), + llvm2ir_op(ctx, LLVMGetOperand(insn, 2), IR_SIZE_T)); } else if (STR_START(name, name_len, "llvm.frameaddress.")) { // TODO: } else if (STR_EQUAL(name, name_len, "llvm.debugtrap")) { ir_TRAP(); return ctx->control; } else if (STR_START(name, name_len, "llvm.sqrt.")) { - // TODO: - } else if (STR_START(name, name_len, "llvm.powi.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "sqrt", "sqrtf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.sin.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "sin", "sinf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.cos.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "cos", "cosf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.pow.")) { - // TODO: + IR_ASSERT(count == 2); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "pow", "powf"); + return ir_CALL_2(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), type)); } else if (STR_START(name, name_len, "llvm.exp.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "exp", "expf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.exp2.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "exp2", "exp2f"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.exp10.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "exp10", "exp10f"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.ldexp.")) { - // TODO: + IR_ASSERT(count == 2); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "ldexp", "ldexpf"); + return ir_CALL_2(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_I32)); } else if (STR_START(name, name_len, "llvm.frexp.")) { - // TODO: + IR_ASSERT(count == 2); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "frexp", "frexpf"); + return ir_CALL_2(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_ADDR)); } else if (STR_START(name, name_len, "llvm.log.")) { - // TODO: - } else if (STR_START(name, name_len, "llvm.log10.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "log", "logf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.log2.")) { - // TODO: - } else if (STR_START(name, name_len, "llvm.frexp.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "log2", "log2f"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); + } else if (STR_START(name, name_len, "llvm.log10.")) { + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "log10", "log10f"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.copysign.")) { - // TODO: + IR_ASSERT(count == 2); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "copysign", "copysignf"); + return ir_CALL_2(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), type)); } else if (STR_START(name, name_len, "llvm.floor.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "floor", "floorf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.ceil.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "ceil", "ceilf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.trunc.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "trunc", "truncf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); } else if (STR_START(name, name_len, "llvm.round.")) { - // TODO: - } else if (STR_START(name, name_len, "llvm.fmuladd.")) { - // TODO: + IR_ASSERT(count == 1); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + func = BUILTIN_FP_FUNC(type, "round", "roundf"); + return ir_CALL_1(type, func, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type)); + } else if (STR_START(name, name_len, "llvm.fmuladd.") + || STR_START(name, name_len, "llvm.fma.")) { + IR_ASSERT(count == 3); + type = llvm2ir_type(LLVMGetReturnType(ftype)); + IR_ASSERT(IR_IS_TYPE_FP(type)); + return ir_fold2(ctx, IR_OPT(IR_ADD, type), + ir_fold2(ctx, IR_OPT(IR_MUL, type), + llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type), + llvm2ir_op(ctx, LLVMGetOperand(insn, 1), type)), + llvm2ir_op(ctx, LLVMGetOperand(insn, 2), type)); } else { fprintf(stderr, "Unsupported LLVM intrinsic: %s\n", name); IR_ASSERT(0);