diff --git a/CMakeLists.txt b/CMakeLists.txt index f25e05b..7cfbc8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,10 +52,13 @@ target_link_libraries(llvm-shared INTERFACE ${LLVM_SHARED_LIBRARIES}) add_library(libbfjit STATIC include/bfjit.h + include/bfjit_internal.h include/bflexer.h include/bfopt.h include/bfparser.h src/bfjit.c + src/bfjit_codegen.c + src/bfjit_runtime.c src/bflexer.c src/bfopt.c src/bfparser.c diff --git a/include/bfjit_internal.h b/include/bfjit_internal.h new file mode 100644 index 0000000..f765bd2 --- /dev/null +++ b/include/bfjit_internal.h @@ -0,0 +1,21 @@ +#ifndef BFJIT_INTERNAL_H +#define BFJIT_INTERNAL_H + +#include "bfjit.h" + +#include +#include +#include +#include + +void bf_jit_err_reset(bf_jit_err *err); +void bf_set_jit_err(bf_jit_err *err, const char *msg); +void bf_set_jit_err_from_llvm(bf_jit_err *err, LLVMErrorRef llvm_err); + +LLVMModuleRef bf_build_module(LLVMContextRef ctx, const bf_program *program, + bf_jit_err *err); +int bf_opt_llvm_module(LLVMModuleRef mod, bf_jit_err *err); + +LLVMOrcMaterializationUnitRef bf_create_io_symbols(LLVMOrcLLJITRef jit); + +#endif diff --git a/include/bfparser.h b/include/bfparser.h index d4ef0c1..4e4ba49 100644 --- a/include/bfparser.h +++ b/include/bfparser.h @@ -13,6 +13,8 @@ typedef enum bf_ir_kind { BF_IR_OUTPUT, BF_IR_LOOP, BF_IR_SET_ZERO, + BF_IR_SET_CONST, + BF_IR_MULTI_ZERO, BF_IR_SCAN, BF_IR_MULTIPLY_LOOP } bf_ir_kind; diff --git a/src/bfjit.c b/src/bfjit.c index 8b0e5c8..49c8ee0 100644 --- a/src/bfjit.c +++ b/src/bfjit.c @@ -1,46 +1,18 @@ #define _GNU_SOURCE -#include "bfjit.h" +#include "bfjit_internal.h" -#include #include -#include -#include -#include #include #include #include #include -#include -#include +#include typedef int (*bf_jit_entry_fn)(uint8_t *tape, size_t tape_size); -static int bf_io_putchar(int c) { return putchar_unlocked(c); } - -static int bf_io_getchar(void) { return getchar_unlocked(); } - -typedef struct bf_codegen { - LLVMContextRef ctx; - LLVMModuleRef mod; - LLVMBuilderRef builder; - LLVMValueRef func; - LLVMValueRef tape_arg; - LLVMValueRef tape_size_arg; - LLVMValueRef pointer_index; - LLVMValueRef memchr_func; - LLVMTypeRef i1_type; - LLVMTypeRef i8_type; - LLVMTypeRef i32_type; - LLVMTypeRef i64_type; - LLVMTypeRef tape_pointer_type; - LLVMValueRef putchar_func; - LLVMValueRef getchar_func; - bf_jit_err *err; -} bf_codegen; - -static void bf_jit_err_reset(bf_jit_err *err) { +void bf_jit_err_reset(bf_jit_err *err) { if (err == NULL) { return; } @@ -49,7 +21,7 @@ static void bf_jit_err_reset(bf_jit_err *err) { err->msg[0] = '\0'; } -static void bf_set_jit_err(bf_jit_err *err, const char *msg) { +void bf_set_jit_err(bf_jit_err *err, const char *msg) { if (err == NULL) { return; } @@ -58,7 +30,7 @@ static void bf_set_jit_err(bf_jit_err *err, const char *msg) { snprintf(err->msg, sizeof(err->msg), "%s", msg); } -static void bf_set_jit_err_from_llvm(bf_jit_err *err, LLVMErrorRef llvm_err) { +void bf_set_jit_err_from_llvm(bf_jit_err *err, LLVMErrorRef llvm_err) { char *msg; if (llvm_err == NULL) { @@ -71,464 +43,6 @@ static void bf_set_jit_err_from_llvm(bf_jit_err *err, LLVMErrorRef llvm_err) { LLVMDisposeErrorMessage(msg); } -static LLVMValueRef bf_const_i8(bf_codegen *codegen, int value) { - return LLVMConstInt(codegen->i8_type, (unsigned long long)(uint8_t)value, - 0); -} - -static LLVMValueRef bf_const_i64(bf_codegen *codegen, uint64_t value) { - return LLVMConstInt(codegen->i64_type, value, 0); -} - -static LLVMValueRef bf_build_current_cell_ptr(bf_codegen *codegen) { - LLVMValueRef indices[1]; - - indices[0] = codegen->pointer_index; - - return LLVMBuildGEP2(codegen->builder, codegen->i8_type, codegen->tape_arg, - indices, 1, "cell_ptr"); -} - -static int bf_codegen_block(bf_codegen *codegen, const bf_ir_block *block); - -static int bf_codegen_loop(bf_codegen *codegen, const bf_ir_node *node) { - LLVMBasicBlockRef pre_loop_block; - LLVMValueRef pre_loop_ptr; - LLVMBasicBlockRef condition_block; - LLVMBasicBlockRef body_block; - LLVMBasicBlockRef exit_block; - LLVMValueRef ptr_phi; - LLVMValueRef cell_ptr; - LLVMValueRef cell_value; - LLVMValueRef loop_condition; - LLVMBasicBlockRef body_end_block; - LLVMValueRef body_end_ptr; - - pre_loop_block = LLVMGetInsertBlock(codegen->builder); - pre_loop_ptr = codegen->pointer_index; - - condition_block = - LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "loop.cond"); - body_block = - LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "loop.body"); - exit_block = - LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "loop.exit"); - - LLVMBuildBr(codegen->builder, condition_block); - - LLVMPositionBuilderAtEnd(codegen->builder, condition_block); - ptr_phi = LLVMBuildPhi(codegen->builder, codegen->i64_type, "ptr.phi"); - LLVMAddIncoming(ptr_phi, &pre_loop_ptr, &pre_loop_block, 1); - codegen->pointer_index = ptr_phi; - - cell_ptr = bf_build_current_cell_ptr(codegen); - cell_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, cell_ptr, - "loop_value"); - loop_condition = LLVMBuildICmp(codegen->builder, LLVMIntNE, cell_value, - bf_const_i8(codegen, 0), "loop_condition"); - LLVMBuildCondBr(codegen->builder, loop_condition, body_block, exit_block); - - LLVMPositionBuilderAtEnd(codegen->builder, body_block); - if (!bf_codegen_block(codegen, &node->body)) { - return 0; - } - - body_end_block = LLVMGetInsertBlock(codegen->builder); - body_end_ptr = codegen->pointer_index; - - if (LLVMGetBasicBlockTerminator(body_end_block) == NULL) { - LLVMBuildBr(codegen->builder, condition_block); - } - - LLVMAddIncoming(ptr_phi, &body_end_ptr, &body_end_block, 1); - - LLVMPositionBuilderAtEnd(codegen->builder, exit_block); - codegen->pointer_index = ptr_phi; - return 1; -} - -static int bf_codegen_node(bf_codegen *codegen, const bf_ir_node *node) { - LLVMValueRef cell_ptr; - LLVMValueRef current_value; - LLVMValueRef updated_value; - LLVMValueRef call_args[1]; - - switch (node->kind) { - case BF_IR_ADD_PTR: - updated_value = LLVMBuildAdd( - codegen->builder, codegen->pointer_index, - LLVMConstInt(codegen->i64_type, - (unsigned long long)(int64_t)node->arg, 1), - "ptr_next"); - codegen->pointer_index = updated_value; - return 1; - case BF_IR_ADD_DATA: - cell_ptr = bf_build_current_cell_ptr(codegen); - current_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, - cell_ptr, "cell_value"); - updated_value = - LLVMBuildAdd(codegen->builder, current_value, - bf_const_i8(codegen, node->arg), "cell_next"); - LLVMBuildStore(codegen->builder, updated_value, cell_ptr); - return 1; - case BF_IR_OUTPUT: - cell_ptr = bf_build_current_cell_ptr(codegen); - current_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, - cell_ptr, "output_value"); - call_args[0] = LLVMBuildZExt(codegen->builder, current_value, - codegen->i32_type, "output_arg"); - LLVMBuildCall2(codegen->builder, - LLVMGlobalGetValueType(codegen->putchar_func), - codegen->putchar_func, call_args, 1, ""); - return 1; - case BF_IR_INPUT: - cell_ptr = bf_build_current_cell_ptr(codegen); - current_value = LLVMBuildCall2( - codegen->builder, LLVMGlobalGetValueType(codegen->getchar_func), - codegen->getchar_func, NULL, 0, "input_value"); - updated_value = LLVMBuildTrunc(codegen->builder, current_value, - codegen->i8_type, "input_trunc"); - LLVMBuildStore(codegen->builder, updated_value, cell_ptr); - return 1; - case BF_IR_LOOP: - return bf_codegen_loop(codegen, node); - case BF_IR_SET_ZERO: - cell_ptr = bf_build_current_cell_ptr(codegen); - LLVMBuildStore(codegen->builder, bf_const_i8(codegen, 0), cell_ptr); - return 1; - case BF_IR_SCAN: { - if (node->arg == 1) { - /* [>] の memchr 最適化パス */ - LLVMValueRef search_ptr; - LLVMValueRef remaining; - LLVMValueRef call_args[3]; - LLVMValueRef result; - LLVMValueRef found; - LLVMValueRef result_int; - LLVMValueRef tape_int; - LLVMValueRef new_index; - LLVMValueRef phi; - LLVMBasicBlockRef pre_block; - LLVMBasicBlockRef found_block; - LLVMBasicBlockRef done_block; - LLVMValueRef pre_ptr; - - pre_block = LLVMGetInsertBlock(codegen->builder); - pre_ptr = codegen->pointer_index; - - search_ptr = bf_build_current_cell_ptr(codegen); - remaining = LLVMBuildSub(codegen->builder, codegen->tape_size_arg, - codegen->pointer_index, "scan_remaining"); - - call_args[0] = search_ptr; - call_args[1] = LLVMConstInt(codegen->i32_type, 0, 0); - call_args[2] = remaining; - result = LLVMBuildCall2( - codegen->builder, LLVMGlobalGetValueType(codegen->memchr_func), - codegen->memchr_func, call_args, 3, "scan_memchr"); - - found = LLVMBuildICmp(codegen->builder, LLVMIntNE, result, - LLVMConstNull(codegen->tape_pointer_type), - "scan_found"); - - found_block = LLVMAppendBasicBlockInContext( - codegen->ctx, codegen->func, "scan.found"); - done_block = LLVMAppendBasicBlockInContext( - codegen->ctx, codegen->func, "scan.done"); - - LLVMBuildCondBr(codegen->builder, found, found_block, done_block); - - LLVMPositionBuilderAtEnd(codegen->builder, found_block); - result_int = LLVMBuildPtrToInt(codegen->builder, result, - codegen->i64_type, "scan_result_i"); - tape_int = LLVMBuildPtrToInt(codegen->builder, codegen->tape_arg, - codegen->i64_type, "scan_tape_i"); - new_index = LLVMBuildSub(codegen->builder, result_int, tape_int, - "scan_new_idx"); - LLVMBuildBr(codegen->builder, done_block); - - LLVMPositionBuilderAtEnd(codegen->builder, done_block); - phi = LLVMBuildPhi(codegen->builder, codegen->i64_type, - "scan_ptr.phi"); - LLVMAddIncoming(phi, &new_index, &found_block, 1); - LLVMAddIncoming(phi, &pre_ptr, &pre_block, 1); - - codegen->pointer_index = phi; - } else { - /* PHI 命令による一般スキャンループ */ - LLVMBasicBlockRef pre_block; - LLVMValueRef pre_ptr; - LLVMBasicBlockRef scan_cond; - LLVMBasicBlockRef scan_step; - LLVMBasicBlockRef scan_exit; - LLVMValueRef scan_phi; - LLVMValueRef scan_val; - LLVMValueRef scan_cmp; - LLVMValueRef next_idx; - - pre_block = LLVMGetInsertBlock(codegen->builder); - pre_ptr = codegen->pointer_index; - - scan_cond = LLVMAppendBasicBlockInContext( - codegen->ctx, codegen->func, "scan.cond"); - scan_step = LLVMAppendBasicBlockInContext( - codegen->ctx, codegen->func, "scan.step"); - scan_exit = LLVMAppendBasicBlockInContext( - codegen->ctx, codegen->func, "scan.exit"); - - LLVMBuildBr(codegen->builder, scan_cond); - - LLVMPositionBuilderAtEnd(codegen->builder, scan_cond); - scan_phi = LLVMBuildPhi(codegen->builder, codegen->i64_type, - "scan_ptr.phi"); - LLVMAddIncoming(scan_phi, &pre_ptr, &pre_block, 1); - codegen->pointer_index = scan_phi; - - cell_ptr = bf_build_current_cell_ptr(codegen); - scan_val = LLVMBuildLoad2(codegen->builder, codegen->i8_type, - cell_ptr, "scan_val"); - scan_cmp = LLVMBuildICmp(codegen->builder, LLVMIntNE, scan_val, - bf_const_i8(codegen, 0), "scan_cmp"); - LLVMBuildCondBr(codegen->builder, scan_cmp, scan_step, scan_exit); - - LLVMPositionBuilderAtEnd(codegen->builder, scan_step); - next_idx = LLVMBuildAdd( - codegen->builder, scan_phi, - LLVMConstInt(codegen->i64_type, - (unsigned long long)(int64_t)node->arg, 1), - "scan_next"); - LLVMAddIncoming(scan_phi, &next_idx, &scan_step, 1); - LLVMBuildBr(codegen->builder, scan_cond); - - LLVMPositionBuilderAtEnd(codegen->builder, scan_exit); - codegen->pointer_index = scan_phi; - } - return 1; - } - case BF_IR_MULTIPLY_LOOP: { - LLVMValueRef base_idx; - LLVMValueRef loop_val; - size_t ti; - - cell_ptr = bf_build_current_cell_ptr(codegen); - loop_val = LLVMBuildLoad2(codegen->builder, codegen->i8_type, cell_ptr, - "mul_loop_val"); - base_idx = codegen->pointer_index; - - for (ti = 0; ti < node->term_count; ++ti) { - LLVMValueRef target_idx; - LLVMValueRef target_ptr; - LLVMValueRef target_indices[1]; - LLVMValueRef old_val; - LLVMValueRef product; - LLVMValueRef new_val; - - target_idx = LLVMBuildAdd( - codegen->builder, base_idx, - LLVMConstInt( - codegen->i64_type, - (unsigned long long)(int64_t)node->terms[ti].offset, 1), - "mul_target_idx"); - target_indices[0] = target_idx; - target_ptr = LLVMBuildGEP2(codegen->builder, codegen->i8_type, - codegen->tape_arg, target_indices, 1, - "mul_target_ptr"); - old_val = LLVMBuildLoad2(codegen->builder, codegen->i8_type, - target_ptr, "mul_old"); - product = LLVMBuildMul(codegen->builder, loop_val, - bf_const_i8(codegen, node->terms[ti].factor), - "mul_product"); - new_val = - LLVMBuildAdd(codegen->builder, old_val, product, "mul_new"); - LLVMBuildStore(codegen->builder, new_val, target_ptr); - } - - /* ループ変数セルをクリア */ - LLVMBuildStore(codegen->builder, bf_const_i8(codegen, 0), cell_ptr); - return 1; - } - default: - bf_set_jit_err(codegen->err, - "unsupported IR node while generating LLVM IR"); - return 0; - } -} - -static int bf_codegen_block(bf_codegen *codegen, const bf_ir_block *block) { - size_t index; - - for (index = 0; index < block->count; ++index) { - if (!bf_codegen_node(codegen, &block->nodes[index])) { - return 0; - } - } - - return 1; -} - -static LLVMModuleRef bf_build_mod(LLVMContextRef ctx, const bf_program *program, - bf_jit_err *err) { - bf_codegen codegen; - LLVMTypeRef entry_args[2]; - LLVMTypeRef entry_type; - LLVMTypeRef libc_args[1]; - LLVMBasicBlockRef entry_block; - char *target_triple; - char *verify_msg; - LLVMModuleRef mod; - LLVMValueRef return_value; - - memset(&codegen, 0, sizeof(codegen)); - codegen.ctx = ctx; - codegen.mod = LLVMModuleCreateWithNameInContext("bfjit.mod", ctx); - codegen.builder = LLVMCreateBuilderInContext(ctx); - codegen.err = err; - codegen.i1_type = LLVMInt1TypeInContext(ctx); - codegen.i8_type = LLVMInt8TypeInContext(ctx); - codegen.i32_type = LLVMInt32TypeInContext(ctx); - codegen.i64_type = LLVMInt64TypeInContext(ctx); - codegen.tape_pointer_type = LLVMPointerTypeInContext(ctx, 0); - - target_triple = LLVMGetDefaultTargetTriple(); - LLVMSetTarget(codegen.mod, target_triple); - LLVMDisposeMessage(target_triple); - - entry_args[0] = codegen.tape_pointer_type; - entry_args[1] = codegen.i64_type; - entry_type = LLVMFunctionType(codegen.i32_type, entry_args, 2, 0); - codegen.func = LLVMAddFunction(codegen.mod, "bf_entry", entry_type); - codegen.tape_arg = LLVMGetParam(codegen.func, 0); - codegen.tape_size_arg = LLVMGetParam(codegen.func, 1); - - { - unsigned noalias_kind = LLVMGetEnumAttributeKindForName("noalias", 7); - unsigned nonnull_kind = LLVMGetEnumAttributeKindForName("nonnull", 7); - unsigned nounwind_kind = LLVMGetEnumAttributeKindForName("nounwind", 8); - - /* tape pointer: noalias nonnull */ - LLVMAddAttributeAtIndex(codegen.func, 1, - LLVMCreateEnumAttribute(ctx, noalias_kind, 0)); - LLVMAddAttributeAtIndex(codegen.func, 1, - LLVMCreateEnumAttribute(ctx, nonnull_kind, 0)); - - /* bf_entry: nounwind */ - LLVMAddAttributeAtIndex(codegen.func, LLVMAttributeFunctionIndex, - LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); - } - - libc_args[0] = codegen.i32_type; - codegen.putchar_func = - LLVMAddFunction(codegen.mod, "bf_io_putchar", - LLVMFunctionType(codegen.i32_type, libc_args, 1, 0)); - codegen.getchar_func = - LLVMAddFunction(codegen.mod, "bf_io_getchar", - LLVMFunctionType(codegen.i32_type, NULL, 0, 0)); - - /* putchar/getchar: nounwind */ - { - unsigned nounwind_kind = LLVMGetEnumAttributeKindForName("nounwind", 8); - LLVMAddAttributeAtIndex(codegen.putchar_func, - LLVMAttributeFunctionIndex, - LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); - LLVMAddAttributeAtIndex(codegen.getchar_func, - LLVMAttributeFunctionIndex, - LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); - } - - /* memchr: ptr @memchr(ptr, i32, i64) nounwind */ - { - LLVMTypeRef memchr_params[3]; - unsigned nounwind_kind = LLVMGetEnumAttributeKindForName("nounwind", 8); - - memchr_params[0] = codegen.tape_pointer_type; - memchr_params[1] = codegen.i32_type; - memchr_params[2] = codegen.i64_type; - codegen.memchr_func = LLVMAddFunction( - codegen.mod, "memchr", - LLVMFunctionType(codegen.tape_pointer_type, memchr_params, 3, 0)); - LLVMAddAttributeAtIndex(codegen.memchr_func, LLVMAttributeFunctionIndex, - LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); - } - - entry_block = LLVMAppendBasicBlockInContext(ctx, codegen.func, "entry"); - LLVMPositionBuilderAtEnd(codegen.builder, entry_block); - codegen.pointer_index = bf_const_i64(&codegen, 0); - - if (!bf_codegen_block(&codegen, &program->root)) { - LLVMDisposeBuilder(codegen.builder); - LLVMDisposeModule(codegen.mod); - return NULL; - } - - return_value = codegen.pointer_index; - LLVMBuildRet(codegen.builder, LLVMBuildTrunc(codegen.builder, return_value, - codegen.i32_type, "result")); - - if (LLVMVerifyModule(codegen.mod, LLVMReturnStatusAction, &verify_msg) != - 0) { - bf_set_jit_err(err, verify_msg); - LLVMDisposeMessage(verify_msg); - LLVMDisposeBuilder(codegen.builder); - LLVMDisposeModule(codegen.mod); - return NULL; - } - - LLVMDisposeBuilder(codegen.builder); - mod = codegen.mod; - return mod; -} - -static int bf_opt_mod(LLVMModuleRef mod, bf_jit_err *err) { - LLVMTargetRef target; - LLVMTargetMachineRef target_machine; - LLVMPassBuilderOptionsRef pass_options; - LLVMErrorRef llvm_err; - char *triple; - char *cpu; - char *features; - char *err_msg; - - triple = LLVMGetDefaultTargetTriple(); - - if (LLVMGetTargetFromTriple(triple, &target, &err_msg) != 0) { - bf_set_jit_err(err, - err_msg != NULL ? err_msg : "failed to resolve target"); - LLVMDisposeMessage(err_msg); - LLVMDisposeMessage(triple); - return 0; - } - - cpu = LLVMGetHostCPUName(); - features = LLVMGetHostCPUFeatures(); - - target_machine = LLVMCreateTargetMachine( - target, triple, cpu, features, LLVMCodeGenLevelAggressive, - LLVMRelocDefault, LLVMCodeModelDefault); - LLVMDisposeMessage(triple); - LLVMDisposeMessage(cpu); - LLVMDisposeMessage(features); - - if (target_machine == NULL) { - bf_set_jit_err(err, "failed to create target machine"); - return 0; - } - - pass_options = LLVMCreatePassBuilderOptions(); - llvm_err = LLVMRunPasses(mod, - "function(instcombine," - "simplifycfg,gvn)", - target_machine, pass_options); - LLVMDisposePassBuilderOptions(pass_options); - LLVMDisposeTargetMachine(target_machine); - - if (llvm_err != NULL) { - bf_set_jit_err_from_llvm(err, llvm_err); - return 0; - } - - return 1; -} - bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, size_t tape_size, bf_jit_err *err) { LLVMOrcLLJITRef jit; @@ -536,12 +50,12 @@ bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, LLVMOrcThreadSafeModuleRef thread_safe_mod; LLVMOrcDefinitionGeneratorRef generator; LLVMOrcMaterializationUnitRef io_mu; - LLVMOrcCSymbolMapPair io_symbols[2]; LLVMErrorRef llvm_err; LLVMOrcExecutorAddress entry_address; LLVMModuleRef mod; LLVMContextRef ctx; bf_jit_entry_fn entry_func; + int execution_result; bf_jit_err_reset(err); @@ -563,22 +77,7 @@ bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, return false; } - /* unlock された I/O ラッパーを絶対シンボルとして登録 (名前修飾に対応) */ - io_symbols[0].Name = LLVMOrcLLJITMangleAndIntern(jit, "bf_io_putchar"); - io_symbols[0].Sym.Address = - (LLVMOrcExecutorAddress)(uintptr_t)bf_io_putchar; - io_symbols[0].Sym.Flags.GenericFlags = - LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable; - io_symbols[0].Sym.Flags.TargetFlags = 0; - - io_symbols[1].Name = LLVMOrcLLJITMangleAndIntern(jit, "bf_io_getchar"); - io_symbols[1].Sym.Address = - (LLVMOrcExecutorAddress)(uintptr_t)bf_io_getchar; - io_symbols[1].Sym.Flags.GenericFlags = - LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable; - io_symbols[1].Sym.Flags.TargetFlags = 0; - - io_mu = LLVMOrcAbsoluteSymbols(io_symbols, 2); + io_mu = bf_create_io_symbols(jit); llvm_err = LLVMOrcJITDylibDefine(LLVMOrcLLJITGetMainJITDylib(jit), io_mu); if (llvm_err != NULL) { bf_set_jit_err_from_llvm(err, llvm_err); @@ -598,7 +97,7 @@ bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, ctx = LLVMContextCreate(); thread_safe_ctx = LLVMOrcCreateNewThreadSafeContextFromLLVMContext(ctx); - mod = bf_build_mod(ctx, program, err); + mod = bf_build_module(ctx, program, err); if (mod == NULL) { LLVMOrcDisposeThreadSafeContext(thread_safe_ctx); LLVMOrcDisposeLLJIT(jit); @@ -607,7 +106,7 @@ bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, LLVMSetDataLayout(mod, LLVMOrcLLJITGetDataLayoutStr(jit)); - if (!bf_opt_mod(mod, err)) { + if (!bf_opt_llvm_module(mod, err)) { LLVMDisposeModule(mod); LLVMOrcDisposeThreadSafeContext(thread_safe_ctx); LLVMOrcDisposeLLJIT(jit); @@ -637,7 +136,7 @@ bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, flockfile(stdout); flockfile(stdin); - (void)entry_func(tape, tape_size); + execution_result = entry_func(tape, tape_size); funlockfile(stdin); funlockfile(stdout); @@ -647,5 +146,46 @@ bool bf_jit_execute_program(const bf_program *program, uint8_t *tape, return false; } + if (execution_result < 0) { + switch (-execution_result) { + case 1: + bf_set_jit_err(err, "tape pointer moved out of bounds (add_ptr)"); + break; + case 2: + bf_set_jit_err(err, + "tape pointer moved out of bounds (scan memchr)"); + break; + case 3: + bf_set_jit_err(err, "tape pointer moved out of bounds (scan)"); + break; + case 4: + bf_set_jit_err(err, + "tape pointer moved out of bounds (multiply min)"); + break; + case 5: + bf_set_jit_err(err, + "tape pointer moved out of bounds (multiply max)"); + break; + case 6: + bf_set_jit_err(err, "tape pointer moved out of bounds (loop min)"); + break; + case 7: + bf_set_jit_err(err, "tape pointer moved out of bounds (loop max)"); + break; + case 8: + bf_set_jit_err(err, + "tape pointer moved out of bounds (segment min)"); + break; + case 9: + bf_set_jit_err(err, + "tape pointer moved out of bounds (segment max)"); + break; + default: + bf_set_jit_err(err, "tape pointer moved out of bounds"); + break; + } + return false; + } + return true; } diff --git a/src/bfjit_codegen.c b/src/bfjit_codegen.c new file mode 100644 index 0000000..125bbbd --- /dev/null +++ b/src/bfjit_codegen.c @@ -0,0 +1,956 @@ +#define _GNU_SOURCE + +#include "bfjit_internal.h" + +#include +#include + +#include +#include +#include +#include +#include + +typedef struct bf_codegen { + LLVMContextRef ctx; + LLVMModuleRef mod; + LLVMBuilderRef builder; + LLVMValueRef func; + LLVMBasicBlockRef oob_block; + LLVMValueRef oob_reason_ptr; + LLVMValueRef tape_arg; + LLVMValueRef tape_size_arg; + LLVMValueRef pointer_index; + LLVMValueRef memchr_func; + LLVMValueRef scan_index_func; + LLVMValueRef scan_index_step4_func; + LLVMTypeRef i1_type; + LLVMTypeRef i8_type; + LLVMTypeRef i32_type; + LLVMTypeRef i64_type; + LLVMTypeRef tape_pointer_type; + LLVMValueRef putchar_func; + LLVMValueRef getchar_func; + bf_jit_err *err; + int suppress_ptr_guards; +} bf_codegen; + +typedef struct bf_block_bounds { + int min_offset; + int max_offset; + int final_offset; + int has_motion; +} bf_block_bounds; + +static LLVMValueRef bf_const_i8(bf_codegen *codegen, int value) { + return LLVMConstInt(codegen->i8_type, (unsigned long long)(uint8_t)value, + 0); +} + +static LLVMValueRef bf_const_i64(bf_codegen *codegen, uint64_t value) { + return LLVMConstInt(codegen->i64_type, value, 0); +} + +static LLVMValueRef bf_const_i32(bf_codegen *codegen, uint32_t value) { + return LLVMConstInt(codegen->i32_type, (unsigned long long)value, 0); +} + +static void bf_add_enum_attr_if_known(LLVMContextRef ctx, LLVMValueRef value, + LLVMAttributeIndex index, + const char *name, size_t name_length) { + unsigned kind; + + kind = LLVMGetEnumAttributeKindForName(name, name_length); + if (kind == 0) { + return; + } + + LLVMAddAttributeAtIndex(value, index, + LLVMCreateEnumAttribute(ctx, kind, 0)); +} + +static LLVMBasicBlockRef bf_get_oob_block(bf_codegen *codegen) { + if (codegen->oob_block == NULL) { + codegen->oob_block = + LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "oob"); + } + + return codegen->oob_block; +} + +static void bf_build_bounds_guard(bf_codegen *codegen, LLVMValueRef index, + const char *block_name, uint32_t reason) { + LLVMValueRef in_bounds; + LLVMBasicBlockRef continue_block; + LLVMBasicBlockRef fail_block; + + in_bounds = LLVMBuildICmp(codegen->builder, LLVMIntULT, index, + codegen->tape_size_arg, "in_bounds"); + continue_block = + LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, block_name); + fail_block = + LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "oob.set"); + LLVMBuildCondBr(codegen->builder, in_bounds, continue_block, fail_block); + + LLVMPositionBuilderAtEnd(codegen->builder, fail_block); + LLVMBuildStore(codegen->builder, bf_const_i32(codegen, reason), + codegen->oob_reason_ptr); + LLVMBuildBr(codegen->builder, bf_get_oob_block(codegen)); + + LLVMPositionBuilderAtEnd(codegen->builder, continue_block); +} + +static LLVMValueRef bf_build_current_cell_ptr(bf_codegen *codegen) { + LLVMValueRef indices[1]; + + indices[0] = codegen->pointer_index; + + return LLVMBuildGEP2(codegen->builder, codegen->i8_type, codegen->tape_arg, + indices, 1, "cell_ptr"); +} + +static void bf_bounds_include(int value, int *min_value, int *max_value) { + if (value < *min_value) { + *min_value = value; + } + if (value > *max_value) { + *max_value = value; + } +} + +static int bf_analyze_block_bounds_range(const bf_ir_block *block, + size_t start_index, size_t end_index, + bf_block_bounds *bounds) { + size_t index; + int current_offset; + int min_offset; + int max_offset; + int has_motion; + + current_offset = 0; + min_offset = 0; + max_offset = 0; + has_motion = 0; + + for (index = start_index; index < end_index; ++index) { + const bf_ir_node *node; + + node = &block->nodes[index]; + switch (node->kind) { + case BF_IR_ADD_PTR: + current_offset += node->arg; + has_motion = 1; + bf_bounds_include(current_offset, &min_offset, &max_offset); + break; + case BF_IR_ADD_DATA: + case BF_IR_INPUT: + case BF_IR_OUTPUT: + case BF_IR_SET_ZERO: + case BF_IR_SET_CONST: + bf_bounds_include(current_offset, &min_offset, &max_offset); + break; + case BF_IR_MULTI_ZERO: { + size_t term_index; + + for (term_index = 0; term_index < node->term_count; ++term_index) { + bf_bounds_include(current_offset + + node->terms[term_index].offset, + &min_offset, &max_offset); + } + current_offset += node->arg; + if (node->arg != 0) { + has_motion = 1; + bf_bounds_include(current_offset, &min_offset, &max_offset); + } + break; + } + case BF_IR_MULTIPLY_LOOP: { + size_t term_index; + + bf_bounds_include(current_offset, &min_offset, &max_offset); + for (term_index = 0; term_index < node->term_count; ++term_index) { + bf_bounds_include(current_offset + + node->terms[term_index].offset, + &min_offset, &max_offset); + } + break; + } + case BF_IR_LOOP: { + bf_block_bounds nested_bounds; + + if (!bf_analyze_block_bounds_range(&node->body, 0, node->body.count, + &nested_bounds) || + nested_bounds.final_offset != 0) { + return 0; + } + + bf_bounds_include(current_offset, &min_offset, &max_offset); + bf_bounds_include(current_offset + nested_bounds.min_offset, + &min_offset, &max_offset); + bf_bounds_include(current_offset + nested_bounds.max_offset, + &min_offset, &max_offset); + if (nested_bounds.has_motion) { + has_motion = 1; + } + break; + } + case BF_IR_SCAN: + return 0; + default: + return 0; + } + } + + bounds->min_offset = min_offset; + bounds->max_offset = max_offset; + bounds->final_offset = current_offset; + bounds->has_motion = has_motion; + return 1; +} + +static int bf_analyze_block_bounds(const bf_ir_block *block, + bf_block_bounds *bounds) { + return bf_analyze_block_bounds_range(block, 0, block->count, bounds); +} + +static int bf_find_guarded_range(const bf_ir_block *block, size_t start_index, + size_t *end_index, bf_block_bounds *bounds) { + size_t candidate_end; + int found; + + found = 0; + for (candidate_end = start_index + 1; candidate_end <= block->count; + ++candidate_end) { + bf_block_bounds candidate_bounds; + + if (!bf_analyze_block_bounds_range(block, start_index, candidate_end, + &candidate_bounds)) { + break; + } + + if (candidate_bounds.final_offset == 0 && candidate_bounds.has_motion && + (candidate_bounds.min_offset != 0 || + candidate_bounds.max_offset != 0) && + candidate_end > start_index + 1) { + *end_index = candidate_end; + *bounds = candidate_bounds; + found = 1; + } + } + + return found; +} + +static void bf_build_relative_bounds_guard(bf_codegen *codegen, + LLVMValueRef base_index, + int min_offset, int max_offset, + uint32_t min_reason, + uint32_t max_reason) { + if (min_offset < 0) { + LLVMValueRef min_index; + + min_index = LLVMBuildAdd( + codegen->builder, base_index, + LLVMConstInt(codegen->i64_type, + (unsigned long long)(int64_t)min_offset, 1), + "loop_guard_min_idx"); + bf_build_bounds_guard(codegen, min_index, "loop.min.in_bounds", + min_reason); + } + + if (max_offset > 0) { + LLVMValueRef max_index; + + max_index = LLVMBuildAdd( + codegen->builder, base_index, + LLVMConstInt(codegen->i64_type, + (unsigned long long)(int64_t)max_offset, 1), + "loop_guard_max_idx"); + bf_build_bounds_guard(codegen, max_index, "loop.max.in_bounds", + max_reason); + } +} + +static int bf_codegen_block(bf_codegen *codegen, const bf_ir_block *block); + +static int bf_codegen_loop(bf_codegen *codegen, const bf_ir_node *node) { + LLVMBasicBlockRef pre_loop_block; + LLVMBasicBlockRef initial_block; + LLVMValueRef pre_loop_ptr; + LLVMBasicBlockRef condition_block; + LLVMBasicBlockRef body_block; + LLVMBasicBlockRef exit_block; + LLVMValueRef ptr_phi; + LLVMValueRef cell_ptr; + LLVMValueRef cell_value; + LLVMValueRef loop_condition; + bf_block_bounds loop_bounds; + int use_loop_guard; + LLVMBasicBlockRef body_end_block; + LLVMValueRef body_end_ptr; + + pre_loop_block = LLVMGetInsertBlock(codegen->builder); + pre_loop_ptr = codegen->pointer_index; + + condition_block = + LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "loop.cond"); + body_block = + LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "loop.body"); + exit_block = + LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, "loop.exit"); + + use_loop_guard = bf_analyze_block_bounds(&node->body, &loop_bounds) && + loop_bounds.final_offset == 0 && loop_bounds.has_motion; + if (use_loop_guard) { + LLVMBasicBlockRef guard_block; + + guard_block = LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, + "loop.guard"); + LLVMBuildBr(codegen->builder, guard_block); + LLVMPositionBuilderAtEnd(codegen->builder, guard_block); + bf_build_relative_bounds_guard(codegen, pre_loop_ptr, + loop_bounds.min_offset, + loop_bounds.max_offset, 6, 7); + initial_block = LLVMGetInsertBlock(codegen->builder); + LLVMBuildBr(codegen->builder, condition_block); + } else { + LLVMBuildBr(codegen->builder, condition_block); + initial_block = pre_loop_block; + } + + LLVMPositionBuilderAtEnd(codegen->builder, condition_block); + ptr_phi = LLVMBuildPhi(codegen->builder, codegen->i64_type, "ptr.phi"); + LLVMAddIncoming(ptr_phi, &pre_loop_ptr, &initial_block, 1); + codegen->pointer_index = ptr_phi; + + cell_ptr = bf_build_current_cell_ptr(codegen); + cell_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, cell_ptr, + "loop_value"); + loop_condition = LLVMBuildICmp(codegen->builder, LLVMIntNE, cell_value, + bf_const_i8(codegen, 0), "loop_condition"); + LLVMBuildCondBr(codegen->builder, loop_condition, body_block, exit_block); + + LLVMPositionBuilderAtEnd(codegen->builder, body_block); + if (use_loop_guard) { + codegen->suppress_ptr_guards += 1; + } + if (!bf_codegen_block(codegen, &node->body)) { + return 0; + } + if (use_loop_guard) { + codegen->suppress_ptr_guards -= 1; + } + + body_end_block = LLVMGetInsertBlock(codegen->builder); + body_end_ptr = codegen->pointer_index; + + if (LLVMGetBasicBlockTerminator(body_end_block) == NULL) { + LLVMBuildBr(codegen->builder, condition_block); + } + + LLVMAddIncoming(ptr_phi, &body_end_ptr, &body_end_block, 1); + + LLVMPositionBuilderAtEnd(codegen->builder, exit_block); + codegen->pointer_index = ptr_phi; + return 1; +} + +static int bf_codegen_node(bf_codegen *codegen, const bf_ir_node *node) { + LLVMValueRef cell_ptr; + LLVMValueRef current_value; + LLVMValueRef updated_value; + LLVMValueRef call_args[1]; + + switch (node->kind) { + case BF_IR_ADD_PTR: + updated_value = LLVMBuildAdd( + codegen->builder, codegen->pointer_index, + LLVMConstInt(codegen->i64_type, + (unsigned long long)(int64_t)node->arg, 1), + "ptr_next"); + if (codegen->suppress_ptr_guards == 0) { + bf_build_bounds_guard(codegen, updated_value, "ptr.in_bounds", 1); + } + codegen->pointer_index = updated_value; + return 1; + case BF_IR_ADD_DATA: + cell_ptr = bf_build_current_cell_ptr(codegen); + current_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, + cell_ptr, "cell_value"); + updated_value = + LLVMBuildAdd(codegen->builder, current_value, + bf_const_i8(codegen, node->arg), "cell_next"); + LLVMBuildStore(codegen->builder, updated_value, cell_ptr); + return 1; + case BF_IR_OUTPUT: + cell_ptr = bf_build_current_cell_ptr(codegen); + current_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, + cell_ptr, "output_value"); + call_args[0] = LLVMBuildZExt(codegen->builder, current_value, + codegen->i32_type, "output_arg"); + LLVMBuildCall2(codegen->builder, + LLVMGlobalGetValueType(codegen->putchar_func), + codegen->putchar_func, call_args, 1, ""); + return 1; + case BF_IR_INPUT: + cell_ptr = bf_build_current_cell_ptr(codegen); + current_value = LLVMBuildCall2( + codegen->builder, LLVMGlobalGetValueType(codegen->getchar_func), + codegen->getchar_func, NULL, 0, "input_value"); + updated_value = LLVMBuildTrunc(codegen->builder, current_value, + codegen->i8_type, "input_trunc"); + LLVMBuildStore(codegen->builder, updated_value, cell_ptr); + return 1; + case BF_IR_LOOP: + return bf_codegen_loop(codegen, node); + case BF_IR_SET_ZERO: + cell_ptr = bf_build_current_cell_ptr(codegen); + LLVMBuildStore(codegen->builder, bf_const_i8(codegen, 0), cell_ptr); + return 1; + case BF_IR_SET_CONST: + cell_ptr = bf_build_current_cell_ptr(codegen); + LLVMBuildStore(codegen->builder, bf_const_i8(codegen, node->arg), + cell_ptr); + return 1; + case BF_IR_MULTI_ZERO: { + size_t term_index; + + if (codegen->suppress_ptr_guards == 0 && node->term_count > 0) { + int min_offset; + int max_offset; + + min_offset = node->terms[0].offset; + max_offset = node->terms[0].offset; + for (term_index = 1; term_index < node->term_count; ++term_index) { + if (node->terms[term_index].offset < min_offset) { + min_offset = node->terms[term_index].offset; + } + if (node->terms[term_index].offset > max_offset) { + max_offset = node->terms[term_index].offset; + } + } + if (node->arg < min_offset) { + min_offset = node->arg; + } + if (node->arg > max_offset) { + max_offset = node->arg; + } + + bf_build_relative_bounds_guard(codegen, codegen->pointer_index, + min_offset, max_offset, 8, 9); + } + + for (term_index = 0; term_index < node->term_count; ++term_index) { + LLVMValueRef target_idx; + LLVMValueRef target_indices[1]; + LLVMValueRef target_ptr; + + target_idx = LLVMBuildAdd( + codegen->builder, codegen->pointer_index, + LLVMConstInt( + codegen->i64_type, + (unsigned long long)(int64_t)node->terms[term_index].offset, + 1), + "multi_zero_idx"); + target_indices[0] = target_idx; + target_ptr = LLVMBuildGEP2(codegen->builder, codegen->i8_type, + codegen->tape_arg, target_indices, 1, + "multi_zero_ptr"); + LLVMBuildStore(codegen->builder, bf_const_i8(codegen, 0), + target_ptr); + } + + if (node->arg != 0) { + codegen->pointer_index = LLVMBuildAdd( + codegen->builder, codegen->pointer_index, + LLVMConstInt(codegen->i64_type, + (unsigned long long)(int64_t)node->arg, 1), + "multi_zero_ptr_next"); + } + return 1; + } + case BF_IR_SCAN: { + if (node->arg == 1) { + LLVMValueRef search_ptr; + LLVMValueRef start_value; + LLVMValueRef start_is_zero; + LLVMValueRef remaining; + LLVMValueRef scan_args[3]; + LLVMValueRef result; + LLVMValueRef found; + LLVMValueRef result_int; + LLVMValueRef tape_int; + LLVMValueRef new_index; + LLVMValueRef phi; + LLVMBasicBlockRef pre_block; + LLVMBasicBlockRef search_block; + LLVMBasicBlockRef found_block; + LLVMBasicBlockRef fail_block; + LLVMBasicBlockRef done_block; + LLVMValueRef pre_ptr; + + pre_block = LLVMGetInsertBlock(codegen->builder); + pre_ptr = codegen->pointer_index; + + search_ptr = bf_build_current_cell_ptr(codegen); + start_value = LLVMBuildLoad2(codegen->builder, codegen->i8_type, + search_ptr, "scan_start_val"); + start_is_zero = + LLVMBuildICmp(codegen->builder, LLVMIntEQ, start_value, + bf_const_i8(codegen, 0), "scan_start_is_zero"); + search_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan.search"); + found_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan.found"); + fail_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan.not_found"); + done_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan.done"); + + LLVMBuildCondBr(codegen->builder, start_is_zero, done_block, + search_block); + + LLVMPositionBuilderAtEnd(codegen->builder, search_block); + remaining = LLVMBuildSub(codegen->builder, codegen->tape_size_arg, + codegen->pointer_index, "scan_remaining"); + + scan_args[0] = search_ptr; + scan_args[1] = LLVMConstInt(codegen->i32_type, 0, 0); + scan_args[2] = remaining; + result = LLVMBuildCall2( + codegen->builder, LLVMGlobalGetValueType(codegen->memchr_func), + codegen->memchr_func, scan_args, 3, "scan_memchr"); + + found = LLVMBuildICmp(codegen->builder, LLVMIntNE, result, + LLVMConstNull(codegen->tape_pointer_type), + "scan_found"); + + LLVMBuildCondBr(codegen->builder, found, found_block, fail_block); + + LLVMPositionBuilderAtEnd(codegen->builder, fail_block); + LLVMBuildStore(codegen->builder, bf_const_i32(codegen, 2), + codegen->oob_reason_ptr); + LLVMBuildBr(codegen->builder, bf_get_oob_block(codegen)); + + LLVMPositionBuilderAtEnd(codegen->builder, found_block); + result_int = LLVMBuildPtrToInt(codegen->builder, result, + codegen->i64_type, "scan_result_i"); + tape_int = LLVMBuildPtrToInt(codegen->builder, codegen->tape_arg, + codegen->i64_type, "scan_tape_i"); + new_index = LLVMBuildSub(codegen->builder, result_int, tape_int, + "scan_new_idx"); + LLVMBuildBr(codegen->builder, done_block); + + LLVMPositionBuilderAtEnd(codegen->builder, done_block); + phi = LLVMBuildPhi(codegen->builder, codegen->i64_type, + "scan_ptr.phi"); + LLVMAddIncoming(phi, &pre_ptr, &pre_block, 1); + LLVMAddIncoming(phi, &new_index, &found_block, 1); + codegen->pointer_index = phi; + } else if (node->arg == 4) { + LLVMValueRef scan_args[3]; + LLVMValueRef result_idx; + LLVMValueRef found; + LLVMBasicBlockRef found_block; + LLVMBasicBlockRef fail_block; + + scan_args[0] = codegen->tape_arg; + scan_args[1] = codegen->tape_size_arg; + scan_args[2] = codegen->pointer_index; + result_idx = LLVMBuildCall2( + codegen->builder, + LLVMGlobalGetValueType(codegen->scan_index_step4_func), + codegen->scan_index_step4_func, scan_args, 3, "scan_idx4"); + found = LLVMBuildICmp(codegen->builder, LLVMIntULT, result_idx, + codegen->tape_size_arg, "scan_found4"); + found_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan4.found"); + fail_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan4.not_found"); + LLVMBuildCondBr(codegen->builder, found, found_block, fail_block); + + LLVMPositionBuilderAtEnd(codegen->builder, fail_block); + LLVMBuildStore(codegen->builder, bf_const_i32(codegen, 3), + codegen->oob_reason_ptr); + LLVMBuildBr(codegen->builder, bf_get_oob_block(codegen)); + + LLVMPositionBuilderAtEnd(codegen->builder, found_block); + codegen->pointer_index = result_idx; + } else { + LLVMValueRef scan_args[4]; + LLVMValueRef result_idx; + LLVMValueRef found; + LLVMBasicBlockRef found_block; + LLVMBasicBlockRef fail_block; + + scan_args[0] = codegen->tape_arg; + scan_args[1] = codegen->tape_size_arg; + scan_args[2] = codegen->pointer_index; + scan_args[3] = LLVMConstInt( + codegen->i64_type, (unsigned long long)(int64_t)node->arg, 1); + result_idx = LLVMBuildCall2( + codegen->builder, + LLVMGlobalGetValueType(codegen->scan_index_func), + codegen->scan_index_func, scan_args, 4, "scan_idx"); + found = LLVMBuildICmp(codegen->builder, LLVMIntULT, result_idx, + codegen->tape_size_arg, "scan_found"); + found_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan.found"); + fail_block = LLVMAppendBasicBlockInContext( + codegen->ctx, codegen->func, "scan.not_found"); + LLVMBuildCondBr(codegen->builder, found, found_block, fail_block); + + LLVMPositionBuilderAtEnd(codegen->builder, fail_block); + LLVMBuildStore(codegen->builder, bf_const_i32(codegen, 3), + codegen->oob_reason_ptr); + LLVMBuildBr(codegen->builder, bf_get_oob_block(codegen)); + + LLVMPositionBuilderAtEnd(codegen->builder, found_block); + codegen->pointer_index = result_idx; + } + return 1; + } + case BF_IR_MULTIPLY_LOOP: { + LLVMValueRef base_idx; + LLVMValueRef loop_val; + LLVMValueRef loop_is_nonzero; + LLVMBasicBlockRef body_block; + LLVMBasicBlockRef exit_block; + int min_offset; + int max_offset; + size_t ti; + + cell_ptr = bf_build_current_cell_ptr(codegen); + base_idx = codegen->pointer_index; + + loop_val = LLVMBuildLoad2(codegen->builder, codegen->i8_type, cell_ptr, + "mul_loop_val"); + loop_is_nonzero = + LLVMBuildICmp(codegen->builder, LLVMIntNE, loop_val, + bf_const_i8(codegen, 0), "mul_loop_nonzero"); + body_block = LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, + "mul.body"); + exit_block = LLVMAppendBasicBlockInContext(codegen->ctx, codegen->func, + "mul.exit"); + + LLVMBuildCondBr(codegen->builder, loop_is_nonzero, body_block, + exit_block); + + LLVMPositionBuilderAtEnd(codegen->builder, body_block); + + if (codegen->suppress_ptr_guards == 0 && node->term_count > 0) { + LLVMValueRef min_idx; + LLVMValueRef max_idx; + + min_offset = node->terms[0].offset; + max_offset = node->terms[0].offset; + for (ti = 1; ti < node->term_count; ++ti) { + if (node->terms[ti].offset < min_offset) { + min_offset = node->terms[ti].offset; + } + if (node->terms[ti].offset > max_offset) { + max_offset = node->terms[ti].offset; + } + } + + min_idx = LLVMBuildAdd( + codegen->builder, base_idx, + LLVMConstInt(codegen->i64_type, + (unsigned long long)(int64_t)min_offset, 1), + "mul_min_idx"); + bf_build_bounds_guard(codegen, min_idx, "mul.min.in_bounds", 4); + + if (max_offset != min_offset) { + max_idx = LLVMBuildAdd( + codegen->builder, base_idx, + LLVMConstInt(codegen->i64_type, + (unsigned long long)(int64_t)max_offset, 1), + "mul_max_idx"); + bf_build_bounds_guard(codegen, max_idx, "mul.max.in_bounds", 5); + } + } + + for (ti = 0; ti < node->term_count; ++ti) { + LLVMValueRef target_idx; + LLVMValueRef target_ptr; + LLVMValueRef target_indices[1]; + LLVMValueRef old_val; + LLVMValueRef product; + LLVMValueRef new_val; + + target_idx = LLVMBuildAdd( + codegen->builder, base_idx, + LLVMConstInt( + codegen->i64_type, + (unsigned long long)(int64_t)node->terms[ti].offset, 1), + "mul_target_idx"); + target_indices[0] = target_idx; + target_ptr = LLVMBuildGEP2(codegen->builder, codegen->i8_type, + codegen->tape_arg, target_indices, 1, + "mul_target_ptr"); + old_val = LLVMBuildLoad2(codegen->builder, codegen->i8_type, + target_ptr, "mul_old"); + product = LLVMBuildMul(codegen->builder, loop_val, + bf_const_i8(codegen, node->terms[ti].factor), + "mul_product"); + new_val = + LLVMBuildAdd(codegen->builder, old_val, product, "mul_new"); + LLVMBuildStore(codegen->builder, new_val, target_ptr); + } + + LLVMBuildStore(codegen->builder, bf_const_i8(codegen, 0), cell_ptr); + LLVMBuildBr(codegen->builder, exit_block); + + LLVMPositionBuilderAtEnd(codegen->builder, exit_block); + return 1; + } + default: + bf_set_jit_err(codegen->err, + "unsupported IR node while generating LLVM IR"); + return 0; + } +} + +static int bf_codegen_block(bf_codegen *codegen, const bf_ir_block *block) { + size_t index; + + for (index = 0; index < block->count;) { + if (codegen->suppress_ptr_guards == 0) { + bf_block_bounds guarded_bounds; + size_t guarded_end; + + if (bf_find_guarded_range(block, index, &guarded_end, + &guarded_bounds)) { + bf_build_relative_bounds_guard(codegen, codegen->pointer_index, + guarded_bounds.min_offset, + guarded_bounds.max_offset, 8, 9); + codegen->suppress_ptr_guards += 1; + for (; index < guarded_end; ++index) { + if (!bf_codegen_node(codegen, &block->nodes[index])) { + codegen->suppress_ptr_guards -= 1; + return 0; + } + } + codegen->suppress_ptr_guards -= 1; + continue; + } + } + + if (!bf_codegen_node(codegen, &block->nodes[index])) { + return 0; + } + + index += 1; + } + + return 1; +} + +LLVMModuleRef bf_build_module(LLVMContextRef ctx, const bf_program *program, + bf_jit_err *err) { + bf_codegen codegen; + LLVMTypeRef entry_args[2]; + LLVMTypeRef entry_type; + LLVMTypeRef libc_args[1]; + LLVMBasicBlockRef entry_block; + char *target_triple; + char *verify_msg; + LLVMModuleRef mod; + LLVMValueRef return_value; + + memset(&codegen, 0, sizeof(codegen)); + codegen.ctx = ctx; + codegen.mod = LLVMModuleCreateWithNameInContext("bfjit.mod", ctx); + codegen.builder = LLVMCreateBuilderInContext(ctx); + codegen.err = err; + codegen.i1_type = LLVMInt1TypeInContext(ctx); + codegen.i8_type = LLVMInt8TypeInContext(ctx); + codegen.i32_type = LLVMInt32TypeInContext(ctx); + codegen.i64_type = LLVMInt64TypeInContext(ctx); + codegen.tape_pointer_type = LLVMPointerTypeInContext(ctx, 0); + + target_triple = LLVMGetDefaultTargetTriple(); + LLVMSetTarget(codegen.mod, target_triple); + LLVMDisposeMessage(target_triple); + + entry_args[0] = codegen.tape_pointer_type; + entry_args[1] = codegen.i64_type; + entry_type = LLVMFunctionType(codegen.i32_type, entry_args, 2, 0); + codegen.func = LLVMAddFunction(codegen.mod, "bf_entry", entry_type); + codegen.tape_arg = LLVMGetParam(codegen.func, 0); + codegen.tape_size_arg = LLVMGetParam(codegen.func, 1); + + { + unsigned noalias_kind = LLVMGetEnumAttributeKindForName("noalias", 7); + unsigned nonnull_kind = LLVMGetEnumAttributeKindForName("nonnull", 7); + unsigned nounwind_kind = LLVMGetEnumAttributeKindForName("nounwind", 8); + + LLVMAddAttributeAtIndex(codegen.func, 1, + LLVMCreateEnumAttribute(ctx, noalias_kind, 0)); + LLVMAddAttributeAtIndex(codegen.func, 1, + LLVMCreateEnumAttribute(ctx, nonnull_kind, 0)); + LLVMAddAttributeAtIndex(codegen.func, LLVMAttributeFunctionIndex, + LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); + } + + libc_args[0] = codegen.i32_type; + codegen.putchar_func = + LLVMAddFunction(codegen.mod, "bf_io_putchar", + LLVMFunctionType(codegen.i32_type, libc_args, 1, 0)); + codegen.getchar_func = + LLVMAddFunction(codegen.mod, "bf_io_getchar", + LLVMFunctionType(codegen.i32_type, NULL, 0, 0)); + + { + unsigned nounwind_kind = LLVMGetEnumAttributeKindForName("nounwind", 8); + LLVMAddAttributeAtIndex(codegen.putchar_func, + LLVMAttributeFunctionIndex, + LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); + LLVMAddAttributeAtIndex(codegen.getchar_func, + LLVMAttributeFunctionIndex, + LLVMCreateEnumAttribute(ctx, nounwind_kind, 0)); + } + + { + LLVMTypeRef memchr_params[3]; + LLVMTypeRef scan_params[4]; + LLVMTypeRef scan4_params[3]; + + memchr_params[0] = codegen.tape_pointer_type; + memchr_params[1] = codegen.i32_type; + memchr_params[2] = codegen.i64_type; + codegen.memchr_func = LLVMAddFunction( + codegen.mod, "memchr", + LLVMFunctionType(codegen.tape_pointer_type, memchr_params, 3, 0)); + bf_add_enum_attr_if_known(ctx, codegen.memchr_func, + LLVMAttributeFunctionIndex, "nounwind", 8); + + scan_params[0] = codegen.tape_pointer_type; + scan_params[1] = codegen.i64_type; + scan_params[2] = codegen.i64_type; + scan_params[3] = codegen.i64_type; + codegen.scan_index_func = LLVMAddFunction( + codegen.mod, "bf_io_scan_index", + LLVMFunctionType(codegen.i64_type, scan_params, 4, 0)); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_func, + LLVMAttributeFunctionIndex, "nounwind", 8); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_func, + LLVMAttributeFunctionIndex, "willreturn", 10); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_func, + LLVMAttributeFunctionIndex, "nofree", 6); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_func, 1, "nonnull", + 7); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_func, 1, "nocapture", + 9); + + scan4_params[0] = codegen.tape_pointer_type; + scan4_params[1] = codegen.i64_type; + scan4_params[2] = codegen.i64_type; + codegen.scan_index_step4_func = LLVMAddFunction( + codegen.mod, "bf_io_scan_index_step4", + LLVMFunctionType(codegen.i64_type, scan4_params, 3, 0)); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_step4_func, + LLVMAttributeFunctionIndex, "nounwind", 8); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_step4_func, + LLVMAttributeFunctionIndex, "willreturn", 10); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_step4_func, + LLVMAttributeFunctionIndex, "nofree", 6); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_step4_func, 1, + "nonnull", 7); + bf_add_enum_attr_if_known(ctx, codegen.scan_index_step4_func, 1, + "nocapture", 9); + } + + entry_block = LLVMAppendBasicBlockInContext(ctx, codegen.func, "entry"); + LLVMPositionBuilderAtEnd(codegen.builder, entry_block); + codegen.oob_reason_ptr = + LLVMBuildAlloca(codegen.builder, codegen.i32_type, "oob_reason"); + LLVMBuildStore(codegen.builder, bf_const_i32(&codegen, 0), + codegen.oob_reason_ptr); + codegen.pointer_index = bf_const_i64(&codegen, 0); + + if (!bf_codegen_block(&codegen, &program->root)) { + LLVMDisposeBuilder(codegen.builder); + LLVMDisposeModule(codegen.mod); + return NULL; + } + + return_value = codegen.pointer_index; + LLVMBuildRet(codegen.builder, LLVMBuildTrunc(codegen.builder, return_value, + codegen.i32_type, "result")); + + if (codegen.oob_block != NULL && + LLVMGetBasicBlockTerminator(codegen.oob_block) == NULL) { + LLVMValueRef reason; + + LLVMPositionBuilderAtEnd(codegen.builder, codegen.oob_block); + reason = LLVMBuildLoad2(codegen.builder, codegen.i32_type, + codegen.oob_reason_ptr, "oob_reason_val"); + LLVMBuildRet(codegen.builder, + LLVMBuildNeg(codegen.builder, reason, "oob_ret")); + } + + if (LLVMVerifyModule(codegen.mod, LLVMReturnStatusAction, &verify_msg) != + 0) { + bf_set_jit_err(err, verify_msg); + LLVMDisposeMessage(verify_msg); + LLVMDisposeBuilder(codegen.builder); + LLVMDisposeModule(codegen.mod); + return NULL; + } + + LLVMDisposeBuilder(codegen.builder); + mod = codegen.mod; + return mod; +} + +int bf_opt_llvm_module(LLVMModuleRef mod, bf_jit_err *err) { + LLVMTargetRef target; + LLVMTargetMachineRef target_machine; + LLVMPassBuilderOptionsRef pass_options; + LLVMErrorRef llvm_err; + char *triple; + char *cpu; + char *features; + char *err_msg; + + triple = LLVMGetDefaultTargetTriple(); + + if (LLVMGetTargetFromTriple(triple, &target, &err_msg) != 0) { + bf_set_jit_err(err, + err_msg != NULL ? err_msg : "failed to resolve target"); + LLVMDisposeMessage(err_msg); + LLVMDisposeMessage(triple); + return 0; + } + + cpu = LLVMGetHostCPUName(); + features = LLVMGetHostCPUFeatures(); + + target_machine = LLVMCreateTargetMachine( + target, triple, cpu, features, LLVMCodeGenLevelAggressive, + LLVMRelocDefault, LLVMCodeModelDefault); + LLVMDisposeMessage(triple); + LLVMDisposeMessage(cpu); + LLVMDisposeMessage(features); + + if (target_machine == NULL) { + bf_set_jit_err(err, "failed to create target machine"); + return 0; + } + + pass_options = LLVMCreatePassBuilderOptions(); + llvm_err = LLVMRunPasses(mod, + "function(instcombine," + "simplifycfg,gvn)", + target_machine, pass_options); + LLVMDisposePassBuilderOptions(pass_options); + LLVMDisposeTargetMachine(target_machine); + + if (llvm_err != NULL) { + bf_set_jit_err_from_llvm(err, llvm_err); + return 0; + } + + return 1; +} diff --git a/src/bfjit_runtime.c b/src/bfjit_runtime.c new file mode 100644 index 0000000..bdda450 --- /dev/null +++ b/src/bfjit_runtime.c @@ -0,0 +1,107 @@ +#define _GNU_SOURCE + +#include "bfjit_internal.h" + +#include +#include + +static int bf_io_putchar(int c) { return putchar_unlocked(c); } + +static int bf_io_getchar(void) { return getchar_unlocked(); } + +static size_t bf_io_scan_index(const uint8_t *tape, size_t tape_size, + size_t start_index, int64_t step) { + int64_t index; + int64_t limit; + + if (tape == NULL || step == 0 || start_index >= tape_size) { + return tape_size; + } + + index = (int64_t)start_index; + limit = (int64_t)tape_size; + + while (index >= 0 && index < limit) { + if (tape[index] == 0) { + return (size_t)index; + } + index += step; + } + + return tape_size; +} + +static size_t bf_io_scan_index_step4(const uint8_t *tape, size_t tape_size, + size_t start_index) { + size_t index; + + if (tape == NULL || start_index >= tape_size) { + return tape_size; + } + + index = start_index; + + while (index + 12 < tape_size) { + if (tape[index] == 0) { + return index; + } + if (tape[index + 4] == 0) { + return index + 4; + } + if (tape[index + 8] == 0) { + return index + 8; + } + if (tape[index + 12] == 0) { + return index + 12; + } + index += 16; + } + + while (index < tape_size) { + if (tape[index] == 0) { + return index; + } + if (index + 4 < tape_size) { + index += 4; + } else { + break; + } + } + + return tape_size; +} + +LLVMOrcMaterializationUnitRef bf_create_io_symbols(LLVMOrcLLJITRef jit) { + LLVMOrcCSymbolMapPair io_symbols[4]; + + io_symbols[0].Name = LLVMOrcLLJITMangleAndIntern(jit, "bf_io_putchar"); + io_symbols[0].Sym.Address = + (LLVMOrcExecutorAddress)(uintptr_t)bf_io_putchar; + io_symbols[0].Sym.Flags.GenericFlags = + LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable; + io_symbols[0].Sym.Flags.TargetFlags = 0; + + io_symbols[1].Name = LLVMOrcLLJITMangleAndIntern(jit, "bf_io_getchar"); + io_symbols[1].Sym.Address = + (LLVMOrcExecutorAddress)(uintptr_t)bf_io_getchar; + io_symbols[1].Sym.Flags.GenericFlags = + LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable; + io_symbols[1].Sym.Flags.TargetFlags = 0; + + io_symbols[2].Name = LLVMOrcLLJITMangleAndIntern(jit, "bf_io_scan_index"); + io_symbols[2].Sym.Address = + (LLVMOrcExecutorAddress)(uintptr_t)bf_io_scan_index; + io_symbols[2].Sym.Flags.GenericFlags = + LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable; + io_symbols[2].Sym.Flags.TargetFlags = 0; + + io_symbols[3].Name = + LLVMOrcLLJITMangleAndIntern(jit, "bf_io_scan_index_step4"); + io_symbols[3].Sym.Address = + (LLVMOrcExecutorAddress)(uintptr_t)bf_io_scan_index_step4; + io_symbols[3].Sym.Flags.GenericFlags = + LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable; + io_symbols[3].Sym.Flags.TargetFlags = 0; + + return LLVMOrcAbsoluteSymbols(io_symbols, 4); +} diff --git a/src/bfopt.c b/src/bfopt.c index 45fd871..356474d 100644 --- a/src/bfopt.c +++ b/src/bfopt.c @@ -1,6 +1,7 @@ #include "bfopt.h" #include +#include static void bf_opt_set_zero(bf_ir_node *node) { const bf_ir_block *body; @@ -163,6 +164,261 @@ static void bf_try_recognize_multiply_loop(bf_ir_node *node) { node->term_count = term_count; } +static void bf_opt_fold_set_const(bf_ir_block *block) { + size_t index; + + for (index = 0; index + 1 < block->count; ++index) { + bf_ir_node *node; + bf_ir_node *next; + + node = &block->nodes[index]; + next = &block->nodes[index + 1]; + + if (node->kind == BF_IR_SET_ZERO && next->kind == BF_IR_ADD_DATA) { + node->kind = BF_IR_SET_CONST; + node->arg = next->arg; + memmove(&block->nodes[index + 1], &block->nodes[index + 2], + (block->count - index - 2) * sizeof(*block->nodes)); + block->count -= 1; + index -= (index > 0); + } else if (node->kind == BF_IR_SET_CONST && + next->kind == BF_IR_ADD_DATA) { + node->arg += next->arg; + if (node->arg == 0) { + node->kind = BF_IR_SET_ZERO; + } + memmove(&block->nodes[index + 1], &block->nodes[index + 2], + (block->count - index - 2) * sizeof(*block->nodes)); + block->count -= 1; + index -= (index > 0); + } + } +} + +typedef struct bf_store_record { + int offset; + size_t node_index; +} bf_store_record; + +static size_t bf_find_store_record(const bf_store_record *records, + size_t record_count, int offset) { + size_t index; + + for (index = 0; index < record_count; ++index) { + if (records[index].offset == offset) { + return index; + } + } + + return record_count; +} + +static void bf_drop_store_record(bf_store_record *records, size_t *record_count, + int offset) { + size_t record_index; + + record_index = bf_find_store_record(records, *record_count, offset); + if (record_index == *record_count) { + return; + } + + memmove(&records[record_index], &records[record_index + 1], + (*record_count - record_index - 1) * sizeof(*records)); + *record_count -= 1; +} + +static void bf_kill_prior_store(bf_store_record *records, size_t *record_count, + int offset, unsigned char *remove_mask) { + size_t record_index; + + record_index = bf_find_store_record(records, *record_count, offset); + if (record_index == *record_count) { + return; + } + + remove_mask[records[record_index].node_index] = 1; + memmove(&records[record_index], &records[record_index + 1], + (*record_count - record_index - 1) * sizeof(*records)); + *record_count -= 1; +} + +static void bf_record_store(bf_store_record *records, size_t *record_count, + int offset, size_t node_index, + unsigned char *remove_mask) { + size_t record_index; + + record_index = bf_find_store_record(records, *record_count, offset); + if (record_index < *record_count) { + remove_mask[records[record_index].node_index] = 1; + records[record_index].node_index = node_index; + return; + } + + records[*record_count].offset = offset; + records[*record_count].node_index = node_index; + *record_count += 1; +} + +static void bf_clear_store_records(size_t *record_count) { *record_count = 0; } + +static void bf_opt_eliminate_dead_stores(bf_ir_block *block) { + bf_store_record *records; + unsigned char *remove_mask; + size_t record_count; + size_t index; + size_t write_index; + int current_offset; + int changed; + + if (block->count < 2) { + return; + } + + records = malloc(block->count * sizeof(*records)); + remove_mask = calloc(block->count, sizeof(*remove_mask)); + if (records == NULL || remove_mask == NULL) { + free(records); + free(remove_mask); + return; + } + + record_count = 0; + current_offset = 0; + changed = 0; + + for (index = 0; index < block->count; ++index) { + bf_ir_node *node; + + node = &block->nodes[index]; + switch (node->kind) { + case BF_IR_ADD_PTR: + current_offset += node->arg; + break; + case BF_IR_SET_ZERO: + case BF_IR_SET_CONST: + bf_record_store(records, &record_count, current_offset, index, + remove_mask); + break; + case BF_IR_INPUT: + bf_kill_prior_store(records, &record_count, current_offset, + remove_mask); + break; + case BF_IR_ADD_DATA: + case BF_IR_OUTPUT: + bf_drop_store_record(records, &record_count, current_offset); + break; + case BF_IR_MULTI_ZERO: { + size_t term_index; + + for (term_index = 0; term_index < node->term_count; ++term_index) { + bf_kill_prior_store(records, &record_count, + current_offset + + node->terms[term_index].offset, + remove_mask); + } + current_offset += node->arg; + break; + } + case BF_IR_LOOP: + case BF_IR_SCAN: + case BF_IR_MULTIPLY_LOOP: + bf_clear_store_records(&record_count); + break; + default: + bf_clear_store_records(&record_count); + break; + } + } + + for (index = 0; index < block->count; ++index) { + if (remove_mask[index] != 0) { + changed = 1; + break; + } + } + + if (changed) { + write_index = 0; + for (index = 0; index < block->count; ++index) { + if (remove_mask[index] == 0) { + if (write_index != index) { + block->nodes[write_index] = block->nodes[index]; + } + write_index += 1; + } + } + block->count = write_index; + } + + free(records); + free(remove_mask); +} + +static void bf_try_fold_multi_zero(bf_ir_block *block) { + size_t index; + + for (index = 0; index < block->count; ++index) { + size_t scan_index; + size_t zero_count; + int current_offset; + bf_multiply_term *offsets; + + if (block->nodes[index].kind != BF_IR_SET_ZERO) { + continue; + } + + zero_count = 1; + current_offset = 0; + scan_index = index + 1; + while (scan_index + 1 < block->count && + block->nodes[scan_index].kind == BF_IR_ADD_PTR && + block->nodes[scan_index + 1].kind == BF_IR_SET_ZERO) { + current_offset += block->nodes[scan_index].arg; + zero_count += 1; + scan_index += 2; + } + + if (zero_count < 2) { + continue; + } + + offsets = malloc(zero_count * sizeof(*offsets)); + if (offsets == NULL) { + continue; + } + + offsets[0].offset = 0; + offsets[0].factor = 0; + current_offset = 0; + zero_count = 1; + scan_index = index + 1; + while (scan_index + 1 < block->count && + block->nodes[scan_index].kind == BF_IR_ADD_PTR && + block->nodes[scan_index + 1].kind == BF_IR_SET_ZERO) { + current_offset += block->nodes[scan_index].arg; + offsets[zero_count].offset = current_offset; + offsets[zero_count].factor = 0; + zero_count += 1; + scan_index += 2; + } + + if (scan_index < block->count && + block->nodes[scan_index].kind == BF_IR_ADD_PTR) { + current_offset += block->nodes[scan_index].arg; + scan_index += 1; + } + + block->nodes[index].kind = BF_IR_MULTI_ZERO; + block->nodes[index].arg = current_offset; + block->nodes[index].terms = offsets; + block->nodes[index].term_count = zero_count; + + memmove(&block->nodes[index + 1], &block->nodes[scan_index], + (block->count - scan_index) * sizeof(*block->nodes)); + block->count -= (scan_index - index - 1); + } +} + static void bf_opt_block(bf_ir_block *block) { size_t i; @@ -173,6 +429,11 @@ static void bf_opt_block(bf_ir_block *block) { bf_try_recognize_multiply_loop(&block->nodes[i]); } } + + bf_opt_fold_set_const(block); + bf_opt_eliminate_dead_stores(block); + bf_try_fold_multi_zero(block); + bf_opt_eliminate_dead_stores(block); } void bf_opt_program(bf_program *program) { diff --git a/src/bfparser.c b/src/bfparser.c index 4a18a65..c7d440b 100644 --- a/src/bfparser.c +++ b/src/bfparser.c @@ -52,7 +52,8 @@ void bf_ir_block_dispose(bf_ir_block *block) { if (block->nodes[index].kind == BF_IR_LOOP) { bf_ir_block_dispose(&block->nodes[index].body); } - if (block->nodes[index].kind == BF_IR_MULTIPLY_LOOP) { + if (block->nodes[index].kind == BF_IR_MULTIPLY_LOOP || + block->nodes[index].kind == BF_IR_MULTI_ZERO) { free(block->nodes[index].terms); } } @@ -297,6 +298,10 @@ const char *bf_ir_kind_name(bf_ir_kind kind) { return "loop"; case BF_IR_SET_ZERO: return "set_zero"; + case BF_IR_SET_CONST: + return "set_const"; + case BF_IR_MULTI_ZERO: + return "multi_zero"; case BF_IR_SCAN: return "scan"; case BF_IR_MULTIPLY_LOOP: