Introduce IR Builder API

This commit is contained in:
Dmitry Stogov 2023-02-10 13:34:46 +03:00
parent 9b6b6996c4
commit 6b8a33d726
5 changed files with 1529 additions and 174 deletions

156
README.md
View File

@ -123,7 +123,7 @@ int32_t mandelbrot(double x, double y)
int i = 0;
while(1) {
i ++;
i++;
double temp = zr * zi;
double zr2 = zr * zr;
double zi2 = zi * zi;
@ -143,71 +143,41 @@ This may be done through IR construction API by the following code:
```c
void gen_mandelbrot(ir_ctx *ctx)
{
ir_ref start = ir_emit0(ctx, IR_START);
ir_ref ret;
ir_START();
ir_ref x = ir_PARAM(IR_DOUBLE, "x", 1);
ir_ref y = ir_PARAM(IR_DOUBLE, "y", 2);
ir_ref cr = ir_SUB_D(y, ir_CONST_DOUBLE(0.5));
ir_ref ci = ir_COPY_D(x);
ir_ref zi = ir_COPY_D(ir_CONST_DOUBLE(0.0));
ir_ref zr = ir_COPY_D(ir_CONST_DOUBLE(0.0));
ir_ref i = ir_COPY_D(ir_CONST_I32(0));
ir_ref x_1 = ir_param(ctx, IR_DOUBLE, start, "x", 0);
ir_ref y_1 = ir_param(ctx, IR_DOUBLE, start, "y", 1);
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 cr = ir_var(ctx, IR_DOUBLE, start, "cr");
ir_ref cr_1 = ir_fold2(ctx, IR_OPT(IR_SUB, IR_DOUBLE), y_1,
ir_const_double(ctx, 0.5));
ir_ref ci = ir_var(ctx, IR_DOUBLE, start, "ci");
ir_ref zi = ir_var(ctx, IR_DOUBLE, start, "zi");
ir_ref zr = ir_var(ctx, IR_DOUBLE, start, "zr");
ir_ref i = ir_var(ctx, IR_I32, start, "i");
ir_ref i_2 = ir_ADD_I32(i_1, ir_CONST_I32(1));
ir_ref temp = ir_MUL_D(zr_1, zi_1);
ir_ref zr2 = ir_MUL_D(zr_1, zr_1);
ir_ref zi2 = ir_MUL_D(zi_1, zi_1);
ir_ref zr_2 = ir_ADD_D(ir_SUB_D(zr2, zi2), cr);
ir_ref zi_2 = ir_ADD_D(ir_ADD_D(temp, temp), ci);
ir_ref if_1 = ir_IF(ir_GT(ir_ADD_D(zi2, zr2), ir_CONST_DOUBLE(16.0)));
ir_IF_TRUE(if_1);
ir_RETURN(i_2);
ir_IF_FALSE(if_1);
ir_ref if_2 = ir_IF(ir_GT(i_2, ir_CONST_I32(1000)));
ir_IF_TRUE(if_2);
ir_RETURN(ir_CONST_I32(0));
ir_IF_FALSE(if_2);
ir_ref loop_end = ir_LOOP_END(loop);
ir_ref e_1 = ir_emit1(ctx, IR_END, start);
ir_ref l_1 = ir_emit1(ctx, IR_LOOP_BEGIN, e_1);
ir_ref zi_1 = ir_emit2(ctx, IR_OPT(IR_PHI, IR_DOUBLE), l_1,
ir_const_double(ctx, 0.0));
ir_ref zr_1 = ir_emit2(ctx, IR_OPT(IR_PHI, IR_DOUBLE), l_1,
ir_const_double(ctx, 0.0));
ir_ref i_1 = ir_emit2(ctx, IR_OPT(IR_PHI, IR_I32), l_1,
ir_const_i32(ctx, 0));
ir_ref i_2 = ir_emit2(ctx, IR_OPT(IR_ADD, IR_I32), i_1,
ir_const_i32(ctx, 1));
ir_ref temp = ir_var(ctx, IR_DOUBLE, l_1, "temp");
ir_ref temp_1 = ir_fold2(ctx, IR_OPT(IR_MUL, IR_DOUBLE), zr_1, zi_1);
ir_ref zr2 = ir_var(ctx, IR_DOUBLE, l_1, "zr2");
ir_ref zr2_1 = ir_fold2(ctx, IR_OPT(IR_MUL, IR_DOUBLE), zr_1, zr_1);
ir_ref zi2 = ir_var(ctx, IR_DOUBLE, l_1, "zi2");
ir_ref zi2_1 = ir_fold2(ctx, IR_OPT(IR_MUL, IR_DOUBLE), zi_1, zi_1);
ir_ref zr_2 = ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE),
ir_fold2(ctx, IR_OPT(IR_SUB, IR_DOUBLE), zr2_1, zi2_1),
cr_1);
ir_ref zi_2 = ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE),
ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE), temp_1, temp_1),
x_1);
ir_ref if_1 = ir_emit2(ctx, IR_IF, l_1,
ir_fold2(ctx, IR_OPT(IR_GT, IR_BOOL),
ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE), zi2_1, zr2_1),
ir_const_double(ctx, 16.0)));
ir_ref r_1 = ir_emit1(ctx, IR_IF_TRUE, if_1);
ret = ir_emit2(ctx, IR_OPT(IR_RETURN, IR_I32), r_1, i_2);
ir_ref r_2 = ir_emit1(ctx, IR_IF_FALSE, if_1);
ir_ref if_2 = ir_emit2(ctx, IR_IF, r_2,
ir_fold2(ctx, IR_OPT(IR_GT, IR_BOOL), i_2, ir_const_i32(ctx, 1000)));
ir_ref r_3 = ir_emit1(ctx, IR_IF_TRUE, if_2);
ret = ir_emit3(ctx, IR_OPT(IR_RETURN, IR_I32), r_3, ir_const_i32(ctx, 0), ret);
ir_ref r_4 = ir_emit1(ctx, IR_IF_FALSE, if_2);
ir_ref l_2 = ir_emit2(ctx, IR_LOOP_END, r_4, l_1);
ir_set_op2(ctx, l_1, l_2);
/* close loop */
ir_set_op2(ctx, loop, loop_end);
ir_set_op3(ctx, zi_1, zi_2);
ir_set_op3(ctx, zr_1, zr_2);
ir_set_op3(ctx, i_1, i_2);
ir_set_op1(ctx, start, ret);
}
```
The textual representation of the IR after system independent optimizations:
@ -223,43 +193,35 @@ The textual representation of the IR after system independent optimizations:
int32_t c_7 = 1;
double c_8 = 16;
int32_t c_9 = 1000;
l_1 = START(l_30);
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_36);
double d_13 = PHI(l_11, c_5, d_34);
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 = VAR(l_11, "zr2");
double d_18 = MUL(d_13, d_13);
double d_19 = VAR(l_11, "zi2");
double d_20 = MUL(d_12, d_12);
double d_21 = ADD(d_20, d_18);
bool d_22 = GT(d_21, c_8);
l_23 = IF(l_11, d_22);
l_24 = IF_TRUE(l_23);
l_25 = RETURN(l_24, d_15);
l_26 = IF_FALSE(l_23);
bool d_27 = GT(d_15, c_9);
l_28 = IF(l_26, d_27);
l_29 = IF_TRUE(l_28);
l_30 = RETURN(l_29, c_6, l_25);
l_31 = IF_FALSE(l_28);
double d_32 = MUL(d_12, d_13);
double d_33 = SUB(d_18, d_20);
double d_34 = ADD(d_33, d_5);
double d_35 = ADD(d_32, d_32);
double d_36 = ADD(d_35, d_2);
l_37 = LOOP_END(l_31, l_11);
l_1 = START(l_22);
double d_2 = PARAM(l_1, "x", 1);
double d_3 = PARAM(l_1, "y", 2);
double d_4 = SUB(d_3, c_4);
l_5 = END(l_1);
l_6 = LOOP_BEGIN(l_5, l_29);
double d_7 = PHI(l_6, c_5, d_28);
double d_8 = PHI(l_6, c_5, d_26);
int32_t d_9 = PHI(l_6, c_6, d_10);
int32_t d_10 = ADD(d_9, c_7);
double d_11 = MUL(d_8, d_8);
double d_12 = MUL(d_7, d_7);
double d_13 = ADD(d_12, d_11);
bool d_14 = GT(d_13, c_8);
l_15 = IF(l_6, d_14);
l_16 = IF_TRUE(l_15);
l_17 = RETURN(l_16, d_10);
l_18 = IF_FALSE(l_15);
bool d_19 = GT(d_10, c_9);
l_20 = IF(l_18, d_19);
l_21 = IF_TRUE(l_20);
l_22 = RETURN(l_21, c_6, l_17);
l_23 = IF_FALSE(l_20);
double d_24 = MUL(d_7, d_8);
double d_25 = SUB(d_11, d_12);
double d_26 = ADD(d_25, d_4);
double d_27 = ADD(d_24, d_24);
double d_28 = ADD(d_27, d_2);
l_29 = LOOP_END(l_23, l_6);
}
```
The visualized graph:

814
ir.c
View File

@ -317,6 +317,7 @@ void ir_init(ir_ctx *ctx, ir_ref consts_limit, ir_ref insns_limit)
ctx->regs = NULL;
ctx->prev_ref = NULL;
ctx->data = NULL;
ctx->snapshot_create = NULL;
ctx->code_buffer = NULL;
ctx->code_buffer_size = 0;
@ -1240,3 +1241,816 @@ int ir_mem_flush(void *ptr, size_t size)
#endif
return 1;
}
/* Alias Analyses */
typedef enum _ir_alias {
IR_MAY_ALIAS = -1,
IR_NO_ALIAS = 0,
IR_MUST_ALIAS = 1,
} ir_alias;
#if 0
static ir_alias ir_check_aliasing(ir_ctx *ctx, ir_ref addr1, ir_ref addr2)
{
ir_insn *insn1, *insn2;
if (addr1 == addr2) {
return IR_MUST_ALIAS;
}
insn1 = &ctx->ir_base[addr1];
insn2 = &ctx->ir_base[addr2];
if (insn1->op == IR_ADD && IR_IS_CONST_REF(insn1->op2)) {
if (insn1->op1 == addr2) {
uintptr_t offset1 = ctx->ir_base[insn1->op2].val.u64;
return (offset1 != 0) ? IR_MUST_ALIAS : IR_NO_ALIAS;
} else if (insn2->op == IR_ADD && IR_IS_CONST_REF(insn1->op2) && insn1->op1 == insn2->op1) {
if (insn1->op2 == insn2->op2) {
return IR_MUST_ALIAS;
} else if (IR_IS_CONST_REF(insn1->op2) && IR_IS_CONST_REF(insn2->op2)) {
uintptr_t offset1 = ctx->ir_base[insn1->op2].val.u64;
uintptr_t offset2 = ctx->ir_base[insn2->op2].val.u64;
return (offset1 == offset2) ? IR_MUST_ALIAS : IR_NO_ALIAS;
}
}
} else if (insn2->op == IR_ADD && IR_IS_CONST_REF(insn2->op2)) {
if (insn2->op1 == addr1) {
uintptr_t offset2 = ctx->ir_base[insn2->op2].val.u64;
return (offset2 != 0) ? IR_MUST_ALIAS : IR_NO_ALIAS;
}
}
return IR_MAY_ALIAS;
}
#endif
static ir_alias ir_check_partial_aliasing(ir_ctx *ctx, ir_ref addr1, ir_ref addr2, ir_type type1, ir_type type2)
{
ir_insn *insn1, *insn2;
/* this must be already check */
IR_ASSERT(addr1 != addr2);
insn1 = &ctx->ir_base[addr1];
insn2 = &ctx->ir_base[addr2];
if (insn1->op == IR_ADD && IR_IS_CONST_REF(insn1->op2)) {
if (insn1->op1 == addr2) {
uintptr_t offset1 = ctx->ir_base[insn1->op2].val.u64;
uintptr_t size2 = ir_type_size[type2];
return (offset1 < size2) ? IR_MUST_ALIAS : IR_NO_ALIAS;
} else if (insn2->op == IR_ADD && IR_IS_CONST_REF(insn1->op2) && insn1->op1 == insn2->op1) {
if (insn1->op2 == insn2->op2) {
return IR_MUST_ALIAS;
} else if (IR_IS_CONST_REF(insn1->op2) && IR_IS_CONST_REF(insn2->op2)) {
uintptr_t offset1 = ctx->ir_base[insn1->op2].val.u64;
uintptr_t offset2 = ctx->ir_base[insn2->op2].val.u64;
if (offset1 == offset2) {
return IR_MUST_ALIAS;
} else if (type1 == type2) {
return IR_NO_ALIAS;
} else {
/* check for partail intersection */
uintptr_t size1 = ir_type_size[type1];
uintptr_t size2 = ir_type_size[type2];
if (offset1 > offset2) {
return offset1 < offset2 + size2 ? IR_MUST_ALIAS : IR_NO_ALIAS;
} else {
return offset2 < offset1 + size1 ? IR_MUST_ALIAS : IR_NO_ALIAS;
}
}
}
}
} else if (insn2->op == IR_ADD && IR_IS_CONST_REF(insn2->op2)) {
if (insn2->op1 == addr1) {
uintptr_t offset2 = ctx->ir_base[insn2->op2].val.u64;
uintptr_t size1 = ir_type_size[type1];
return (offset2 < size1) ? IR_MUST_ALIAS : IR_NO_ALIAS;
}
}
return IR_MAY_ALIAS;
}
static ir_ref ir_find_aliasing_load(ir_ctx *ctx, ir_ref ref, ir_type type, ir_ref addr)
{
ir_insn *insn;
uint32_t modified_regset = 0;
while (1) {
insn = &ctx->ir_base[ref];
if (insn->op == IR_LOAD) {
if (insn->type == type && insn->op2 == addr) {
return ref; /* load forwarding (L2L) */
}
} else if (insn->op == IR_STORE) {
ir_type type2 = ctx->ir_base[insn->op3].type;
if (insn->op2 == addr) {
if (type2 == type) {
ref = insn->op3;
insn = &ctx->ir_base[ref];
if (insn->op == IR_RLOAD && (modified_regset & (1 << insn->op2))) {
/* anti-dependency */
return IR_UNUSED;
}
return ref; /* store forwarding (S2L) */
} else if (IR_IS_TYPE_INT(type) && ir_type_size[type2] > ir_type_size[type]) {
return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), insn->op3); /* partial store forwarding (S2L) */
} else {
return IR_UNUSED;
}
} else if (ir_check_partial_aliasing(ctx, addr, insn->op2, type, type2) != IR_NO_ALIAS) {
return IR_UNUSED;
}
} else if (insn->op == IR_RSTORE) {
modified_regset |= (1 << insn->op3);
} else if (insn->op == IR_START
|| insn->op == IR_BEGIN
|| insn->op == IR_IF_TRUE
|| insn->op == IR_IF_FALSE
|| insn->op == IR_CASE_VAL
|| insn->op == IR_CASE_DEFAULT
|| insn->op == IR_MERGE
|| insn->op == IR_LOOP_BEGIN
|| insn->op == IR_ENTRY
|| insn->op == IR_CALL) {
return IR_UNUSED;
}
ref = insn->op1;
}
}
/* IR Construction API */
ir_ref _ir_PARAM(ir_ctx *ctx, ir_type type, const char* name, ir_ref num)
{
IR_ASSERT(ctx->control);
IR_ASSERT(ctx->ir_base[ctx->control].op == IR_START);
IR_ASSERT(ctx->insns_count == num + 1);
return ir_param(ctx, type, ctx->control, name, num);
}
ir_ref _ir_VAR(ir_ctx *ctx, ir_type type, const char* name)
{
IR_ASSERT(ctx->control);
IR_ASSERT(IR_IS_BB_START(ctx->ir_base[ctx->control].op));
return ir_var(ctx, type, ctx->control, name);
}
ir_ref _ir_PHI_2(ir_ctx *ctx, 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);
return ir_emit3(ctx, IR_OPT(IR_PHI, type), ctx->control, src1, src2);
}
ir_ref _ir_PHI_N(ir_ctx *ctx, ir_ref n, ir_ref *inputs)
{
IR_ASSERT(ctx->control);
IR_ASSERT(n > 0);
if (n == 1) {
return inputs[0];
} else {
ir_ref i;
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 (i == n) {
/* all the same */
return ref;
}
ref = ir_emit_N(ctx, IR_OPT(IR_PHI, ctx->ir_base[inputs[0]].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]);
}
return ref;
}
}
void _ir_START(ir_ctx *ctx)
{
IR_ASSERT(!ctx->control);
IR_ASSERT(ctx->insns_count == 1);
ctx->control = ir_emit0(ctx, IR_START);
}
void _ir_ENTRY(ir_ctx *ctx, ir_ref num)
{
IR_ASSERT(!ctx->control);
ctx->control = ir_emit2(ctx, IR_ENTRY, num, ctx->ir_base[1].op2);
ctx->ir_base[1].op2 = ctx->control;
}
void _ir_BEGIN(ir_ctx *ctx, ir_ref src)
{
// IR_ASSERT(!ctx->control);
if (src
&& src + 1 == ctx->insns_count
&& ctx->ir_base[src].op == IR_END) {
/* merge with the last END */
ctx->control = ctx->ir_base[src].op1;
ctx->insns_count--;
} else {
ctx->control = ir_emit1(ctx, IR_BEGIN, src);
}
}
ir_ref _ir_IF(ir_ctx *ctx, ir_ref condition)
{
ir_ref if_ref;
IR_ASSERT(ctx->control);
if_ref = ir_emit2(ctx, IR_IF, ctx->control, condition);
ctx->control = IR_UNUSED;
return if_ref;
}
void _ir_IF_TRUE(ir_ctx *ctx, ir_ref if_ref)
{
// IR_ASSERT(!ctx->control);
IR_ASSERT(if_ref);
IR_ASSERT(ctx->ir_base[if_ref].op == IR_IF);
ctx->control = ir_emit1(ctx, IR_IF_TRUE, if_ref);
}
void _ir_IF_TRUE_cold(ir_ctx *ctx, ir_ref if_ref)
{
// IR_ASSERT(!ctx->control);
IR_ASSERT(if_ref);
IR_ASSERT(ctx->ir_base[if_ref].op == IR_IF);
/* op2 is used as an indicator of low-probability branch */
ctx->control = ir_emit2(ctx, IR_IF_TRUE, if_ref, 1);
}
void _ir_IF_FALSE(ir_ctx *ctx, ir_ref if_ref)
{
// IR_ASSERT(!ctx->control);
IR_ASSERT(if_ref);
IR_ASSERT(ctx->ir_base[if_ref].op == IR_IF);
ctx->control = ir_emit1(ctx, IR_IF_FALSE, if_ref);
}
void _ir_IF_FALSE_cold(ir_ctx *ctx, ir_ref if_ref)
{
// IR_ASSERT(!ctx->control);
IR_ASSERT(if_ref);
IR_ASSERT(ctx->ir_base[if_ref].op == IR_IF);
/* op2 is used as an indicator of low-probability branch */
ctx->control = ir_emit2(ctx, IR_IF_FALSE, if_ref, 1);
}
ir_ref _ir_END(ir_ctx *ctx)
{
ir_ref ref;
IR_ASSERT(ctx->control);
ref = ir_emit1(ctx, IR_END, ctx->control);
ctx->control = IR_UNUSED;
return ref;
}
void _ir_MERGE_2(ir_ctx *ctx, ir_ref src1, ir_ref src2)
{
// IR_ASSERT(!ctx->control);
ctx->control = ir_emit2(ctx, IR_MERGE, src1, src2);
}
void _ir_MERGE_N(ir_ctx *ctx, ir_ref n, ir_ref *inputs)
{
// IR_ASSERT(!ctx->control);
IR_ASSERT(n > 0);
if (n == 1) {
_ir_BEGIN(ctx, inputs[0]);
} else {
ir_ref *ops;
ctx->control = ir_emit_N(ctx, IR_MERGE, n);
ops = ctx->ir_base[ctx->control].ops;
while (n) {
n--;
ops[n + 1] = inputs[n];
}
}
}
ir_ref _ir_END_LIST(ir_ctx *ctx, ir_ref list)
{
ir_ref ref;
IR_ASSERT(ctx->control);
IR_ASSERT(!list || ctx->ir_base[list].op == IR_END);
/* create a liked list of END nodes with the same destination through END.op2 */
ref = ir_emit2(ctx, IR_END, ctx->control, list);
ctx->control = IR_UNUSED;
return ref;
}
void _ir_MERGE_LIST(ir_ctx *ctx, ir_ref list)
{
ir_ref ref = list;
if (list != IR_UNUSED) {
uint32_t n = 0;
// IR_ASSERT(!ctx->control);
/* count inputs count */
do {
ir_insn *insn = &ctx->ir_base[ref];
IR_ASSERT(insn->op == IR_END);
ref = insn->op2;
n++;
} while (ref != IR_UNUSED);
/* create MERGE node */
IR_ASSERT(n > 0);
if (n == 1) {
ctx->ir_base[list].op2 = IR_UNUSED;
_ir_BEGIN(ctx, list);
} else {
ctx->control = ir_emit_N(ctx, IR_MERGE, n);
ref = list;
while (n) {
ir_insn *insn = &ctx->ir_base[ref];
ir_set_op(ctx, ctx->control, n, ref);
ref = insn->op2;
insn->op2 = IR_UNUSED;
n--;
}
}
}
}
ir_ref _ir_LOOP_BEGIN(ir_ctx *ctx, ir_ref src1)
{
// IR_ASSERT(!ctx->control);
ctx->control = ir_emit2(ctx, IR_LOOP_BEGIN, src1, IR_UNUSED);
return ctx->control;
}
ir_ref _ir_LOOP_END(ir_ctx *ctx, ir_ref loop)
{
ir_ref ref;
IR_ASSERT(ctx->control);
ref = ir_emit2(ctx, IR_LOOP_END, ctx->control, loop);
ctx->control = IR_UNUSED;
return ref;
}
ir_ref _ir_CALL(ir_ctx *ctx, ir_type type, ir_ref func)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_CALL, type), ctx->control, func);
}
ir_ref _ir_CALL_1(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_OPT(IR_CALL, type), 3);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ctx->control = call;
return call;
}
ir_ref _ir_CALL_2(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_OPT(IR_CALL, type), 4);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ctx->control = call;
return call;
}
ir_ref _ir_CALL_3(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_OPT(IR_CALL, type), 5);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ir_set_op(ctx, call, 5, arg3);
ctx->control = call;
return call;
}
ir_ref _ir_CALL_4(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_OPT(IR_CALL, type), 6);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ir_set_op(ctx, call, 5, arg3);
ir_set_op(ctx, call, 6, arg4);
ctx->control = call;
return call;
}
ir_ref _ir_CALL_5(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4, ir_ref arg5)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_OPT(IR_CALL, type), 7);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ir_set_op(ctx, call, 5, arg3);
ir_set_op(ctx, call, 6, arg4);
ir_set_op(ctx, call, 7, arg5);
ctx->control = call;
return call;
}
void _ir_UNREACHABLE(ir_ctx *ctx)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_UNREACHABLE, ctx->control, IR_UNUSED, ctx->ir_base[1].op1);
ctx->ir_base[1].op1 = ctx->control;
// ctx->control = IE_UNUSED;
}
void _ir_TAILCALL(ir_ctx *ctx, ir_ref func)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit2(ctx, IR_TAILCALL, ctx->control, func);
_ir_UNREACHABLE(ctx);
}
void _ir_TAILCALL_1(ir_ctx *ctx, ir_ref func, ir_ref arg1)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_TAILCALL, 3);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ctx->control = call;
_ir_UNREACHABLE(ctx);
}
void _ir_TAILCALL_2(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_TAILCALL, 4);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ctx->control = call;
_ir_UNREACHABLE(ctx);
}
void _ir_TAILCALL_3(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_TAILCALL, 5);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ir_set_op(ctx, call, 5, arg3);
ctx->control = call;
_ir_UNREACHABLE(ctx);
}
void _ir_TAILCALL_4(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_TAILCALL, 6);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ir_set_op(ctx, call, 5, arg3);
ir_set_op(ctx, call, 6, arg4);
ctx->control = call;
_ir_UNREACHABLE(ctx);
}
void _ir_TAILCALL_5(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4, ir_ref arg5)
{
ir_ref call;
IR_ASSERT(ctx->control);
call = ir_emit_N(ctx, IR_TAILCALL, 7);
ir_set_op(ctx, call, 1, ctx->control);
ir_set_op(ctx, call, 2, func);
ir_set_op(ctx, call, 3, arg1);
ir_set_op(ctx, call, 4, arg2);
ir_set_op(ctx, call, 5, arg3);
ir_set_op(ctx, call, 6, arg4);
ir_set_op(ctx, call, 7, arg5);
ctx->control = call;
_ir_UNREACHABLE(ctx);
}
ir_ref _ir_SWITCH(ir_ctx *ctx, ir_ref val)
{
ir_ref ref;
IR_ASSERT(ctx->control);
ref = ir_emit2(ctx, IR_SWITCH, ctx->control, val);
ctx->control = IR_UNUSED;
return ref;
}
void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val)
{
// IR_ASSERT(!ctx->control);
ctx->control = ir_emit2(ctx, IR_CASE_VAL, switch_ref, val);
}
void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref)
{
// IR_ASSERT(!ctx->control);
ctx->control = ir_emit1(ctx, IR_CASE_DEFAULT, switch_ref);
}
void _ir_RETURN(ir_ctx *ctx, ir_ref val)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_RETURN, ctx->control, val, ctx->ir_base[1].op1);
ctx->ir_base[1].op1 = ctx->control;
// ctx-control = IR_UNUSED;
}
void _ir_IJMP(ir_ctx *ctx, ir_ref addr)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_IJMP, ctx->control, addr, ctx->ir_base[1].op1);
ctx->ir_base[1].op1 = ctx->control;
ctx->control = IR_UNUSED;
}
ir_ref _ir_ADD_OFFSET(ir_ctx *ctx, ir_ref addr, uintptr_t offset)
{
if (offset) {
addr = ir_fold2(ctx, IR_OPT(IR_ADD, IR_ADDR), addr, ir_const_addr(ctx, offset));
}
return addr;
}
void _ir_GUARD(ir_ctx *ctx, ir_ref condition, ir_ref addr)
{
IR_ASSERT(ctx->control);
if (condition == IR_TRUE) {
return;
} else {
ir_ref ref = ctx->control;
ir_insn *insn;
while (ref > condition) {
insn = &ctx->ir_base[ref];
if (insn->op == IR_GUARD) {
if (insn->op2 == condition) {
return;
}
} else if (insn->op == IR_GUARD_NOT) {
if (insn->op2 == condition) {
condition = IR_FALSE;
break;
}
} else if (insn->op == IR_START
|| insn->op == IR_BEGIN
|| insn->op == IR_IF_TRUE
|| insn->op == IR_IF_FALSE
|| insn->op == IR_CASE_VAL
|| insn->op == IR_CASE_DEFAULT
|| insn->op == IR_MERGE
|| insn->op == IR_LOOP_BEGIN
|| insn->op == IR_ENTRY) {
break;
}
ref = insn->op1;
}
}
if (ctx->snapshot_create) {
ctx->snapshot_create(ctx, addr);
}
ctx->control = ir_emit3(ctx, IR_GUARD, ctx->control, condition, addr);
}
void _ir_GUARD_NOT(ir_ctx *ctx, ir_ref condition, ir_ref addr)
{
IR_ASSERT(ctx->control);
if (condition == IR_FALSE) {
return;
} else {
ir_ref ref = ctx->control;
ir_insn *insn;
while (ref > condition) {
insn = &ctx->ir_base[ref];
if (insn->op == IR_GUARD_NOT) {
if (insn->op2 == condition) {
return;
}
} else if (insn->op == IR_GUARD) {
if (insn->op2 == condition) {
condition = IR_TRUE;
break;
}
} else if (insn->op == IR_START
|| insn->op == IR_BEGIN
|| insn->op == IR_IF_TRUE
|| insn->op == IR_IF_FALSE
|| insn->op == IR_CASE_VAL
|| insn->op == IR_CASE_DEFAULT
|| insn->op == IR_MERGE
|| insn->op == IR_LOOP_BEGIN
|| insn->op == IR_ENTRY) {
break;
}
ref = insn->op1;
}
}
if (ctx->snapshot_create) {
ctx->snapshot_create(ctx, addr);
}
ctx->control = ir_emit3(ctx, IR_GUARD_NOT, ctx->control, condition, addr);
}
ir_ref _ir_SNAPSHOT(ir_ctx *ctx, ir_ref n)
{
ir_ref snapshot;
IR_ASSERT(ctx->control);
snapshot = ir_emit_N(ctx, IR_SNAPSHOT, 1 + n); /* op1 is used for control */
ctx->ir_base[snapshot].op1 = ctx->control;
ctx->control = snapshot;
return snapshot;
}
void _ir_SNAPSHOT_ADD(ir_ctx *ctx, ir_ref snapshot, ir_ref pos, ir_ref val)
{
ir_insn *insn = &ctx->ir_base[snapshot];
ir_ref *ops = insn->ops;
IR_ASSERT(val < snapshot);
IR_ASSERT(insn->op == IR_SNAPSHOT);
pos++; /* op1 is used for control */
IR_ASSERT(pos > 1 && pos <= insn->inputs_count);
ops[pos] = val;
}
ir_ref _ir_EXITCALL(ir_ctx *ctx, ir_ref func)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_EXITCALL, IR_I32), ctx->control, func);
}
ir_ref _ir_ALLOCA(ir_ctx *ctx, ir_ref size)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_ALLOCA, IR_ADDR), ctx->control, size);
}
void _ir_AFREE(ir_ctx *ctx, ir_ref size)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit2(ctx, IR_AFREE, ctx->control, size);
}
ir_ref _ir_VLOAD(ir_ctx *ctx, ir_type type, ir_ref var)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_VLOAD, type), ctx->control, var);
}
void _ir_VSTORE(ir_ctx *ctx, ir_ref var, ir_ref val)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_VSTORE, ctx->control, var, val);
}
ir_ref _ir_TLS(ir_ctx *ctx, ir_ref index, ir_ref offset)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit3(ctx, IR_OPT(IR_TLS, IR_ADDR), ctx->control, index, offset);
}
ir_ref _ir_RLOAD(ir_ctx *ctx, ir_type type, ir_ref reg)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_RLOAD, type), ctx->control, reg);
}
void _ir_RSTORE(ir_ctx *ctx, ir_ref reg, ir_ref val)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_RSTORE, ctx->control, val, reg);
}
ir_ref _ir_LOAD(ir_ctx *ctx, ir_type type, ir_ref addr)
{
ir_ref ref = ir_find_aliasing_load(ctx, ctx->control, type, addr);
IR_ASSERT(ctx->control);
if (!ref) {
ctx->control = ref = ir_emit2(ctx, IR_OPT(IR_LOAD, type), ctx->control, addr);
}
return ref;
}
void _ir_STORE(ir_ctx *ctx, ir_ref addr, ir_ref val)
{
ir_ref ref = ctx->control;
ir_ref prev = IR_UNUSED;
ir_insn *insn;
ir_type type = ctx->ir_base[val].type;
ir_type type2;
IR_ASSERT(ctx->control);
while (1) {
insn = &ctx->ir_base[ref];
if (insn->op == IR_STORE) {
if (insn->op2 == addr) {
if (ctx->ir_base[insn->op3].type == type) {
if (insn->op3 == val) {
return;
} else {
if (prev) {
ctx->ir_base[prev].op1 = insn->op1;
} else {
ctx->control = insn->op1;
}
insn->optx = IR_NOP;
insn->op1 = IR_NOP;
insn->op2 = IR_NOP;
insn->op3 = IR_NOP;
break;
}
} else {
break;
}
} else {
type2 = ctx->ir_base[insn->op3].type;
goto check_aliasing;
}
} else if (insn->op == IR_LOAD) {
if (insn->op2 == addr) {
break;
}
type2 = insn->type;
check_aliasing:
if (ir_check_partial_aliasing(ctx, addr, insn->op2, type, type2) != IR_NO_ALIAS) {
break;
}
} else if (insn->op == IR_START
|| insn->op == IR_BEGIN
|| insn->op == IR_IF_TRUE
|| insn->op == IR_IF_FALSE
|| insn->op == IR_CASE_VAL
|| insn->op == IR_CASE_DEFAULT
|| insn->op == IR_MERGE
|| insn->op == IR_LOOP_BEGIN
|| insn->op == IR_ENTRY
|| insn->op == IR_CALL) {
break;
}
prev = ref;
ref = insn->op1;
}
ctx->control = ir_emit3(ctx, IR_STORE, ctx->control, addr, val);
}

13
ir.h
View File

@ -470,12 +470,15 @@ void ir_strtab_free(ir_strtab *strtab);
# define IR_DEBUG_RA (1<<30)
#endif
typedef struct _ir_ctx ir_ctx;
typedef struct _ir_use_list ir_use_list;
typedef struct _ir_block ir_block;
typedef struct _ir_live_interval ir_live_interval;
typedef int8_t ir_regs[4];
typedef struct _ir_ctx {
typedef void (*ir_snapshot_create_t)(ir_ctx *ctx, ir_ref addr);
struct _ir_ctx {
ir_insn *ir_base; /* two directional array - instructions grow down, constants grow up */
ir_ref insns_count;
ir_ref insns_limit;
@ -504,7 +507,11 @@ typedef struct _ir_ctx {
ir_live_interval **live_intervals;
ir_regs *regs;
ir_ref *prev_ref;
void *data;
union {
void *data;
ir_ref control; /* used by IR construction API (see ir_builder.h) */
};
ir_snapshot_create_t snapshot_create;
uint32_t rodata_offset;
uint32_t jmp_table_offset;
void *code_buffer;
@ -512,7 +519,7 @@ typedef struct _ir_ctx {
ir_strtab strtab;
ir_ref prev_insn_chain[IR_LAST_FOLDABLE_OP + 1];
ir_ref prev_const_chain[IR_LAST_TYPE];
} ir_ctx;
};
/* Basic IR Construction API (implementation in ir.c) */
void ir_init(ir_ctx *ctx, ir_ref consts_limit, ir_ref insns_limit);

616
ir_builder.h Normal file
View File

@ -0,0 +1,616 @@
/*
* IR - Lightweight JIT Compilation Framework
* (IR Construction API)
* Copyright (C) 2022 Zend by Perforce.
* Authors: Dmitry Stogov <dmitry@php.net>
*/
#ifndef IR_BUILDER_H
#define IR_BUILDER_H
/* _ir_CTX may be redefined by the user */
#define _ir_CTX ctx
#define ir_NOP() ir_emit0(_ir_CTX)
#define ir_CONST_BOOL(_val) ir_const_bool(_ir_CTX, (_val))
#define ir_CONST_U8(_val) ir_const_u8(_ir_CTX, (_val))
#define ir_CONST_U16(_val) ir_const_u16(_ir_CTX, (_val))
#define ir_CONST_U32(_val) ir_const_u32(_ir_CTX, (_val))
#define ir_CONST_U64(_val) ir_const_u64(_ir_CTX, (_val))
#define ir_CONST_ADDR(_val) ir_const_addr(_ir_CTX, (uintptr_t)(_val))
#define ir_CONST_CHAR(_val) ir_const_char(_ir_CTX, (_val))
#define ir_CONST_I8(_val) ir_const_i8(_ir_CTX, (_val))
#define ir_CONST_I16(_val) ir_const_i16(_ir_CTX, (_val))
#define ir_CONST_I32(_val) ir_const_i32(_ir_CTX, (_val))
#define ir_CONST_I64(_val) ir_const_i64(_ir_CTX, (_val))
#define ir_CONST_DOUBLE(_val) ir_const_double(_ir_CTX, (_val))
#define ir_CONST_FLOAT(_val) ir_const_float(_ir_CTX, (_val))
#define ir_CMP_OP(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_BOOL), (_op1), (_op2))
#define ir_UNARY_OP(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), _ir_CTX->ir_base[(_op1)].type), (_op1))
#define ir_UNARY_OP_B(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_BOOL), (_op1))
#define ir_UNARY_OP_U8(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_U8), (_op1))
#define ir_UNARY_OP_U16(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_U16), (_op1))
#define ir_UNARY_OP_U32(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_U32), (_op1))
#define ir_UNARY_OP_U64(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_U64), (_op1))
#define ir_UNARY_OP_A(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_ADDR), (_op1))
#define ir_UNARY_OP_C(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_CHAR), (_op1))
#define ir_UNARY_OP_I8(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_I8), (_op1))
#define ir_UNARY_OP_I16(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_I16), (_op1))
#define ir_UNARY_OP_I32(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_I32), (_op1))
#define ir_UNARY_OP_I64(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_I64), (_op1))
#define ir_UNARY_OP_D(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_DOUBLE), (_op1))
#define ir_UNARY_OP_F(_op, _op1) ir_fold1(_ir_CTX, IR_OPT((_op), IR_FLOAT), (_op1))
#define ir_BINARY_OP(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), _ir_CTX->ir_base[(_op1)].type), (_op1), (_op2))
#define ir_BINARY_OP_B(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_BOOL), (_op1), (_op2))
#define ir_BINARY_OP_U8(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_U8), (_op1), (_op2))
#define ir_BINARY_OP_U16(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_U16), (_op1), (_op2))
#define ir_BINARY_OP_U32(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_U32), (_op1), (_op2))
#define ir_BINARY_OP_U64(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_U64), (_op1), (_op2))
#define ir_BINARY_OP_A(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_ADDR), (_op1), (_op2))
#define ir_BINARY_OP_C(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_CHAR), (_op1), (_op2))
#define ir_BINARY_OP_I8(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_I8), (_op1), (_op2))
#define ir_BINARY_OP_I16(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_I16), (_op1), (_op2))
#define ir_BINARY_OP_I32(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_I32), (_op1), (_op2))
#define ir_BINARY_OP_I64(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_I64), (_op1), (_op2))
#define ir_BINARY_OP_D(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_DOUBLE), (_op1), (_op2))
#define ir_BINARY_OP_F(_op, _op1, _op2) ir_fold2(_ir_CTX, IR_OPT((_op), IR_FLOAT), (_op1), (_op2))
#define ir_EQ(_op1, _op2) ir_CMP_OP(IR_EQ, (_op1), (_op2))
#define ir_NE(_op1, _op2) ir_CMP_OP(IR_NE, (_op1), (_op2))
#define ir_LT(_op1, _op2) ir_CMP_OP(IR_LT, (_op1), (_op2))
#define ir_GE(_op1, _op2) ir_CMP_OP(IR_GE, (_op1), (_op2))
#define ir_LE(_op1, _op2) ir_CMP_OP(IR_LE, (_op1), (_op2))
#define ir_GT(_op1, _op2) ir_CMP_OP(IR_GT, (_op1), (_op2))
#define ir_ULT(_op1, _op2) ir_CMP_OP(IR_ULT, (_op1), (_op2))
#define ir_UGE(_op1, _op2) ir_CMP_OP(IR_UGE, (_op1), (_op2))
#define ir_ULE(_op1, _op2) ir_CMP_OP(IR_ULE, (_op1), (_op2))
#define ir_UGT(_op1, _op2) ir_CMP_OP(IR_UGT, (_op1), (_op2))
#define ir_ADD(_op1, _op2) ir_BINARY_OP(IR_ADD, (_op1), (_op2))
#define ir_ADD_U8(_op1, _op2) ir_BINARY_OP_U8(IR_ADD, (_op1), (_op2))
#define ir_ADD_U16(_op1, _op2) ir_BINARY_OP_U16(IR_ADD, (_op1), (_op2))
#define ir_ADD_U32(_op1, _op2) ir_BINARY_OP_U32(IR_ADD, (_op1), (_op2))
#define ir_ADD_U64(_op1, _op2) ir_BINARY_OP_U64(IR_ADD, (_op1), (_op2))
#define ir_ADD_A(_op1, _op2) ir_BINARY_OP_A(IR_ADD, (_op1), (_op2))
#define ir_ADD_C(_op1, _op2) ir_BINARY_OP_C(IR_ADD, (_op1), (_op2))
#define ir_ADD_I8(_op1, _op2) ir_BINARY_OP_I8(IR_ADD, (_op1), (_op2))
#define ir_ADD_I16(_op1, _op2) ir_BINARY_OP_I16(IR_ADD, (_op1), (_op2))
#define ir_ADD_I32(_op1, _op2) ir_BINARY_OP_I32(IR_ADD, (_op1), (_op2))
#define ir_ADD_I64(_op1, _op2) ir_BINARY_OP_I64(IR_ADD, (_op1), (_op2))
#define ir_ADD_D(_op1, _op2) ir_BINARY_OP_D(IR_ADD, (_op1), (_op2))
#define ir_ADD_F(_op1, _op2) ir_BINARY_OP_F(IR_ADD, (_op1), (_op2))
#define ir_SUB(_op1, _op2) ir_BINARY_OP(IR_SUB, (_op1), (_op2))
#define ir_SUB_U8(_op1, _op2) ir_BINARY_OP_U8(IR_SUB, (_op1), (_op2))
#define ir_SUB_U16(_op1, _op2) ir_BINARY_OP_U16(IR_SUB, (_op1), (_op2))
#define ir_SUB_U32(_op1, _op2) ir_BINARY_OP_U32(IR_SUB, (_op1), (_op2))
#define ir_SUB_U64(_op1, _op2) ir_BINARY_OP_U64(IR_SUB, (_op1), (_op2))
#define ir_SUB_A(_op1, _op2) ir_BINARY_OP_A(IR_SUB, (_op1), (_op2))
#define ir_SUB_C(_op1, _op2) ir_BINARY_OP_C(IR_SUB, (_op1), (_op2))
#define ir_SUB_I8(_op1, _op2) ir_BINARY_OP_I8(IR_SUB, (_op1), (_op2))
#define ir_SUB_I16(_op1, _op2) ir_BINARY_OP_I16(IR_SUB, (_op1), (_op2))
#define ir_SUB_I32(_op1, _op2) ir_BINARY_OP_I32(IR_SUB, (_op1), (_op2))
#define ir_SUB_I64(_op1, _op2) ir_BINARY_OP_I64(IR_SUB, (_op1), (_op2))
#define ir_SUB_D(_op1, _op2) ir_BINARY_OP_D(IR_SUB, (_op1), (_op2))
#define ir_SUB_F(_op1, _op2) ir_BINARY_OP_F(IR_SUB, (_op1), (_op2))
#define ir_MUL(_op1, _op2) ir_BINARY_OP(IR_MUL, (_op1), (_op2))
#define ir_MUL_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MUL, (_op1), (_op2))
#define ir_MUL_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MUL, (_op1), (_op2))
#define ir_MUL_U32(_op1, _op2) ir_BINARY_OP_U32(IR_MUL, (_op1), (_op2))
#define ir_MUL_U64(_op1, _op2) ir_BINARY_OP_U64(IR_MUL, (_op1), (_op2))
#define ir_MUL_A(_op1, _op2) ir_BINARY_OP_A(IR_MUL, (_op1), (_op2))
#define ir_MUL_C(_op1, _op2) ir_BINARY_OP_C(IR_MUL, (_op1), (_op2))
#define ir_NUL_I8(_op1, _op2) ir_BINARY_OP_I8(IR_MUL, (_op1), (_op2))
#define ir_MUL_I16(_op1, _op2) ir_BINARY_OP_I16(IR_MUL, (_op1), (_op2))
#define ir_MUL_I32(_op1, _op2) ir_BINARY_OP_I32(IR_MUL, (_op1), (_op2))
#define ir_MUL_I64(_op1, _op2) ir_BINARY_OP_I64(IR_MUL, (_op1), (_op2))
#define ir_MUL_D(_op1, _op2) ir_BINARY_OP_D(IR_MUL, (_op1), (_op2))
#define ir_MUL_F(_op1, _op2) ir_BINARY_OP_F(IR_MUL, (_op1), (_op2))
#define ir_DIV(_op1, _op2) ir_BINARY_OP(IR_DIV, (_op1), (_op2))
#define ir_DIV_U8(_op1, _op2) ir_BINARY_OP_U8(IR_DIV, (_op1), (_op2))
#define ir_DIV_U16(_op1, _op2) ir_BINARY_OP_U16(IR_DIV, (_op1), (_op2))
#define ir_DIV_U32(_op1, _op2) ir_BINARY_OP_U32(IR_DIV, (_op1), (_op2))
#define ir_DIV_U64(_op1, _op2) ir_BINARY_OP_U64(IR_DIV, (_op1), (_op2))
#define ir_DIV_A(_op1, _op2) ir_BINARY_OP_A(IR_DIV, (_op1), (_op2))
#define ir_DIV_C(_op1, _op2) ir_BINARY_OP_C(IR_DIV, (_op1), (_op2))
#define ir_DIV_I8(_op1, _op2) ir_BINARY_OP_I8(IR_DIV, (_op1), (_op2))
#define ir_DIV_I16(_op1, _op2) ir_BINARY_OP_I16(IR_DIV, (_op1), (_op2))
#define ir_DIV_I32(_op1, _op2) ir_BINARY_OP_I32(IR_DIV, (_op1), (_op2))
#define ir_DIV_I64(_op1, _op2) ir_BINARY_OP_I64(IR_DIV, (_op1), (_op2))
#define ir_DIV_D(_op1, _op2) ir_BINARY_OP_D(IR_DIV, (_op1), (_op2))
#define ir_DIV_F(_op1, _op2) ir_BINARY_OP_F(IR_DIV, (_op1), (_op2))
#define ir_MOD(_op1, _op2) ir_BINARY_OP(IR_MOD, (_op1), (_op2))
#define ir_MOD_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MOD, (_op1), (_op2))
#define ir_MOD_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MOD, (_op1), (_op2))
#define ir_MOD_U32(_op1, _op2) ir_BINARY_OP_U32(IR_MOD, (_op1), (_op2))
#define ir_MOD_U64(_op1, _op2) ir_BINARY_OP_U64(IR_MOD, (_op1), (_op2))
#define ir_MOD_A(_op1, _op2) ir_BINARY_OP_A(IR_MOD, (_op1), (_op2))
#define ir_MOD_C(_op1, _op2) ir_BINARY_OP_C(IR_MOD, (_op1), (_op2))
#define ir_MOD_I8(_op1, _op2) ir_BINARY_OP_I8(IR_MOD, (_op1), (_op2))
#define ir_MOD_I16(_op1, _op2) ir_BINARY_OP_I16(IR_MOD, (_op1), (_op2))
#define ir_MOD_I32(_op1, _op2) ir_BINARY_OP_I32(IR_MOD, (_op1), (_op2))
#define ir_MOD_I64(_op1, _op2) ir_BINARY_OP_I64(IR_MOD, (_op1), (_op2))
#define ir_NEG(_op1) ir_UNARY_OP(IR_NEG, (_op1))
#define ir_NEG_C(_op1) ir_UNARY_OP_C(IR_NEG, (_op1))
#define ir_NEG_I8(_op1) ir_UNARY_OP_I8(IR_NEG, (_op1))
#define ir_NEG_I16(_op1) ir_UNARY_OP_I16(IR_NEG, (_op1))
#define ir_NEG_I32(_op1) ir_UNARY_OP_I32(IR_NEG, (_op1))
#define ir_NEG_I64(_op1) ir_UNARY_OP_I64(IR_NEG, (_op1))
#define ir_NEG_D(_op1) ir_UNARY_OP_D(IR_NEG, (_op1))
#define ir_NEG_F(_op1) ir_UNARY_OP_F(IR_NEG, (_op1))
#define ir_ABS(_op1) ir_UNARY_OP(IR_ABS, (_op1))
#define ir_ABS_C(_op1) ir_UNARY_OP_C(IR_ABS, (_op1))
#define ir_ABS_I8(_op1) ir_UNARY_OP_I8(IR_ABS, (_op1))
#define ir_ABS_I16(_op1) ir_UNARY_OP_I16(IR_ABS, (_op1))
#define ir_ABS_I32(_op1) ir_UNARY_OP_I32(IR_ABS, (_op1))
#define ir_ABS_I64(_op1) ir_UNARY_OP_I64(IR_ABS, (_op1))
#define ir_ABS_D(_op1) ir_UNARY_OP_D(IR_ABS, (_op1))
#define ir_ABS_F(_op1) ir_UNARY_OP_F(IR_ABS, (_op1))
#define ir_SEXT_U8(_op1) ir_UNARY_OP_U8(IR_SEXT, (_op1))
#define ir_SEXT_U16(_op1) ir_UNARY_OP_U16(IR_SEXT, (_op1))
#define ir_SEXT_U32(_op1) ir_UNARY_OP_U32(IR_SEXT, (_op1))
#define ir_SEXT_U64(_op1) ir_UNARY_OP_U64(IR_SEXT, (_op1))
#define ir_SEXT_A(_op1) ir_UNARY_OP_A(IR_SEXT, (_op1))
#define ir_SEXT_C(_op1) ir_UNARY_OP_C(IR_SEXT, (_op1))
#define ir_SEXT_I8(_op1) ir_UNARY_OP_I8(IR_SEXT, (_op1))
#define ir_SEXT_I16(_op1) ir_UNARY_OP_I16(IR_SEXT, (_op1))
#define ir_SEXT_I32(_op1) ir_UNARY_OP_I32(IR_SEXT, (_op1))
#define ir_SEXT_I64(_op1) ir_UNARY_OP_I64(IR_SEXT, (_op1))
#define ir_ZEXT_U8(_op1) ir_UNARY_OP_U8(IR_ZEXT, (_op1))
#define ir_ZEXT_U16(_op1) ir_UNARY_OP_U16(IR_ZEXT, (_op1))
#define ir_ZEXT_U32(_op1) ir_UNARY_OP_U32(IR_ZEXT, (_op1))
#define ir_ZEXT_U64(_op1) ir_UNARY_OP_U64(IR_ZEXT, (_op1))
#define ir_ZEXT_A(_op1) ir_UNARY_OP_A(IR_ZEXT, (_op1))
#define ir_ZEXT_C(_op1) ir_UNARY_OP_C(IR_ZEXT, (_op1))
#define ir_ZEXT_I8(_op1) ir_UNARY_OP_I8(IR_ZEXT, (_op1))
#define ir_ZEXT_I16(_op1) ir_UNARY_OP_I16(IR_ZEXT, (_op1))
#define ir_ZEXT_I32(_op1) ir_UNARY_OP_I32(IR_ZEXT, (_op1))
#define ir_ZEXT_I64(_op1) ir_UNARY_OP_I64(IR_ZEXT, (_op1))
#define ir_TRUNC_U8(_op1) ir_UNARY_OP_U8(IR_TRUNC, (_op1))
#define ir_TRUNC_U16(_op1) ir_UNARY_OP_U16(IR_TRUNC, (_op1))
#define ir_TRUNC_U32(_op1) ir_UNARY_OP_U32(IR_TRUNC, (_op1))
#define ir_TRUNC_U64(_op1) ir_UNARY_OP_U64(IR_TRUNC, (_op1))
#define ir_TRUNC_A(_op1) ir_UNARY_OP_A(IR_TRUNC, (_op1))
#define ir_TRUNC_C(_op1) ir_UNARY_OP_C(IR_TRUNC, (_op1))
#define ir_TRUNC_I8(_op1) ir_UNARY_OP_I8(IR_TRUNC, (_op1))
#define ir_TRUNC_I16(_op1) ir_UNARY_OP_I16(IR_TRUNC, (_op1))
#define ir_TRUNC_I32(_op1) ir_UNARY_OP_I32(IR_TRUNC, (_op1))
#define ir_TRUNC_I64(_op1) ir_UNARY_OP_I64(IR_TRUNC, (_op1))
#define ir_BITCAST_U8(_op1) ir_UNARY_OP_U8(IR_BITCAST, (_op1))
#define ir_BITCAST_U16(_op1) ir_UNARY_OP_U16(IR_BITCAST, (_op1))
#define ir_BITCAST_U32(_op1) ir_UNARY_OP_U32(IR_BITCAST, (_op1))
#define ir_BITCAST_U64(_op1) ir_UNARY_OP_U64(IR_BITCAST, (_op1))
#define ir_BITCAST_A(_op1) ir_UNARY_OP_A(IR_BITCAST, (_op1))
#define ir_BITCAST_C(_op1) ir_UNARY_OP_C(IR_BITCAST, (_op1))
#define ir_BITCAST_I8(_op1) ir_UNARY_OP_I8(IR_BITCAST, (_op1))
#define ir_BITCAST_I16(_op1) ir_UNARY_OP_I16(IR_BITCAST, (_op1))
#define ir_BITCAST_I32(_op1) ir_UNARY_OP_I32(IR_BITCAST, (_op1))
#define ir_BITCAST_I64(_op1) ir_UNARY_OP_I64(IR_BITCAST, (_op1))
#define ir_BITCAST_D(_op1) ir_UNARY_OP_D(IR_BITCAST, (_op1))
#define ir_BITCAST_F(_op1) ir_UNARY_OP_F(IR_BITCAST, (_op1))
#define ir_INT2D(_op1) ir_UNARY_OP_D(IR_INT2FP, (_op1))
#define ir_INT2F(_op1) ir_UNARY_OP_F(IR_INT2FP, (_op1))
#define ir_FP2U8(_op1) ir_UNARY_OP_U8(IR_FP2INT, (_op1))
#define ir_FP2U16(_op1) ir_UNARY_OP_U16(IR_FP2INT, (_op1))
#define ir_FP2U32(_op1) ir_UNARY_OP_U32(IR_FP2INT, (_op1))
#define ir_FP2U64(_op1) ir_UNARY_OP_U64(IR_FP2INT, (_op1))
#define ir_FP2I8(_op1) ir_UNARY_OP_I8(IR_FP2INT, (_op1))
#define ir_FP2I16(_op1) ir_UNARY_OP_I16(IR_FP2INT, (_op1))
#define ir_FP2I32(_op1) ir_UNARY_OP_I32(IR_FP2INT, (_op1))
#define ir_FP2I64(_op1) ir_UNARY_OP_I64(IR_FP2INT, (_op1))
#define ir_F2D(_op1) ir_UNARY_OP_D(IR_FP2FP, (_op1))
#define ir_D2F(_op1) ir_UNARY_OP_F(IR_FP2FP, (_op1))
#define ir_ADD_OV(_op1, _op2) ir_BINARY_OP(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_U8(_op1, _op2) ir_BINARY_OP_U8(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_U16(_op1, _op2) ir_BINARY_OP_U16(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_U32(_op1, _op2) ir_BINARY_OP_U32(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_U64(_op1, _op2) ir_BINARY_OP_U64(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_A(_op1, _op2) ir_BINARY_OP_A(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_C(_op1, _op2) ir_BINARY_OP_C(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_I8(_op1, _op2) ir_BINARY_OP_I8(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_I16(_op1, _op2) ir_BINARY_OP_I16(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_I32(_op1, _op2) ir_BINARY_OP_I32(IR_ADD_OV, (_op1), (_op2))
#define ir_ADD_OV_I64(_op1, _op2) ir_BINARY_OP_I64(IR_ADD_OV, (_op1), (_op2))
#define ir_SUB_OV(_op1, _op2) ir_BINARY_OP(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_U8(_op1, _op2) ir_BINARY_OP_U8(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_U16(_op1, _op2) ir_BINARY_OP_U16(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_U32(_op1, _op2) ir_BINARY_OP_U32(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_U64(_op1, _op2) ir_BINARY_OP_U64(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_A(_op1, _op2) ir_BINARY_OP_A(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_C(_op1, _op2) ir_BINARY_OP_C(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_I8(_op1, _op2) ir_BINARY_OP_I8(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_I16(_op1, _op2) ir_BINARY_OP_I16(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_I32(_op1, _op2) ir_BINARY_OP_I32(IR_SUB_OV, (_op1), (_op2))
#define ir_SUB_OV_I64(_op1, _op2) ir_BINARY_OP_I64(IR_SUB_OV, (_op1), (_op2))
#define ir_MUL_OV(_op1, _op2) ir_BINARY_OP(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_U32(_op1, _op2) ir_BINARY_OP_U32(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_U64(_op1, _op2) ir_BINARY_OP_U64(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_A(_op1, _op2) ir_BINARY_OP_A(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_C(_op1, _op2) ir_BINARY_OP_C(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_I8(_op1, _op2) ir_BINARY_OP_I8(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_I16(_op1, _op2) ir_BINARY_OP_I16(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_I32(_op1, _op2) ir_BINARY_OP_I32(IR_MUL_OV, (_op1), (_op2))
#define ir_MUL_OV_I64(_op1, _op2) ir_BINARY_OP_I64(IR_MUL_OV, (_op1), (_op2))
#define ir_OVERFLOW(_op1) ir_fold1(_ir_CTX, IR_OPT(IR_OVERFLOW, IR_BOOL), (_op1))
#define ir_NOT(_op1) ir_UNARY_OP(IR_NOT, (_op1))
#define ir_NOT_B(_op1) ir_UNARY_OP_B(IR_NOT, (_op1))
#define ir_NOT_U8(_op1) ir_UNARY_OP_U8(IR_NOT, (_op1))
#define ir_NOT_U16(_op1) ir_UNARY_OP_U16(IR_NOT, (_op1))
#define ir_NOT_U32(_op1) ir_UNARY_OP_U32(IR_NOT, (_op1))
#define ir_NOT_U64(_op1) ir_UNARY_OP_U64(IR_NOT, (_op1))
#define ir_NOT_A(_op1) ir_UNARY_OP_A(IR_NOT, (_op1))
#define ir_NOT_C(_op1) ir_UNARY_OP_C(IR_NOT, (_op1))
#define ir_NOT_I8(_op1) ir_UNARY_OP_I8(IR_NOT, (_op1))
#define ir_NOT_I16(_op1) ir_UNARY_OP_I16(IR_NOT, (_op1))
#define ir_NOT_I32(_op1) ir_UNARY_OP_I32(IR_NOT, (_op1))
#define ir_NOT_I64(_op1) ir_UNARY_OP_I64(IR_NOT, (_op1))
#define ir_OR(_op1, _op2) ir_BINARY_OP(IR_OR, (_op1), (_op2))
#define ir_OR_B(_op1, _op2) ir_BINARY_OP_B(IR_OR, (_op1), (_op2))
#define ir_OR_U8(_op1, _op2) ir_BINARY_OP_U8(IR_OR, (_op1), (_op2))
#define ir_OR_U16(_op1, _op2) ir_BINARY_OP_U16(IR_OR, (_op1), (_op2))
#define ir_OR_U32(_op1, _op2) ir_BINARY_OP_U32(IR_OR, (_op1), (_op2))
#define ir_OR_U64(_op1, _op2) ir_BINARY_OP_U64(IR_OR, (_op1), (_op2))
#define ir_OR_A(_op1, _op2) ir_BINARY_OP_A(IR_OR, (_op1), (_op2))
#define ir_OR_C(_op1, _op2) ir_BINARY_OP_C(IR_OR, (_op1), (_op2))
#define ir_OR_I8(_op1, _op2) ir_BINARY_OP_I8(IR_OR, (_op1), (_op2))
#define ir_OR_I16(_op1, _op2) ir_BINARY_OP_I16(IR_OR, (_op1), (_op2))
#define ir_OR_I32(_op1, _op2) ir_BINARY_OP_I32(IR_OR, (_op1), (_op2))
#define ir_OR_I64(_op1, _op2) ir_BINARY_OP_I64(IR_OR, (_op1), (_op2))
#define ir_AND(_op1, _op2) ir_BINARY_OP(IR_AND, (_op1), (_op2))
#define ir_AND_B(_op1, _op2) ir_BINARY_OP_B(IR_AND, (_op1), (_op2))
#define ir_AND_U8(_op1, _op2) ir_BINARY_OP_U8(IR_AND, (_op1), (_op2))
#define ir_AND_U16(_op1, _op2) ir_BINARY_OP_U16(IR_AND, (_op1), (_op2))
#define ir_AND_U32(_op1, _op2) ir_BINARY_OP_U32(IR_AND, (_op1), (_op2))
#define ir_AND_U64(_op1, _op2) ir_BINARY_OP_U64(IR_AND, (_op1), (_op2))
#define ir_AND_A(_op1, _op2) ir_BINARY_OP_A(IR_AND, (_op1), (_op2))
#define ir_AND_C(_op1, _op2) ir_BINARY_OP_C(IR_AND, (_op1), (_op2))
#define ir_AND_I8(_op1, _op2) ir_BINARY_OP_I8(IR_AND, (_op1), (_op2))
#define ir_AND_I16(_op1, _op2) ir_BINARY_OP_I16(IR_AND, (_op1), (_op2))
#define ir_AND_I32(_op1, _op2) ir_BINARY_OP_I32(IR_AND, (_op1), (_op2))
#define ir_AND_I64(_op1, _op2) ir_BINARY_OP_I64(IR_AND, (_op1), (_op2))
#define ir_XOR(_op1, _op2) ir_BINARY_OP(IR_XOR, (_op1), (_op2))
#define ir_XOR_B(_op1, _op2) ir_BINARY_OP_B(IR_XOR, (_op1), (_op2))
#define ir_XOR_U8(_op1, _op2) ir_BINARY_OP_U8(IR_XOR, (_op1), (_op2))
#define ir_XOR_U16(_op1, _op2) ir_BINARY_OP_U16(IR_XOR, (_op1), (_op2))
#define ir_XOR_U32(_op1, _op2) ir_BINARY_OP_U32(IR_XOR, (_op1), (_op2))
#define ir_XOR_U64(_op1, _op2) ir_BINARY_OP_U64(IR_XOR, (_op1), (_op2))
#define ir_XOR_A(_op1, _op2) ir_BINARY_OP_A(IR_XOR, (_op1), (_op2))
#define ir_XOR_C(_op1, _op2) ir_BINARY_OP_C(IR_XOR, (_op1), (_op2))
#define ir_XOR_I8(_op1, _op2) ir_BINARY_OP_I8(IR_XOR, (_op1), (_op2))
#define ir_XOR_I16(_op1, _op2) ir_BINARY_OP_I16(IR_XOR, (_op1), (_op2))
#define ir_XOR_I32(_op1, _op2) ir_BINARY_OP_I32(IR_XOR, (_op1), (_op2))
#define ir_XOR_I64(_op1, _op2) ir_BINARY_OP_I64(IR_XOR, (_op1), (_op2))
#define ir_SHL(_op1, _op2) ir_BINARY_OP(IR_SHL, (_op1), (_op2))
#define ir_SHL_U8(_op1, _op2) ir_BINARY_OP_U8(IR_SHL, (_op1), (_op2))
#define ir_SHL_U16(_op1, _op2) ir_BINARY_OP_U16(IR_SHL, (_op1), (_op2))
#define ir_SHL_U32(_op1, _op2) ir_BINARY_OP_U32(IR_SHL, (_op1), (_op2))
#define ir_SHL_U64(_op1, _op2) ir_BINARY_OP_U64(IR_SHL, (_op1), (_op2))
#define ir_SHL_A(_op1, _op2) ir_BINARY_OP_A(IR_SHL, (_op1), (_op2))
#define ir_SHL_C(_op1, _op2) ir_BINARY_OP_C(IR_SHL, (_op1), (_op2))
#define ir_SHL_I8(_op1, _op2) ir_BINARY_OP_I8(IR_SHL, (_op1), (_op2))
#define ir_SHL_I16(_op1, _op2) ir_BINARY_OP_I16(IR_SHL, (_op1), (_op2))
#define ir_SHL_I32(_op1, _op2) ir_BINARY_OP_I32(IR_SHL, (_op1), (_op2))
#define ir_SHL_I64(_op1, _op2) ir_BINARY_OP_I64(IR_SHL, (_op1), (_op2))
#define ir_SHR(_op1, _op2) ir_BINARY_OP(IR_SHR, (_op1), (_op2))
#define ir_SHR_U8(_op1, _op2) ir_BINARY_OP_U8(IR_SHR, (_op1), (_op2))
#define ir_SHR_U16(_op1, _op2) ir_BINARY_OP_U16(IR_SHR, (_op1), (_op2))
#define ir_SHR_U32(_op1, _op2) ir_BINARY_OP_U32(IR_SHR, (_op1), (_op2))
#define ir_SHR_U64(_op1, _op2) ir_BINARY_OP_U64(IR_SHR, (_op1), (_op2))
#define ir_SHR_A(_op1, _op2) ir_BINARY_OP_A(IR_SHR, (_op1), (_op2))
#define ir_SHR_C(_op1, _op2) ir_BINARY_OP_C(IR_SHR, (_op1), (_op2))
#define ir_SHR_I8(_op1, _op2) ir_BINARY_OP_I8(IR_SHR, (_op1), (_op2))
#define ir_SHR_I16(_op1, _op2) ir_BINARY_OP_I16(IR_SHR, (_op1), (_op2))
#define ir_SHR_I32(_op1, _op2) ir_BINARY_OP_I32(IR_SHR, (_op1), (_op2))
#define ir_SHR_I64(_op1, _op2) ir_BINARY_OP_I64(IR_SHR, (_op1), (_op2))
#define ir_SAR(_op1, _op2) ir_BINARY_OP(IR_SAR, (_op1), (_op2))
#define ir_SAR_U8(_op1, _op2) ir_BINARY_OP_U8(IR_SAR, (_op1), (_op2))
#define ir_SAR_U16(_op1, _op2) ir_BINARY_OP_U16(IR_SAR, (_op1), (_op2))
#define ir_SAR_U32(_op1, _op2) ir_BINARY_OP_U32(IR_SAR, (_op1), (_op2))
#define ir_SAR_U64(_op1, _op2) ir_BINARY_OP_U64(IR_SAR, (_op1), (_op2))
#define ir_SAR_A(_op1, _op2) ir_BINARY_OP_A(IR_SAR, (_op1), (_op2))
#define ir_SAR_C(_op1, _op2) ir_BINARY_OP_C(IR_SAR, (_op1), (_op2))
#define ir_SAR_I8(_op1, _op2) ir_BINARY_OP_I8(IR_SAR, (_op1), (_op2))
#define ir_SAR_I16(_op1, _op2) ir_BINARY_OP_I16(IR_SAR, (_op1), (_op2))
#define ir_SAR_I32(_op1, _op2) ir_BINARY_OP_I32(IR_SAR, (_op1), (_op2))
#define ir_SAR_I64(_op1, _op2) ir_BINARY_OP_I64(IR_SAR, (_op1), (_op2))
#define ir_ROL(_op1, _op2) ir_BINARY_OP(IR_ROL, (_op1), (_op2))
#define ir_ROL_U8(_op1, _op2) ir_BINARY_OP_U8(IR_ROL, (_op1), (_op2))
#define ir_ROL_U16(_op1, _op2) ir_BINARY_OP_U16(IR_ROL, (_op1), (_op2))
#define ir_ROL_U32(_op1, _op2) ir_BINARY_OP_U32(IR_ROL, (_op1), (_op2))
#define ir_ROL_U64(_op1, _op2) ir_BINARY_OP_U64(IR_ROL, (_op1), (_op2))
#define ir_ROL_A(_op1, _op2) ir_BINARY_OP_A(IR_ROL, (_op1), (_op2))
#define ir_ROL_C(_op1, _op2) ir_BINARY_OP_C(IR_ROL, (_op1), (_op2))
#define ir_ROL_I8(_op1, _op2) ir_BINARY_OP_I8(IR_ROL, (_op1), (_op2))
#define ir_ROL_I16(_op1, _op2) ir_BINARY_OP_I16(IR_ROL, (_op1), (_op2))
#define ir_ROL_I32(_op1, _op2) ir_BINARY_OP_I32(IR_ROL, (_op1), (_op2))
#define ir_ROL_I64(_op1, _op2) ir_BINARY_OP_I64(IR_ROL, (_op1), (_op2))
#define ir_ROR(_op1, _op2) ir_BINARY_OP(IR_ROR, (_op1), (_op2))
#define ir_ROR_U8(_op1, _op2) ir_BINARY_OP_U8(IR_ROR, (_op1), (_op2))
#define ir_ROR_U16(_op1, _op2) ir_BINARY_OP_U16(IR_ROR, (_op1), (_op2))
#define ir_ROR_U32(_op1, _op2) ir_BINARY_OP_U32(IR_ROR, (_op1), (_op2))
#define ir_ROR_U64(_op1, _op2) ir_BINARY_OP_U64(IR_ROR, (_op1), (_op2))
#define ir_ROR_A(_op1, _op2) ir_BINARY_OP_A(IR_ROR, (_op1), (_op2))
#define ir_ROR_C(_op1, _op2) ir_BINARY_OP_C(IR_ROR, (_op1), (_op2))
#define ir_ROR_I8(_op1, _op2) ir_BINARY_OP_I8(IR_ROR, (_op1), (_op2))
#define ir_ROR_I16(_op1, _op2) ir_BINARY_OP_I16(IR_ROR, (_op1), (_op2))
#define ir_ROR_I32(_op1, _op2) ir_BINARY_OP_I32(IR_ROR, (_op1), (_op2))
#define ir_ROR_I64(_op1, _op2) ir_BINARY_OP_I64(IR_ROR, (_op1), (_op2))
#define ir_BSWAP(_op1) ir_UNARY_OP(IR_BSWAP, (_op1))
#define ir_BSWAP_U16(_op1) ir_UNARY_OP_U16(IR_BSWAP, (_op1))
#define ir_BSWAP_U32(_op1) ir_UNARY_OP_U32(IR_BSWAP, (_op1))
#define ir_BSWAP_U64(_op1) ir_UNARY_OP_U64(IR_BSWAP, (_op1))
#define ir_BSWAP_A(_op1) ir_UNARY_OP_A(IR_BSWAP, (_op1))
#define ir_BSWAP_I16(_op1) ir_UNARY_OP_I16(IR_BSWAP, (_op1))
#define ir_BSWAP_I32(_op1) ir_UNARY_OP_I32(IR_BSWAP, (_op1))
#define ir_BSWAP_I64(_op1) ir_UNARY_OP_I64(IR_BSWAP, (_op1))
#define ir_MIN(_op1, _op2) ir_BINARY_OP(IR_MIN, (_op1), (_op2))
#define ir_MIN_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MIN, (_op1), (_op2))
#define ir_MIN_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MIN, (_op1), (_op2))
#define ir_MIN_U32(_op1, _op2) ir_BINARY_OP_U32(IR_MIN, (_op1), (_op2))
#define ir_MIN_U64(_op1, _op2) ir_BINARY_OP_U64(IR_MIN, (_op1), (_op2))
#define ir_MIN_A(_op1, _op2) ir_BINARY_OP_A(IR_MIN, (_op1), (_op2))
#define ir_MIN_C(_op1, _op2) ir_BINARY_OP_C(IR_MIN, (_op1), (_op2))
#define ir_MIN_I8(_op1, _op2) ir_BINARY_OP_I8(IR_MIN, (_op1), (_op2))
#define ir_MIN_I16(_op1, _op2) ir_BINARY_OP_I16(IR_MIN, (_op1), (_op2))
#define ir_MIN_I32(_op1, _op2) ir_BINARY_OP_I32(IR_MIN, (_op1), (_op2))
#define ir_MIN_I64(_op1, _op2) ir_BINARY_OP_I64(IR_MIN, (_op1), (_op2))
#define ir_MIN_D(_op1, _op2) ir_BINARY_OP_D(IR_MIN, (_op1), (_op2))
#define ir_MIN_F(_op1, _op2) ir_BINARY_OP_F(IR_MIN, (_op1), (_op2))
#define ir_MAX(_op1, _op2) ir_BINARY_OP(IR_MAX, (_op1), (_op2))
#define ir_MAX_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MAX, (_op1), (_op2))
#define ir_MAX_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MAX, (_op1), (_op2))
#define ir_MAX_U32(_op1, _op2) ir_BINARY_OP_U32(IR_MAX, (_op1), (_op2))
#define ir_MAX_U64(_op1, _op2) ir_BINARY_OP_U64(IR_MAX, (_op1), (_op2))
#define ir_MAX_A(_op1, _op2) ir_BINARY_OP_A(IR_MAX, (_op1), (_op2))
#define ir_MAX_C(_op1, _op2) ir_BINARY_OP_C(IR_MAX, (_op1), (_op2))
#define ir_MAX_I8(_op1, _op2) ir_BINARY_OP_I8(IR_MAX, (_op1), (_op2))
#define ir_MAX_I16(_op1, _op2) ir_BINARY_OP_I16(IR_MAX, (_op1), (_op2))
#define ir_MAX_I32(_op1, _op2) ir_BINARY_OP_I32(IR_MAX, (_op1), (_op2))
#define ir_MAX_I64(_op1, _op2) ir_BINARY_OP_I64(IR_MAX, (_op1), (_op2))
#define ir_MAX_D(_op1, _op2) ir_BINARY_OP_D(IR_MAX, (_op1), (_op2))
#define ir_MAX_F(_op1, _op2) ir_BINARY_OP_F(IR_MAX, (_op1), (_op2))
#define ir_COND(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, _ir_CTX->ir_base[(_op2)].type), (_op1), (_op2), (_op3))
#define ir_COND_U8(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_U8), (_op1), (_op2), (_op3))
#define ir_COND_U16(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_U16), (_op1), (_op2), (_op3))
#define ir_COND_U32(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_U32), (_op1), (_op2), (_op3))
#define ir_COND_U64(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_U64), (_op1), (_op2), (_op3))
#define ir_COND_A(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_ADDR), (_op1), (_op2), (_op3))
#define ir_COND_C(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_CHAR), (_op1), (_op2), (_op3))
#define ir_COND_I8(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_I8), (_op1), (_op2), (_op3))
#define ir_COND_I16(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COMD, IR_I16), (_op1), (_op2), (_op3))
#define ir_COND_I32(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_I32), (_op1), (_op2), (_op3))
#define ir_COND_I64(_op1, _op2, _op3) ir_fold3(_ir_CTX, IR_OPT(IR_COND, IR_I64), (_op1), (_op2), (_op3))
#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_COPY(_op1) ir_UNARY_OP(IR_COPY, (_op1))
#define ir_COPY_B(_op1) ir_UNARY_OP_B(IR_COPY, (_op1))
#define ir_COPY_U8(_op1) ir_UNARY_OP_U8(IR_COPY, (_op1))
#define ir_COPY_U16(_op1) ir_UNARY_OP_U16(IR_COPY, (_op1))
#define ir_COPY_U32(_op1) ir_UNARY_OP_U32(IR_COPY, (_op1))
#define ir_COPY_U64(_op1) ir_UNARY_OP_U64(IR_COPY, (_op1))
#define ir_COPY_A(_op1) ir_UNARY_OP_A(IR_COPY, (_op1))
#define ir_COPY_C(_op1) ir_UNARY_OP_C(IR_COPY, (_op1))
#define ir_COPY_I8(_op1) ir_UNARY_OP_I8(IR_COPY, (_op1))
#define ir_COPY_I16(_op1) ir_UNARY_OP_I16(IR_COPY, (_op1))
#define ir_COPY_I32(_op1) ir_UNARY_OP_I32(IR_COPY, (_op1))
#define ir_COPY_I64(_op1) ir_UNARY_OP_I64(IR_COPY, (_op1))
#define ir_COPY_D(_op1) ir_UNARY_OP_I64(IR_COPY, (_op1))
#define ir_COPY_F(_op1) ir_UNARY_OP_I64(IR_COPY, (_op1))
/* Helper to add address with a constant offset */
#define ir_ADD_OFFSET(_addr, _offset) _ir_ADD_OFFSET(_ir_CTX, (_addr), (_offset))
/* Unfoldable variant of COPY */
#define ir_HARD_COPY(_op1) ir_BINARY_OP(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_B(_op1) ir_BINARY_OP_B(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_U8(_op1) ir_BINARY_OP_U8(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_U16(_op1) ir_BINARY_OP_U16(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_U32(_op1) ir_BINARY_OP_U32(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_U64(_op1) ir_BINARY_OP_U64(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_A(_op1) ir_BINARY_OP_A(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_C(_op1) ir_BINARY_OP_C(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_I8(_op1) ir_BINARY_OP_I8(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_I16(_op1) ir_BINARY_OP_I16(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_I32(_op1) ir_BINARY_OP_I32(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_I64(_op1) ir_BINARY_OP_I64(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_D(_op1) ir_BINARY_OP_I64(IR_COPY, (_op1), 1)
#define ir_HARD_COPY_F(_op1) ir_BIMARY_OP_I64(IR_COPY, (_op1), 1)
#define ir_PARAM(_type, _name, _num) _ir_PARAM(_ir_CTX, (_type), (_name), (_num))
#define ir_VAR(_type, _name) _ir_VAR(_ir_CTX, (_type), (_name))
#define ir_CALL(type, func) _ir_CALL(_ir_CTX, type, func)
#define ir_CALL_1(type, func, a1) _ir_CALL_1(_ir_CTX, type, func, a1)
#define ir_CALL_2(type, func, a1, a2) _ir_CALL_2(_ir_CTX, type, func, a1, a2)
#define ir_CALL_3(type, func, a1, a2, a3) _ir_CALL_3(_ir_CTX, type, func, a1, a2, a3)
#define ir_CALL_4(type, func, a1, a2, a3, a4) _ir_CALL_4(_ir_CTX, type, func, a1, a2, a3, a4)
#define ir_CALL_5(type, func, a1, a2, a3, a4, a5) _ir_CALL_5(_ir_CTX, type, func, a1, a2, a3, a4, a5)
#define ir_TAILCALL(func) _ir_TAILCALL(_ir_CTX, func)
#define ir_TAILCALL_1(func, a1) _ir_TAILCALL_1(_ir_CTX, func, a1)
#define ir_TAILCALL_2(func, a1, a2) _ir_TAILCALL_2(_ir_CTX, func, a1, a2)
#define ir_TAILCALL_3(func, a1, a2, a3) _ir_TAILCALL_3(_ir_CTX, func, a1, a2, a3)
#define ir_TAILCALL_4(func, a1, a2, a3, a4) _ir_TAILCALL_4(_ir_CTX, func, a1, a2, a3, a4)
#define ir_TAILCALL_5(func, a1, a2, a3, a4, a5) _ir_TAILCALL_5(_ir_CTX, func, a1, a2, a3, a4, a5)
#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_CTX, (_var))
#define ir_VLOAD(_var) _ir_VLOAD(_ir_CTX, _ir_CTX->ir_base[(_var)].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))
#define ir_VLOAD_U32(_var) _ir_VLOAD(_ir_CTX, IR_U32, (_var))
#define ir_VLOAD_U64(_var) _ir_VLOAD(_ir_CTX, IR_U64, (_var))
#define ir_VLOAD_A(_var) _ir_VLOAD(_ir_CTX, IR_ADDR, (_var))
#define ir_VLOAD_C(_var) _ir_VLOAD(_ir_CTX, IR_CHAR, (_var))
#define ir_VLOAD_I8(_var) _ir_VLOAD(_ir_CTX, IR_I8, (_var))
#define ir_VLOAD_I16(_var) _ir_VLOAD(_ir_CTX, IR_I16, (_var))
#define ir_VLOAD_I32(_var) _ir_VLOAD(_ir_CTX, IR_I32, (_var))
#define ir_VLOAD_I64(_var) _ir_VLOAD(_ir_CTX, IR_I64, (_var))
#define ir_VLOAD_D(_var) _ir_VLOAD(_ir_CTX, IR_DOUBLE, (_var))
#define ir_VLOAD_F(_var) _ir_VLOAD(_ir_CTX, IR_FLOAT, (_var))
#define ir_VSTORE(_var) _ir_VSTRORE(_ir_CTX, (_var), (_val))
#define ir_RLOAD(_type, _reg) _ir_RLOAD(_ir_CTX, (_type), (_reg))
#define ir_RLOAD_B(_reg) _ir_RLOAD(_ir_CTX, IR_BOOL, (_reg))
#define ir_RLOAD_U8(_reg) _ir_RLOAD(_ir_CTX, IR_U8, (_reg))
#define ir_RLOAD_U16(_reg) _ir_RLOAD(_ir_CTX, IR_U16, (_reg))
#define ir_RLOAD_U32(_reg) _ir_RLOAD(_ir_CTX, IR_U32, (_reg))
#define ir_RLOAD_U64(_reg) _ir_RLOAD(_ir_CTX, IR_U64, (_reg))
#define ir_RLOAD_A(_reg) _ir_RLOAD(_ir_CTX, IR_ADDR, (_reg))
#define ir_RLOAD_C(_reg) _ir_RLOAD(_ir_CTX, IR_CHAR, (_reg))
#define ir_RLOAD_I8(_reg) _ir_RLOAD(_ir_CTX, IR_I8, (_reg))
#define ir_RLOAD_I16(_reg) _ir_RLOAD(_ir_CTX, IR_I16, (_reg))
#define ir_RLOAD_I32(_reg) _ir_RLOAD(_ir_CTX, IR_I32, (_reg))
#define ir_RLOAD_I64(_reg) _ir_RLOAD(_ir_CTX, IR_I64, (_reg))
#define ir_RLOAD_D(_reg) _ir_RLOAD(_ir_CTX, IR_DOUBLE, (_reg))
#define ir_RLOAD_F(_reg) _ir_RLOAD(_ir_CTX, IR_FLOAT, (_reg))
#define ir_RSTORE(_reg, _val) _ir_RSTORE(_ir_CTX, (_reg), (_val))
#define ir_LOAD(_type, _addr) _ir_LOAD(_ir_CTX, (_type), (_addr))
#define ir_LOAD_B(_addr) _ir_LOAD(_ir_CTX, IR_BOOL, (_addr))
#define ir_LOAD_U8(_addr) _ir_LOAD(_ir_CTX, IR_U8, (_addr))
#define ir_LOAD_U16(_addr) _ir_LOAD(_ir_CTX, IR_U16, (_addr))
#define ir_LOAD_U32(_addr) _ir_LOAD(_ir_CTX, IR_U32, (_addr))
#define ir_LOAD_U64(_addr) _ir_LOAD(_ir_CTX, IR_U64, (_addr))
#define ir_LOAD_A(_addr) _ir_LOAD(_ir_CTX, IR_ADDR, (_addr))
#define ir_LOAD_C(_addr) _ir_LOAD(_ir_CTX, IR_CHAR, (_addr))
#define ir_LOAD_I8(_addr) _ir_LOAD(_ir_CTX, IR_I8, (_addr))
#define ir_LOAD_I16(_addr) _ir_LOAD(_ir_CTX, IR_I16, (_addr))
#define ir_LOAD_I32(_addr) _ir_LOAD(_ir_CTX, IR_I32, (_addr))
#define ir_LOAD_I64(_addr) _ir_LOAD(_ir_CTX, IR_I64, (_addr))
#define ir_LOAD_D(_addr) _ir_LOAD(_ir_CTX, IR_DOUBLE, (_addr))
#define ir_LOAD_F(_addr) _ir_LOAD(_ir_CTX, IR_FLOAT, (_addr))
#define ir_STORE(_addr, _val) _ir_STORE(_ir_CTX, (_addr), (_val))
#define ir_TLS(_index, _offset) _ir_TLS(_irCTX, (_index), (_offset))
#define ir_TRAP() do {_ir_CTX->control = ir_emit1(_ir_CTX, IR_TRAP, _ir_CTX->control);} while (0)
#define ir_START() _ir_START(_ir_CTX)
#define ir_ENTRY(_num) _ir_ENTRY(_ir_CTX, (_num))
#define ir_BEGIN(_src) _ir_BEGIN(_ir_CTX, (_src))
#define ir_IF(_condition) _ir_IF(_ir_CTX, (_condition))
#define ir_IF_TRUE(_if) _ir_IF_TRUE(_ir_CTX, (_if))
#define ir_IF_TRUE_cold(_if) _ir_IF_TRUE_cold(_ir_CTX, (_if))
#define ir_IF_FALSE(_if) _ir_IF_FALSE(_ir_CTX, (_if))
#define ir_IF_FALSE_cold(_if) _ir_IF_FALSE_cold(_ir_CTX, (_if))
#define ir_END() _ir_END(_ir_CTX)
#define ir_MERGE_2(_src1, _src2) _ir_MERGE_2(_ir_CTX, (_src1), (_src2))
#define ir_MERGE_N(_n, _inputs) _ir_MERGE_N(_ir_CTX, (_n), (_inputs))
#define ir_LOOP_BEGIN(_src1) _ir_LOOP_BEGIN(_ir_CTX, (_src1))
#define ir_LOOP_END(_loop) _ir_LOOP_END(_ir_CTX, (_loop))
#define ir_SWITCH(_val) _ir_SWITCH(_ir_CTX, (_val))
#define ir_CASE_VAL(_switch, _val) _ir_CASE_VAL(_ir_CTX, (_switch), (_val))
#define ir_CASE_DEFAULT(_switch) _ir_CASE_DEFAULT(_ir_CTX, (_switch))
#define ir_RETURN(_val) _ir_RETURN(_ir_CTX, (_val))
#define ir_IJMP(_addr) _ir_IJMP(_ir_CTX, (_addr))
#define ir_UNREACHABLE() _ir_UNREACHABLE(_ir_CTX)
#define ir_GUARD(_condition, _addr) _ir_GUARD(_ir_CTX, (_condition), (_addr))
#define ir_GUARD_NOT(_condition, _addr) _ir_GUARD_NOT(_ir_CTX, (_condition), (_addr))
#define ir_SNAPSHOT(_n) _ir_SNAPSHOT(_ir_CTX, (_n))
#define ir_SNAPSHOT_ADD(_ref, _pos, _val) _ir_SNAPSHOT_ADD(_ir_CTX, (_ref), (_pos), (_val))
#define ir_EXITCALL(_func) _ir_EXITCALL(_ir_CTX,(_func))
#define ir_END_list(_list) do { _list = _ir_END_LIST(_ir_CTX, _list); } while (0)
#define ir_MERGE_list(_list) _ir_MERGE_LIST(_ir_CTX, (_list))
#define ir_MERGE_WITH(_src2) do {ir_ref end = ir_END(); ir_MERGE_2(end, _src2);} while (0)
#define ir_MERGE_WITH_EMPTY_TRUE(_if) do {ir_ref end = ir_END(); ir_IF_TRUE(_if); ir_MERGE_2(end, ir_END());} while (0)
#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_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);
ir_ref _ir_CALL(ir_ctx *ctx, ir_type type, ir_ref func);
ir_ref _ir_CALL_1(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1);
ir_ref _ir_CALL_2(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2);
ir_ref _ir_CALL_3(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3);
ir_ref _ir_CALL_4(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4);
ir_ref _ir_CALL_5(ir_ctx *ctx, ir_type type, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4, ir_ref arg5);
void _ir_TAILCALL(ir_ctx *ctx, ir_ref func);
void _ir_TAILCALL_1(ir_ctx *ctx, ir_ref func, ir_ref arg1);
void _ir_TAILCALL_2(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2);
void _ir_TAILCALL_3(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3);
void _ir_TAILCALL_4(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4);
void _ir_TAILCALL_5(ir_ctx *ctx, ir_ref func, ir_ref arg1, ir_ref arg2, ir_ref arg3, ir_ref arg4, ir_ref arg5);
ir_ref _ir_ALLOCA(ir_ctx *ctx, ir_ref size);
void _ir_AFREE(ir_ctx *ctx, ir_ref size);
ir_ref _ir_VLOAD(ir_ctx *ctx, ir_type type, ir_ref var);
void _ir_VSTORE(ir_ctx *ctx, ir_ref var, ir_ref val);
ir_ref _ir_RLOAD(ir_ctx *ctx, ir_type type, ir_ref reg);
void _ir_RSTORE(ir_ctx *ctx, ir_ref reg, ir_ref val);
ir_ref _ir_LOAD(ir_ctx *ctx, ir_type type, ir_ref addr);
void _ir_STORE(ir_ctx *ctx, ir_ref addr, ir_ref val);
void _ir_START(ir_ctx *ctx);
void _ir_ENTRY(ir_ctx *ctx, ir_ref num);
void _ir_BEGIN(ir_ctx *ctx, ir_ref src);
ir_ref _ir_END(ir_ctx *ctx);
ir_ref _ir_END_LIST(ir_ctx *ctx, ir_ref list);
ir_ref _ir_IF(ir_ctx *ctx, ir_ref condition);
void _ir_IF_TRUE(ir_ctx *ctx, ir_ref if_ref);
void _ir_IF_TRUE_cold(ir_ctx *ctx, ir_ref if_ref);
void _ir_IF_FALSE(ir_ctx *ctx, ir_ref if_ref);
void _ir_IF_FALSE_cold(ir_ctx *ctx, ir_ref if_ref);
void _ir_MERGE_2(ir_ctx *ctx, ir_ref src1, ir_ref src2);
void _ir_MERGE_N(ir_ctx *ctx, ir_ref n, ir_ref *inputs);
void _ir_MERGE_LIST(ir_ctx *ctx, ir_ref list);
ir_ref _ir_LOOP_BEGIN(ir_ctx *ctx, ir_ref src1);
ir_ref _ir_LOOP_END(ir_ctx *ctx, ir_ref loop);
ir_ref _ir_TLS(ir_ctx *ctx, ir_ref index, ir_ref offset);
void _ir_UNREACHABLE(ir_ctx *ctx);
ir_ref _ir_SWITCH(ir_ctx *ctx, ir_ref val);
void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val);
void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref);
void _ir_RETURN(ir_ctx *ctx, ir_ref val);
void _ir_IJMP(ir_ctx *ctx, ir_ref addr);
void _ir_GUARD(ir_ctx *ctx, ir_ref condition, ir_ref addr);
void _ir_GUARD_NOT(ir_ctx *ctx, ir_ref condition, ir_ref addr);
ir_ref _ir_SNAPSHOT(ir_ctx *ctx, ir_ref n);
void _ir_SNAPSHOT_ADD(ir_ctx *ctx, ir_ref snapshot, ir_ref pos, ir_ref val);
ir_ref _ir_EXITCALL(ir_ctx *ctx, ir_ref func);
#endif /* IR_BUILDER_H */

104
ir_test.c
View File

@ -6,6 +6,7 @@
*/
#include "ir.h"
#include "ir_builder.h"
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
@ -15,88 +16,43 @@
void gen_mandelbrot(ir_ctx *ctx)
{
ir_ref start = ir_emit0(ctx, IR_START);
ir_ref ret;
ir_START();
ir_ref x = ir_PARAM(IR_DOUBLE, "x", 1);
ir_ref y = ir_PARAM(IR_DOUBLE, "y", 2);
ir_ref cr = ir_SUB_D(y, ir_CONST_DOUBLE(0.5));
ir_ref ci = ir_COPY_D(x);
ir_ref zi = ir_COPY_D(ir_CONST_DOUBLE(0.0));
ir_ref zr = ir_COPY_D(ir_CONST_DOUBLE(0.0));
ir_ref i = ir_COPY_D(ir_CONST_I32(0));
ir_ref x_1 = ir_param(ctx, IR_DOUBLE, start, "x", 0);
ir_ref y_1 = ir_param(ctx, IR_DOUBLE, start, "y", 1);
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 cr = ir_var(ctx, IR_DOUBLE, start, "cr");
ir_ref cr_1 = ir_fold2(ctx, IR_OPT(IR_SUB, IR_DOUBLE), y_1,
ir_const_double(ctx, 0.5));
ir_bind(ctx, cr, cr_1); // cr=cr_1
ir_ref ci = ir_var(ctx, IR_DOUBLE, start, "ci");
ir_bind(ctx, ci, x_1); // cr=cr_1, ci=x_1
ir_ref zi = ir_var(ctx, IR_DOUBLE, start, "zi");
ir_bind(ctx, zi, ir_const_double(ctx, 0.0)); // cr=cr_1, ci=x_1, zi=0.0
ir_ref zr = ir_var(ctx, IR_DOUBLE, start, "zr");
ir_bind(ctx, zr, ir_const_double(ctx, 0.0)); // cr=cr_1, ci=x_1, zi=0.0, zr=0.0
ir_ref i = ir_var(ctx, IR_I32, start, "i");
ir_bind(ctx, i, ir_const_i32(ctx, 0)); // cr=cr_1, ci=x_1, zi=0.0, zr=0.0, i=0
ir_ref i_2 = ir_ADD_I32(i_1, ir_CONST_I32(1));
ir_ref temp = ir_MUL_D(zr_1, zi_1);
ir_ref zr2 = ir_MUL_D(zr_1, zr_1);
ir_ref zi2 = ir_MUL_D(zi_1, zi_1);
ir_ref zr_2 = ir_ADD_D(ir_SUB_D(zr2, zi2), cr);
ir_ref zi_2 = ir_ADD_D(ir_ADD_D(temp, temp), ci);
ir_ref if_1 = ir_IF(ir_GT(ir_ADD_D(zi2, zr2), ir_CONST_DOUBLE(16.0)));
ir_IF_TRUE(if_1);
ir_RETURN(i_2);
ir_IF_FALSE(if_1);
ir_ref if_2 = ir_IF(ir_GT(i_2, ir_CONST_I32(1000)));
ir_IF_TRUE(if_2);
ir_RETURN(ir_CONST_I32(0));
ir_IF_FALSE(if_2);
ir_ref loop_end = ir_LOOP_END(loop);
ir_ref e_1 = ir_emit1(ctx, IR_END, start);
ir_ref l_1 = ir_emit1(ctx, IR_LOOP_BEGIN, e_1);
ir_ref zi_1 = ir_emit2(ctx, IR_OPT(IR_PHI, IR_DOUBLE), l_1,
ir_const_double(ctx, 0.0));
ir_bind(ctx, zi, zi_1); // cr=cr_1, ci=x_1, zi=zi_1, zr=0.0, i=0
ir_ref zr_1 = ir_emit2(ctx, IR_OPT(IR_PHI, IR_DOUBLE), l_1,
ir_const_double(ctx, 0.0));
ir_bind(ctx, zr, zr_1); // cr=cr_1, ci=x_1, zi=zi_1, zr=zr_1, i=0
ir_ref i_1 = ir_emit2(ctx, IR_OPT(IR_PHI, IR_I32), l_1,
ir_const_i32(ctx, 0));
ir_bind(ctx, i, i_1); // cr=cr_1, ci=x_1, zi=zi_1, zr=zr_1, i=i_1
ir_ref i_2 = ir_emit2(ctx, IR_OPT(IR_ADD, IR_I32), i_1,
ir_const_i32(ctx, 1));
ir_bind(ctx, i, i_2); // cr=cr_1, ci=x_1, zi=zi_1, zr=zr_1, i=i_2
ir_ref temp = ir_var(ctx, IR_DOUBLE, l_1, "temp");
ir_ref temp_1 = ir_fold2(ctx, IR_OPT(IR_MUL, IR_DOUBLE), zr_1, zi_1);
ir_bind(ctx, temp, temp_1); // ... temp=temp_1
ir_ref zr2 = ir_var(ctx, IR_DOUBLE, l_1, "zr2");
ir_ref zr2_1 = ir_fold2(ctx, IR_OPT(IR_MUL, IR_DOUBLE), zr_1, zr_1);
ir_bind(ctx, zr2, zr2_1); // ... temp=temp_1, zr2=zr2_1
ir_ref zi2 = ir_var(ctx, IR_DOUBLE, l_1, "zi2");
ir_ref zi2_1 = ir_fold2(ctx, IR_OPT(IR_MUL, IR_DOUBLE), zi_1, zi_1);
ir_bind(ctx, zi2, zi2_1); // ... temp=temp_1, zr2=zr2_1, zi2=zi2_1
ir_ref zr_2 = ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE),
ir_fold2(ctx, IR_OPT(IR_SUB, IR_DOUBLE), zr2_1, zi2_1),
cr_1);
ir_bind(ctx, zr, zr_2); // cr=cr_1, ci=x_1, zi=zi_1, zr=zr_2, i=i_2
ir_ref zi_2 = ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE),
ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE), temp_1, temp_1),
x_1);
ir_bind(ctx, zi, zi_2); // cr=cr_1, ci=x_1, zi=zi_2 zr=zr_2, i=i_2
ir_ref if_1 = ir_emit2(ctx, IR_IF, l_1,
ir_fold2(ctx, IR_OPT(IR_GT, IR_BOOL),
ir_fold2(ctx, IR_OPT(IR_ADD, IR_DOUBLE), zi2_1, zr2_1),
ir_const_double(ctx, 16.0)));
ir_ref r_1 = ir_emit1(ctx, IR_IF_TRUE, if_1);
ret = ir_emit2(ctx, IR_RETURN, r_1, i_2);
ir_ref r_2 = ir_emit1(ctx, IR_IF_FALSE, if_1);
ir_ref if_2 = ir_emit2(ctx, IR_IF, r_2,
ir_fold2(ctx, IR_OPT(IR_GT, IR_BOOL), i_2, ir_const_i32(ctx, 1000)));
ir_ref r_3 = ir_emit1(ctx, IR_IF_TRUE, if_2);
ret = ir_emit3(ctx, IR_RETURN, r_3, ir_const_i32(ctx, 0), ret);
ir_ref r_4 = ir_emit1(ctx, IR_IF_FALSE, if_2);
ir_ref l_2 = ir_emit2(ctx, IR_LOOP_END, r_4, l_1);
ir_set_op2(ctx, l_1, l_2);
/* close loop */
ir_set_op2(ctx, loop, loop_end);
ir_set_op3(ctx, zi_1, zi_2);
ir_set_op3(ctx, zr_1, zr_2);
ir_set_op3(ctx, i_1, i_2);
ir_set_op1(ctx, start, ret);
}
typedef int (*mandelbrot_t)(double, double);
void run(mandelbrot_t mandelbrot)