dstogov / ir Goto Github PK
View Code? Open in Web Editor NEWLightweight JIT Compilation Framework
License: MIT License
Lightweight JIT Compilation Framework
License: MIT License
The IR function below failed to compile with -O0
option, but okay with -O2
option.
tony@Win11Z790I:~/ir$ cat t.ir
{
uintptr_t c_1 = 0;
bool c_2 = 0;
bool c_3 = 1;
int32_t c_4 = 1;
int32_t c_5 = 2;
l_1 = START(l_20);
bool d_2 = PARAM(l_1, "cond", 0);
bool d_3 = COPY(c_4);
l_4 = END(l_1);
l_5 = LOOP_BEGIN(l_4, l_18);
int32_t d_6 = PHI(l_5, d_3, d_15);
bool d_7 = NE(d_6, c_4);
l_8 = IF(l_5, d_7);
l_9 = IF_TRUE(l_8);
int32_t d_10 = COPY(c_5);
l_11 = END(l_9);
l_12 = IF_FALSE(l_8);
l_13 = END(l_12);
l_14 = MERGE(l_11, l_13);
int32_t d_15 = PHI(l_14, d_10, d_6);
l_16 = IF(l_14, d_2);
l_17 = IF_TRUE(l_16);
l_18 = LOOP_END(l_17);
l_19 = IF_FALSE(l_16);
l_20 = RETURN(l_19, d_15);
}
tony@Win11Z790I:~/ir$ ~/ir/ir t.ir -O0 --save
ir_base[6].ops[2] (3) type is incompatible with result type (1 != 10)
ir: ir_check.c:347: ir_check: Assertion `ok' failed.
Aborted
tony@Win11Z790I:~/ir$ ~/ir/ir t.ir -O2 --save
{
uintptr_t c_1 = 0;
bool c_2 = 0;
bool c_3 = 1;
int32_t c_4 = 1;
l_1 = START(l_9);
bool d_2 = PARAM(l_1, "cond", 0);
l_3 = END(l_1);
l_4 = LOOP_BEGIN(l_3, l_7);
l_5 = IF(l_4, d_2);
l_6 = IF_TRUE(l_5);
l_7 = LOOP_END(l_6);
l_8 = IF_FALSE(l_5);
l_9 = RETURN(l_8, c_4);
}
This is the good -O2
dump --- sorry for paste wrong program in first report.
tony@Win11Z790I:~/ir$ cat t.ir
{
uintptr_t c_1 = 0;
bool c_2 = 0;
bool c_3 = 1;
int32_t c_4 = 1;
l_1 = START(l_9);
bool d_2 = PARAM(l_1, "cond", 0);
l_3 = END(l_1);
l_4 = LOOP_BEGIN(l_3, l_7);
l_5 = IF(l_4, d_2);
l_6 = IF_TRUE(l_5);
l_7 = LOOP_END(l_6);
l_8 = IF_FALSE(l_5);
l_9 = RETURN(l_8, c_4);
}
tony@Win11Z790I:~/ir$ ./ir -O0 t.ir --save
ir_base[6].ops[2] (3) type is incompatible with result type (1 != 10)
ir: ir_check.c:347: ir_check: Assertion `ok' failed.
Aborted
tony@Win11Z790I:~/ir$ ./ir -O2 t.ir --save
{
uintptr_t c_1 = 0;
bool c_2 = 0;
bool c_3 = 1;
int32_t c_4 = 1;
l_1 = START(l_9);
bool d_2 = PARAM(l_1, "cond", 0);
l_3 = END(l_1);
l_4 = LOOP_BEGIN(l_3, l_7);
l_5 = IF(l_4, d_2);
l_6 = IF_TRUE(l_5);
l_7 = LOOP_END(l_6);
l_8 = IF_FALSE(l_5);
l_9 = RETURN(l_8, c_4);
}
The idea behind this is to create a collection of a code snippets that can be used for both education purposes and later for embedding into documentation.
Implement a Rust crate to provide the IR functionality for the Rust based development. The documentation is contained under:
https://docs.rust-embedded.org/book/interoperability/c-with-rust.html
The availability of the console tool can be not guaranteed. A diff implementation can be put into the test tool directly, thus eliminating the dependency on the external tooling. It is thinkable to implement a simplified diff algorithm or include a class with a compatible license, that implements the diff functionality.
I am still quite native to IR construction APIs and programming, so my IR function to implement a for loop could be fundamentally wrong! Please advise.
void gen_myfunc(ir_ctx *ctx)
{
ir_START();
ir_ref cnt = ir_PARAM(IR_I32, "loop_count", 1);
ir_ref init_idx = ir_CONST_I32(1);
ir_ref init_total = ir_CONST_I32(0);
/* loop begin */
ir_ref loop = ir_LOOP_BEGIN(ir_END());
ir_ref phi_idx = ir_PHI_2(init_idx, IR_UNUSED);
ir_ref phi_total = ir_PHI_2(init_total, IR_UNUSED);
ir_ref if1 = ir_IF(ir_LE(phi_idx, cnt));
ir_IF_TRUE(if1);
ir_ref phi_idx_2 = ir_ADD_I32(phi_idx, ir_CONST_I32(1));
ir_ref ret = ir_ADD_I32(phi_total, phi_idx);
ir_ref loop_end = ir_LOOP_END(); /* loop end */
ir_IF_FALSE(if1); /* loop exit */
ir_RETURN(ret);
ir_MERGE_SET_OP(loop, 2, loop_end);
ir_PHI_SET_OP(phi_idx, 2, phi_idx_2);
ir_PHI_SET_OP(phi_total, 2, ret);
}
After ir_jit_compile()
with O2, ir_ref ret = ir_ADD_I32(phi_total, phi_idx);
unexpectedly (at least to me) moved between LE
and IF
(expect to be placed after IF_TRUE
: do addition only when idx<=loop_count
); hence, do one more addition than expected.
Based on ir.h
, ir_test.h
and ir_test.c
example implementation, develop a C++ wrapper for the IR functionality. Yet it is to be started in a simple manner to make the C++ code as simple as possible, while keeping in mind to prospectively modernize for the C++11 and onwards.
Make tests executing concurrently can speed-up the test runs and thus save developer and pipeline time. This will especially become relevant with the growing number of tests. An implementation might be simplest done through the utilization of std::async
APIs but needs to be thought well so then possible exceptions can be caught in a safe way.
When running ir-test.exe
with --show-diff
on Windows, 'no diff error' happens.
Let's use 'fc.exe' instead.
# 001.irt is specially changed to make test FAIL.
PS C:\Users\Tony\ir> .\win32\build_x86-windows\ir-test.exe --show-diff .\tests\001.irt
FAIL: 001: add function [.\tests\001.irt]
'diff' is not recognized as an internal or external command,
operable program or batch file.
Certain features can be enabled automatically, based on the data advertised through CPUID. There needs to be a framework to
IR
to self modify the generated code depending on processor featuresThe feature enablement can be done an the library level or provided to the consumer for further decision.
Hi @dstogov,
I noticed that you had fixed the ir_add_veneer()
assertion failure in 1671b3d. After your fix, there is no more assertion failure, but below IR tests still fail on MacOS/AArch64.
001: Argument Passing [./tests/debug.aarch64/args_001.irt]
002: Argument Passing [./tests/debug.aarch64/args_002.irt]
Simple CALL -O0 [./tests/debug.aarch64/call-O0.irt]
Simple CALL [./tests/debug.aarch64/call.irt]
CALL with parallel argument passing [./tests/debug.aarch64/call2.irt]
Simple CALL with ALLOCA [./tests/debug.aarch64/call_alloca.irt]
Simple CALL with VADDR [./tests/debug.aarch64/call_vaddr.irt]
003: Parameter Loading and argument passing [./tests/debug.aarch64/params_003.irt]
Fib [./tests/debug.aarch64/regset-fib.irt]
Fib2 [./tests/debug.aarch64/regset-fib2.irt]
FibI [./tests/debug.aarch64/regset-fibi.irt]
Simple CALL [./tests/debug.aarch64/tailcall_001.irt]
FibI [./tests/fibi_min.irt]
CTLZ 001: [./tests/run/ctlz_001.irt]
CTPOP 001: [./tests/run/ctpop_001.irt]
CTTZ 001: [./tests/run/cttz_001.irt]
Floating Point number comparison (001: CMP edge cases) [./tests/run/fcmp_001.irt]
Floating Point number comparison (002: CMP+COND edge cases) [./tests/run/fcmp_002.irt]
VA_ARG 001: va_arg(int) [./tests/run/vaarg_001.irt]
VA_ARG 002: va_arg(double) [./tests/run/vaarg_002.irt]
VA_ARG 003: va_arg(float) [./tests/run/vaarg_003.irt]
VA_ARG 004: va_arg() expanded on AArch64 [./tests/run/vaarg_004_aarch64.irt]
The failures are caused by the difference between .exp
and .out
, for example ./tests/debug.aarch64/call.irt
call.out
main:
stp x29, x30, [sp, #-0x10]!
mov x29, sp
adr x0, .L1
movz w1, #0x2a
movz x17, #0x9f54
movk x17, #0x8d06, lsl #16
movk x17, #0x1, lsl #32
blr x17
ldp x29, x30, [sp], #0x10
ret
.rodata
.L1:
.db 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x25, 0x64, 0x21, 0x0a, 0x00, 0x00
hello 1799882064!
exit code = 18
call.exp
main:
stp x29, x30, [sp, #-0x10]!
mov x29, sp
adr x0, .L1
movz w1, #0x2a
bl printf
ldp x29, x30, [sp], #0x10
ret
.rodata
.db 0x1f, 0x20, 0x03, 0xd5
.L1:
.db 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x25, 0x64, 0x21, 0x0a, 0x00, 0x00
hello 42!
exit code = 10
It looks that the issues are still related to calling convention differences between Linux/AArch64 and MacOS/AArch64. Perhaps you are still working on some complete fixes for this. But I'd like to add this issue here to track this.
The public library API needs to be documented for the end consumer. It is thinkable to approach this using code inline documentation and then generate a readable format using tools like doxygen or similar. The exact method on how to implement this is to be evaluated and discussed closer, to achieve the best result.
Currently there's a simple parsing there, by looping the argv
array and checking for a present string. There needs to be a full featured console argument parsing for this tool.
I wrote a simple C function (see gen_ir() section below) using IR construction APIs (I'm not sure my programming is correct) and I can dump out this function by 'ir_save()' but ir
will fail with -O0
with the error message different with #32 and #33.
-O2
will make if-else
disappear after optimization.
tony@Win11Z790I:~/ir/examples/c$ ~/ir/ir -O0 002_if.ir -S
ir: ./ir_x86.dasc:3653: ir_emit_cmp_int_common: Assertion `0' failed.
Aborted
{
uintptr_t c_1 = 0;
bool c_2 = 0;
bool c_3 = 1;
int32_t c_4 = 1;
int32_t c_5 = 4;
l_1 = START(l_9);
bool d_2 = LT(c_4, c_5);
l_3 = IF(l_1, d_2);
l_4 = IF_TRUE(l_3);
l_5 = END(l_4);
l_6 = IF_FALSE(l_3);
l_7 = END(l_6);
l_8 = MERGE(l_5, l_7);
l_9 = RETURN(l_8, c_5);
}
/* ctx: used by IR construction API defined in ir_builder.h
* Note: must be named as 'ctx'
*/
void gen_ir(ir_ctx* ctx)
{
ir_ref c_4 = ir_CONST_I32(1);
ir_ref c_5 = ir_CONST_I32(4);
ir_START(); /* Mark function start */
ir_ref d_10 = ir_LT(c_4, c_5);
ir_ref end_inputs = IR_UNUSED;
ir_ref if1 = ir_IF(d_10);
ir_IF_TRUE(if1);
ir_END_list(end_inputs);
ir_IF_FALSE(if1);
ir_END_list(end_inputs);
ir_MERGE_list(end_inputs);
ir_RETURN(c_5);
}
/*
This example shows a basic program framework of how to use IR.
*/
/* IR public APIs and target platform*/
#include "ir.h"
#include "ir_builder.h"
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
# include "ir_x86.h"
#endif /* IR_TARGET_X86 || IR_TARGET_X64 */
#include <stdio.h>
/* ctx: used by IR construction API defined in ir_builder.h
* Note: must be named as 'ctx'
*/
void gen_ir(ir_ctx* ctx)
{
ir_ref c_4 = ir_CONST_I32(1);
ir_ref c_5 = ir_CONST_I32(4);
ir_START(); /* Mark function start */
ir_ref d_10 = ir_LT(c_4, c_5);
ir_ref end_inputs = IR_UNUSED;
ir_ref if1 = ir_IF(d_10);
ir_IF_TRUE(if1);
ir_END_list(end_inputs);
ir_IF_FALSE(if1);
ir_END_list(end_inputs);
ir_MERGE_list(end_inputs);
ir_RETURN(c_5);
}
int ir_compile_and_run_func(ir_ctx *ctx, int opt_level)
{
gen_ir(ctx);
puts("Before ...");
ir_save(ctx, stderr);
FILE* f = fopen("002_if.dot", "w+");
ir_dump_dot(ctx, f);
fclose(f);
if (opt_level > 0 || (ctx->flags & (IR_GEN_NATIVE|IR_GEN_C))) {
ir_build_def_use_lists(ctx);
}
ir_check(ctx);
/* Global Optimization */
if (opt_level > 1) {
ir_sccp(ctx);
}
if (opt_level > 0 || (ctx->flags & (IR_GEN_NATIVE|IR_GEN_C))) {
ir_build_cfg(ctx);
}
/* Schedule */
if (opt_level > 0) {
ir_build_dominators_tree(ctx);
ir_find_loops(ctx);
ir_gcm(ctx);
ir_schedule(ctx);
}
if (ctx->flags & IR_GEN_NATIVE) {
ir_match(ctx);
}
if (opt_level > 0) {
ir_assign_virtual_registers(ctx);
ir_compute_live_ranges(ctx);
ir_coalesce(ctx);
if (ctx->flags & IR_GEN_NATIVE) {
ir_reg_alloc(ctx);
}
ir_schedule_blocks(ctx);
} else if (ctx->flags & (IR_GEN_NATIVE|IR_GEN_C)) {
ir_assign_virtual_registers(ctx);
ir_compute_dessa_moves(ctx);
}
ir_check(ctx);
puts("After ...\n");
ir_save(ctx, stderr);
/* generate native code and return function entry pointer */
typedef void (*ir_func_t)(void);
size_t size;
ir_func_t entry = (ir_func_t) ir_emit_code(ctx, &size);
if(entry) {
entry();
}
return 1;
}
int main(void)
{
ir_ctx ctx;
/* generate a function */
uint32_t flags = IR_FUNCTION | IR_GEN_NATIVE | IR_GEN_C;
ir_consistency_check();
/* 256: constants limit; 1024: instructions limit */
ir_init(&ctx, flags, 256, 1024);
ir_compile_and_run_func(&ctx, 2);
ir_free(&ctx);
return 0;
}
This would include choosing an appropriate test framework, generating the test data and adding the test cases.
tony@Win11Z790I:~/ir/tests/debug$ cat ./dce_001.ir
{
uintptr_t c_1 = 0;
bool c_2 = 0;
bool c_3 = 1;
int32_t c_4 = 10;
int32_t c_5 = 2;
int32_t c_6 = 1;
int32_t c_7 = 0;
l_1 = START(l_14);
int32_t d_2 = PARAM(l_1, "x", 0);
l_3 = END(l_1);
l_4 = LOOP_BEGIN(l_3, l_12);
int32_t d_5 = PHI(l_4, c_7, d_7);
int32_t d_6 = ADD(d_2, c_6);
int32_t d_7 = ADD(d_6, d_5);
int32_t d_8 = MUL(d_7, c_5);
int32_t xxx = DIV(d_8, c_4);
bool d_9 = LT(d_7, c_4);
l_10 = IF(l_4, d_9);
l_11 = IF_TRUE(l_10);
l_12 = LOOP_END(l_11);
l_13 = IF_FALSE(l_10);
l_14 = RETURN(l_13, d_8);
}
# failed with -O0 option
tony@Win11Z790I:~/ir/tests/debug$ ~/ir/ir -O0 ./dce_001.ir -S
ir: ./ir_x86.dasc:3173: ir_emit_mul_div_mod: Assertion `def_reg != -1' failed.
Aborted
# succeeded with -O2 option
tony@Win11Z790I:~/ir/tests/debug$ ~/ir/ir -O2 ./dce_001.ir -S
test:
leal 1(%rdi), %eax
xorl %ecx, %ecx
.L1:
addl %eax, %ecx
cmpl $0xa, %ecx
jl .L1
leal (%rcx, %rcx), %eax
retq
A simple switch-case IR function and ir_emit_c() dumping is missing BB2
(or maybe BB1
) .
int32_t test(int32_t num)
{
int32_t d_1 = num;
int32_t d_2;
int32_t d_3;
int32_t d_4;
int32_t d_5;
d_2 = d_1 % 3;
d_3 = d_1 * 3;
d_4 = d_1 + 1;
d_5 = d_1 - 2;
switch (d_2) {
case 0: goto bb2;
case 1: goto bb4;
case 2: goto bb3;
default: goto bb3;
}
d_5 = d_3;
bb3:
return d_5;
bb4:
d_5 = d_4;
goto bb3;
}
void gen_myfunc(ir_ctx *ctx)
{
ir_ref divisor = ir_CONST_I32(3);
ir_ref rem0 = ir_CONST_I32(0);
ir_ref rem1 = ir_CONST_I32(1);
ir_ref rem2 = ir_CONST_I32(2);
ir_START();
ir_ref num = ir_PARAM(IR_I32, "num", 1);
ir_ref rets[4], ends[4];
ir_ref remainder = ir_MOD_I32(num, divisor);
ir_ref s = ir_SWITCH(remainder);
ir_CASE_VAL(s, rem0);
rets[0] = ir_MUL_I32(num, ir_CONST_I32(3));
ends[0] = ir_END();
ir_CASE_VAL(s, rem1);
rets[1] = ir_ADD_I32(num, ir_CONST_I32(1));
ends[1] = ir_END();
ir_CASE_VAL(s, rem2);
rets[2] = ir_SUB_I32(num, ir_CONST_I32(2));
ends[2] = ir_END();
ir_CASE_DEFAULT(s);
rets[3] = rets[2];
ends[3] = ir_END();
ir_MERGE_N(4, ends);
ir_ref ret = ir_PHI_N(4, rets);
ir_RETURN(ret);
}
int32_t myfunc(int32_t num) {
int remainder = num % 3;
switch (remainder)
{
case 0: num = num * 3; break;
case 1: num = num + 1; break;
case 2: // fall through
default: num = num - 2; break;
}
return num;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.