Implemented code generation for COND (not optimized)

This commit is contained in:
Dmitry Stogov 2023-10-13 20:50:23 +03:00
parent 2984f34fe7
commit 613fca0327
19 changed files with 623 additions and 1 deletions

2
TODO
View File

@ -27,7 +27,7 @@
- Try to avoid live-interval construction, see "Efficient Global Register Allocation" Ian Rogers
? code generation
- COND
- COND optimization
- TAILCALL with stack arguments (tests/x86/tailcall_001.itr)
- 32-bit x86 back-end 64-bit integers support
(add_009.irt, conv_001.irt, conv_002.irt, conv_004.irt, conv_010.irt, sub_009.irt)

View File

@ -137,6 +137,15 @@ static bool aarch64_may_encode_addr_offset(int64_t offset, uint32_t type_size)
|| }
|.endmacro
|.macro ASM_FP_REG_IMM_OP, op, type, reg, val
|| if (type == IR_DOUBLE) {
| op Rd(reg-IR_REG_FP_FIRST), #val
|| } else {
|| IR_ASSERT(type == IR_FLOAT);
| op Rs(reg-IR_REG_FP_FIRST), #val
|| }
|.endmacro
|.macro ASM_FP_REG_REG_REG_OP, op, type, dst, src1, src2
|| if (type == IR_DOUBLE) {
| op Rd(dst-IR_REG_FP_FIRST), Rd(src1-IR_REG_FP_FIRST), Rd(src2-IR_REG_FP_FIRST)
@ -2366,6 +2375,69 @@ static void ir_emit_if_int(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn)
ir_emit_jcc(ctx, IR_NE, b, def, insn, 1);
}
static void ir_emit_cond(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_ref op3 = insn->op3;
ir_type op1_type = ctx->ir_base[op1].type;
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_reg op3_reg = ctx->regs[def][3];
IR_ASSERT(def_reg != IR_REG_NONE);
if (IR_REG_SPILLED(op2_reg) || IR_IS_CONST_REF(op2)) {
op2_reg = IR_REG_NUM(op2_reg);
ir_emit_load(ctx, type, op2_reg, op2);
if (op1 == op2) {
op1_reg = op2_reg;
}
if (op3 == op2) {
op3_reg = op2_reg;
}
}
if (op3 != op2 && (IR_REG_SPILLED(op3_reg) || IR_IS_CONST_REF(op3))) {
op3_reg = IR_REG_NUM(op3_reg);
ir_emit_load(ctx, type, op3_reg, op3);
if (op1 == op2) {
op1_reg = op3_reg;
}
}
if (op1 != op2 && op1 != op3 && (IR_REG_SPILLED(op1_reg) || IR_IS_CONST_REF(op1))) {
op1_reg = IR_REG_NUM(op1_reg);
ir_emit_load(ctx, op1_type, op1_reg, op1);
}
if (IR_IS_TYPE_INT(op1_type)) {
| ASM_REG_IMM_OP cmp, op1_type, op1_reg, 0
} else{
| ASM_FP_REG_IMM_OP fcmp, op1_type, op1_reg, 0.0
}
if (IR_IS_TYPE_INT(type)) {
if (ir_type_size[type] == 8) {
| csel Rx(def_reg), Rx(op2_reg), Rx(op3_reg), eq
} else {
| csel Rw(def_reg), Rw(op2_reg), Rw(op3_reg), eq
}
} else{
if (type == IR_DOUBLE) {
| fcsel Rd(def_reg-IR_REG_FP_FIRST), Rd(op2_reg-IR_REG_FP_FIRST), Rd(op3_reg-IR_REG_FP_FIRST), eq
} else {
| fcsel Rs(def_reg-IR_REG_FP_FIRST), Rs(op2_reg-IR_REG_FP_FIRST), Rs(op3_reg-IR_REG_FP_FIRST), eq
}
}
if (IR_REG_SPILLED(ctx->regs[def][0])) {
ir_emit_store(ctx, type, def, def_reg);
}
}
static void ir_emit_return_void(ir_ctx *ctx)
{
ir_backend_data *data = ctx->data;
@ -4952,6 +5024,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr)
case IR_IF_INT:
ir_emit_if_int(ctx, b, i, insn);
break;
case IR_COND:
ir_emit_cond(ctx, i, insn);
break;
case IR_SWITCH:
ir_emit_switch(ctx, b, i, insn);
break;

View File

@ -369,6 +369,7 @@ typedef struct _ir_backend_data {
bool float_neg_const;
bool double_abs_const;
bool float_abs_const;
bool double_zero_const;
} ir_backend_data;
#define IR_GP_REG_NAME(code, name64, name32, name16, name8, name8h) \
@ -4399,6 +4400,116 @@ static void ir_emit_if_int(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn)
ir_emit_jcc(ctx, IR_NE, b, def, insn, 1);
}
static void ir_emit_cond(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_ref op3 = insn->op3;
ir_type op1_type = ctx->ir_base[op1].type;
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_reg op3_reg = ctx->regs[def][3];
IR_ASSERT(def_reg != IR_REG_NONE);
if (op2_reg != IR_REG_NONE && (IR_REG_SPILLED(op2_reg) || IR_IS_CONST_REF(op2))) {
op2_reg = IR_REG_NUM(op2_reg);
ir_emit_load(ctx, type, op2_reg, op2);
if (op1 == op2) {
op1_reg = op2_reg;
}
if (op3 == op2) {
op3_reg = op2_reg;
}
}
if (op3_reg != IR_REG_NONE && op3 != op2 && (IR_REG_SPILLED(op3_reg) || IR_IS_CONST_REF(op3))) {
op3_reg = IR_REG_NUM(op3_reg);
ir_emit_load(ctx, type, op3_reg, op3);
if (op1 == op2) {
op1_reg = op3_reg;
}
}
if (op1_reg != IR_REG_NONE && op1 != op2 && op1 != op3 && (IR_REG_SPILLED(op1_reg) || IR_IS_CONST_REF(op1))) {
op1_reg = IR_REG_NUM(op1_reg);
ir_emit_load(ctx, op1_type, op1_reg, op1);
}
if (IR_IS_TYPE_INT(op1_type)) {
if (op1_reg != IR_REG_NONE) {
| ASM_REG_REG_OP test, op1_type, op1_reg, op1_reg
} else {
ir_reg fp;
int32_t offset = ir_ref_spill_slot(ctx, op1, &fp);
| ASM_MEM_IMM_OP cmp, op1_type, [Ra(fp)+offset], 0
}
| je >2
} else {
if (!data->double_zero_const) {
data->double_zero_const = 1;
ir_rodata(ctx);
|.align 16
|->double_zero_const:
|.dword 0, 0
|.code
}
| ASM_FP_REG_MEM_OP ucomiss, ucomisd, vucomiss, vucomisd, op1_type, op1_reg, [->double_zero_const]
| jp >1
| je >2
|1:
}
if (op2_reg != IR_REG_NONE) {
if (def_reg != op2_reg) {
if (IR_IS_TYPE_INT(type)) {
ir_emit_mov(ctx, type, def_reg, op2_reg);
} else {
ir_emit_fp_mov(ctx, type, def_reg, op2_reg);
}
}
} else if (IR_IS_CONST_REF(op2) || !(ir_rule(ctx, op2) & IR_FUSED)) {
ir_emit_load(ctx, type, def_reg, op2);
} else {
int32_t offset = ir_fuse_load(ctx, op2, &op2_reg);
if (IR_IS_TYPE_INT(type)) {
ir_emit_load_mem_int(ctx, type, def_reg, op2_reg, offset);
} else {
ir_emit_load_mem_fp(ctx, type, def_reg, op2_reg, offset);
}
}
| jmp >3
|2:
if (op3_reg != IR_REG_NONE) {
if (def_reg != op3_reg) {
if (IR_IS_TYPE_INT(type)) {
ir_emit_mov(ctx, type, def_reg, op3_reg);
} else {
ir_emit_fp_mov(ctx, type, def_reg, op3_reg);
}
}
} else if (IR_IS_CONST_REF(op3) || !(ir_rule(ctx, op3) & IR_FUSED)) {
ir_emit_load(ctx, type, def_reg, op3);
} else {
int32_t offset = ir_fuse_load(ctx, op3, &op3_reg);
if (IR_IS_TYPE_INT(type)) {
ir_emit_load_mem_int(ctx, type, def_reg, op3_reg, offset);
} else {
ir_emit_load_mem_fp(ctx, type, def_reg, op3_reg, offset);
}
}
|3:
if (IR_REG_SPILLED(ctx->regs[def][0])) {
ir_emit_store(ctx, type, def, def_reg);
}
}
static void ir_emit_return_void(ir_ctx *ctx)
{
ir_backend_data *data = ctx->data;
@ -8041,6 +8152,7 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr)
data.float_neg_const = 0;
data.double_abs_const = 0;
data.float_abs_const = 0;
data.double_zero_const = 0;
ctx->data = &data;
if (!ctx->live_intervals) {
@ -8514,6 +8626,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr)
case IR_IF_INT:
ir_emit_if_int(ctx, b, i, insn);
break;
case IR_COND:
ir_emit_cond(ctx, i, insn);
break;
case IR_SWITCH:
ir_emit_switch(ctx, b, i, insn);
break;

View File

@ -0,0 +1,25 @@
--TEST--
001: COND(I,I,I)
--TARGET--
Windows-x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
testl %edi, %edi
je .L1
movl %esi, %eax
jmp .L2
.L1:
movl %edx, %eax
.L2:
retq

View File

@ -0,0 +1,31 @@
--TEST--
002: COND(D,I,I)
--TARGET--
Windows-x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
ucomisd .L4(%rip), %xmm0
jp .L1
je .L2
.L1:
movl %edi, %eax
jmp .L3
.L2:
movl %esi, %eax
.L3:
retq
.rodata
.db 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
.L4:
.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

View File

@ -0,0 +1,24 @@
--TEST--
003: COND(I,D,D)
--TARGET--
Windows-x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
testq %rdi, %rdi
je .L1
jmp .L2
.L1:
movapd %xmm1, %xmm0
.L2:
retq

View File

@ -0,0 +1,31 @@
--TEST--
004: COND(D,D,D)
--TARGET--
Windows-x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
ucomisd .L4(%rip), %xmm0
jp .L1
je .L2
.L1:
movapd %xmm1, %xmm0
jmp .L3
.L2:
movapd %xmm2, %xmm0
.L3:
retq
.rodata
.db 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
.L4:
.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

View File

@ -0,0 +1,20 @@
--TEST--
001: COND(I,I,I)
--TARGET--
aarch64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
cmp w0, #0
csel w0, w1, w2, eq
ret

View File

@ -0,0 +1,20 @@
--TEST--
002: COND(D,I,I)
--TARGET--
aarch64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
fcmp d0, #0.0
csel w0, w0, w1, eq
ret

View File

@ -0,0 +1,20 @@
--TEST--
003: COND(I,D,D)
--TARGET--
aarch64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
cmp w0, #0
fcsel d0, d0, d1, eq
ret

View File

@ -0,0 +1,20 @@
--TEST--
004: COND(D,D,D)
--TARGET--
aarch64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
fcmp d0, #0.0
fcsel d0, d1, d2, eq
ret

28
tests/x86/cond_001.irt Normal file
View File

@ -0,0 +1,28 @@
--TEST--
001: COND(I,I,I)
--TARGET--
x86
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
movl 4(%esp), %eax
movl 8(%esp), %ecx
movl 0xc(%esp), %edx
testl %eax, %eax
je .L1
movl %ecx, %eax
jmp .L2
.L1:
movl %edx, %eax
.L2:
retl

33
tests/x86/cond_002.irt Normal file
View File

@ -0,0 +1,33 @@
--TEST--
002: COND(D,I,I)
--TARGET--
x86
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
movsd 4(%esp), %xmm0
movl 0xc(%esp), %eax
movl 0x10(%esp), %ecx
ucomisd .L4, %xmm0
jp .L1
je .L2
.L1:
jmp .L3
.L2:
movl %ecx, %eax
.L3:
retl
.rodata
.db 0x90
.L4:
.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

31
tests/x86/cond_003.irt Normal file
View File

@ -0,0 +1,31 @@
--TEST--
003: COND(I,D,D)
--TARGET--
x86
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
subl $8, %esp
movl 0xc(%esp), %eax
movsd 0x10(%esp), %xmm0
movsd 0x18(%esp), %xmm1
testl %eax, %eax
je .L1
jmp .L2
.L1:
movapd %xmm1, %xmm0
.L2:
movsd %xmm0, (%esp)
fldl (%esp)
addl $8, %esp
retl

38
tests/x86/cond_004.irt Normal file
View File

@ -0,0 +1,38 @@
--TEST--
004: COND(D,D,D)
--TARGET--
x86
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
subl $8, %esp
movsd 0xc(%esp), %xmm0
movsd 0x14(%esp), %xmm1
movsd 0x1c(%esp), %xmm2
ucomisd .L4, %xmm0
jp .L1
je .L2
.L1:
movapd %xmm1, %xmm0
jmp .L3
.L2:
movapd %xmm2, %xmm0
.L3:
movsd %xmm0, (%esp)
fldl (%esp)
addl $8, %esp
retl
.rodata
.db 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
.L4:
.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

25
tests/x86_64/cond_001.irt Normal file
View File

@ -0,0 +1,25 @@
--TEST--
001: COND(I,I,I)
--TARGET--
x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
testl %edi, %edi
je .L1
movl %esi, %eax
jmp .L2
.L1:
movl %edx, %eax
.L2:
retq

31
tests/x86_64/cond_002.irt Normal file
View File

@ -0,0 +1,31 @@
--TEST--
002: COND(D,I,I)
--TARGET--
x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
int32_t y = PARAM(l_1, "y", 2);
int32_t z = PARAM(l_1, "z", 3);
int32_t val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
ucomisd .L4(%rip), %xmm0
jp .L1
je .L2
.L1:
movl %edi, %eax
jmp .L3
.L2:
movl %esi, %eax
.L3:
retq
.rodata
.db 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
.L4:
.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

24
tests/x86_64/cond_003.irt Normal file
View File

@ -0,0 +1,24 @@
--TEST--
003: COND(I,D,D)
--TARGET--
x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
int32_t x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
testl %edi, %edi
je .L1
jmp .L2
.L1:
movapd %xmm1, %xmm0
.L2:
retq

31
tests/x86_64/cond_004.irt Normal file
View File

@ -0,0 +1,31 @@
--TEST--
004: COND(D,D,D)
--TARGET--
x86_64
--ARGS--
-S
--CODE--
{
l_1 = START(ret);
double x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 3);
double val = COND(x, y, z);
ret = RETURN(l_1, val);
}
--EXPECT--
test:
ucomisd .L4(%rip), %xmm0
jp .L1
je .L2
.L1:
movapd %xmm1, %xmm0
jmp .L3
.L2:
movapd %xmm2, %xmm0
.L3:
retq
.rodata
.db 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
.L4:
.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00