From 6482f6b754df9f599d2040dfc70866471129054f Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 4 Oct 2023 18:19:09 +0300 Subject: [PATCH] Initial support for LLVM loader (incomplete) --- Makefile | 11 +- examples/0001-while.c | 2 +- ir.c | 124 ++++++- ir.h | 4 + ir_builder.h | 9 +- ir_load_llvm.c | 813 ++++++++++++++++++++++++++++++++++++++++++ ir_main.c | 54 ++- ir_private.h | 12 + ir_test.c | 6 +- 9 files changed, 1004 insertions(+), 31 deletions(-) create mode 100644 ir_load_llvm.c diff --git a/Makefile b/Makefile index ba4617ee..e81f3d4d 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ TARGET = x86_64 BUILD = debug BUILD_DIR = . SRC_DIR = . +HAVE_LLVM = no EXAMPLES_SRC_DIR = $(SRC_DIR)/examples EXAMPLES_BUILD_DIR = $(BUILD_DIR)/examples @@ -39,12 +40,18 @@ ifeq (aarch64, $(TARGET)) DASM_FLAGS = -M endif +ifeq (yes, $(HAVE_LLVM)) + override CFLAGS += -DHAVE_LLVM + LLVM_OBJS=$(BUILD_DIR)/ir_load_llvm.o + LLVM_LIBS=-lLLVM +endif + OBJS_COMMON = $(BUILD_DIR)/ir.o $(BUILD_DIR)/ir_strtab.o $(BUILD_DIR)/ir_cfg.o \ $(BUILD_DIR)/ir_sccp.o $(BUILD_DIR)/ir_gcm.o $(BUILD_DIR)/ir_ra.o $(BUILD_DIR)/ir_emit.o \ $(BUILD_DIR)/ir_load.o $(BUILD_DIR)/ir_save.o $(BUILD_DIR)/ir_emit_c.o $(BUILD_DIR)/ir_dump.o \ $(BUILD_DIR)/ir_disasm.o $(BUILD_DIR)/ir_gdb.o $(BUILD_DIR)/ir_perf.o $(BUILD_DIR)/ir_check.o \ $(BUILD_DIR)/ir_cpuinfo.o $(BUILD_DIR)/ir_emit_llvm.o -OBJS_IR = $(BUILD_DIR)/ir_main.o +OBJS_IR = $(BUILD_DIR)/ir_main.o $(LLVM_OBJS) OBJS_IR_TEST = $(BUILD_DIR)/ir_test.o EXAMPLE_EXES = $(EXAMPLES_BUILD_DIR)/0001-basic $(EXAMPLES_BUILD_DIR)/0001-while $(EXAMPLES_BUILD_DIR)/0005-basic-runner-func \ $(EXAMPLES_BUILD_DIR)/0001-pointer $(EXAMPLES_BUILD_DIR)/0001-func @@ -58,7 +65,7 @@ $(EXAMPLES_BUILD_DIR): @mkdir -p $(EXAMPLES_BUILD_DIR) $(BUILD_DIR)/ir: $(OBJS_COMMON) $(OBJS_IR) - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -lcapstone + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LLVM_LIBS) -lcapstone $(BUILD_DIR)/ir_test: $(OBJS_COMMON) $(OBJS_IR_TEST) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -lcapstone diff --git a/examples/0001-while.c b/examples/0001-while.c index 92c65277..c81dea2f 100644 --- a/examples/0001-while.c +++ b/examples/0001-while.c @@ -25,7 +25,7 @@ void gen_myfunc(ir_ctx *ctx) /* Declare loop counter. */ ir_ref i = ir_COPY_I32(ir_CONST_I32(0)); ir_ref loop = ir_LOOP_BEGIN(ir_END()); - ir_ref phi_i_1 = ir_PHI_2(i, IR_UNUSED); + ir_ref phi_i_1 = ir_PHI_2(IR_I32, i, IR_UNUSED); ir_ref i_2 = ir_ADD_I32(phi_i_1, ir_CONST_I32(1)); ir_ref cond = ir_IF(ir_LT(phi_i_1, ir_CONST_I32(42))); ir_IF_TRUE(cond); diff --git a/ir.c b/ir.c index 3236a38f..89afe976 100644 --- a/ir.c +++ b/ir.c @@ -1315,6 +1315,104 @@ void ir_hashtab_key_sort(ir_hashtab *tab) } while (--i); } +static void ir_addrtab_resize(ir_hashtab *tab) +{ + uint32_t old_hash_size = (uint32_t)(-(int32_t)tab->mask); + char *old_data = tab->data; + uint32_t size = tab->size * 2; + uint32_t hash_size = ir_hashtab_hash_size(size); + char *data = ir_mem_malloc(hash_size * sizeof(uint32_t) + size * sizeof(ir_addrtab_bucket)); + ir_addrtab_bucket *p; + uint32_t pos, i; + + memset(data, -1, hash_size * sizeof(uint32_t)); + tab->data = data + (hash_size * sizeof(uint32_t)); + tab->mask = (uint32_t)(-(int32_t)hash_size); + tab->size = size; + + memcpy(tab->data, old_data, tab->count * sizeof(ir_addrtab_bucket)); + ir_mem_free(old_data - (old_hash_size * sizeof(uint32_t))); + + i = tab->count; + pos = 0; + p = (ir_addrtab_bucket*)tab->data; + do { + uint32_t key = (uint32_t)p->key | tab->mask; + p->next = ((uint32_t*)tab->data)[(int32_t)key]; + ((uint32_t*)tab->data)[(int32_t)key] = pos; + pos += sizeof(ir_addrtab_bucket); + p++; + } while (--i); +} + +void ir_addrtab_init(ir_hashtab *tab, uint32_t size) +{ + IR_ASSERT(size > 0); + uint32_t hash_size = ir_hashtab_hash_size(size); + char *data = ir_mem_malloc(hash_size * sizeof(uint32_t) + size * sizeof(ir_addrtab_bucket)); + memset(data, -1, hash_size * sizeof(uint32_t)); + tab->data = (data + (hash_size * sizeof(uint32_t))); + tab->mask = (uint32_t)(-(int32_t)hash_size); + tab->size = size; + tab->count = 0; + tab->pos = 0; +} + +void ir_addrtab_free(ir_hashtab *tab) +{ + uint32_t hash_size = (uint32_t)(-(int32_t)tab->mask); + char *data = (char*)tab->data - (hash_size * sizeof(uint32_t)); + ir_mem_free(data); + tab->data = NULL; +} + +ir_ref ir_addrtab_find(const ir_hashtab *tab, uint64_t key) +{ + const char *data = (const char*)tab->data; + uint32_t pos = ((uint32_t*)data)[(int32_t)(key | tab->mask)]; + ir_addrtab_bucket *p; + + while (pos != IR_INVALID_IDX) { + p = (ir_addrtab_bucket*)(data + pos); + if (p->key == key) { + return p->val; + } + pos = p->next; + } + return IR_INVALID_VAL; +} + +bool ir_addrtab_add(ir_hashtab *tab, uint64_t key, ir_ref val) +{ + char *data = (char*)tab->data; + uint32_t pos = ((uint32_t*)data)[(int32_t)(key | tab->mask)]; + ir_addrtab_bucket *p; + + while (pos != IR_INVALID_IDX) { + p = (ir_addrtab_bucket*)(data + pos); + if (p->key == key) { + return p->val == val; + } + pos = p->next; + } + + if (UNEXPECTED(tab->count >= tab->size)) { + ir_addrtab_resize(tab); + data = tab->data; + } + + pos = tab->pos; + tab->pos += sizeof(ir_addrtab_bucket); + tab->count++; + p = (ir_addrtab_bucket*)(data + pos); + p->key = key; + p->val = val; + key |= tab->mask; + p->next = ((uint32_t*)data)[(int32_t)key]; + ((uint32_t*)data)[(int32_t)key] = pos; + return 1; +} + /* Memory API */ #ifdef _WIN32 void *ir_mem_mmap(size_t size) @@ -1550,19 +1648,17 @@ ir_ref _ir_VAR(ir_ctx *ctx, ir_type type, const char* name) return ir_var(ctx, type, ctx->control, name); } -ir_ref _ir_PHI_2(ir_ctx *ctx, ir_ref src1, ir_ref src2) +ir_ref _ir_PHI_2(ir_ctx *ctx, ir_type type, ir_ref src1, ir_ref src2) { - ir_type type = ctx->ir_base[src1].type; - IR_ASSERT(ctx->control); IR_ASSERT(ctx->ir_base[ctx->control].op == IR_MERGE || ctx->ir_base[ctx->control].op == IR_LOOP_BEGIN); - if (src1 == src2) { + if (src1 == src2 && src1 != IR_UNUSED) { return src1; } return ir_emit3(ctx, IR_OPTX(IR_PHI, type, 3), ctx->control, src1, src2); } -ir_ref _ir_PHI_N(ir_ctx *ctx, ir_ref n, ir_ref *inputs) +ir_ref _ir_PHI_N(ir_ctx *ctx, ir_type type, ir_ref n, ir_ref *inputs) { IR_ASSERT(ctx->control); IR_ASSERT(n > 0); @@ -1573,17 +1669,19 @@ ir_ref _ir_PHI_N(ir_ctx *ctx, ir_ref n, ir_ref *inputs) ir_ref ref = inputs[0]; IR_ASSERT(ctx->ir_base[ctx->control].op == IR_MERGE || ctx->ir_base[ctx->control].op == IR_LOOP_BEGIN); - for (i = 1; i < n; i++) { - if (inputs[i] != ref) { - break; + if (ref != IR_UNUSED) { + for (i = 1; i < n; i++) { + if (inputs[i] != ref) { + break; + } + } + if (i == n) { + /* all the same */ + return ref; } - } - if (i == n) { - /* all the same */ - return ref; } - ref = ir_emit_N(ctx, IR_OPT(IR_PHI, ctx->ir_base[inputs[0]].type), n + 1); + ref = ir_emit_N(ctx, IR_OPT(IR_PHI, type), n + 1); ir_set_op(ctx, ref, 1, ctx->control); for (i = 0; i < n; i++) { ir_set_op(ctx, ref, i + 2, inputs[i]); diff --git a/ir.h b/ir.h index c0e7761b..0256adf6 100644 --- a/ir.h +++ b/ir.h @@ -750,6 +750,10 @@ void ir_loader_init(void); void ir_loader_free(void); int ir_load(ir_ctx *ctx, FILE *f); +/* IR LLVM load API (implementation in ir_load_llvm.c) */ +int ir_load_llvm_bitcode(const char *filename, uint32_t flags); +int ir_load_llvm_asm(const char *filename, uint32_t flags); + /* IR save API (implementation in ir_save.c) */ void ir_save(const ir_ctx *ctx, FILE *f); diff --git a/ir_builder.h b/ir_builder.h index 394a64c7..e01e7618 100644 --- a/ir_builder.h +++ b/ir_builder.h @@ -399,8 +399,8 @@ extern "C" { #define ir_COND_D(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_DOUBLE), (_op1), (_op2), (_op3)) #define ir_COND_F(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_FLOAT), (_op1), (_op2), (_op3)) -#define ir_PHI_2(_src1, _src2) _ir_PHI_2(_ir_CTX, (_src1), (_src2)) -#define ir_PHI_N(_n, _inputs) _ir_PHI_N(_ir_CTX, (_n), (_inputs)) +#define ir_PHI_2(type, _src1, _src2) _ir_PHI_2(_ir_CTX, type, (_src1), (_src2)) +#define ir_PHI_N(type, _n, _inputs) _ir_PHI_N(_ir_CTX, type, (_n), (_inputs)) #define ir_PHI_SET_OP(_ref, _pos, _src) _ir_PHI_SET_OP(_ir_CTX, (_ref), (_pos), (_src)) #define ir_COPY_B(_op1) ir_UNARY_OP_B(IR_COPY, (_op1)) @@ -455,6 +455,7 @@ extern "C" { #define ir_ALLOCA(_size) _ir_ALLOCA(_ir_CTX, (_size)) #define ir_AFREE(_size) _ir_AFREE(_ir_CTX, (_size)) #define ir_VADDR(_var) ir_emit1(_ir_CTX, IR_OPT(IR_VADDR, IR_ADDR), (_var)) +#define ir_VLOAD(_type, _var) _ir_VLOAD(_ir_CTX, (_type), (_var)) #define ir_VLOAD_B(_var) _ir_VLOAD(_ir_CTX, IR_BOOL, (_var)) #define ir_VLOAD_U8(_var) _ir_VLOAD(_ir_CTX, IR_U8, (_var)) #define ir_VLOAD_U16(_var) _ir_VLOAD(_ir_CTX, IR_U16, (_var)) @@ -539,8 +540,8 @@ extern "C" { #define ir_MERGE_WITH_EMPTY_FALSE(_if) do {ir_ref end = ir_END(); ir_IF_FALSE(_if); ir_MERGE_2(end, ir_END());} while (0) ir_ref _ir_ADD_OFFSET(ir_ctx *ctx, ir_ref addr, uintptr_t offset); -ir_ref _ir_PHI_2(ir_ctx *ctx, ir_ref src1, ir_ref src2); -ir_ref _ir_PHI_N(ir_ctx *ctx, ir_ref n, ir_ref *inputs); +ir_ref _ir_PHI_2(ir_ctx *ctx, ir_type type, ir_ref src1, ir_ref src2); +ir_ref _ir_PHI_N(ir_ctx *ctx, ir_type type, ir_ref n, ir_ref *inputs); void _ir_PHI_SET_OP(ir_ctx *ctx, ir_ref phi, ir_ref pos, ir_ref src); ir_ref _ir_PARAM(ir_ctx *ctx, ir_type type, const char* name, ir_ref num); ir_ref _ir_VAR(ir_ctx *ctx, ir_type type, const char* name); diff --git a/ir_load_llvm.c b/ir_load_llvm.c new file mode 100644 index 00000000..c918f8c8 --- /dev/null +++ b/ir_load_llvm.c @@ -0,0 +1,813 @@ +/* + * IR - Lightweight JIT Compilation Framework + * (LLVM loader) + * Copyright (C) 2022 Zend by Perforce. + * Authors: Dmitry Stogov + */ + +#include "ir.h" +#include "ir_builder.h" +#include "ir_private.h" + +#include +#include +#include + +#define IR_BAD_TYPE IR_LAST_TYPE + +static ir_ref llvm2ir_const_expr(ir_ctx *ctx, LLVMValueRef insn); +static ir_ref llvm2ir_auto_cast(ir_ctx *ctx, ir_ref ref, ir_type type); + +static ir_type llvm2ir_type(LLVMTypeRef type) +{ + char *str; + + switch (LLVMGetTypeKind(type)) { + case LLVMVoidTypeKind: + return IR_VOID; + case LLVMIntegerTypeKind: + switch (LLVMGetIntTypeWidth(type)) { + case 1: return IR_BOOL; + case 8: return IR_I8; + case 16: return IR_I16; + case 32: return IR_I32; + case 64: return IR_I64; + default: + break; + } + break; + case LLVMFloatTypeKind: + return IR_FLOAT; + case LLVMDoubleTypeKind: + return IR_DOUBLE; + case LLVMPointerTypeKind: + case LLVMFunctionTypeKind: + case LLVMLabelTypeKind: + return IR_ADDR; + default: + break; + } + + str = LLVMPrintTypeToString(type); + fprintf(stderr, "Unsupported LLVM type: %s\n", str); + LLVMDisposeMessage(str); + return IR_BAD_TYPE; +} + +static ir_ref llvm2ir_op(ir_ctx *ctx, LLVMValueRef op, ir_type type) +{ + ir_ref ref; + LLVMBool lose; + + switch( LLVMGetValueKind(op)) { + case LLVMConstantIntValueKind: + switch (type) { + case IR_BOOL: + return ir_const_bool(ctx, LLVMConstIntGetZExtValue(op)); + case IR_I8: + return ir_const_i8(ctx, LLVMConstIntGetSExtValue(op)); + case IR_I16: + return ir_const_i16(ctx, LLVMConstIntGetSExtValue(op)); + case IR_I32: + return ir_const_i32(ctx, LLVMConstIntGetSExtValue(op)); + case IR_I64: + return ir_const_i64(ctx, LLVMConstIntGetSExtValue(op)); + case IR_U8: + return ir_const_u8(ctx, LLVMConstIntGetZExtValue(op)); + case IR_U16: + return ir_const_u16(ctx, LLVMConstIntGetZExtValue(op)); + case IR_U32: + return ir_const_u32(ctx, LLVMConstIntGetZExtValue(op)); + case IR_U64: + return ir_const_u64(ctx, LLVMConstIntGetZExtValue(op)); + case IR_CHAR: + return ir_const_char(ctx, LLVMConstIntGetZExtValue(op)); + default: + IR_ASSERT(0); + return 0; + } + case LLVMConstantFPValueKind: + if (type == IR_DOUBLE) { + return ir_const_double(ctx, LLVMConstRealGetDouble(op, &lose)); + } else { + return ir_const_float(ctx, (float)LLVMConstRealGetDouble(op, &lose)); + } + break; + case LLVMConstantPointerNullValueKind: + return IR_NULL; + case LLVMConstantExprValueKind: + ref = llvm2ir_const_expr(ctx, op); + if (ctx->ir_base[ref].type != type) { + ref = llvm2ir_auto_cast(ctx, ref, type); + } + return ref; + case LLVMArgumentValueKind: + case LLVMInstructionValueKind: + ref = ir_addrtab_find(ctx->binding, (uintptr_t)op); + IR_ASSERT(ref != (ir_ref)IR_INVALID_VAL); + return ref; + case LLVMFunctionValueKind: + case LLVMGlobalVariableValueKind: + // TODO: + default: + IR_ASSERT(0); + return 0; + } + + return 0; +} + +static void llvm2ir_ret(ir_ctx *ctx, LLVMValueRef insn) +{ + ir_ref ref; + + if (LLVMGetNumOperands(insn) == 0) { + ref = IR_UNUSED; + } else { + LLVMValueRef op0 = LLVMGetOperand(insn, 0); + ir_type type = llvm2ir_type(LLVMTypeOf(op0)); + + ref = llvm2ir_op(ctx, op0, type); + } + ir_RETURN(ref); +} + +static void llvm2ir_jmp(ir_ctx *ctx, LLVMValueRef insn) +{ + ir_ref ref; + + ref = ir_END(); // TODO: LOOP_END + ir_addrtab_add(ctx->binding, (uintptr_t)insn, ref); +} + +static void llvm2ir_if(ir_ctx *ctx, LLVMValueRef insn) +{ + ir_ref ref; + LLVMValueRef op0 = LLVMGetOperand(insn, 0); + ir_type type = llvm2ir_type(LLVMTypeOf(op0)); + + ref = ir_IF(llvm2ir_op(ctx, op0, type)); + ir_addrtab_add(ctx->binding, (uintptr_t)insn, ref); +} + +static void llvm2ir_switch(ir_ctx *ctx, LLVMValueRef insn) +{ + ir_ref ref; + LLVMValueRef op0 = LLVMGetOperand(insn, 0); + ir_type type = llvm2ir_type(LLVMTypeOf(op0)); + + ref = ir_SWITCH(llvm2ir_op(ctx, op0, type)); + ir_addrtab_add(ctx->binding, (uintptr_t)insn, ref); +} + +static ir_ref llvm2ir_unary_op(ir_ctx *ctx, LLVMValueRef expr, ir_op op) +{ + LLVMValueRef op0 = LLVMGetOperand(expr, 0); + ir_type type = llvm2ir_type(LLVMTypeOf(expr)); + ir_ref ref; + + ref = ir_fold1(ctx, IR_OPT(op, type), llvm2ir_op(ctx, op0, type)); + ir_addrtab_add(ctx->binding, (uintptr_t)expr, ref); + return ref; +} + +static ir_ref llvm2ir_binary_op(ir_ctx *ctx, LLVMOpcode opcode, LLVMValueRef expr, ir_op op) +{ + LLVMValueRef op0 = LLVMGetOperand(expr, 0); + LLVMValueRef op1 = LLVMGetOperand(expr, 1); + ir_type type = llvm2ir_type(LLVMTypeOf(expr)); + ir_ref ref; + + if (opcode == LLVMUDiv || opcode == LLVMURem) { + IR_ASSERT(type >= IR_I8 && type <= IR_I64); + type = type - IR_I8 + IR_U8; + } + ref = ir_fold2(ctx, IR_OPT(op, type), llvm2ir_op(ctx, op0, type), llvm2ir_op(ctx, op1, type)); + ir_addrtab_add(ctx->binding, (uintptr_t)expr, ref); + return ref; +} + +static ir_ref llvm2ir_cast_op(ir_ctx *ctx, LLVMValueRef expr, ir_op op) +{ + LLVMValueRef op0 = LLVMGetOperand(expr, 0); + ir_type type; + ir_ref ref; + + type = llvm2ir_type(LLVMTypeOf(op0)); + ref = llvm2ir_op(ctx, op0, type); + type = llvm2ir_type(LLVMTypeOf(expr)); + ref = ir_fold1(ctx, IR_OPT(op, type), ref); + ir_addrtab_add(ctx->binding, (uintptr_t)expr, ref); + return ref; +} + +static ir_ref llvm2ir_icmp_op(ir_ctx *ctx, LLVMValueRef expr) +{ + LLVMValueRef op0 = LLVMGetOperand(expr, 0); + LLVMValueRef op1 = LLVMGetOperand(expr, 1); + ir_type type = llvm2ir_type(LLVMTypeOf(op0)); + ir_ref ref; + ir_op op; + + switch (LLVMGetICmpPredicate(expr)) { + case LLVMIntEQ: op = IR_EQ; break; + case LLVMIntNE: op = IR_NE; break; + case LLVMIntUGT: op = IR_UGT; break; + case LLVMIntUGE: op = IR_UGE; break; + case LLVMIntULT: op = IR_ULT; break; + case LLVMIntULE: op = IR_ULE; break; + case LLVMIntSGT: op = IR_GT; break; + case LLVMIntSGE: op = IR_GE; break; + case LLVMIntSLT: op = IR_LT; break; + case LLVMIntSLE: op = IR_LE; break; + 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)); + 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); + LLVMValueRef op1 = LLVMGetOperand(expr, 1); + ir_type type = llvm2ir_type(LLVMTypeOf(op0)); + ir_ref ref; + ir_op op; + + switch (LLVMGetFCmpPredicate(expr)) { + case LLVMRealOEQ: + case LLVMRealONE: op = IR_NE; break; + case LLVMRealUEQ: + case LLVMRealUNE: op = IR_NE; break; + case LLVMRealUGT: op = IR_UGT; break; + case LLVMRealUGE: op = IR_UGE; break; + case LLVMRealULT: op = IR_ULT; break; + case LLVMRealULE: op = IR_ULE; break; + case LLVMRealOGT: op = IR_GT; break; + case LLVMRealOGE: op = IR_GE; break; + case LLVMRealOLT: op = IR_LT; break; + case LLVMRealOLE: op = IR_LE; break; + 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)); + ir_addrtab_add(ctx->binding, (uintptr_t)expr, ref); + return ref; +} + +static ir_ref llvm2ir_cond_op(ir_ctx *ctx, LLVMValueRef expr) +{ + LLVMValueRef op0 = LLVMGetOperand(expr, 0); + LLVMValueRef op1 = LLVMGetOperand(expr, 1); + LLVMValueRef op2 = LLVMGetOperand(expr, 2); + ir_type type = llvm2ir_type(LLVMTypeOf(expr)); + ir_ref ref; + + ref = ir_fold3(ctx, IR_OPT(IR_COND, type), llvm2ir_op(ctx, op0, IR_BOOL), llvm2ir_op(ctx, op1, type), llvm2ir_op(ctx, op2, type)); + ir_addrtab_add(ctx->binding, (uintptr_t)expr, ref); + return ref; +} + +static void llvm2ir_alloca(ir_ctx *ctx, LLVMValueRef insn) +{ + ir_type type = llvm2ir_type(LLVMGetAllocatedType(insn)); + LLVMValueRef op0 = LLVMGetOperand(insn, 0); + ir_ref ref; + + if (LLVMGetValueKind(op0) == LLVMConstantIntValueKind && LLVMConstIntGetZExtValue(op0) == 1) { + ref = ir_VAR(type, ""); // TODO: unique name + } else { + ref = ir_ALLOCA(ir_MUL_A(llvm2ir_op(ctx, op0, IR_ADDR), ir_const_addr(ctx, ir_type_size[type]))); + } + ir_addrtab_add(ctx->binding, (uintptr_t)insn, ref); +} + +static void llvm2ir_load(ir_ctx *ctx, LLVMValueRef insn) +{ + LLVMValueRef op0 = LLVMGetOperand(insn, 0); + ir_type type = llvm2ir_type(LLVMTypeOf(insn)); + ir_ref ref; + + ref = llvm2ir_op(ctx, op0, IR_ADDR); + if (ctx->ir_base[ref].op == IR_VAR) { + ref = ir_VLOAD(type, ref); + } else { + ref = ir_LOAD(type, ref); + } + ir_addrtab_add(ctx->binding, (uintptr_t)insn, ref); +} + +static void llvm2ir_store(ir_ctx *ctx, LLVMValueRef insn) +{ + LLVMValueRef op0 = LLVMGetOperand(insn, 0); + LLVMValueRef op1 = LLVMGetOperand(insn, 1); + ir_type type = llvm2ir_type(LLVMTypeOf(op0)); + ir_ref ref, val; + + ref = llvm2ir_op(ctx, op1, IR_ADDR); + val = llvm2ir_op(ctx, op0, type); + if (ctx->ir_base[ref].op == IR_VAR) { + ir_VSTORE(ref, val); + } else { + ir_STORE(ref, val); + } + ir_addrtab_add(ctx->binding, (uintptr_t)insn, ctx->control); +} + +static ir_ref llvm2ir_const_expr(ir_ctx *ctx, LLVMValueRef expr) +{ + LLVMOpcode opcode = LLVMGetInstructionOpcode(expr); + + switch (opcode) { + case LLVMFNeg: + return llvm2ir_unary_op(ctx, expr, IR_NEG); + case LLVMAdd: + case LLVMFAdd: + return llvm2ir_binary_op(ctx, opcode, expr, IR_ADD); + case LLVMSub: + case LLVMFSub: + return llvm2ir_binary_op(ctx, opcode, expr, IR_SUB); + case LLVMMul: + case LLVMFMul: + return llvm2ir_binary_op(ctx, opcode, expr, IR_MUL); + case LLVMUDiv: + case LLVMSDiv: + case LLVMFDiv: + return llvm2ir_binary_op(ctx, opcode, expr, IR_DIV); + case LLVMURem: + case LLVMSRem: + break; + case LLVMShl: + return llvm2ir_binary_op(ctx, opcode, expr, IR_SHL); + case LLVMLShr: + return llvm2ir_binary_op(ctx, opcode, expr, IR_SHR); + case LLVMAShr: + return llvm2ir_binary_op(ctx, opcode, expr, IR_SAR); + case LLVMAnd: + return llvm2ir_binary_op(ctx, opcode, expr, IR_AND); + case LLVMOr: + return llvm2ir_binary_op(ctx, opcode, expr, IR_OR); + case LLVMXor: + return llvm2ir_binary_op(ctx, opcode, expr, IR_XOR); + case LLVMTrunc: + return llvm2ir_cast_op(ctx, expr, IR_TRUNC); + case LLVMZExt: + return llvm2ir_cast_op(ctx, expr, IR_ZEXT); + case LLVMSExt: + return llvm2ir_cast_op(ctx, expr, IR_SEXT); + case LLVMFPTrunc: + case LLVMFPExt: + return llvm2ir_cast_op(ctx, expr, IR_FP2FP); + case LLVMFPToUI: + case LLVMFPToSI: + return llvm2ir_cast_op(ctx, expr, IR_FP2INT); + case LLVMUIToFP: + case LLVMSIToFP: + return llvm2ir_cast_op(ctx, expr, IR_INT2FP); + case LLVMPtrToInt: + case LLVMIntToPtr: + case LLVMBitCast: + return llvm2ir_cast_op(ctx, expr, IR_BITCAST); + case LLVMICmp: + return llvm2ir_icmp_op(ctx, expr); + case LLVMFCmp: + return llvm2ir_fcmp_op(ctx, expr); + case LLVMSelect: + return llvm2ir_cond_op(ctx, expr); + default: + break; + } + fprintf(stderr, "Unsupported LLVM expr: %d\n", opcode); + return 0; +} + +static ir_ref llvm2ir_auto_cast(ir_ctx *ctx, ir_ref ref, ir_type type) +{ + // TODO: + IR_ASSERT(0); + return ref; +} + +static void llvm2ir_bb_start(ir_ctx *ctx, LLVMBasicBlockRef bb, LLVMBasicBlockRef pred_bb) +{ + LLVMValueRef insn = LLVMGetLastInstruction(pred_bb); + LLVMOpcode opcode = LLVMGetInstructionOpcode(insn); + + if (opcode == LLVMBr) { + ir_ref ref = ir_addrtab_find(ctx->binding, (uintptr_t)insn); + + IR_ASSERT(ref != (ir_ref)IR_INVALID_VAL); + if (!LLVMIsConditional(insn)) { + ir_BEGIN(ref); + } else { + LLVMBasicBlockRef true_bb, false_bb; + + IR_ASSERT(LLVMGetNumSuccessors(insn) == 2); + true_bb = LLVMGetSuccessor(insn, 0); /* true branch */ + false_bb = LLVMGetSuccessor(insn, 1); /* false branch */ + if (bb == true_bb) { + ir_IF_TRUE(ref); + } else { + IR_ASSERT(bb == false_bb); + ir_IF_FALSE(ref); + } + } + } else if (opcode == LLVMSwitch) { + // TODO: + IR_ASSERT(0); + } else { + IR_ASSERT(0); + } +} + +static ir_ref llvm2ir_bb_end(ir_ctx *ctx, LLVMBasicBlockRef bb, LLVMBasicBlockRef pred_bb) +{ + LLVMValueRef insn = LLVMGetLastInstruction(pred_bb); + LLVMOpcode opcode = LLVMGetInstructionOpcode(insn); + ir_ref ref; + + if (opcode == LLVMBr) { + ref = ir_addrtab_find(ctx->binding, (uintptr_t)insn); + if (ref == (ir_ref)IR_INVALID_VAL) { + ref = IR_UNUSED; + } else if (LLVMIsConditional(insn)) { + LLVMBasicBlockRef true_bb, false_bb; + + IR_ASSERT(LLVMGetNumSuccessors(insn) == 2); + true_bb = LLVMGetSuccessor(insn, 0); /* true branch */ + false_bb = LLVMGetSuccessor(insn, 1); /* false branch */ + if (bb == true_bb) { + ir_IF_TRUE(ref); + ref = ir_END(); + } else { + IR_ASSERT(bb == false_bb); + ir_IF_FALSE(ref); + ref = ir_END(); + } + } + } else if (opcode == LLVMSwitch) { + // TODO: + IR_ASSERT(0); + ref = IR_UNUSED; + } else { + IR_ASSERT(0); + ref = IR_UNUSED; + } + return ref; +} + +static int llvm2ir_func(ir_ctx *ctx, LLVMModuleRef module, LLVMValueRef func) +{ + uint32_t i, j, count, cconv, bb_count; + LLVMBasicBlockRef *bbs, bb; + LLVMValueRef param, insn; + LLVMOpcode opcode; + ir_type type; + ir_ref ref, max_inputs_count; + ir_hashtab bb_hash; + ir_use_list *predecessors; + uint32_t *predecessor_edges; + ir_ref *inputs, *bb_starts; + + cconv = LLVMGetFunctionCallConv(func); + if (cconv == LLVMCCallConv) { + /* skip */ + } else if (cconv == LLVMX86FastcallCallConv) { + ctx->flags |= IR_FASTCALL_FUNC; + } else { + fprintf(stderr, "Unsupported Calling Convention: %d\n", cconv); + return 0; + } + if (LLVMIsFunctionVarArg(LLVMTypeOf(func))) { + // TODO: + } + + /* Reuse "binding" for LLVMValueRef -> ir_ref hash */ + ctx->binding = ir_mem_malloc(sizeof(ir_hashtab)); + ir_addrtab_init(ctx->binding, 256); + + ir_START(); + for (i = 0; i < LLVMCountParams(func); i++) { + param = LLVMGetParam(func, i); + type = llvm2ir_type(LLVMTypeOf(param)); + if (type == IR_BAD_TYPE) { + return 0; + } + ref = ir_PARAM(type, "", i + 1); // TODO: unique name + ir_addrtab_add(ctx->binding, (uintptr_t)param, ref); + } + + /* Find LLVM BasicBlocks Predecessors */ + bb_count = LLVMCountBasicBlocks(func); + bbs = ir_mem_malloc(bb_count * sizeof(LLVMBasicBlockRef)); + predecessors = ir_mem_calloc(bb_count, sizeof(ir_use_list)); + LLVMGetBasicBlocks(func, bbs); + ir_addrtab_init(&bb_hash, bb_count); + for (i = 0; i < bb_count; i++) { + bb = bbs[i]; + ir_addrtab_add(&bb_hash, (uintptr_t)bb, i); + } + for (i = 0; i < bb_count; i++) { + bb = bbs[i]; + insn = LLVMGetLastInstruction(bb); + opcode = LLVMGetInstructionOpcode(insn); + if (opcode == LLVMBr || opcode == LLVMSwitch) { + count = LLVMGetNumSuccessors(insn); + for (j = 0; j < count; j++) { + uint32_t b = ir_addrtab_find(&bb_hash, (uintptr_t)LLVMGetSuccessor(insn, j)); + + IR_ASSERT(b < bb_count); + predecessors[b].count++; + } + } + } + count = 0; + max_inputs_count = 0; + for (i = 0; i < bb_count; i++) { + predecessors[i].refs = count; + count += predecessors[i].count; + max_inputs_count = IR_MAX(max_inputs_count, predecessors[i].count); + predecessors[i].count = 0; + } + predecessor_edges = ir_mem_malloc(sizeof(uint32_t) * count); + inputs = ir_mem_malloc(sizeof(ir_ref) * max_inputs_count); + for (i = 0; i < bb_count; i++) { + bb = bbs[i]; + insn = LLVMGetLastInstruction(bb); + opcode = LLVMGetInstructionOpcode(insn); + if (opcode == LLVMBr || opcode == LLVMSwitch) { + count = LLVMGetNumSuccessors(insn); + for (j = 0; j < count; j++) { + uint32_t b = ir_addrtab_find(&bb_hash, (uintptr_t)LLVMGetSuccessor(insn, j)); + + IR_ASSERT(b < bb_count); + predecessor_edges[predecessors[b].refs + predecessors[b].count++] = i; + } + } + } + + bb_starts = ir_mem_malloc(bb_count * sizeof(ir_ref)); + for (i = 0; i < bb_count; i++) { + bb = bbs[i]; + count = predecessors[i].count; + if (count == 1) { + llvm2ir_bb_start(ctx, bb, bbs[predecessor_edges[predecessors[i].refs]]); + } else if (count > 1) { + for (j = 0; j < count; j++) { + inputs[j] = llvm2ir_bb_end(ctx, bb, bbs[predecessor_edges[predecessors[i].refs + j]]); + } + ir_MERGE_N(count, inputs); // TODO: LOOP_BEGIN + } else if (i != 0) { + ir_BEGIN(IR_UNUSED); /* unreachable block */ + } + bb_starts[i] = ctx->control; + + for (insn = LLVMGetFirstInstruction(bb); insn; insn = LLVMGetNextInstruction(insn)) { + opcode = LLVMGetInstructionOpcode(insn); + switch (opcode) { + case LLVMRet: + llvm2ir_ret(ctx, insn); + break; + case LLVMBr: + if (!LLVMIsConditional(insn)) { + llvm2ir_jmp(ctx, insn); + } else { + llvm2ir_if(ctx, insn); + } + break; + case LLVMSwitch: + llvm2ir_switch(ctx, insn); + break; + case LLVMIndirectBr: + // TODO: + break; + case LLVMFNeg: + llvm2ir_unary_op(ctx, insn, IR_NEG); + break; + case LLVMAdd: + case LLVMFAdd: + llvm2ir_binary_op(ctx, opcode, insn, IR_ADD); + break; + case LLVMSub: + case LLVMFSub: + llvm2ir_binary_op(ctx, opcode, insn, IR_SUB); + break; + case LLVMMul: + case LLVMFMul: + llvm2ir_binary_op(ctx, opcode, insn, IR_MUL); + break; + case LLVMUDiv: + case LLVMSDiv: + case LLVMFDiv: + llvm2ir_binary_op(ctx, opcode, insn, IR_DIV); + break; + case LLVMURem: + case LLVMSRem: + llvm2ir_binary_op(ctx, opcode, insn, IR_MOD); + break; + case LLVMShl: + llvm2ir_binary_op(ctx, opcode, insn, IR_SHL); + break; + case LLVMLShr: + llvm2ir_binary_op(ctx, opcode, insn, IR_SHR); + break; + case LLVMAShr: + llvm2ir_binary_op(ctx, opcode, insn, IR_SAR); + break; + case LLVMAnd: + llvm2ir_binary_op(ctx, opcode, insn, IR_AND); + break; + case LLVMOr: + llvm2ir_binary_op(ctx, opcode, insn, IR_OR); + break; + case LLVMXor: + llvm2ir_binary_op(ctx, opcode, insn, IR_XOR); + break; + case LLVMAlloca: + llvm2ir_alloca(ctx, insn); + break; + case LLVMLoad: + llvm2ir_load(ctx, insn); + break; + case LLVMStore: + llvm2ir_store(ctx, insn); + break; + case LLVMTrunc: + llvm2ir_cast_op(ctx, insn, IR_TRUNC); + break; + case LLVMZExt: + llvm2ir_cast_op(ctx, insn, IR_ZEXT); + break; + case LLVMSExt: + llvm2ir_cast_op(ctx, insn, IR_SEXT); + break; + case LLVMFPTrunc: + case LLVMFPExt: + llvm2ir_cast_op(ctx, insn, IR_FP2FP); + break; + case LLVMFPToUI: + case LLVMFPToSI: + llvm2ir_cast_op(ctx, insn, IR_FP2INT); + break; + case LLVMUIToFP: + case LLVMSIToFP: + llvm2ir_cast_op(ctx, insn, IR_INT2FP); + break; + case LLVMPtrToInt: + case LLVMIntToPtr: + case LLVMBitCast: + llvm2ir_cast_op(ctx, insn, IR_BITCAST); + break; + case LLVMICmp: + llvm2ir_icmp_op(ctx, insn); + break; + case LLVMFCmp: + llvm2ir_fcmp_op(ctx, insn); + break; + case LLVMPHI: + count = LLVMCountIncoming(insn); + memset(inputs, 0, count * sizeof(ir_ref)); + ref = ir_PHI_N(llvm2ir_type(LLVMTypeOf(insn)), count, inputs); + ir_addrtab_add(ctx->binding, (uint64_t)insn, ref); + break; + case LLVMSelect: + llvm2ir_cond_op(ctx, insn); + break; + case LLVMCall: + // TODO: + break; + default: + fprintf(stderr, "Unsupported LLVM insn: %d\n", opcode); + return 0; + } + } + } + + /* Backpatch MERGEs and PHIs */ + for (i = 0; i < bb_count; i++) { + ir_ref merge, phi, *merge_ops; + uint32_t k; + + bb = bbs[i]; + count = predecessors[i].count; + if (count <= 1) continue; + merge = bb_starts[i]; + IR_ASSERT(ctx->ir_base[merge].op == IR_MERGE || ctx->ir_base[merge].op == IR_LOOP_BEGIN); + for (k = 0; k < count; k++) { + merge_ops = ctx->ir_base[merge].ops + 1; + if (!merge_ops[k]) { + ref = llvm2ir_bb_end(ctx, bb, bbs[predecessor_edges[predecessors[i].refs + k]]); + IR_ASSERT(ref != IR_UNUSED); + ir_MERGE_SET_OP(merge, k + 1, ref); + } + } + for (insn = LLVMGetFirstInstruction(bb); + insn && LLVMGetInstructionOpcode(insn) == LLVMPHI; + insn = LLVMGetNextInstruction(insn)) { + phi = ir_addrtab_find(ctx->binding, (uint64_t)insn); + IR_ASSERT(phi != (ir_ref)IR_INVALID_VAL); + IR_ASSERT(ctx->ir_base[phi].op == IR_PHI); + IR_ASSERT(LLVMCountIncoming(insn) == count); + for (j = 0; j < count; j++) { + LLVMValueRef op = LLVMGetIncomingValue(insn, j); + uint32_t b = ir_addrtab_find(&bb_hash, (uintptr_t)LLVMGetIncomingBlock(insn, j)); + + IR_ASSERT(b < bb_count); + ref = llvm2ir_op(ctx, op, ctx->ir_base[phi].type); + for (k = 0; k < count; k++) { + if (predecessor_edges[predecessors[i].refs + k] == b) { + ir_PHI_SET_OP(phi, k + 1, ref); + } + } + } + } + } + + ir_mem_free(bb_starts); + ir_addrtab_free(ctx->binding); + ir_mem_free(ctx->binding); + ctx->binding = NULL; + ir_addrtab_free(&bb_hash); + ir_mem_free(bbs); + ir_mem_free(predecessors); + ir_mem_free(predecessor_edges); + ir_mem_free(inputs); + + ir_save(ctx, stderr); + + return 1; +} + +static int ir_load_llvm_module(LLVMModuleRef module, uint32_t flags) +{ + ir_ctx ctx; + LLVMValueRef func; + + for (func = LLVMGetFirstFunction(module); func; func = LLVMGetNextFunction(func)) { + if (LLVMIsDeclaration(func)) continue; + ir_init(&ctx, flags, 256, 1024); + if (!llvm2ir_func(&ctx, module, func)) { + ir_free(&ctx); + return 0; + } + ir_free(&ctx); + } + + return 1; +} + +int ir_load_llvm_bitcode(const char *filename, uint32_t flags) +{ + LLVMMemoryBufferRef memory_buffer; + LLVMModuleRef module; + LLVMBool ret; + char *message; + + if (LLVMCreateMemoryBufferWithContentsOfFile(filename, &memory_buffer, &message)) { + fprintf(stderr, "Cannot open '%s': %s\n", filename, message); + free(message); + return 0; + } + ret = LLVMParseBitcodeInContext2(LLVMGetGlobalContext(), memory_buffer, &module); + LLVMDisposeMemoryBuffer(memory_buffer); + if (ret) { + fprintf(stderr, "Cannot parse LLVM bitcode\n"); + return 0; + } + if (!ir_load_llvm_module(module, flags)) { + LLVMDisposeModule(module); + fprintf(stderr, "Cannot convert LLVM to IR\n"); + return 0; + } + LLVMDisposeModule(module); + + return 1; +} + +int ir_load_llvm_asm(const char *filename, uint32_t flags) +{ + LLVMMemoryBufferRef memory_buffer; + LLVMModuleRef module; + LLVMBool ret; + char *message; + + if (LLVMCreateMemoryBufferWithContentsOfFile(filename, &memory_buffer, &message)) { + fprintf(stderr, "Cannot open '%s': %s\n", filename, message); + free(message); + return 0; + } + ret = LLVMParseIRInContext(LLVMGetGlobalContext(), memory_buffer, &module, &message); + if (ret) { + fprintf(stderr, "Cannot parse LLVM file: %s\n", message); + free(message); + return 0; + } + if (!ir_load_llvm_module(module, flags)) { + LLVMDisposeModule(module); + fprintf(stderr, "Cannot convert LLVM to IR\n"); + return 0; + } + LLVMDisposeModule(module); + + return 1; +} diff --git a/ir_main.c b/ir_main.c index 017ebb8c..06086e22 100644 --- a/ir_main.c +++ b/ir_main.c @@ -16,7 +16,11 @@ static void help(const char *cmd) { printf( +#if HAVE_LLVM + "Usage: %s [options] [--llvm-bitcode|--llvm-asm] input-file...\n" +#else "Usage: %s [options] input-file...\n" +#endif "Options:\n" " -O[012] - optimization level (default: 2)\n" " -S - dump final target assembler code\n" @@ -244,6 +248,10 @@ int main(int argc, char **argv) bool dump_size = 0; #ifdef _WIN32 bool abort_fault = 1; +#endif +#if HAVE_LLVM + bool load_llvm_bitcode = 0; + bool load_llvm_asm = 0; #endif ir_consistency_check(); @@ -360,6 +368,22 @@ int main(int argc, char **argv) #ifdef _WIN32 } else if (strcmp(argv[i], "--no-abort-fault") == 0) { abort_fault = 0; +#endif +#if HAVE_LLVM + } else if (strcmp(argv[i], "--llvm-bitcode") == 0) { + if (input || i + 1 == argc || argv[i + 1][0] == '-') { + fprintf(stderr, "ERROR: Invalid usage' (use --help)\n"); + return 1; + } + load_llvm_bitcode = 1; + input = argv[++i]; + } else if (strcmp(argv[i], "--llvm-asm") == 0) { + if (input || i + 1 == argc || argv[i + 1][0] == '-') { + fprintf(stderr, "ERROR: Invalid usage' (use --help)\n"); + return 1; + } + load_llvm_asm = 1; + input = argv[++i]; #endif } else if (argv[i][0] == '-') { fprintf(stderr, "ERROR: Unknown option '%s' (use --help)\n", argv[i]); @@ -384,12 +408,6 @@ int main(int argc, char **argv) return 1; } - f = fopen(input, "rb"); - if (!f) { - fprintf(stderr, "ERROR: Cannot open input file '%s'\n", input); - return 1; - } - #if defined(IR_TARGET_X86) || defined(IR_TARGET_X64) uint32_t cpuinfo = ir_cpuinfo(); @@ -411,8 +429,6 @@ int main(int argc, char **argv) } #endif - ir_loader_init(); - flags |= IR_FUNCTION; if (opt_level > 0) { @@ -424,6 +440,28 @@ int main(int argc, char **argv) if (dump_asm || run) { flags |= IR_GEN_NATIVE; } + +#if HAVE_LLVM + if (load_llvm_bitcode) { + if (!ir_load_llvm_bitcode(input, flags)) { + return 1; + } + return 0; + } else if (load_llvm_asm) { + if (!ir_load_llvm_asm(input, flags)) { + return 1; + } + return 0; + } +#endif + f = fopen(input, "rb"); + if (!f) { + fprintf(stderr, "ERROR: Cannot open input file '%s'\n", input); + return 1; + } + + ir_loader_init(); + ir_init(&ctx, flags, 256, 1024); ctx.mflags = mflags; ctx.fixed_regset = ~debug_regset; diff --git a/ir_private.h b/ir_private.h index 76704f58..0f6267bd 100644 --- a/ir_private.h +++ b/ir_private.h @@ -746,6 +746,18 @@ ir_ref ir_hashtab_find(const ir_hashtab *tab, uint32_t key); bool ir_hashtab_add(ir_hashtab *tab, uint32_t key, ir_ref val); void ir_hashtab_key_sort(ir_hashtab *tab); +/* IR Addr Table */ +typedef struct _ir_addrtab_bucket { + uint64_t key; + ir_ref val; + uint32_t next; +} ir_addrtab_bucket; + +void ir_addrtab_init(ir_hashtab *tab, uint32_t size); +void ir_addrtab_free(ir_hashtab *tab); +ir_ref ir_addrtab_find(const ir_hashtab *tab, uint64_t key); +bool ir_addrtab_add(ir_hashtab *tab, uint64_t key, ir_ref val); + /*** IR OP info ***/ extern const uint8_t ir_type_flags[IR_LAST_TYPE]; extern const char *ir_type_name[IR_LAST_TYPE]; diff --git a/ir_test.c b/ir_test.c index 8e5c7e7a..db85ab2e 100644 --- a/ir_test.c +++ b/ir_test.c @@ -31,9 +31,9 @@ void gen_mandelbrot(ir_ctx *ctx) ir_ref i = ir_COPY_I32(ir_CONST_I32(0)); ir_ref loop = ir_LOOP_BEGIN(ir_END()); - ir_ref zi_1 = ir_PHI_2(zi, IR_UNUSED); - ir_ref zr_1 = ir_PHI_2(zr, IR_UNUSED); - ir_ref i_1 = ir_PHI_2(i, IR_UNUSED); + ir_ref zi_1 = ir_PHI_2(IR_DOUBLE, zi, IR_UNUSED); + ir_ref zr_1 = ir_PHI_2(IR_DOUBLE, zr, IR_UNUSED); + ir_ref i_1 = ir_PHI_2(IR_I32, i, IR_UNUSED); ir_ref i_2 = ir_ADD_I32(i_1, ir_CONST_I32(1)); ir_ref temp = ir_MUL_D(zr_1, zi_1);