mirror of
https://github.com/danog/ir.git
synced 2025-01-21 21:21:19 +01:00
Implement code generation for MIN and MAX instructions
This commit is contained in:
parent
8ccb7bc13a
commit
bae7df6a5f
2
TODO
2
TODO
@ -35,7 +35,7 @@
|
||||
- separate INT and FP allocation phases (for performance)
|
||||
|
||||
? code generation
|
||||
- MIN, MAX, COND
|
||||
- COND
|
||||
- CAST
|
||||
- return merge/split
|
||||
? binary code emission without DynAsm
|
||||
|
133
ir_x86.dasc
133
ir_x86.dasc
@ -95,6 +95,23 @@
|
||||
|| }
|
||||
|.endmacro
|
||||
|
||||
|.macro ASM_REG_REG_OP2, op, type, dst, src
|
||||
|| switch (ir_type_size[type]) {
|
||||
|| case 1:
|
||||
|| case 2:
|
||||
| op Rw(dst), Rw(src)
|
||||
|| break;
|
||||
|| case 4:
|
||||
| op Rd(dst), Rd(src)
|
||||
|| break;
|
||||
|| case 8:
|
||||
| op Rq(dst), Rq(src)
|
||||
|| break;
|
||||
|| default:
|
||||
|| IR_ASSERT(0);
|
||||
|| }
|
||||
|.endmacro
|
||||
|
||||
|.macro ASM_REG_IMM_OP, op, type, dst, src
|
||||
|| switch (ir_type_size[type]) {
|
||||
|| case 1:
|
||||
@ -556,6 +573,7 @@ typedef enum _ir_rule {
|
||||
// op1=mem, op2=reg(GP)|imm
|
||||
IR_CMP_AND_BRANCH_FP, // op1=reg(FP), op2=reg(FP)|mem
|
||||
IR_OVERFLOW_AND_BRANCH,
|
||||
IR_MIN_MAX_INT,
|
||||
|
||||
IR_MEM_OP_INT,
|
||||
IR_MEM_INC,
|
||||
@ -803,6 +821,7 @@ bool ir_result_reuses_op1_reg(ir_ctx *ctx, ir_ref ref)
|
||||
case IR_MOD_PWR2:
|
||||
case IR_OP_INT:
|
||||
case IR_OP_FP:
|
||||
case IR_MIN_MAX_INT:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -824,6 +843,8 @@ uint8_t ir_get_use_flags(ir_ctx *ctx, ir_ref ref, int op_num)
|
||||
case IR_MOD_INT:
|
||||
case IR_IF_INT:
|
||||
return (op_num == 2) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
||||
case IR_MIN_MAX_INT:
|
||||
return (op_num == 1) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
||||
case IR_CMP_FP:
|
||||
insn = &ctx->ir_base[ref];
|
||||
if (insn->op == IR_LT || insn->op == IR_LE) {
|
||||
@ -906,6 +927,7 @@ int ir_get_temporary_regs(ir_ctx *ctx, ir_ref ref, ir_tmp_reg *tmp_regs)
|
||||
break;
|
||||
case IR_DIV_INT:
|
||||
case IR_MOD_INT:
|
||||
case IR_MIN_MAX_INT:
|
||||
insn = &ctx->ir_base[ref];
|
||||
if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) {
|
||||
tmp_regs[0].num = 2;
|
||||
@ -1271,10 +1293,6 @@ binop_fp:
|
||||
}
|
||||
return IR_MOD_INT;
|
||||
// case IR_CAST:
|
||||
// case IR_ADD_OV:
|
||||
// case IR_SUB_OV:
|
||||
// case IR_MUL_OV:
|
||||
// case IR_OVERFLOW:
|
||||
case IR_BSWAP:
|
||||
case IR_NOT:
|
||||
if (insn->type == IR_BOOL) {
|
||||
@ -1370,8 +1388,14 @@ binop_fp:
|
||||
return IR_SHIFT_CONST;
|
||||
}
|
||||
return IR_SHIFT;
|
||||
// case IR_MIN:
|
||||
// case IR_MAX:
|
||||
case IR_MIN:
|
||||
case IR_MAX:
|
||||
if (IR_IS_TYPE_INT(insn->type)) {
|
||||
return IR_MIN_MAX_INT;
|
||||
} else {
|
||||
goto binop_fp;
|
||||
}
|
||||
break;
|
||||
// case IR_COND:
|
||||
case IR_COPY:
|
||||
if (IR_IS_TYPE_INT(insn->type)) {
|
||||
@ -1962,6 +1986,63 @@ static void ir_emit_binop_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_emit_min_max_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
{
|
||||
ir_backend_data *data = ctx->data;
|
||||
dasm_State **Dst = &data->dasm_state;
|
||||
ir_type type = insn->type;
|
||||
ir_ref op1 = insn->op1;
|
||||
ir_ref op2 = insn->op2;
|
||||
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
||||
ir_reg op1_reg = ctx->regs[def][1];
|
||||
ir_reg op2_reg = ctx->regs[def][2];
|
||||
|
||||
IR_ASSERT(def_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
||||
|
||||
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
||||
op1_reg &= ~IR_REG_SPILL_LOAD;
|
||||
ir_emit_load(ctx, type, op1_reg, op1);
|
||||
}
|
||||
if (def_reg != op1_reg) {
|
||||
if (op1_reg >= 0) {
|
||||
ir_emit_mov(ctx, type, def_reg, op1_reg);
|
||||
} else {
|
||||
ir_emit_load(ctx, type, def_reg, op1);
|
||||
}
|
||||
}
|
||||
|
||||
if (op1 == op2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IR_IS_CONST_REF(op2)) {
|
||||
ir_emit_load(ctx, type, op2_reg, op2);
|
||||
} else if (op2_reg & IR_REG_SPILL_LOAD) {
|
||||
op2_reg &= ~IR_REG_SPILL_LOAD;
|
||||
ir_emit_load(ctx, type, op2_reg, op2);
|
||||
}
|
||||
|
||||
| ASM_REG_REG_OP cmp, type, def_reg, op2_reg
|
||||
if (insn->op == IR_MIN) {
|
||||
if (IR_IS_TYPE_SIGNED(type)) {
|
||||
| ASM_REG_REG_OP2 cmovl, type, def_reg, op2_reg
|
||||
} else {
|
||||
| ASM_REG_REG_OP2 cmovb, type, def_reg, op2_reg
|
||||
}
|
||||
} else {
|
||||
IR_ASSERT(insn->op == IR_MAX);
|
||||
if (IR_IS_TYPE_SIGNED(type)) {
|
||||
| ASM_REG_REG_OP2 cmovg, type, def_reg, op2_reg
|
||||
} else {
|
||||
| ASM_REG_REG_OP2 cmova, type, def_reg, op2_reg
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
||||
ir_emit_store(ctx, type, def, def_reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_emit_overflow(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
{
|
||||
ir_backend_data *data = ctx->data;
|
||||
@ -2518,20 +2599,7 @@ static void ir_emit_abs_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
|
||||
ir_emit_mov(ctx, insn->type, def_reg, op1_reg);
|
||||
| ASM_REG_OP neg, insn->type, def_reg
|
||||
switch (ir_type_size[insn->type]) {
|
||||
case 1:
|
||||
case 2:
|
||||
| cmovs Rw(def_reg), Rw(op1_reg)
|
||||
break;
|
||||
case 4:
|
||||
| cmovs Rd(def_reg), Rd(op1_reg)
|
||||
break;
|
||||
case 8:
|
||||
| cmovs Rq(def_reg), Rq(op1_reg)
|
||||
break;
|
||||
default:
|
||||
IR_ASSERT(0);
|
||||
}
|
||||
| ASM_REG_REG_OP2, cmovs, type, def_reg, op1_reg
|
||||
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
||||
ir_emit_store(ctx, type, def, def_reg);
|
||||
}
|
||||
@ -2816,6 +2884,12 @@ static void ir_emit_binop_sse2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
case IR_DIV:
|
||||
| ASM_SSE2_REG_REG_OP divss, divsd, type, def_reg, op2_reg
|
||||
break;
|
||||
case IR_MIN:
|
||||
| ASM_SSE2_REG_REG_OP minss, minsd, type, def_reg, op2_reg
|
||||
break;
|
||||
case IR_MAX:
|
||||
| ASM_SSE2_REG_REG_OP maxss, maxsd, type, def_reg, op2_reg
|
||||
break;
|
||||
default:
|
||||
IR_ASSERT(0 && "NIY binary op");
|
||||
break;
|
||||
@ -2834,6 +2908,12 @@ static void ir_emit_binop_sse2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
case IR_DIV:
|
||||
| ASM_SSE2_REG_MREF_OP divss, divsd, type, def_reg, op2
|
||||
break;
|
||||
case IR_MIN:
|
||||
| ASM_SSE2_REG_MREF_OP minss, minsd, type, def_reg, op2
|
||||
break;
|
||||
case IR_MAX:
|
||||
| ASM_SSE2_REG_MREF_OP maxss, maxsd, type, def_reg, op2
|
||||
break;
|
||||
default:
|
||||
IR_ASSERT(0 && "NIY binary op");
|
||||
break;
|
||||
@ -2884,6 +2964,12 @@ static void ir_emit_binop_avx(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
case IR_DIV:
|
||||
| ASM_AVX_REG_REG_REG_OP vdivss, vdivsd, type, def_reg, op1_reg, op2_reg
|
||||
break;
|
||||
case IR_MIN:
|
||||
| ASM_AVX_REG_REG_REG_OP vminss, vminsd, type, def_reg, op1_reg, op2_reg
|
||||
break;
|
||||
case IR_MAX:
|
||||
| ASM_AVX_REG_REG_REG_OP vmaxss, vmaxsd, type, def_reg, op1_reg, op2_reg
|
||||
break;
|
||||
default:
|
||||
IR_ASSERT(0 && "NIY binary op");
|
||||
break;
|
||||
@ -2901,6 +2987,10 @@ static void ir_emit_binop_avx(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
||||
break;
|
||||
case IR_DIV:
|
||||
| ASM_AVX_REG_REG_MREF_OP vdivss, vdivsd, type, def_reg, op1_reg, op2
|
||||
case IR_MIN:
|
||||
| ASM_AVX_REG_REG_MREF_OP vminss, vminsd, type, def_reg, op1_reg, op2
|
||||
case IR_MAX:
|
||||
| ASM_AVX_REG_REG_MREF_OP vmaxss, vmaxsd, type, def_reg, op1_reg, op2
|
||||
break;
|
||||
default:
|
||||
IR_ASSERT(0 && "NIY binary op");
|
||||
@ -5670,6 +5760,9 @@ void *ir_emit(ir_ctx *ctx, size_t *size)
|
||||
case IR_SWITCH:
|
||||
ir_emit_switch(ctx, i, insn);
|
||||
break;
|
||||
case IR_MIN_MAX_INT:
|
||||
ir_emit_min_max_int(ctx, i, insn);
|
||||
break;
|
||||
case IR_OVERFLOW:
|
||||
ir_emit_overflow(ctx, i, insn);
|
||||
break;
|
||||
|
18
tests/debug/memop_008.irt
Normal file
18
tests/debug/memop_008.irt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
008: Memory update (LOAD/STORE)
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
int32_t c = 0x80;
|
||||
l_1 = START(l_4);
|
||||
uintptr_t p = PARAM(l_1, "p", 1);
|
||||
int32_t z, l_2 = LOAD(l_1, p);
|
||||
int32_t ret = AND(z, c);
|
||||
l_3 = STORE(l_2, p, ret);
|
||||
l_4 = RETURN(l_3);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
andl $0x80, (%rdi)
|
||||
retq
|
18
tests/x86_64/add_ov_001.irt
Normal file
18
tests/x86_64/add_ov_001.irt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
001: add_ov function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
int32_t c = 2;
|
||||
l_1 = START(l_2);
|
||||
int32_t x = PARAM(l_1, "x", 1);
|
||||
int32_t ret = ADD_OV(x, c);
|
||||
bool ov = OVERFLOW(ret);
|
||||
l_2 = RETURN(l_1, ov);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
addl $2, %edi
|
||||
seto %al
|
||||
retq
|
18
tests/x86_64/add_ov_002.irt
Normal file
18
tests/x86_64/add_ov_002.irt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
002: add_ov function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
uint32_t c = 2;
|
||||
l_1 = START(l_2);
|
||||
uint32_t x = PARAM(l_1, "x", 1);
|
||||
uint32_t ret = ADD_OV(x, c);
|
||||
bool ov = OVERFLOW(ret);
|
||||
l_2 = RETURN(l_1, ov);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
addl $2, %edi
|
||||
setb %al
|
||||
retq
|
29
tests/x86_64/add_ov_003.irt
Normal file
29
tests/x86_64/add_ov_003.irt
Normal file
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
003: add_ov function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
int32_t c_1 = 0;
|
||||
int32_t c = 2;
|
||||
l_1 = START(l_8);
|
||||
int32_t x = PARAM(l_1, "x", 1);
|
||||
int32_t ret1 = ADD_OV(x, c);
|
||||
bool ov = OVERFLOW(ret1);
|
||||
l_2 = IF(l_1, ov);
|
||||
l_3 = IF_TRUE(l_2);
|
||||
l_4 = END(l_3);
|
||||
l_5 = IF_FALSE(l_2);
|
||||
l_6 = END(l_5);
|
||||
l_7 = MERGE(l_4, l_6);
|
||||
int32_t ret = PHI(l_7, c_1, ret1);
|
||||
l_8 = RETURN(l_7, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
movl %edi, %eax
|
||||
addl $2, %eax
|
||||
jno .L1
|
||||
xorl %eax, %eax
|
||||
.L1:
|
||||
retq
|
16
tests/x86_64/min_001.irt
Normal file
16
tests/x86_64/min_001.irt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
001: min function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
l_1 = START(l_4);
|
||||
float x = PARAM(l_1, "x", 1);
|
||||
float y = PARAM(l_1, "y", 2);
|
||||
float ret = MIN(x, y);
|
||||
l_4 = RETURN(l_1, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
minss %xmm1, %xmm0
|
||||
retq
|
16
tests/x86_64/min_002.irt
Normal file
16
tests/x86_64/min_002.irt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
002: min function
|
||||
--ARGS--
|
||||
-S -mavx
|
||||
--CODE--
|
||||
{
|
||||
l_1 = START(l_4);
|
||||
float x = PARAM(l_1, "x", 1);
|
||||
float y = PARAM(l_1, "y", 2);
|
||||
float ret = MIN(x, y);
|
||||
l_4 = RETURN(l_1, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
vminss %xmm0, %xmm1, %xmm0
|
||||
retq
|
16
tests/x86_64/min_003.irt
Normal file
16
tests/x86_64/min_003.irt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
003: min function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
l_1 = START(l_4);
|
||||
double x = PARAM(l_1, "x", 1);
|
||||
double y = PARAM(l_1, "y", 2);
|
||||
double ret = MIN(x, y);
|
||||
l_4 = RETURN(l_1, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
minsd %xmm1, %xmm0
|
||||
retq
|
16
tests/x86_64/min_004.irt
Normal file
16
tests/x86_64/min_004.irt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
004: min function
|
||||
--ARGS--
|
||||
-S -mavx
|
||||
--CODE--
|
||||
{
|
||||
l_1 = START(l_4);
|
||||
double x = PARAM(l_1, "x", 1);
|
||||
double y = PARAM(l_1, "y", 2);
|
||||
double ret = MIN(x, y);
|
||||
l_4 = RETURN(l_1, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
vminsd %xmm0, %xmm1, %xmm0
|
||||
retq
|
18
tests/x86_64/min_005.irt
Normal file
18
tests/x86_64/min_005.irt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
005: min function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
l_1 = START(l_4);
|
||||
int32_t x = PARAM(l_1, "x", 1);
|
||||
int32_t y = PARAM(l_1, "y", 2);
|
||||
int32_t ret = MIN(x, y);
|
||||
l_4 = RETURN(l_1, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
movl %esi, %eax
|
||||
cmpl %edi, %eax
|
||||
cmovll %edi, %eax
|
||||
retq
|
18
tests/x86_64/min_006.irt
Normal file
18
tests/x86_64/min_006.irt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
006: min function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
l_1 = START(l_4);
|
||||
uint8_t x = PARAM(l_1, "x", 1);
|
||||
uint8_t y = PARAM(l_1, "y", 2);
|
||||
uint8_t ret = MIN(x, y);
|
||||
l_4 = RETURN(l_1, ret);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
movb %sil, %al
|
||||
cmpb %dil, %al
|
||||
cmovbw %di, %ax
|
||||
retq
|
18
tests/x86_64/mul_ov_001.irt
Normal file
18
tests/x86_64/mul_ov_001.irt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
001: mul_ov function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
int32_t c = 2;
|
||||
l_1 = START(l_2);
|
||||
int32_t x = PARAM(l_1, "x", 1);
|
||||
int32_t ret = MUL_OV(x, c);
|
||||
bool ov = OVERFLOW(ret);
|
||||
l_2 = RETURN(l_1, ov);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
imull $2, %edi, %edi
|
||||
seto %al
|
||||
retq
|
20
tests/x86_64/mul_ov_002.irt
Normal file
20
tests/x86_64/mul_ov_002.irt
Normal file
@ -0,0 +1,20 @@
|
||||
--TEST--
|
||||
002: mul_ov function
|
||||
--ARGS--
|
||||
-S
|
||||
--CODE--
|
||||
{
|
||||
uint32_t c = 2;
|
||||
l_1 = START(l_2);
|
||||
uint32_t x = PARAM(l_1, "x", 1);
|
||||
uint32_t ret = MUL_OV(x, c);
|
||||
bool ov = OVERFLOW(ret);
|
||||
l_2 = RETURN(l_1, ov);
|
||||
}
|
||||
--EXPECT--
|
||||
test:
|
||||
movl %edi, %eax
|
||||
movl $2, %edx
|
||||
mull %edx
|
||||
setb %al
|
||||
retq
|
Loading…
x
Reference in New Issue
Block a user