x86_64: Optimize integer comparison with zero

This commit is contained in:
Dmitry Stogov 2022-04-08 10:49:22 +03:00
parent 1210b5814e
commit 552aeec7d5
12 changed files with 278 additions and 7 deletions

View File

@ -1960,6 +1960,7 @@ void ir_emit_cmp_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg reg;
ir_op op;
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
@ -1978,12 +1979,34 @@ void ir_emit_cmp_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
}
}
op = insn->op;
if (op2_reg >= 0) {
| ASM_REF_REG_OP cmp, type, insn->op1, op2_reg
} else if (op1_reg >= 0) {
| ASM_REG_REF_OP cmp, type, op1_reg, insn->op2
if (IR_IS_CONST_REF(insn->op2) && ctx->ir_base[insn->op2].val.u64 == 0) {
if (op == IR_ULT) {
/* always false */
if (def_reg >= 0) {
| xor Ra(def_reg), Ra(def_reg)
} else {
| ASM_REF_IMM_OP mov, insn->type, def, 0
}
return;
} else if (op == IR_UGE) {
/* always true */
| ASM_REF_IMM_OP mov, insn->type, def, 1
return;
} else if (op == IR_ULE) {
op = IR_EQ;
} else if (op == IR_UGT) {
op = IR_NE;
}
| ASM_REG_REG_OP test, type, op1_reg, op1_reg
} else {
| ASM_REG_REF_OP cmp, type, op1_reg, insn->op2
}
}
switch (insn->op) {
switch (op) {
case IR_EQ:
| sete Rb(reg)
break;
@ -2082,12 +2105,74 @@ void ir_emit_cmp_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
}
}
static void ir_emit_jmp_true(ir_ctx *ctx, int b, ir_ref def)
{
ir_use_list *use_list;
ir_insn *use_insn;
ir_ref i, *p, use, n;
int true_block = 0, false_block = 0, next_block;
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
use_list = &ctx->use_lists[def];
n = use_list->count;
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
use = *p;
use_insn = &ctx->ir_base[use];
if (use_insn->op == IR_IF_TRUE) {
true_block = ir_skip_empty_blocks(ctx, ctx->bb_num[use]);
} else if (use_insn->op == IR_IF_FALSE) {
false_block = ir_skip_empty_blocks(ctx, ctx->bb_num[use]);
} else {
IR_ASSERT(0);
}
}
IR_ASSERT(true_block && false_block);
next_block = ir_skip_empty_blocks(ctx, b + 1);
if (true_block != next_block) {
| jmp =>true_block
}
}
static void ir_emit_jmp_false(ir_ctx *ctx, int b, ir_ref def)
{
ir_use_list *use_list;
ir_insn *use_insn;
ir_ref i, *p, use, n;
int true_block = 0, false_block = 0, next_block;
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
use_list = &ctx->use_lists[def];
n = use_list->count;
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
use = *p;
use_insn = &ctx->ir_base[use];
if (use_insn->op == IR_IF_TRUE) {
true_block = ir_skip_empty_blocks(ctx, ctx->bb_num[use]);
} else if (use_insn->op == IR_IF_FALSE) {
false_block = ir_skip_empty_blocks(ctx, ctx->bb_num[use]);
} else {
IR_ASSERT(0);
}
}
IR_ASSERT(true_block && false_block);
next_block = ir_skip_empty_blocks(ctx, b + 1);
if (false_block != next_block) {
| jmp =>false_block
}
}
static void ir_emit_jcc(ir_ctx *ctx, uint8_t op, int b, ir_ref def, ir_insn *insn, bool int_cmp)
{
ir_use_list *use_list;
ir_insn *use_insn;
ir_ref i, *p, use, n;
int true_block = 0, false_block = 0, next_block;
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
use_list = &ctx->use_lists[def];
n = use_list->count;
@ -2117,8 +2202,6 @@ static void ir_emit_jcc(ir_ctx *ctx, uint8_t op, int b, ir_ref def, ir_insn *ins
false_block = 0;
}
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
if (int_cmp) {
switch (op) {
case IR_EQ:
@ -2195,6 +2278,7 @@ void ir_emit_cmp_and_branch_int(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
ir_type type = ctx->ir_base[cmp_insn->op1].type;
ir_reg op1_reg = ir_ref_reg(ctx, cmp_insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, cmp_insn->op2);
ir_op op;
if (op1_reg < 0 && op2_reg < 0) {
if (IR_IS_CONST_REF(cmp_insn->op2)) {
@ -2208,12 +2292,31 @@ void ir_emit_cmp_and_branch_int(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
op = cmp_insn->op;
if (op2_reg >= 0) {
| ASM_REF_REG_OP cmp, type, cmp_insn->op1, op2_reg
} else if (op1_reg >= 0) {
| ASM_REG_REF_OP cmp, type, op1_reg, cmp_insn->op2
if (IR_IS_CONST_REF(cmp_insn->op2) && ctx->ir_base[cmp_insn->op2].val.u64 == 0) {
if (op == IR_ULT) {
/* always false */
ir_emit_jmp_false(ctx, b, def);
return;
} else if (op == IR_UGE) {
/* always true */
ir_emit_jmp_true(ctx, b, def);
return;
} else if (op == IR_ULE) {
op = IR_EQ;
} else if (op == IR_UGT) {
op = IR_NE;
}
| ASM_REG_REG_OP test, type, op1_reg, op1_reg
} else {
| ASM_REG_REF_OP cmp, type, op1_reg, cmp_insn->op2
}
}
ir_emit_jcc(ctx, cmp_insn->op, b, def, insn, 1);
ir_emit_jcc(ctx, op, b, def, insn, 1);
}
void ir_emit_cmp_and_branch_fp(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)

17
tests/x86_64/eq_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: eq function
--ARGS--
-S
--CODE--
{
int32_t c = 0;
l_1 = START(l_4);
int32_t x = PARAM(l_1, "x", 1);
bool ret = EQ(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
sete %al
retq

17
tests/x86_64/ge_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: ge function
--ARGS--
-S
--CODE--
{
int32_t c = 0;
l_1 = START(l_4);
int32_t x = PARAM(l_1, "x", 1);
bool ret = GE(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
setge %al
retq

17
tests/x86_64/gt_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: gt function
--ARGS--
-S
--CODE--
{
int32_t c = 0;
l_1 = START(l_4);
int32_t x = PARAM(l_1, "x", 1);
bool ret = GT(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
setg %al
retq

17
tests/x86_64/le_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: le function
--ARGS--
-S
--CODE--
{
int32_t c = 0;
l_1 = START(l_4);
int32_t x = PARAM(l_1, "x", 1);
bool ret = LE(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
setle %al
retq

17
tests/x86_64/lt_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: lt function
--ARGS--
-S
--CODE--
{
int32_t c = 0;
l_1 = START(l_4);
int32_t x = PARAM(l_1, "x", 1);
bool ret = LT(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
setl %al
retq

17
tests/x86_64/ne_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: ne function
--ARGS--
-S
--CODE--
{
int32_t c = 0;
l_1 = START(l_4);
int32_t x = PARAM(l_1, "x", 1);
bool ret = NE(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
setne %al
retq

16
tests/x86_64/uge_003.irt Normal file
View File

@ -0,0 +1,16 @@
--TEST--
003: uge function
--ARGS--
-S
--CODE--
{
uint32_t c = 0;
l_1 = START(l_4);
uint32_t x = PARAM(l_1, "x", 1);
bool ret = UGE(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
movb $1, %al
retq

17
tests/x86_64/ugt_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: ugt function
--ARGS--
-S
--CODE--
{
uint32_t c = 0;
l_1 = START(l_4);
uint32_t x = PARAM(l_1, "x", 1);
bool ret = UGT(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
setne %al
retq

17
tests/x86_64/ule_003.irt Normal file
View File

@ -0,0 +1,17 @@
--TEST--
003: ule function
--ARGS--
-S
--CODE--
{
uint32_t c = 0;
l_1 = START(l_4);
uint32_t x = PARAM(l_1, "x", 2);
bool ret = ULE(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
testl %edi, %edi
sete %al
retq

View File

@ -1,5 +1,5 @@
--TEST--
001: ult function
002: ult function
--ARGS--
-S
--CODE--

16
tests/x86_64/ult_003.irt Normal file
View File

@ -0,0 +1,16 @@
--TEST--
003: ult function
--ARGS--
-S
--CODE--
{
uint32_t c = 0;
l_1 = START(l_4);
uint32_t x = PARAM(l_1, "x", 1);
bool ret = ULT(x, c);
l_4 = RETURN(l_1, ret);
}
--EXPECT--
test:
xorq %rax, %rax
retq