From bae7df6a5fc6e75352e1d7ff3e38e52eb2e5d811 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 19 May 2022 17:03:00 +0300 Subject: [PATCH] Implement code generation for MIN and MAX instructions --- TODO | 2 +- ir_x86.dasc | 133 ++++++++++++++++++++++++++++++------ tests/debug/memop_008.irt | 18 +++++ tests/x86_64/add_ov_001.irt | 18 +++++ tests/x86_64/add_ov_002.irt | 18 +++++ tests/x86_64/add_ov_003.irt | 29 ++++++++ tests/x86_64/min_001.irt | 16 +++++ tests/x86_64/min_002.irt | 16 +++++ tests/x86_64/min_003.irt | 16 +++++ tests/x86_64/min_004.irt | 16 +++++ tests/x86_64/min_005.irt | 18 +++++ tests/x86_64/min_006.irt | 18 +++++ tests/x86_64/mul_ov_001.irt | 18 +++++ tests/x86_64/mul_ov_002.irt | 20 ++++++ 14 files changed, 335 insertions(+), 21 deletions(-) create mode 100644 tests/debug/memop_008.irt create mode 100644 tests/x86_64/add_ov_001.irt create mode 100644 tests/x86_64/add_ov_002.irt create mode 100644 tests/x86_64/add_ov_003.irt create mode 100644 tests/x86_64/min_001.irt create mode 100644 tests/x86_64/min_002.irt create mode 100644 tests/x86_64/min_003.irt create mode 100644 tests/x86_64/min_004.irt create mode 100644 tests/x86_64/min_005.irt create mode 100644 tests/x86_64/min_006.irt create mode 100644 tests/x86_64/mul_ov_001.irt create mode 100644 tests/x86_64/mul_ov_002.irt diff --git a/TODO b/TODO index 41a0ee0..ea17a91 100644 --- a/TODO +++ b/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 diff --git a/ir_x86.dasc b/ir_x86.dasc index 380e153..500c0ab 100644 --- a/ir_x86.dasc +++ b/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; diff --git a/tests/debug/memop_008.irt b/tests/debug/memop_008.irt new file mode 100644 index 0000000..b1cae31 --- /dev/null +++ b/tests/debug/memop_008.irt @@ -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 diff --git a/tests/x86_64/add_ov_001.irt b/tests/x86_64/add_ov_001.irt new file mode 100644 index 0000000..3a8843c --- /dev/null +++ b/tests/x86_64/add_ov_001.irt @@ -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 diff --git a/tests/x86_64/add_ov_002.irt b/tests/x86_64/add_ov_002.irt new file mode 100644 index 0000000..e0c7a98 --- /dev/null +++ b/tests/x86_64/add_ov_002.irt @@ -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 diff --git a/tests/x86_64/add_ov_003.irt b/tests/x86_64/add_ov_003.irt new file mode 100644 index 0000000..4247587 --- /dev/null +++ b/tests/x86_64/add_ov_003.irt @@ -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 diff --git a/tests/x86_64/min_001.irt b/tests/x86_64/min_001.irt new file mode 100644 index 0000000..f1ad373 --- /dev/null +++ b/tests/x86_64/min_001.irt @@ -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 diff --git a/tests/x86_64/min_002.irt b/tests/x86_64/min_002.irt new file mode 100644 index 0000000..4237ee1 --- /dev/null +++ b/tests/x86_64/min_002.irt @@ -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 diff --git a/tests/x86_64/min_003.irt b/tests/x86_64/min_003.irt new file mode 100644 index 0000000..4ac0a47 --- /dev/null +++ b/tests/x86_64/min_003.irt @@ -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 diff --git a/tests/x86_64/min_004.irt b/tests/x86_64/min_004.irt new file mode 100644 index 0000000..31a8b05 --- /dev/null +++ b/tests/x86_64/min_004.irt @@ -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 diff --git a/tests/x86_64/min_005.irt b/tests/x86_64/min_005.irt new file mode 100644 index 0000000..9a1388b --- /dev/null +++ b/tests/x86_64/min_005.irt @@ -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 diff --git a/tests/x86_64/min_006.irt b/tests/x86_64/min_006.irt new file mode 100644 index 0000000..1fbce74 --- /dev/null +++ b/tests/x86_64/min_006.irt @@ -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 diff --git a/tests/x86_64/mul_ov_001.irt b/tests/x86_64/mul_ov_001.irt new file mode 100644 index 0000000..bff7507 --- /dev/null +++ b/tests/x86_64/mul_ov_001.irt @@ -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 diff --git a/tests/x86_64/mul_ov_002.irt b/tests/x86_64/mul_ov_002.irt new file mode 100644 index 0000000..83acd59 --- /dev/null +++ b/tests/x86_64/mul_ov_002.irt @@ -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