diff --git a/Makefile b/Makefile index 728547e..0dc19b6 100644 --- a/Makefile +++ b/Makefile @@ -60,11 +60,14 @@ OBJS_COMMON = $(BUILD_DIR)/ir.o $(BUILD_DIR)/ir_strtab.o $(BUILD_DIR)/ir_cfg.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 $(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 +EXAMPLE_EXES = $(EXAMPLES_BUILD_DIR)/mandelbrot \ + $(EXAMPLES_BUILD_DIR)/0001-basic \ + $(EXAMPLES_BUILD_DIR)/0001-while \ + $(EXAMPLES_BUILD_DIR)/0001-pointer \ + $(EXAMPLES_BUILD_DIR)/0001-func \ + $(EXAMPLES_BUILD_DIR)/0005-basic-runner-func -all: $(BUILD_DIR) $(BUILD_DIR)/ir $(BUILD_DIR)/ir_test $(BUILD_DIR)/tester +all: $(BUILD_DIR) $(BUILD_DIR)/ir $(BUILD_DIR)/tester $(BUILD_DIR): @mkdir -p $(BUILD_DIR) @@ -75,13 +78,9 @@ $(EXAMPLES_BUILD_DIR): $(BUILD_DIR)/ir: $(OBJS_COMMON) $(OBJS_IR) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LLVM_LIBS) -lcapstone -$(BUILD_DIR)/ir_test: $(OBJS_COMMON) $(OBJS_IR_TEST) - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -lcapstone - $(OBJS_COMMON): $(SRC_DIR)/ir.h $(SRC_DIR)/ir_private.h $(BUILD_DIR)/ir_main.o: $(SRC_DIR)/ir.h -$(BUILD_DIR)/ir_test.o: $(SRC_DIR)/ir.h $(SRC_DIR)/ir_builder.h $(BUILD_DIR)/ir.o: $(SRC_DIR)/ir_fold.h $(BUILD_DIR)/ir_fold_hash.h $(BUILD_DIR)/ir_ra.o: $(SRC_DIR)/ir_$(DASM_ARCH).h $(BUILD_DIR)/ir_emit.o: $(SRC_DIR)/ir_$(DASM_ARCH).h $(BUILD_DIR)/ir_emit_$(DASM_ARCH).h @@ -103,7 +102,7 @@ $(BUILD_DIR)/minilua: $(SRC_DIR)/dynasm/minilua.c $(BUILD_DIR)/ir_emit_$(DASM_ARCH).h: $(SRC_DIR)/ir_$(DASM_ARCH).dasc $(SRC_DIR)/dynasm/*.lua $(BUILD_DIR)/minilua $(BUILD_DIR)/minilua $(SRC_DIR)/dynasm/dynasm.lua $(DASM_FLAGS) -o $@ $(SRC_DIR)/ir_$(DASM_ARCH).dasc -$(OBJS_COMMON) $(OBJS_IR) $(OBJS_IR_TEST): $(BUILD_DIR)/$(notdir %.o): $(SRC_DIR)/$(notdir %.c) +$(OBJS_COMMON) $(OBJS_IR): $(BUILD_DIR)/$(notdir %.o): $(SRC_DIR)/$(notdir %.c) $(CC) $(CFLAGS) -I$(BUILD_DIR) -o $@ -c $< $(EXAMPLE_EXES): $(EXAMPLES_BUILD_DIR)/$(notdir %): $(EXAMPLES_SRC_DIR)/$(notdir %.c) @@ -113,9 +112,6 @@ $(BUILD_DIR)/tester: $(SRC_DIR)/tools/tester.c $(CC) $(BUILD_CFLAGS) -o $@ $< test: $(BUILD_DIR)/ir $(BUILD_DIR)/tester - $(BUILD_DIR)/ir $(SRC_DIR)/test.ir --dump --save 2>$(BUILD_DIR)/test.log - $(BUILD_DIR)/ir $(SRC_DIR)/test.ir --dot $(BUILD_DIR)/ir.dot - dot -Tpdf $(BUILD_DIR)/ir.dot -o $(BUILD_DIR)/ir.pdf $(BUILD_DIR)/tester --test-cmd $(BUILD_DIR)/ir --target $(TARGET) --default-args "--save" \ --test-extension ".irt" --code-extension ".ir" $(SRC_DIR)/tests @@ -126,10 +122,10 @@ test-ci: $(BUILD_DIR)/ir $(BUILD_DIR)/tester examples: $(OBJS_COMMON) $(EXAMPLES_BUILD_DIR) $(EXAMPLE_EXES) clean: - rm -rf $(BUILD_DIR)/ir $(BUILD_DIR)/ir_test $(BUILD_DIR)/*.o \ + rm -rf $(BUILD_DIR)/ir $(BUILD_DIR)/*.o \ $(BUILD_DIR)/minilua $(BUILD_DIR)/ir_emit_$(DASM_ARCH).h \ $(BUILD_DIR)/ir_fold_hash.h $(BUILD_DIR)/gen_ir_fold_hash \ - $(BUILD_DIR)/ir.dot $(BUILD_DIR)/ir.pdf $(BUILD_DIR)/test.log \ + $(EXAMPLE_EXES) \ $(BUILD_DIR)/tester find $(SRC_DIR)/tests -type f -name '*.diff' -delete find $(SRC_DIR)/tests -type f -name '*.out' -delete diff --git a/README.md b/README.md index 29a0d43..d825420 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,9 @@ the LuaJIT IR designed by Mike Pall [3]. ## IR Generation -TODO... +IR Framework provides a simple IR Builder API ([ir_builder.h](ir_builder.h)). +It's implemented as a number of C preprocessor macros, where each macro just +creates a corresponding IR node and ties it with the sources. ## IR Optimization @@ -56,6 +58,9 @@ IR in a proper (Reverse Post) order, that would emit all Nodes before their first usage. (In case of different order the scope of the folding should be limited). +All the folding rules are written in a declarative style in the single file - +[ir_fold.h](ir_fold.h) + Folding Engine performs Constants Folding, Copy Propagation, Algebraic Simplifications, Algebraic Re-Association and Common Sub-Expression Elimination. The simple and fast declarative implementation is borrowed from @@ -113,13 +118,30 @@ and inserts the necessary spill load/store and SSA deconstruction code. - GDB/JIT interface to allow debugging of JIT-ed code - Linux perf interface to analyze the code performance -## LLVM interopability +## Building and Playing with IR -Under development... +IR Framework is under active development and doesn't provide any stable libraries yet. +In case you like to you IR, it's better to embed the necessary sources into your project +(like [PHP]((https://github.com/php/php-src/tree/master/ext/opcache/jit)) does]. + +However, we provide a simple driver that may be built to run tests and play with IR. + +``` +git clone https://github.com/dstogov/ir.git +cd ir +make +make test +./ir bench/mandelbrit.ir --run +./ir bench/mandelbrit.ir -S +./ir --help +``` ## IR Example -Let's try to generate code for the following function: +The complete described example may be found in [examples/mandelbrot.c](examples/mandelbrot.c)) +and built using ``make examples``. + +It generate the code for the following C function: ```c int32_t mandelbrot(double x, double y) @@ -146,7 +168,7 @@ int32_t mandelbrot(double x, double y) } ``` -This may be done through IR construction API by the following code: +This is done through IR builder API by the following code: ```c void gen_mandelbrot(ir_ctx *ctx) @@ -234,9 +256,16 @@ The textual representation of the IR after system independent optimizations: ``` The visualized graph: -![IR example](example.svg) +![IR example](examples/mandelbrot.svg) -The final generated code: +The graph was generated by the commands: + +``` +./ir bench/mandelbrot.ir --dot mandelbrot.dot +dot -Tsvg mandelbrot.dot -o mandelbrot.svg +``` + +The final generated assembler code: ```asm test: @@ -275,6 +304,36 @@ test: .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40 ``` +## LLVM interopability + +IR is partially compatible with LLVM. It's possible to convert IR +into LLVM and then compile it. + +``` +./ir bench/mandelbrot.ir --emit-llvm mandelbrot.ll +llc mandelbrot.ll +gcc mandelbrot.s +./a.out +``` + +It's also possible to read an LLVM file and convert it to IR, but this +requres LLVM loader and therefore IR should be rebuilt with LLVM support. + +``` +make clean +make HAVE_LLVM=yes +``` + +Now you may compile some C file into LLVM, and then convert it to IR. + +``` +clang -O2 -fno-vectorize -fno-slp-vectorize -S -emit-llvm -o minilua.ll ./dynasm/minilua.c +./ir --llvm-asm minilua.ll --save 2>minilua.ir +./ir minilua.ir --run bench/mandelbrot.lua +``` + +Note that the last comand above compiles and runs the Lua interpreter. + ## PHP JIT based on IR A new experimental JIT for PHP based on this project is developed at [master](https://github.com/php/php-src/tree/master/ext/opcache/jit) php-src branch. diff --git a/ir_test.c b/examples/mandelbrot.c similarity index 99% rename from ir_test.c rename to examples/mandelbrot.c index 27c3168..e6269b9 100644 --- a/ir_test.c +++ b/examples/mandelbrot.c @@ -191,7 +191,7 @@ int main(int argc, char **argv) // ir_dump(&ctx, stderr); ir_save(&ctx, stderr); ir_dump_live_ranges(&ctx, stderr); - f = fopen("ir.dot", "w+"); + f = fopen("mandelbrot.dot", "w+"); ir_dump_dot(&ctx, f); fclose(f); diff --git a/example.svg b/examples/mandelbrot.svg similarity index 100% rename from example.svg rename to examples/mandelbrot.svg diff --git a/ir.h b/ir.h index 518ed32..dd0a6ca 100644 --- a/ir.h +++ b/ir.h @@ -827,7 +827,7 @@ void ir_save(const ir_ctx *ctx, FILE *f); /* IR debug dump API (implementation in ir_dump.c) */ void ir_dump(const ir_ctx *ctx, FILE *f); -void ir_dump_dot(const ir_ctx *ctx, FILE *f); +void ir_dump_dot(const ir_ctx *ctx, const char *name, FILE *f); void ir_dump_use_lists(const ir_ctx *ctx, FILE *f); void ir_dump_cfg(ir_ctx *ctx, FILE *f); void ir_dump_cfg_map(const ir_ctx *ctx, FILE *f); diff --git a/ir_dump.c b/ir_dump.c index 7204e8b..dbc18d8 100644 --- a/ir_dump.c +++ b/ir_dump.c @@ -52,7 +52,7 @@ void ir_dump(const ir_ctx *ctx, FILE *f) } } -void ir_dump_dot(const ir_ctx *ctx, FILE *f) +void ir_dump_dot(const ir_ctx *ctx, const char *name, FILE *f) { int DATA_WEIGHT = 0; int CONTROL_WEIGHT = 5; @@ -61,7 +61,7 @@ void ir_dump_dot(const ir_ctx *ctx, FILE *f) ir_insn *insn; uint32_t flags; - fprintf(f, "digraph ir {\n"); + fprintf(f, "digraph %s {\n", name); fprintf(f, "\trankdir=TB;\n"); for (i = 1 - ctx->consts_count, insn = ctx->ir_base + i; i < IR_UNUSED; i++, insn++) { fprintf(f, "\tc%d [label=\"C%d: CONST %s(", -i, -i, ir_type_name[insn->type]); diff --git a/ir_main.c b/ir_main.c index 1e87143..45bdcf4 100644 --- a/ir_main.c +++ b/ir_main.c @@ -131,7 +131,7 @@ static int _save(ir_ctx *ctx, uint32_t dump, uint32_t pass, FILE *f, const char ir_dump(ctx, f); } if (dump & IR_DUMP_DOT) { - ir_dump_dot(ctx, f); + ir_dump_dot(ctx, func_name, f); } if (dump & IR_DUMP_USE_LISTS) { ir_dump_use_lists(ctx, f); diff --git a/test.ir b/test.ir deleted file mode 100644 index 4ce5c42..0000000 --- a/test.ir +++ /dev/null @@ -1,48 +0,0 @@ -{ - uintptr_t c_1 = 0; - bool c_2 = 0; - bool c_3 = 1; - double c_4 = 0.5; - double c_5 = 0; - int32_t c_6 = 0; - int32_t c_7 = 1; - double c_8 = 16; - int32_t c_9 = 1000; - l_1 = START(l_35); - double d_2 = PARAM(l_1, "x", 0); - double d_3 = PARAM(l_1, "y", 1); - double d_4 = VAR(l_1, "cr"); - double d_5 = SUB(d_3, c_4); - double d_6 = VAR(l_1, "ci"); - double d_7 = VAR(l_1, "zi"); - double d_8 = VAR(l_1, "zr"); - int32_t d_9 = VAR(l_1, "i"); - l_10 = END(l_1); - l_11 = LOOP_BEGIN(l_10, l_37); - double d_12 = PHI(l_11, c_5, d_25); - double d_13 = PHI(l_11, c_5, d_23); - int32_t d_14 = PHI(l_11, c_6, d_15); - int32_t d_15 = ADD(d_14, c_7); - double d_16 = VAR(l_11, "temp"); - double d_17 = MUL(d_13, d_12); - double d_18 = VAR(l_11, "zr2"); - double d_19 = MUL(d_13, d_13); - double d_20 = VAR(l_11, "zi2"); - double d_21 = MUL(d_12, d_12); - double d_22 = SUB(d_19, d_21); - double d_23 = ADD(d_22, d_5); - double d_24 = ADD(d_17, d_17); - double d_25 = ADD(d_24, d_2); - double d_26 = ADD(d_21, d_19); - bool d_27 = GT(d_26, c_8); - l_28 = IF(l_11, d_27); - l_29 = IF_TRUE(l_28); - l_30 = RETURN(l_29, d_15); - l_31 = IF_FALSE(l_28); - bool d_32 = GT(d_15, c_9); - l_33 = IF(l_31, d_32); - l_34 = IF_TRUE(l_33); - l_35 = RETURN(l_34, c_6, l_30); - l_36 = IF_FALSE(l_33); - l_37 = LOOP_END(l_36); -}