ir/ir_x86.dasc

3667 lines
95 KiB
Plaintext

#define IR_DASM 1
#define IR_X64 1
#include "ir.h"
#include "ir_private.h"
#include "ir_x86.h"
#ifdef _WIN32
# define IR_SET_ALIGNED(alignment, decl) __declspec(align(alignment)) decl
#elif defined(HAVE_ATTRIBUTE_ALIGNED)
# define IR_SET_ALIGNED(alignment, decl) decl __attribute__ ((__aligned__ (alignment)))
#else
# define IR_SET_ALIGNED(alignment, decl) decl
#endif
#include "dynasm/dasm_proto.h"
#include "dynasm/dasm_x86.h"
|.if X64
|.arch x64
|.else
|.arch x86
|.endif
|.actionlist dasm_actions
|.globals ir_lb
|.section code, cold_code, rodata
|.macro ASM_REG_OP, op, type, reg
|| switch (ir_type_size[type]) {
|| case 1:
| op Rb(reg)
|| break;
|| case 2:
| op Rw(reg)
|| break;
|| case 4:
| op Rd(reg)
|| break;
|| case 8:
| op Rq(reg)
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_MEM_OP, op, type, mem
|| switch (ir_type_size[type]) {
|| case 1:
| op byte mem
|| break;
|| case 2:
| op word mem
|| break;
|| case 4:
| op dword mem
|| break;
|| case 8:
| op qword mem
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_REF_OP, op, type, ref
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[ref]];
|| if (ival->reg >= 0) {
| ASM_REG_OP op, type, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_MEM_OP op, type, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_OP op, type, [rsp+offset]
|| }
|| }
|.endmacro
|.macro ASM_REG_REG_OP, op, type, dst, src
|| switch (ir_type_size[type]) {
|| case 1:
| op Rb(dst), Rb(src)
|| break;
|| 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:
| op Rb(dst), src
|| break;
|| case 2:
| op Rw(dst), src
|| break;
|| case 4:
| op Rd(dst), src
|| break;
|| case 8:
| op Rq(dst), src
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_MEM_REG_OP, op, type, dst, src
|| switch (ir_type_size[type]) {
|| case 1:
| op byte dst, Rb(src)
|| break;
|| case 2:
| op word dst, Rw(src)
|| break;
|| case 4:
| op dword dst, Rd(src)
|| break;
|| case 8:
| op qword dst, Rq(src)
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_MEM_IMM_OP, op, type, dst, src
|| switch (ir_type_size[type]) {
|| case 1:
| op byte dst, src
|| break;
|| case 2:
| op word dst, src
|| break;
|| case 4:
| op dword dst, src
|| break;
|| case 8:
| op qword dst, src
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_REG_MEM_OP, op, type, dst, src
|| switch (ir_type_size[type]) {
|| case 1:
| op Rb(dst), byte src
|| break;
|| case 2:
| op Rw(dst), word src
|| break;
|| case 4:
| op Rd(dst), dword src
|| break;
|| case 8:
| op Rq(dst), qword src
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_VREG_OP, op, type, vreg
|| ir_live_interval *ival = ctx->live_intervals[vreg];
|| if (ival->reg >= 0) {
| op Ra(ival->reg)
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| op aword [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| op aword [rsp+offset]
|| }
|| }
|.endmacro
|.macro ASM_VREGX_OP, op, type, vreg
|| if (IR_IS_CONST_REF(vreg)) {
|| ir_insn *_insn = &ctx->ir_base[vreg];
|| int32_t val = _insn->val.i32;
| op dword val // TODO:
|| } else {
| ASM_VREG_OP op, type, vreg
|| }
|.endmacro
|.macro ASM_REG_REF_OP, op, type, dst, src
|| if (IR_IS_CONST_REF(src)) {
|| ir_insn *_insn = &ctx->ir_base[src];
| ASM_REG_IMM_OP op, type, dst, _insn->val.u32 // TODO:
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[src]];
|| if (ival->reg >= 0) {
| ASM_REG_REG_OP op, type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_REG_MEM_OP op, type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_REG_MEM_OP op, type, dst, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_VREG_IMM_OP, op, type, dst, src
|| ir_insn *_insn = &ctx->ir_base[src];
|| ir_live_interval *ival = ctx->live_intervals[dst];
|| if (ival->reg >= 0) {
| ASM_REG_IMM_OP op, type, ival->reg, _insn->val.u32
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_MEM_IMM_OP op, type, [rbp+offset], _insn->val.u32
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_IMM_OP op, type, [rsp+offset], _insn->val.u32
|| }
|| }
|.endmacro
|.macro ASM_VREG_REG_OP, op, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[dst];
|| if (ival->reg >= 0) {
| ASM_REG_REG_OP op, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_MEM_REG_OP op, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_REG_OP op, type, [rsp+offset], src
|| }
|| }
|.endmacro
|.macro ASM_REG_VREG_OP, op, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[src];
|| if (ival->reg >= 0) {
| ASM_REG_REG_OP op, type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_REG_MEM_OP op, type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_REG_MEM_OP op, type, dst, [rsp+offset]
|| }
|| }
|.endmacro
|.macro ASM_REF_REG_OP, op, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[dst]];
|| if (ival->reg >= 0) {
| ASM_REG_REG_OP op, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_MEM_REG_OP op, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_REG_OP op, type, [rsp+offset], src
|| }
|| }
|.endmacro
|.macro ASM_REF_IMM_OP, op, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[dst]];
|| if (ival->reg >= 0) {
| ASM_REG_IMM_OP op, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_MEM_IMM_OP op, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_IMM_OP op, type, [rsp+offset], src
|| }
|| }
|.endmacro
|.macro ASM_REG_REG_IMUL, type, dst, src
|| switch (ir_type_size[type]) {
|| case 2:
| imul Rw(dst), Rw(src)
|| break;
|| case 4:
| imul Rd(dst), Rd(src)
|| break;
|| case 8:
| imul Rq(dst), Rq(src)
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_REG_IMM_IMUL, type, dst, src
|| switch (ir_type_size[type]) {
|| case 2:
| imul Rw(dst), src
|| break;
|| case 4:
| imul Rd(dst), src
|| break;
|| case 8:
| imul Rq(dst), src
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_REG_MEM_IMUL, type, dst, src
|| switch (ir_type_size[type]) {
|| case 2:
| imul Rw(dst), word src
|| break;
|| case 4:
| imul Rd(dst), dword src
|| break;
|| case 8:
| imul Rq(dst), qword src
|| break;
|| default:
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_REG_REF_IMUL, type, dst, src
|| if (IR_IS_CONST_REF(src)) {
|| ir_insn *_insn = &ctx->ir_base[src];
| ASM_REG_IMM_IMUL type, dst, _insn->val.u32 // TODO:
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[src]];
|| if (ival->reg >= 0) {
| ASM_REG_REG_IMUL type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_REG_MEM_IMUL type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_REG_MEM_IMUL type, dst, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_SSE2_REG_REG_OP, fop, dop, type, dst, src
|| if (type == IR_FLOAT) {
| fop xmm(dst-IR_REG_XMM0), xmm(src-IR_REG_XMM0)
|| } else if (type == IR_DOUBLE) {
| dop xmm(dst-IR_REG_XMM0), xmm(src-IR_REG_XMM0)
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_SSE2_MEM_REG_OP, fop, dop, type, dst, src
|| if (type == IR_FLOAT) {
| fop dword dst, xmm(src-IR_REG_XMM0)
|| } else if (type == IR_DOUBLE) {
| dop qword dst, xmm(src-IR_REG_XMM0)
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_SSE2_REG_MEM_OP, fop, dop, type, dst, src
|| if (type == IR_FLOAT) {
| fop xmm(dst-IR_REG_XMM0), dword src
|| } else if (type == IR_DOUBLE) {
| dop xmm(dst-IR_REG_XMM0), qword src
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_SSE2_REG_REF_OP, fop, dop, type, dst, src
|| if (IR_IS_CONST_REF(src)) {
|| ir_insn *_insn = &ctx->ir_base[src];
|| int label = ctx->cfg_blocks_count - src;
| ASM_SSE2_REG_MEM_OP fop, dop, type, dst, [=>label]
|| _insn->emit_const = 1;
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[src]];
|| if (ival->reg >= 0) {
| ASM_SSE2_REG_REG_OP fop, dop, type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_SSE2_REG_MEM_OP fop, dop, type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_SSE2_REG_MEM_OP fop, dop, type, dst, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_SSE2_REF_REG_OP, fop, dop, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[dst]];
|| if (ival->reg >= 0) {
| ASM_SSE2_REG_REG_OP fop, dop, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_SSE2_MEM_REG_OP fop, dop, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_SSE2_MEM_REG_OP fop, dop, type, [rsp+offset], src
|| }
|| }
|.endmacro
|.macro ASM_AVX_REG_REG_REG_OP, fop, dop, type, dst, op1, op2
|| if (type == IR_FLOAT) {
| fop xmm(dst-IR_REG_XMM0), xmm(op1-IR_REG_XMM0), xmm(op2-IR_REG_XMM0)
|| } else if (type == IR_DOUBLE) {
| dop xmm(dst-IR_REG_XMM0), xmm(op1-IR_REG_XMM0), xmm(op2-IR_REG_XMM0)
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_AVX_REG_REG_MEM_OP, fop, dop, type, dst, op1, op2
|| if (type == IR_FLOAT) {
| fop xmm(dst-IR_REG_XMM0), xmm(op1-IR_REG_XMM0), dword op2
|| } else if (type == IR_DOUBLE) {
| dop xmm(dst-IR_REG_XMM0), xmm(op1-IR_REG_XMM0), qword op2
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_AVX_REG_REG_REF_OP, fop, dop, type, dst, op1, op2
|| if (IR_IS_CONST_REF(op2)) {
|| ir_insn *_insn = &ctx->ir_base[op2];
|| int label = ctx->cfg_blocks_count - op2;
| ASM_AVX_REG_REG_MEM_OP fop, dop, type, dst, op1, [=>label]
|| _insn->emit_const = 1;
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[op2]];
|| if (ival->reg >= 0) {
| ASM_AVX_REG_REG_REG_OP fop, dop, type, dst, op1, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_AVX_REG_REG_MEM_OP fop, dop, type, dst, op1, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_AVX_REG_REG_MEM_OP fop, dop, type, dst, op1, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_FP_REG_REG_OP, fop, dop, avx_fop, avx_dop, type, dst, src
|| if (type == IR_FLOAT) {
|| if (ctx->flags & IR_AVX) {
| avx_fop xmm(dst-IR_REG_XMM0), xmm(src-IR_REG_XMM0)
|| } else {
| fop xmm(dst-IR_REG_XMM0), xmm(src-IR_REG_XMM0)
|| }
|| } else if (type == IR_DOUBLE) {
|| if (ctx->flags & IR_AVX) {
| avx_dop xmm(dst-IR_REG_XMM0), xmm(src-IR_REG_XMM0)
|| } else {
| dop xmm(dst-IR_REG_XMM0), xmm(src-IR_REG_XMM0)
|| }
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_FP_MEM_REG_OP, fop, dop, avx_fop, avx_dop, type, dst, src
|| if (type == IR_FLOAT) {
|| if (ctx->flags & IR_AVX) {
| avx_fop dword dst, xmm(src-IR_REG_XMM0)
|| } else {
| fop dword dst, xmm(src-IR_REG_XMM0)
|| }
|| } else if (type == IR_DOUBLE) {
|| if (ctx->flags & IR_AVX) {
| avx_dop qword dst, xmm(src-IR_REG_XMM0)
|| } else {
| dop qword dst, xmm(src-IR_REG_XMM0)
|| }
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_FP_REG_MEM_OP, fop, dop, avx_fop, avx_dop, type, dst, src
|| if (type == IR_FLOAT) {
|| if (ctx->flags & IR_AVX) {
| avx_fop xmm(dst-IR_REG_XMM0), dword src
|| } else {
| fop xmm(dst-IR_REG_XMM0), dword src
|| }
|| } else if (type == IR_DOUBLE) {
|| if (ctx->flags & IR_AVX) {
| avx_dop xmm(dst-IR_REG_XMM0), qword src
|| } else {
| dop xmm(dst-IR_REG_XMM0), qword src
|| }
|| } else {
|| IR_ASSERT(0);
|| }
|.endmacro
|.macro ASM_FP_REG_REF_OP, fop, dop, avx_fop, avx_dop, type, dst, src
|| if (IR_IS_CONST_REF(src)) {
|| ir_insn *_insn = &ctx->ir_base[src];
|| int label = ctx->cfg_blocks_count - src;
| ASM_FP_REG_MEM_OP fop, dop, avx_fop, avx_dop, type, dst, [=>label]
|| _insn->emit_const = 1;
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[src]];
|| if (ival->reg >= 0) {
| ASM_FP_REG_REG_OP fop, dop, avx_fop, avx_dop, type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_FP_REG_MEM_OP fop, dop, avx_fop, avx_dop, type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_REG_MEM_OP fop, dop, avx_fop, avx_dop, type, dst, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_FP_REF_REG_OP, fop, dop, avx_fop, avx_dop, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[dst]];
|| if (ival->reg >= 0) {
| ASM_FP_REG_REG_OP fop, dop, avx_fop, avx_dop, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP fop, dop, avx_fop, avx_dop, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP fop, dop, avx_fop, avx_dop, type, [rsp+offset], src
|| }
|| }
|.endmacro
|.macro ASM_FP_MOV_REG_REF_OP, type, dst, src
|| if (IR_IS_CONST_REF(src)) {
|| ir_insn *_insn = &ctx->ir_base[src];
|| int label = ctx->cfg_blocks_count - src;
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, dst, [=>label]
|| _insn->emit_const = 1;
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[src]];
|| if (ival->reg >= 0) {
| ASM_FP_REG_REG_OP movaps, movapd, vmovaps, vmovapd, type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, dst, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_FP_MOV_REF_REG_OP, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[ctx->vregs[dst]];
|| if (ival->reg >= 0) {
| ASM_FP_REG_REG_OP movaps, movapd, vmovaps, vmovapd, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP movss, movsd, vmovss, vmovsd, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP movss, movsd, vmovss, vmovsd, type, [rsp+offset], src
|| }
|| }
|.endmacro
|.macro ASM_FP_MOV_REG_VREG_OP, type, dst, src
|| if (IR_IS_CONST_REF(src)) {
|| ir_insn *_insn = &ctx->ir_base[src];
|| int label = ctx->cfg_blocks_count - src;
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, dst, [=>label]
|| _insn->emit_const = 1;
|| } else {
|| ir_live_interval *ival = ctx->live_intervals[src];
|| if (ival->reg >= 0) {
| ASM_FP_REG_REG_OP movaps, movapd, vmovaps, vmovapd, type, dst, ival->reg
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, dst, [rbp+offset]
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, dst, [rsp+offset]
|| }
|| }
|| }
|.endmacro
|.macro ASM_FP_MOV_VREG_REG_OP, type, dst, src
|| ir_live_interval *ival = ctx->live_intervals[dst];
|| if (ival->reg >= 0) {
| ASM_FP_REG_REG_OP movaps, movapd, vmovaps, vmovapd, type, ival->reg, src
|| } else if (ival->stack_spill_pos) {
|| if (ctx->flags & IR_USE_FRAME_POINTER) {
|| int32_t offset = -ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP movss, movsd, vmovss, vmovsd, type, [rbp+offset], src
|| } else {
|| int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP movss, movsd, vmovss, vmovsd, type, [rsp+offset], src
|| }
|| }
|.endmacro
typedef struct _ir_backend_data {
uint32_t stack_frame_size;
ir_regset used_preserved_regs;
#if IR_DASM
dasm_State *dasm_state;
#endif
} ir_backend_data;
#define IR_GP_REG_NAME(code, name64, name32, name16, name8, name8h) \
#name64,
#define IR_GP_REG_NAME32(code, name64, name32, name16, name8, name8h) \
#name32,
#define IR_GP_REG_NAME16(code, name64, name32, name16, name8, name8h) \
#name16,
#define IR_GP_REG_NAME8(code, name64, name32, name16, name8, name8h) \
#name8,
#define IR_FP_REG_NAME(code, name) \
#name,
static const char *_ir_reg_name[IR_REG_NUM] = {
IR_GP_REGS(IR_GP_REG_NAME)
IR_FP_REGS(IR_FP_REG_NAME)
};
static const char *_ir_reg_name32[IR_REG_NUM] = {
IR_GP_REGS(IR_GP_REG_NAME32)
};
static const char *_ir_reg_name16[IR_REG_NUM] = {
IR_GP_REGS(IR_GP_REG_NAME16)
};
static const char *_ir_reg_name8[IR_REG_NUM] = {
IR_GP_REGS(IR_GP_REG_NAME8)
};
/* Calling Convention */
#ifdef _WIN64
static const int8_t _ir_int_reg_params[IR_REG_INT_ARGS] = {
IR_REG_INT_ARG1,
IR_REG_INT_ARG2,
IR_REG_INT_ARG3,
IR_REG_INT_ARG4,
};
static const int8_t _ir_fp_reg_params[IR_REG_FP_ARGS] = {
IR_REG_FP_ARG1,
IR_REG_FP_ARG2,
IR_REG_FP_ARG3,
IR_REG_FP_ARG4,
};
#elif defined(__x86_64__)
static const int8_t _ir_int_reg_params[IR_REG_INT_ARGS] = {
IR_REG_INT_ARG1,
IR_REG_INT_ARG2,
IR_REG_INT_ARG3,
IR_REG_INT_ARG4,
IR_REG_INT_ARG5,
IR_REG_INT_ARG6,
};
static const int8_t _ir_fp_reg_params[IR_REG_FP_ARGS] = {
IR_REG_FP_ARG1,
IR_REG_FP_ARG2,
IR_REG_FP_ARG3,
IR_REG_FP_ARG4,
IR_REG_FP_ARG5,
IR_REG_FP_ARG6,
IR_REG_FP_ARG7,
IR_REG_FP_ARG8,
};
#else
static const int8_t *_ir_int_reg_params = NULL;
static const int8_t *_ir_fp_reg_params = NULL;
#endif
const char *ir_reg_name(int8_t reg, ir_type type)
{
IR_ASSERT(reg >= 0 && reg < IR_REG_NUM);
if (IR_IS_TYPE_FP(type) || ir_type_size[type] == 8) {
return _ir_reg_name[reg];
} else if (ir_type_size[type] == 4) {
return _ir_reg_name32[reg];
} else if (ir_type_size[type] == 2) {
return _ir_reg_name16[reg];
} else if (ir_type_size[type] == 1) {
return _ir_reg_name8[reg];
}
IR_ASSERT(0);
return NULL;
}
typedef enum _ir_rule {
IR_SKIP = IR_LAST_OP,
IR_SKIP_REG,
IR_CMP_INT, // res=reg({%rax,%rbx,%rcx,%rdx}), op1=reg(GP), op2=reg(GP)|mem|imm
// res=reg({%rax,%rbx,%rcx,%rdx}), op1=mem, op2=reg(GP)|imm
IR_CMP_FP, // res=reg({%rax,%rbx,%rcx,%rdx}), op1=reg(FP), op2=reg(FP)|mem
IR_MUL_INT, // res=%rax op1=%rax, op2=reg(GP)|mem, change=%rdx
IR_DIV_INT, // res=%rax op1=%rax, op2=reg(GP)|mem, change=%rdx
IR_MOD_INT, // res=%rdx op1=%rax, op2=reg(GP)|mem, change=%rax
IR_LEA_OB, // res=reg(GP), op1=reg(GP)
IR_LEA_SI, // res=reg(GP), op1=reg(GP)
IR_LEA_SIB, // res=reg(GP), op1=reg(GP)
IR_LEA_IB, // res=reg(GP), op1=reg(GP), op2=reg(GP)
IR_LEA_SI_O,
IR_LEA_SIB_O,
IR_LEA_IB_O,
IR_LEA_I_OB,
IR_LEA_OB_I,
IR_LEA_OB_SI,
IR_LEA_SI_OB,
IR_LEA_B_SI,
IR_LEA_SI_B,
IR_INC,
IR_DEC,
IR_MUL_2,
IR_MUL_PWR2,
IR_DIV_PWR2,
IR_MOD_PWR2,
IR_OP_INT,
IR_BINOP_INT, // res=reg(GP), op1.reg(GP, hint=res), op2=any
IR_BINOP_SSE2, // res=reg(FP), op1.reg(FP, hint=res), op2=reg(FP)|mem
IR_BINOP_AVX, // res=reg(FP), op1.reg(FP), op2=reg(FP)|mem
IR_SHIFT, // res=reg(GP), op1.reg(GP, hint=res), op2=%rcx
IR_SHIFT_CONST, // res=reg(GP), op1.reg(GP, hint=res), imm
IR_COPY_INT, // res=reg(GP), op1=reg(GP)|mem|imm
// res=mem, op1=reg(GP)|imm
IR_COPY_FP, // res=reg(FP), op1=reg(FP)|mem
// res=mem, op2=reg(GP)
IR_CMP_AND_BRANCH_INT, // op1=reg(GP), op2=reg(GP)|mem|imm
// op1=mem, op2=reg(GP)|imm
IR_CMP_AND_BRANCH_FP, // op1=reg(FP), op2=reg(FP)|mem
IR_IF_INT, // op1=reg(GP)|mem
IR_RETURN_VOID, //
IR_RETURN_INT, // op1=reg(GP, hint=%rax)|mem|imm
IR_RETURN_FP, // op1=reg(FP, hint=%xmm0)|mem
} ir_rule;
/* instruction selection */
//TODO: mov, xor, test, btsl, btrl, neg
bool ir_needs_def_reg(ir_ctx *ctx, ir_ref ref)
{
IR_ASSERT(ctx->rules);
return ctx->rules[ref] != IR_SKIP;
}
ir_regset ir_get_fixed_regset(ir_ctx *ctx, ir_ref ref)
{
ir_ref rule;
rule = ctx->rules[ref];
// if (rule == IR_SHIFT) {
// return IR_REGSET(IR_REG_RCX);
// if (rule == IR_MUL_INT || rule == IR_DIV_INT || rule == IR_MOD_INT) {
// return IR_REGSET(IR_REG_RAX) | IR_REGSET(IR_REG_RDX);
// }
if (rule == IR_MOD_INT && IR_IS_CONST_REF(ctx->ir_base[ref].op1)) {
return IR_REGSET(IR_REG_RAX);
}
return IR_REGSET_EMPTY;
}
ir_reg ir_uses_fixed_reg(ir_ctx *ctx, ir_ref ref, int op_num)
{
ir_ref rule;
rule = ctx->rules[ref];
if (rule == IR_SHIFT) {
if (op_num == 2) {
return IR_REG_RCX;
}
} else if (rule == IR_MUL_INT || rule == IR_DIV_INT) {
if (op_num == 0 || op_num == 1) {
return IR_REG_RAX;
}
} else if (rule == IR_MOD_INT) {
if (op_num == 0) {
return IR_REG_RDX;
} else if (op_num == 1) {
return IR_REG_RAX;
}
} else if (rule == IR_RETURN_INT) {
if (op_num == 2) {
return IR_REG_RAX;
}
} else if (rule == IR_RETURN_FP) {
if (op_num == 2) {
return IR_REG_XMM0;
}
}
return IR_REG_NONE;
}
bool ir_result_reuses_op1(ir_ctx *ctx, ir_ref ref)
{
ir_ref rule;
rule = ctx->rules[ref];
switch (rule) {
case IR_BINOP_INT:
case IR_BINOP_SSE2:
case IR_SHIFT:
case IR_SHIFT_CONST:
case IR_MUL_INT:
case IR_DIV_INT:
case IR_MOD_INT:
return 1;
}
return 0;
}
static uint32_t ir_match_insn(ir_ctx *ctx, ir_ref ref, ir_block *bb)
{
ir_insn *op1_insn, *op2_insn;
ir_insn *insn = &ctx->ir_base[ref];
switch (insn->op) {
case IR_EQ:
case IR_NE:
case IR_LT:
case IR_GE:
case IR_LE:
case IR_GT:
case IR_ULT:
case IR_UGE:
case IR_ULE:
case IR_UGT:
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)) {
return IR_CMP_INT;
} else {
return IR_CMP_FP;
}
break;
case IR_ADD:
case IR_SUB:
if (IR_IS_TYPE_INT(insn->type)) {
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.i64 == 0) {
return IR_COPY_INT;
} else if (ir_type_size[insn->type] == sizeof(void*)) {
if (insn->op1 > bb->start && insn->op1 < ref && ctx->use_lists[insn->op1].count == 1) {
if (!ctx->rules[insn->op1]) {
ctx->rules[insn->op1] = ir_match_insn(ctx, insn->op1, bb);
}
}
if (ctx->rules[insn->op1] == IR_LEA_SI) {
ctx->rules[insn->op1] = IR_SKIP;
return IR_LEA_SI_O; // lea ret, [op1.op1.reg*op1.op2.scale+op2.offset]
} else if (ctx->rules[insn->op1] == IR_LEA_SIB) {
ctx->rules[insn->op1] = IR_SKIP;
return IR_LEA_SIB_O; // lea ret, [op1.op1.reg+op1.op1.reg*op1.op2.scale+op2.offset]
} else if (ctx->rules[insn->op1] == IR_LEA_IB) {
ctx->rules[insn->op1] = IR_SKIP;
return IR_LEA_IB_O; // lea ret, [op1.op1.reg+op1.op2.reg+op2.offset]
}
return IR_LEA_OB; // lea ret, [op1.reg+op2.offset]
} else if (op2_insn->val.i64 == 1 || op2_insn->val.i64 == -1) {
if (insn->op == IR_ADD) {
if (op2_insn->val.i64 == 1) {
return IR_INC; // inc op1
} else {
return IR_DEC; // dec op1
}
} else {
if (op2_insn->val.i64 == 1) {
return IR_DEC; // dec op1
} else {
return IR_INC; // inc op1
}
}
}
} else if (insn->op == IR_ADD && ir_type_size[insn->type] == sizeof(void*)) {
if (insn->op1 > bb->start && insn->op1 < ref && ctx->use_lists[insn->op1].count == 1) {
if (!ctx->rules[insn->op1]) {
ctx->rules[insn->op1] = ir_match_insn(ctx, insn->op1, bb);
}
}
if (insn->op2 > bb->start && insn->op2 < ref && ctx->use_lists[insn->op2].count == 1) {
if (!ctx->rules[insn->op2]) {
ctx->rules[insn->op2] = ir_match_insn(ctx, insn->op2, bb);
}
}
if (ctx->rules[insn->op1] == IR_LEA_OB) {
ctx->rules[insn->op1] = IR_SKIP;
if (ctx->rules[insn->op2] == IR_LEA_SI) {
ctx->rules[insn->op2] = IR_SKIP;
return IR_LEA_OB_SI; // lea ret, [op1.op1.reg+op1.op2.offset+op2.op1.reg*op2.op2.scale]
}
return IR_LEA_OB_I; // lea ret, [op1.op1.reg+op1.op2.offset+op2.reg]
}
if (ctx->rules[insn->op2] == IR_LEA_OB) {
ctx->rules[insn->op2] = IR_SKIP;
if (ctx->rules[insn->op1] == IR_LEA_SI) {
ctx->rules[insn->op1] = IR_SKIP;
return IR_LEA_SI_OB; // lea ret, [op1.op1.reg*op1.op2.scale+op2.op1.reg+op2.op2.offset]
}
return IR_LEA_I_OB; // lea ret, [op1.reg+op2.op1.reg+op2.op2.offset]
}
return IR_LEA_IB; // lea ret, [op1.reg+op2.reg]
}
return IR_BINOP_INT;
} else if (ctx->flags & IR_AVX) {
return IR_BINOP_AVX;
} else {
return IR_BINOP_SSE2;
}
break;
case IR_MUL:
if (IR_IS_TYPE_INT(insn->type)) {
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.u64 == 0) {
// 0
} else if (op2_insn->val.u64 == 1) {
return IR_COPY_INT;
} else if (ir_type_size[insn->type] == sizeof(void*)) {
if (op2_insn->val.u64 == 2 || op2_insn->val.u64 == 4 || op2_insn->val.u64 == 8) {
return IR_LEA_SI; // lea ret, [op1.reg*op2.scale]
} else if (op2_insn->val.u64 == 3 || op2_insn->val.u64 == 5 || op2_insn->val.u64 == 9) {
return IR_LEA_SIB; // lea ret, [op1.reg+op1.reg*op2.scale]
}
} else if (op2_insn->val.u64 == 2) {
return IR_MUL_2; // add op1, op1
} else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64)) {
return IR_MUL_PWR2; // shl op1, IR_LOG2(op2_insn->val.u64)
}
}
return (IR_IS_TYPE_SIGNED(insn->type) && ir_type_size[insn->type] != 1) ? IR_BINOP_INT : IR_MUL_INT;
} else if (ctx->flags & IR_AVX) {
return IR_BINOP_AVX;
} else {
return IR_BINOP_SSE2;
}
break;
case IR_DIV:
if (IR_IS_TYPE_INT(insn->type)) {
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.u64 == 1) {
return IR_COPY_INT;
} else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64)) {
return IR_DIV_PWR2; // shr op1, IR_LOG2(op2_insn->val.u64)
}
}
return IR_DIV_INT;
} else if (ctx->flags & IR_AVX) {
return IR_BINOP_AVX;
} else {
return IR_BINOP_SSE2;
}
break;
case IR_MOD:
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64)) {
return IR_MOD_PWR2; // and op1, IR_LOG2(op2_insn->val.u64)-1
}
}
return IR_MOD_INT;
// case IR_POW:
// case IR_CAST:
// case IR_ADD_OV:
// case IR_SUB_OV:
// case IR_MUL_OV:
// case IR_OVERFLOW:
case IR_NOT:
return IR_OP_INT;
case IR_NEG:
if (IR_IS_TYPE_INT(insn->type)) {
return IR_OP_INT;
} else {
IR_ASSERT(0); return IR_SKIP; // xorps
}
case IR_ABS:
if (IR_IS_TYPE_INT(insn->type)) {
IR_ASSERT(0); return IR_SKIP; // TODO: ???
} else {
IR_ASSERT(0); return IR_SKIP; // andps
}
case IR_OR:
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.i64 == 0) {
return IR_COPY_INT;
} else if (op2_insn->val.i64 == -1) { // TODO: type len
// -1
}
} else if (IR_IS_CONST_REF(insn->op1)) {
op1_insn = &ctx->ir_base[insn->op1];
if (op1_insn->val.i64 == 0) {
// op2
} else if (op1_insn->val.i64 == -1) { // TODO: type len
// -1
}
}
return IR_BINOP_INT;
case IR_AND:
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.i64 == 0) {
// 0
} else if (op2_insn->val.i64 == -1) { // TODO: type len
return IR_COPY_INT;
}
} else if (IR_IS_CONST_REF(insn->op1)) {
op1_insn = &ctx->ir_base[insn->op1];
if (op1_insn->val.i64 == 0) {
// 0
} else if (op1_insn->val.i64 == -1) { // TODO: type len
// op2
}
}
return IR_BINOP_INT;
case IR_XOR:
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
}
}
return IR_BINOP_INT;
case IR_SHL:
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.u64 == 0) {
return IR_COPY_INT;
} else if (ir_type_size[insn->type] == sizeof(void*)) {
if (op2_insn->val.u64 == 1) {
// lea [op1*2]
} else if (op2_insn->val.u64 == 2) {
// lea [op1*4]
} else if (op2_insn->val.u64 == 3) {
// lea [op1*8]
}
}
return IR_SHIFT_CONST;
}
return IR_SHIFT;
case IR_SHR:
case IR_SAR:
case IR_ROL:
case IR_ROR:
if (IR_IS_CONST_REF(insn->op2)) {
op2_insn = &ctx->ir_base[insn->op2];
if (IR_IS_CONST_REF(insn->op1)) {
// const
} else if (op2_insn->val.u64 == 0) {
return IR_COPY_INT;
}
return IR_SHIFT_CONST;
}
return IR_SHIFT;
// case IR_BSWAP:
// case IR_MIN:
// case IR_MAX:
// case IR_COND:
case IR_COPY:
if (IR_IS_TYPE_INT(insn->type)) {
return IR_COPY_INT;
} else {
return IR_COPY_FP;
}
break;
case IR_PHI:
case IR_PI:
case IR_PARAM:
return IR_SKIP_REG;
case IR_VAR:
case IR_START:
case IR_BEGIN:
// case IR_END:
case IR_IF_TRUE:
case IR_IF_FALSE:
case IR_CASE_VAL:
case IR_CASE_DEFAULT:
case IR_MERGE:
case IR_LOOP_BEGIN:
// case IR_LOOP_END:
case IR_LOOP_EXIT:
return IR_SKIP;
case IR_RETURN:
if (!insn->op2) {
return IR_RETURN_VOID;
} else if (IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)) {
return IR_RETURN_INT;
} else {
return IR_RETURN_FP;
}
case IR_IF:
if (insn->op2 > bb->start && insn->op2 < ref && ctx->use_lists[insn->op2].count == 1) {
op2_insn = &ctx->ir_base[insn->op2];
if (op2_insn
&& op2_insn->op >= IR_EQ
&& op2_insn->op <= IR_UGT) {
ctx->rules[insn->op2] = IR_SKIP;
if (IR_IS_TYPE_INT(ctx->ir_base[op2_insn->op1].type)) {
return IR_CMP_AND_BRANCH_INT;
} else {
return IR_CMP_AND_BRANCH_FP;
}
}
}
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op2].type)) {
return IR_IF_INT;
} else {
IR_ASSERT(0 && "NIY IR_IF_FP");
}
default:
break;
}
return insn->op;
}
int ir_match(ir_ctx *ctx)
{
int b, n;
ir_ref i;
ir_block *bb;
ir_insn *insn;
if (!ctx->prev_insn_len) {
ctx->prev_insn_len = ir_mem_malloc(ctx->insns_count * sizeof(uint32_t));
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
n = b; /* The first insn of BB keeps BB number in prev_insn_len[] */
for (i = bb->start, insn = ctx->ir_base + i; i <= bb->end;) {
ctx->prev_insn_len[i] = n; /* The first insn of BB keeps BB number in prev_insn_len[] */
n = ir_operands_count(ctx, insn);
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
i += n;
insn += n;
}
}
}
ctx->rules = ir_mem_calloc(ctx->insns_count, sizeof(uint32_t));
for (b = ctx->cfg_blocks_count, bb = ctx->cfg_blocks + b; b > 0; b--, bb--) {
for (i = bb->end; i >= bb->start; i -= ctx->prev_insn_len[i]) {
insn = &ctx->ir_base[i];
if (!ctx->rules[i]) {
// if (ctx->rules[i] != IR_SKIP) {
ctx->rules[i] = ir_match_insn(ctx, i, bb);
}
}
}
return 1;
}
/* code genertion */
static int ir_skip_empty_blocks(ir_ctx *ctx, int b)
{
while (ctx->cfg_blocks[b].flags & IR_BB_MAY_SKIP) {
b++;
}
return b;
}
static ir_reg ir_vreg_reg(ir_ctx *ctx, ir_ref v)
{
return v < 0 ? IR_REG_NONE : ctx->live_intervals[v]->reg;
}
static ir_reg ir_ref_reg(ir_ctx *ctx, ir_ref ref)
{
return ref < 0 ? IR_REG_NONE : ctx->live_intervals[ctx->vregs[ref]]->reg;
}
static bool ir_last_use(ir_ctx *ctx, ir_ref ref)
{
// TODO:
return 0;
}
#if !IR_DASM
static char ir_int_suffix(uint8_t type)
{
switch (ir_type_size[type]) {
case 1: return 'b';
case 2: return 'w';
case 4: return 'l';
case 8: return 'q';
default: break;
}
IR_ASSERT(0);
return '?';
}
static char ir_fp_suffix(uint8_t type)
{
if (type == IR_DOUBLE) {
return 'd';
} else if (type == IR_FLOAT) {
return 's';
}
IR_ASSERT(0);
return '?';
}
static void ir_emit_reg(int8_t reg, ir_type type)
{
fprintf(stderr, "%%%s", ir_reg_name(reg, type));
}
static void ir_emit_vreg(ir_ctx *ctx, int v)
{
ir_live_interval *ival = ctx->live_intervals[v];
if (ival->reg >= 0) {
ir_emit_reg(ival->reg, ival->type);
} else if (ival->stack_spill_pos) {
if (ctx->flags & IR_USE_FRAME_POINTER) {
fprintf(stderr, "%d(%%rbp)", -ival->stack_spill_pos);
} else {
ir_backend_data *data = ctx->data;
fprintf(stderr, "%d(%%rsp)", data->stack_frame_size - ival->stack_spill_pos);
}
} else {
fprintf(stderr, "d_%d", v);
IR_ASSERT(0 && "NIY TODO: unassigned spill slot");
}
}
static void ir_emit_ref(ir_ctx *ctx, ir_ref ref)
{
if (IR_IS_CONST_REF(ref)) {
ir_insn *insn = &ctx->ir_base[ref];
if (IR_IS_TYPE_INT(insn->type)) {
// TODO: support for 64-bit constant
fprintf(stderr, "$");
ir_print_const(ctx, insn, stderr);
} else if (IR_IS_TYPE_FP(insn->type)) {
fprintf(stderr, ".LLC%d(%%rip)", -ref);
insn->emit_const = 1;
}
} else {
ir_emit_vreg(ctx, ctx->vregs[ref]);
}
}
#endif
static void ir_emit_load(ir_ctx *ctx, ir_type type, ir_ref src, ir_reg reg)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
| ASM_REG_REF_OP mov, type, reg, src
#else
fprintf(stderr, "\tmov%c ", ir_int_suffix(type));
ir_emit_ref(ctx, src);
fprintf(stderr, ", ");
ir_emit_reg(reg, type);
fprintf(stderr, "\n");
#endif
}
static void ir_emit_store(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref dst)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
| ASM_REF_REG_OP mov, type, dst, reg
#else
fprintf(stderr, "\tmov%c ", ir_int_suffix(type));
ir_emit_reg(reg, type);
fprintf(stderr, ", ");
ir_emit_ref(ctx, dst);
fprintf(stderr, "\n");
#endif
}
static void ir_emit_fp_load(ir_ctx *ctx, ir_type type, ir_ref src, ir_reg reg)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
| ASM_FP_MOV_REG_REF_OP type, reg, src
#else
char suffix = ir_fp_suffix(type);
if (ir_ref_reg(ctx, src) >= 0) {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovap%c ", suffix);
} else {
fprintf(stderr, "\tmovap%c ", suffix);
}
} else {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovs%c ", suffix);
} else {
fprintf(stderr, "\tmovs%c ", suffix);
}
}
ir_emit_ref(ctx, src);
fprintf(stderr, ", ");
ir_emit_reg(reg, type);
fprintf(stderr, "\n");
#endif
}
static void ir_emit_fp_store(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref dst)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
| ASM_FP_MOV_REF_REG_OP type, dst, reg
#else
char suffix = ir_fp_suffix(type);
if (ir_ref_reg(ctx, dst) >= 0) {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovap%c ", suffix);
} else {
fprintf(stderr, "\tmovap%c ", suffix);
}
} else {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovs%c ", suffix);
} else {
fprintf(stderr, "\tmovs%c ", suffix);
}
}
ir_emit_reg(reg, type);
fprintf(stderr, ", ");
ir_emit_ref(ctx, dst);
fprintf(stderr, "\n");
#endif
}
static void ir_emit_prologue(ir_ctx *ctx)
{
ir_backend_data *data = ctx->data;
#if IR_DASM
dasm_State **Dst = &data->dasm_state;
#endif
if (ctx->flags & IR_USE_FRAME_POINTER) {
#if IR_DASM
| push rbp
| mov rbp, rsp
#else
if (IR_X64) {
fprintf(stderr, "\tpushq %%rbp\n");
fprintf(stderr, "\tmovq %%rsp, %%rbp\n");
} else {
fprintf(stderr, "\tpushl %%ebp\n");
fprintf(stderr, "\tmovl %%esp, %%ebp\n");
}
#endif
}
if (data->stack_frame_size) {
#if IR_DASM
| sub rsp, data->stack_frame_size
#else
if (IR_X64) {
fprintf(stderr, "\tsubq $%d, %%rsp\n", data->stack_frame_size);
} else {
fprintf(stderr, "\tsubl $%d, %%esp\n", data->stack_frame_size);
}
#endif
}
if (data->used_preserved_regs) {
int offset;
uint32_t i;
if (ctx->flags & IR_USE_FRAME_POINTER) {
offset = -(int)sizeof(void*);
} else {
offset = data->stack_frame_size;
}
for (i = 0; i < IR_REG_NUM; i++) {
if (IR_REGSET_IN(data->used_preserved_regs, i)) {
if (i < IR_REG_FP_FIRST) {
#if IR_DASM
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_RBP : IR_REG_RSP;
| mov aword [Ra(fp)+offset], Ra(i)
#else
if (IR_X64) {
fprintf(stderr, "\tmovl ");
} else {
fprintf(stderr, "\tmovq ");
}
ir_emit_reg(i, IR_ADDR);
fprintf(stderr, ", %d", offset);
if (IR_X64) {
if (ctx->flags & IR_USE_FRAME_POINTER) {
fprintf(stderr, "(%%rbp)");
} else {
fprintf(stderr, "(%%rsp)");
}
} else {
if (ctx->flags & IR_USE_FRAME_POINTER) {
fprintf(stderr, "(%%ebp)");
} else {
fprintf(stderr, "(%%esp)");
}
}
fprintf(stderr, "\n");
#endif
offset -= sizeof(void*);
} else {
IR_ASSERT(0 && "NIY FP register saing");
}
}
}
}
}
static void ir_emit_epilogue(ir_ctx *ctx)
{
ir_backend_data *data = ctx->data;
#if IR_DASM
dasm_State **Dst = &data->dasm_state;
#endif
if (data->used_preserved_regs) {
int offset;
uint32_t i;
if (ctx->flags & IR_USE_FRAME_POINTER) {
offset = -(int)sizeof(void*);
} else {
offset = data->stack_frame_size;
}
for (i = 0; i < IR_REG_NUM; i++) {
if (IR_REGSET_IN(data->used_preserved_regs, i)) {
if (i < IR_REG_FP_FIRST) {
#if IR_DASM
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_RBP : IR_REG_RSP;
| mov Ra(i), aword [Ra(fp)+offset]
#else
if (IR_X64) {
fprintf(stderr, "\tmovl ");
} else {
fprintf(stderr, "\tmovq ");
}
fprintf(stderr, "%d", offset);
if (IR_X64) {
if (ctx->flags & IR_USE_FRAME_POINTER) {
fprintf(stderr, "(%%rbp)");
} else {
fprintf(stderr, "(%%rsp)");
}
} else {
if (ctx->flags & IR_USE_FRAME_POINTER) {
fprintf(stderr, "(%%ebp)");
} else {
fprintf(stderr, "(%%esp)");
}
}
fprintf(stderr, ", ");
ir_emit_reg(i, IR_ADDR);
fprintf(stderr, "\n");
#endif
offset -= sizeof(void*);
} else {
IR_ASSERT(0 && "NIY FP register saing");
}
}
}
}
if (ctx->flags & IR_USE_FRAME_POINTER) {
#if IR_DASM
| mov rsp, rbp
| pop rbp
#else
if (IR_X64) {
fprintf(stderr, "\tmovq %%rbp, %%rsp\n");
fprintf(stderr, "\tpopq %%rbp\n");
} else {
fprintf(stderr, "\tmovl %%ebp, %%esp\n");
fprintf(stderr, "\tpopl %%ebp\n");
}
#endif
} else if (data->stack_frame_size) {
#if IR_DASM
| add rsp, data->stack_frame_size
#else
if (IR_X64) {
fprintf(stderr, "\taddq $%d, %%rsp\n", data->stack_frame_size);
} else {
fprintf(stderr, "\taddl $%d, %%esp\n", data->stack_frame_size);
}
#endif
}
}
void ir_emit_binop_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = insn->type;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg reg;
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_int_suffix(type);
#endif
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
// TODO: add $imm, mem
// TODO: commutative insns
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, type, insn->op1, reg);
}
if (op2_reg < 0 && insn->op1 == insn->op2) {
op2_reg = reg;
}
#if IR_DASM
if (op2_reg >= 0) {
switch (insn->op) {
case IR_ADD:
| ASM_REG_REG_OP add, type, reg, op2_reg
break;
case IR_SUB:
| ASM_REG_REG_OP sub, type, reg, op2_reg
break;
case IR_MUL:
| ASM_REG_REG_IMUL type, reg, op2_reg
break;
case IR_OR:
| ASM_REG_REG_OP or, type, reg, op2_reg
break;
case IR_AND:
| ASM_REG_REG_OP and, type, reg, op2_reg
break;
case IR_XOR:
| ASM_REG_REG_OP xor, type, reg, op2_reg
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
} else {
switch (insn->op) {
case IR_ADD:
| ASM_REG_REF_OP add, type, reg, insn->op2
break;
case IR_SUB:
| ASM_REG_REF_OP sub, type, reg, insn->op2
break;
case IR_MUL:
| ASM_REG_REF_IMUL type, reg, insn->op2
break;
case IR_OR:
| ASM_REG_REF_OP or, type, reg, insn->op2
break;
case IR_AND:
| ASM_REG_REF_OP and, type, reg, insn->op2
break;
case IR_XOR:
| ASM_REG_REF_OP xor, type, reg, insn->op2
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
}
#else
switch (insn->op) {
case IR_ADD: fprintf(stderr, "\tadd%c ", suffix); break;
case IR_SUB: fprintf(stderr, "\tsub%c ", suffix); break;
case IR_MUL: fprintf(stderr, "\timul%c ", suffix); break;
case IR_OR: fprintf(stderr, "\tor%c ", suffix); break;
case IR_AND: fprintf(stderr, "\tand%c ", suffix); break;
case IR_XOR: fprintf(stderr, "\txor%c ", suffix); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
if (op2_reg >= 0) {
ir_emit_reg(op2_reg, type);
} else {
ir_emit_ref(ctx, insn->op2);
}
fprintf(stderr, ", ");
ir_emit_reg(reg, type);
fprintf(stderr, "\n");
#endif
if (def_reg != reg) {
ir_emit_store(ctx, type, reg, def);
}
}
#if IR_DASM
void ir_emit_incdec(ir_ctx *ctx, uint32_t rule, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
if (rule == IR_INC) {
| ASM_REG_OP inc, insn->type, reg
} else {
| ASM_REG_OP dec, insn->type, reg
}
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_mul2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
| ASM_REG_REG_OP add, insn->type, reg, reg
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_mul_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg reg;
uint32_t shift = IR_LOG2(ctx->ir_base[insn->op2].val.u64);
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
| ASM_REG_IMM_OP shl, insn->type, reg, shift
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_div_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
uint32_t shift = IR_LOG2(ctx->ir_base[insn->op2].val.u64);
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
| ASM_REG_IMM_OP shr, insn->type, reg, shift
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_mod_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
uint32_t mask = IR_LOG2(ctx->ir_base[insn->op2].val.u64) - 1;
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
| ASM_REG_IMM_OP and, insn->type, reg, mask
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_shift(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
if (op2_reg != IR_REG_RCX) {
ir_emit_load(ctx, insn->type, insn->op2, IR_REG_RCX);
}
switch (insn->op) {
case IR_SHL:
| ASM_REG_IMM_OP shl, insn->type, reg, cl
break;
case IR_SHR:
| ASM_REG_IMM_OP shr, insn->type, reg, cl
break;
case IR_SAR:
| ASM_REG_IMM_OP sar, insn->type, reg, cl
break;
case IR_ROL:
| ASM_REG_IMM_OP rol, insn->type, reg, cl
break;
case IR_ROR:
| ASM_REG_IMM_OP ror, insn->type, reg, cl
break;
default:
IR_ASSERT(0);
}
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_shift_const(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
uint32_t shift = ctx->ir_base[insn->op2].val.u64;
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
switch (insn->op) {
case IR_SHL:
| ASM_REG_IMM_OP shl, insn->type, reg, shift
break;
case IR_SHR:
| ASM_REG_IMM_OP shr, insn->type, reg, shift
break;
case IR_SAR:
| ASM_REG_IMM_OP sar, insn->type, reg, shift
break;
case IR_ROL:
| ASM_REG_IMM_OP rol, insn->type, reg, shift
break;
case IR_ROR:
| ASM_REG_IMM_OP ror, insn->type, reg, shift
break;
default:
IR_ASSERT(0);
}
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_op_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg reg;
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_load(ctx, insn->type, insn->op1, reg);
}
if (insn->op == IR_NOT) {
| ASM_REG_OP not, insn->type, reg
} else if (insn->op == IR_NEG) {
| ASM_REG_OP neg, insn->type, reg
} else {
IR_ASSERT(0);
}
if (def_reg != reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
#endif
void ir_emit_mul_div_mod(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = insn->type;
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg def_reg = ir_ref_reg(ctx, def);
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_int_suffix(type);
#endif
if (op1_reg != IR_REG_RAX) {
ir_emit_load(ctx, type, insn->op1, IR_REG_RAX);
}
if (op2_reg < 0) {
if (insn->op1 == insn->op2) {
op2_reg = IR_REG_RAX;
} else if (IR_IS_CONST_REF(insn->op2)) {
op2_reg = IR_REG_RCX;
ir_emit_load(ctx, type, insn->op2, IR_REG_RCX); // TODO: temporary register 2
}
}
#if IR_DASM
if (insn->op == IR_MUL) {
if (IR_IS_TYPE_SIGNED(insn->type)) {
if (op2_reg >= 0) {
| ASM_REG_OP imul, type, op2_reg
} else {
| ASM_REF_OP imul, type, insn->op2
}
} else {
if (op2_reg >= 0) {
| ASM_REG_OP mul, type, op2_reg
} else {
| ASM_REF_OP mul, type, insn->op2
}
}
} else {
if (IR_IS_TYPE_SIGNED(type)) {
| cdq
if (op2_reg >= 0) {
| ASM_REG_OP idiv, type, op2_reg
} else {
| ASM_REF_OP idiv, type, insn->op2
}
} else {
| ASM_REG_REG_OP xor, type, IR_REG_RDX, IR_REG_RDX
if (op2_reg >= 0) {
| ASM_REG_OP div, type, op2_reg
} else {
| ASM_REF_OP div, type, insn->op2
}
}
}
#else
if (insn->op == IR_MUL) {
if (IR_IS_TYPE_SIGNED(insn->type)) {
fprintf(stderr, "\timul%c ", suffix);
} else {
fprintf(stderr, "\tmul%c ", suffix);
}
} else {
if (IR_IS_TYPE_SIGNED(type)) {
fprintf(stderr, "\tcdq\n");
fprintf(stderr, "\tidiv%c ", suffix);
} else {
fprintf(stderr, "\txor ");
ir_emit_reg(IR_REG_RDX, type);
fprintf(stderr, " ,");
ir_emit_reg(IR_REG_RDX, type);
fprintf(stderr, "\n");
fprintf(stderr, "\tdiv%c ", suffix);
}
}
ir_emit_reg(op2_reg, type);
fprintf(stderr, "\n");
#endif
if (insn->op == IR_MUL || insn->op == IR_DIV) {
if (def_reg != IR_REG_RAX) {
ir_emit_store(ctx, type, IR_REG_RAX, def);
}
} else if (insn->op == IR_MOD) {
if (ir_type_size[type] == 1) {
#if IR_DASM
ir_live_interval *ival = ctx->live_intervals[ctx->vregs[def]];
if (ival->reg >= 0) {
| mov al, ah
| mov Rb(ival->reg), al
} else if (ival->stack_spill_pos) {
if (ctx->flags & IR_USE_FRAME_POINTER) {
int32_t offset = -ival->stack_spill_pos;
| mov byte [rbp+offset], ah
} else {
int32_t offset = data->stack_frame_size - ival->stack_spill_pos;
| mov byte [rsp+offset], ah
}
}
#else
fprintf(stderr, "\tmov%c %%ah, ", suffix);
ir_emit_ref(ctx, def);
fprintf(stderr, "\n");
#endif
} else if (def_reg != IR_REG_RDX) {
ir_emit_store(ctx, type, IR_REG_RDX, def);
}
} else {
IR_ASSERT(0);
}
}
void ir_emit_binop_sse2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = insn->type;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg reg;
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_fp_suffix(type);
#endif
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
// TODO: commutative insns
reg = IR_REG_XMM0; // TODO: temporary register
}
if (op1_reg != reg) {
ir_emit_fp_load(ctx, type, insn->op1, reg);
}
if (op2_reg < 0 && insn->op1 == insn->op2) {
op2_reg = reg;
}
#if IR_DASM
if (op2_reg >= 0) {
switch (insn->op) {
case IR_ADD:
| ASM_SSE2_REG_REG_OP addss, addsd, type, reg, op2_reg
break;
case IR_SUB:
| ASM_SSE2_REG_REG_OP subss, subsd, type, reg, op2_reg
break;
case IR_MUL:
| ASM_SSE2_REG_REG_OP mulss, mulsd, type, reg, op2_reg
break;
case IR_DIV:
| ASM_SSE2_REG_REG_OP divss, divsd, type, reg, op2_reg
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
} else {
switch (insn->op) {
case IR_ADD:
| ASM_SSE2_REG_REF_OP addss, addsd, type, reg, insn->op2
break;
case IR_SUB:
| ASM_SSE2_REG_REF_OP subss, subsd, type, reg, insn->op2
break;
case IR_MUL:
| ASM_SSE2_REG_REF_OP mulss, mulsd, type, reg, insn->op2
break;
case IR_DIV:
| ASM_SSE2_REG_REF_OP divss, divsd, type, reg, insn->op2
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
}
#else
switch (insn->op) {
case IR_ADD: fprintf(stderr, "\tadds%c ", suffix); break;
case IR_SUB: fprintf(stderr, "\tsubs%c ", suffix); break;
case IR_MUL: fprintf(stderr, "\tmuls%c ", suffix); break;
case IR_DIV: fprintf(stderr, "\tdivs%c ", suffix); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
if (op2_reg >= 0) {
ir_emit_reg(op2_reg, type);
} else {
ir_emit_ref(ctx, insn->op2);
}
fprintf(stderr, ", ");
ir_emit_reg(reg, type);
fprintf(stderr, "\n");
#endif
if (def_reg != reg) {
ir_emit_fp_store(ctx, type, reg, def);
}
}
void ir_emit_binop_avx(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = insn->type;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg reg;
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_fp_suffix(type);
#endif
if (def_reg >= 0) {
reg = def_reg;
} else if (op1_reg >= 0 && ir_last_use(ctx, insn->op1)) {
reg = op1_reg;
} else {
// TODO: commutative insns
reg = IR_REG_XMM0; // TODO: temporary register
}
if (op1_reg < 0) {
ir_emit_fp_load(ctx, type, insn->op1, reg);
op1_reg = reg;
}
if (op2_reg < 0 && insn->op1 == insn->op2) {
op2_reg = reg;
}
#if IR_DASM
if (op2_reg >= 0) {
switch (insn->op) {
case IR_ADD:
| ASM_AVX_REG_REG_REG_OP vaddss, vaddsd, type, reg, op1_reg, op2_reg
break;
case IR_SUB:
| ASM_AVX_REG_REG_REG_OP vsubss, vsubsd, type, reg, op1_reg, op2_reg
break;
case IR_MUL:
| ASM_AVX_REG_REG_REG_OP vmulss, vmulsd, type, reg, op1_reg, op2_reg
break;
case IR_DIV:
| ASM_AVX_REG_REG_REG_OP vdivss, vdivsd, type, reg, op1_reg, op2_reg
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
} else {
switch (insn->op) {
case IR_ADD:
| ASM_AVX_REG_REG_REF_OP vaddss, vaddsd, type, reg, op1_reg, insn->op2
break;
case IR_SUB:
| ASM_AVX_REG_REG_REF_OP vsubss, vsubsd, type, reg, op1_reg, insn->op2
break;
case IR_MUL:
| ASM_AVX_REG_REG_REF_OP vmulss, vmulsd, type, reg, op1_reg, insn->op2
break;
case IR_DIV:
| ASM_AVX_REG_REG_REF_OP vdivss, vdivsd, type, reg, op1_reg, insn->op2
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
}
#else
switch (insn->op) {
case IR_ADD: fprintf(stderr, "\tvadds%c ", suffix); break;
case IR_SUB: fprintf(stderr, "\tvsubs%c ", suffix); break;
case IR_MUL: fprintf(stderr, "\tvmuls%c ", suffix); break;
case IR_DIV: fprintf(stderr, "\tvdivs%c ", suffix); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
if (op2_reg >= 0) {
ir_emit_reg(op2_reg, type);
} else {
ir_emit_ref(ctx, insn->op2);
}
fprintf(stderr, ", ");
// if (op1_reg >= 0) {
ir_emit_reg(op1_reg, type);
// } else {
// ir_emit_ref(ctx, insn->op1);
// }
fprintf(stderr, ", ");
ir_emit_reg(reg, type);
fprintf(stderr, "\n");
#endif
if (def_reg != reg) {
ir_emit_fp_store(ctx, type, reg, def);
}
}
void ir_emit_cmp_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = ctx->ir_base[insn->op1].type;
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg reg;
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_int_suffix(type);
#endif
if (def_reg >= 0) {
reg = def_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg < 0 && op2_reg < 0) {
if (IR_IS_CONST_REF(insn->op2)) {
/* cmp $imm, mem */
} else {
// TODO: commutative insns
op1_reg = IR_REG_RAX; // TODO: temporary register
ir_emit_load(ctx, type, insn->op1, op1_reg);
}
}
#if IR_DASM
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
}
switch (insn->op) {
case IR_EQ:
| sete Rb(reg)
break;
case IR_NE:
| setne Rb(reg)
break;
case IR_LT:
| setl Rb(reg)
break;
case IR_GE:
| setge Rb(reg)
break;
case IR_LE:
| setle Rb(reg)
break;
case IR_GT:
| setg Rb(reg)
break;
case IR_ULT:
| setb Rb(reg)
break;
case IR_UGE:
| setae Rb(reg)
break;
case IR_ULE:
| setbe Rb(reg)
break;
case IR_UGT:
| seta Rb(reg)
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
#else
fprintf(stderr, "\tcmp%c ", suffix);
if (op2_reg >= 0) {
ir_emit_reg(op2_reg, type);
} else {
ir_emit_ref(ctx, insn->op2);
}
fprintf(stderr, ", ");
if (op1_reg >= 0) {
ir_emit_reg(op1_reg, type);
} else {
ir_emit_ref(ctx, insn->op1);
}
fprintf(stderr, "\n");
switch (insn->op) {
case IR_EQ: fprintf(stderr, "\tsete "); break;
case IR_NE: fprintf(stderr, "\tsetne "); break;
case IR_LT: fprintf(stderr, "\tsetl "); break;
case IR_GE: fprintf(stderr, "\tsetge "); break;
case IR_LE: fprintf(stderr, "\tsetle "); break;
case IR_GT: fprintf(stderr, "\tsetg "); break;
case IR_ULT: fprintf(stderr, "\tsetb "); break;
case IR_UGE: fprintf(stderr, "\tsetae "); break;
case IR_ULE: fprintf(stderr, "\tsetbe "); break;
case IR_UGT: fprintf(stderr, "\tseta "); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
ir_emit_reg(reg, insn->type);
fprintf(stderr, "\n");
#endif
if (reg != def_reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
void ir_emit_cmp_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = ctx->ir_base[insn->op1].type;
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg reg;
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_fp_suffix(type);
#endif
if (def_reg >= 0) {
reg = def_reg;
} else {
reg = IR_REG_RAX; // TODO: temporary register
}
if (op1_reg < 0 && op2_reg < 0) {
// TODO: commutative insns
op1_reg = IR_REG_XMM0; // TODO: temporary register
ir_emit_fp_load(ctx, type, insn->op1, op1_reg);
}
#if IR_DASM
if (op1_reg >= 0) {
| ASM_FP_REG_REF_OP ucomiss, ucomisd, vucomiss, vucomisd, type, op1_reg, insn->op2
} else {
// ASM_FP_REF_REG_OP ucomiss, ucomisd, vucomiss, vucomisd, type, insn->op1, op2_reg
IR_ASSERT(0);
}
switch (insn->op) {
case IR_EQ:
| sete Rb(reg)
break;
case IR_NE:
| setne Rb(reg)
break;
case IR_LT:
| setb Rb(reg)
break;
case IR_GE:
| setae Rb(reg)
break;
case IR_LE:
| setbe Rb(reg)
break;
case IR_GT:
fprintf(stderr, "\tseta ");
break;
// case IR_ULT: fprintf(stderr, "\tsetb "); break;
// case IR_UGE: fprintf(stderr, "\tsetae "); break;
// case IR_ULE: fprintf(stderr, "\tsetbe "); break;
// case IR_UGT: fprintf(stderr, "\tseta "); break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
#else
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvucomis%c ", suffix);
} else {
fprintf(stderr, "\tucomis%c ", suffix);
}
if (op2_reg >= 0) {
ir_emit_reg(op2_reg, type);
} else {
ir_emit_ref(ctx, insn->op2);
}
fprintf(stderr, ", ");
if (op1_reg >= 0) {
ir_emit_reg(op1_reg, type);
} else {
ir_emit_ref(ctx, insn->op1);
}
fprintf(stderr, "\n");
switch (insn->op) {
case IR_EQ: fprintf(stderr, "\tsete "); break;
case IR_NE: fprintf(stderr, "\tsetne "); break;
case IR_LT: fprintf(stderr, "\tsetb "); break;
case IR_GE: fprintf(stderr, "\tsetae "); break;
case IR_LE: fprintf(stderr, "\tsetbe "); break;
case IR_GT: fprintf(stderr, "\tseta "); break;
// case IR_ULT: fprintf(stderr, "\tsetb "); break;
// case IR_UGE: fprintf(stderr, "\tsetae "); break;
// case IR_ULE: fprintf(stderr, "\tsetbe "); break;
// case IR_UGT: fprintf(stderr, "\tseta "); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
ir_emit_reg(reg, insn->type);
fprintf(stderr, "\n");
#endif
if (reg != def_reg) {
ir_emit_store(ctx, insn->type, reg, def);
}
}
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;
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) {
if (op < IR_LT) {
op ^= 1; // reverse
} else {
op ^= 3; // reverse
}
true_block = false_block;
false_block = 0;
} else if (false_block != next_block) {
false_block = 0;
}
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
if (int_cmp) {
switch (op) {
case IR_EQ:
| je =>true_block
break;
case IR_NE:
| jne =>true_block
break;
case IR_LT:
| jl =>true_block
break;
case IR_GE:
| jge =>true_block
break;
case IR_LE:
| jle =>true_block
break;
case IR_GT:
| jg =>true_block
break;
case IR_ULT:
| jb =>true_block
break;
case IR_UGE:
| jae =>true_block
break;
case IR_ULE:
| jbe =>true_block
break;
case IR_UGT:
| ja =>true_block
break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
} else {
switch (op) {
case IR_EQ:
| je =>true_block
break;
case IR_NE:
| jne =>true_block
break;
case IR_LT:
| jb =>true_block
break;
case IR_GE:
| jae =>true_block
break;
case IR_LE:
| jbe =>true_block
break;
case IR_GT:
| ja =>true_block
break;
// case IR_ULT: fprintf(stderr, "\tjb .LL%d\n", true_block); break;
// case IR_UGE: fprintf(stderr, "\tjae .LL%d\n", true_block); break;
// case IR_ULE: fprintf(stderr, "\tjbe .LL%d\n", true_block); break;
// case IR_UGT: fprintf(stderr, "\tja .LL%d\n", true_block); break;
default:
IR_ASSERT(0 && "NIY binary op");
break;
}
}
if (false_block) {
| jmp =>false_block
}
#else
if (int_cmp) {
switch (op) {
case IR_EQ: fprintf(stderr, "\tje .LL%d\n", true_block); break;
case IR_NE: fprintf(stderr, "\tjne .LL%d\n", true_block); break;
case IR_LT: fprintf(stderr, "\tjl .LL%d\n", true_block); break;
case IR_GE: fprintf(stderr, "\tjge .LL%d\n", true_block); break;
case IR_LE: fprintf(stderr, "\tjle .LL%d\n", true_block); break;
case IR_GT: fprintf(stderr, "\tjg .LL%d\n", true_block); break;
case IR_ULT: fprintf(stderr, "\tjb .LL%d\n", true_block); break;
case IR_UGE: fprintf(stderr, "\tjae .LL%d\n", true_block); break;
case IR_ULE: fprintf(stderr, "\tjbe .LL%d\n", true_block); break;
case IR_UGT: fprintf(stderr, "\tja .LL%d\n", true_block); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
} else {
switch (op) {
case IR_EQ: fprintf(stderr, "\tje .LL%d\n", true_block); break;
case IR_NE: fprintf(stderr, "\tjne .LL%d\n", true_block); break;
case IR_LT: fprintf(stderr, "\tjb .LL%d\n", true_block); break;
case IR_GE: fprintf(stderr, "\tjae .LL%d\n", true_block); break;
case IR_LE: fprintf(stderr, "\tjbe .LL%d\n", true_block); break;
case IR_GT: fprintf(stderr, "\tja .LL%d\n", true_block); break;
// case IR_ULT: fprintf(stderr, "\tjb .LL%d\n", true_block); break;
// case IR_UGE: fprintf(stderr, "\tjae .LL%d\n", true_block); break;
// case IR_ULE: fprintf(stderr, "\tjbe .LL%d\n", true_block); break;
// case IR_UGT: fprintf(stderr, "\tja .LL%d\n", true_block); break;
default: IR_ASSERT(0 && "NIY binary op"); break;
}
}
if (false_block) {
fprintf(stderr, "\tjmp .LL%d\n", false_block);
}
#endif
}
void ir_emit_cmp_and_branch_int(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
{
ir_insn *cmp_insn = &ctx->ir_base[insn->op2];
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);
if (op1_reg < 0 && op2_reg < 0) {
if (IR_IS_CONST_REF(cmp_insn->op2)) {
/* cmp $imm, mem */
} else {
// TODO: commutative insns
op1_reg = IR_REG_RAX; // TODO: temporary register
ir_emit_load(ctx, type, cmp_insn->op1, op1_reg);
}
}
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
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
}
#else
char suffix = ir_int_suffix(type);
fprintf(stderr, "\tcmp%c ", suffix);
if (op2_reg >= 0) {
ir_emit_reg(op2_reg, type);
} else {
ir_emit_ref(ctx, cmp_insn->op2);
}
fprintf(stderr, ", ");
if (op1_reg >= 0) {
ir_emit_reg(op1_reg, type);
} else {
ir_emit_ref(ctx, cmp_insn->op1);
}
fprintf(stderr, "\n");
#endif
ir_emit_jcc(ctx, cmp_insn->op, b, def, insn, 1);
}
void ir_emit_cmp_and_branch_fp(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
{
ir_insn *cmp_insn = &ctx->ir_base[insn->op2];
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);
if (op1_reg < 0 && op2_reg < 0) {
// TODO: commutative insns
op1_reg = IR_REG_XMM0; // TODO: temporary register
ir_emit_fp_load(ctx, type, cmp_insn->op1, op1_reg);
}
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
if (op1_reg >= 0) {
| ASM_FP_REG_REF_OP ucomiss, ucomisd, vucomiss, vucomisd, type, op1_reg, cmp_insn->op2
} else {
// ASM_FP_REF_REG_OP ucomiss, ucomisd, vucomiss, vucomisd, type, cmp_insn->op1, op2_reg
IR_ASSERT(0);
}
#else
char suffix = ir_fp_suffix(type);
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvucomis%c ", suffix);
} else {
fprintf(stderr, "\tucomis%c ", suffix);
}
ir_emit_ref(ctx, cmp_insn->op2);
fprintf(stderr, ", ");
if (op1_reg) {
ir_emit_reg(op1_reg, type);
} else {
ir_emit_ref(ctx, cmp_insn->op1);
}
fprintf(stderr, "\n");
#endif
ir_emit_jcc(ctx, cmp_insn->op, b, def, insn, 0);
}
void ir_emit_if_int(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
{
ir_type type = ctx->ir_base[insn->op2].type;
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix = ir_int_suffix(type);
#endif
if (op2_reg >= 0) {
#if IR_DASM
| ASM_REG_REG_OP test, type, op2_reg, op2_reg
#else
fprintf(stderr, "\ttest%c ", suffix);
ir_emit_reg(op2_reg, type);
fprintf(stderr, ", ");
ir_emit_reg(op2_reg, type);
fprintf(stderr, "\n");
#endif
} else {
#if IR_DASM
| ASM_REF_IMM_OP cmp, type, insn->op2, 0
#else
fprintf(stderr, "\tcmp%c $0, ", suffix);
ir_emit_ref(ctx, insn->op2);
fprintf(stderr, "\n");
#endif
}
ir_emit_jcc(ctx, IR_NE, b, def, insn, 1);
}
void ir_emit_return_void(ir_ctx *ctx)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#endif
ir_emit_epilogue(ctx);
#if IR_DASM
| ret
#else
fprintf(stderr, "\tret\n");
#endif
}
void ir_emit_return_int(ir_ctx *ctx, ir_insn *insn)
{
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
if (op2_reg != IR_REG_INT_RET1) {
ir_emit_load(ctx, ctx->ir_base[insn->op2].type, insn->op2, IR_REG_INT_RET1);
}
ir_emit_return_void(ctx);
}
void ir_emit_return_fp(ir_ctx *ctx, ir_insn *insn)
{
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
if (op2_reg != IR_REG_INT_RET1) {
ir_emit_fp_load(ctx, ctx->ir_base[insn->op2].type, insn->op2, IR_REG_FP_RET1);
}
ir_emit_return_void(ctx);
}
static void ir_emit_copy_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_ref type = insn->type;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg reg;
if (def_reg >= 0 && def_reg == op1_reg) {
/* same reg */
} else if (def_reg >= 0) {
ir_emit_load(ctx, type, insn->op1, def_reg);
} else if (op1_reg >= 0) {
ir_emit_store(ctx, type, op1_reg, def);
} else if (IR_IS_CONST_REF(insn->op1)) {
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
| ASM_REF_IMM_OP mov, type, def, insn->op1
#else
char suffix = ir_int_suffix(type);
/* mov $imm, mem */
fprintf(stderr, "\tmov%c ", suffix);
ir_emit_ref(ctx, insn->op1);
fprintf(stderr, ", ");
ir_emit_ref(ctx, def);
fprintf(stderr, "\n");
#endif
} else {
reg = IR_REG_RAX; // TODO: temporary register
ir_emit_load(ctx, type, insn->op1, reg);
ir_emit_store(ctx, type, reg, def);
}
}
static void ir_emit_copy_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_type type = insn->type;
ir_reg def_reg = ir_ref_reg(ctx, def);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg reg;
if (def_reg > 0 && def_reg == op1_reg) {
/* same reg */
} else if (def_reg >= 0) {
ir_emit_fp_load(ctx, type, insn->op1, def_reg);
} else if (op1_reg >= 0) {
ir_emit_fp_store(ctx, type, op1_reg, def);
} else {
reg = IR_REG_XMM0; // TODO: temporary register
ir_emit_fp_load(ctx, type, insn->op1, reg);
ir_emit_fp_store(ctx, type, reg, def);
}
}
static int ir_emit_dessa_move(ir_ctx *ctx, uint8_t type, int from, int to)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
#else
char suffix;
#endif
ir_insn *from_insn;
int8_t to_reg, from_reg, dst_reg;
from_insn =&ctx->ir_base[from];
if (IR_IS_TYPE_INT(type)) {
to_reg = to ? ir_vreg_reg(ctx, to) : IR_REG_RAX; // %rax is a temporary register
from_reg = from ? ir_vreg_reg(ctx, from) : IR_REG_RAX; // %rax is a temporary register
if (IR_IS_CONST_REF(from) && to_reg >= 0 && IR_IS_TYPE_INT(from_insn->type) && from_insn->val.u64 == 0) {
#if IR_DASM
| ASM_REG_REG_OP xor, type, to_reg, to_reg
#else
suffix = ir_int_suffix(type);
fprintf(stderr, "\txor%c ", suffix);
ir_emit_reg(to_reg, type);
fprintf(stderr, ", ");
ir_emit_reg(to_reg, type);
fprintf(stderr, "\n");
#endif
} else if (from > 0 && from_reg < 0 && to_reg < 0) {
#if IR_DASM
| ASM_VREGX_OP push, IR_ADDR, from
| ASM_VREG_OP pop, IR_ADDR, to
#else
fprintf(stderr, "\tpushq ");
ir_emit_vreg(ctx, from);
fprintf(stderr, "\n");
fprintf(stderr, "\tpopq ");
ir_emit_vreg(ctx, to);
fprintf(stderr, "\n");
#endif
} else {
#if IR_DASM
if (from_reg >= 0 && to_reg >= 0) {
| ASM_REG_REG_OP mov, type, to_reg, from_reg
} else if (to_reg >= 0 && IR_IS_CONST_REF(from)) {
ir_insn *_insn = &ctx->ir_base[from];
| ASM_REG_IMM_OP mov, type, to_reg, _insn->val.i32 // TODO:
} else if (IR_IS_CONST_REF(from)) {
| ASM_VREG_IMM_OP mov, type, to, from
} else if (from_reg >= 0) {
| ASM_VREG_REG_OP mov, type, to, from_reg
} else if (to_reg >= 0) {
| ASM_REG_VREG_OP mov, type, to_reg, from
}
#else
suffix = ir_int_suffix(type);
fprintf(stderr, "\tmov%c ", suffix);
if (IR_IS_CONST_REF(from)) {
// TODO: support for 64-bit constant
fprintf(stderr, "$");
ir_print_const(ctx, from_insn, stderr);
} else if (from_reg >= 0) {
ir_emit_reg(from_reg, type);
} else {
ir_emit_vreg(ctx, from);
}
fprintf(stderr, ", ");
if (to_reg >= 0) {
ir_emit_reg(to_reg, type);
} else {
ir_emit_vreg(ctx, to);
}
fprintf(stderr, "\n");
#endif
}
} else {
to_reg = to ? ir_vreg_reg(ctx, to) : IR_REG_XMM0; // %xmm0 is a temporary register
from_reg = from ? ir_vreg_reg(ctx, from) : IR_REG_XMM0; // %xmm0 is a temporary register
dst_reg = to_reg;
if (to_reg < 0 && from_reg < 0) {
to_reg = IR_REG_XMM0; // TODO: temporary register
}
#if IR_DASM
if (IR_IS_CONST_REF(from) && to_reg >= 0
&& from_insn->type == IR_FLOAT && from_insn->val.f == 0) {
if (ctx->flags & IR_AVX) {
| vxorps xmm(to_reg-IR_REG_XMM0), xmm(to_reg-IR_REG_XMM0), xmm(to_reg-IR_REG_XMM0)
} else {
| xorps xmm(to_reg-IR_REG_XMM0), xmm(to_reg-IR_REG_XMM0)
}
} else if (IR_IS_CONST_REF(from) && to_reg >= 0
&& from_insn->type == IR_DOUBLE && from_insn->val.d == 0) {
if (ctx->flags & IR_AVX) {
| vxorpd xmm(to_reg-IR_REG_XMM0), xmm(to_reg-IR_REG_XMM0), xmm(to_reg-IR_REG_XMM0)
} else {
| xorpd xmm(to_reg-IR_REG_XMM0), xmm(to_reg-IR_REG_XMM0)
}
} else {
if (to_reg >= 0 && from_reg >= 0) {
| ASM_FP_REG_REG_OP movaps, movapd, vmovaps, vmovapd, type, to_reg, from_reg
} else if (from_reg >= 0) {
| ASM_FP_MOV_VREG_REG_OP type, to, from_reg
} else if (to_reg >= 0) {
| ASM_FP_MOV_REG_VREG_OP type, to_reg, from
}
}
if (to_reg != dst_reg) {
| ASM_FP_MOV_VREG_REG_OP type, to, to_reg
}
#else
char suffix = ir_fp_suffix(type);
if (IR_IS_CONST_REF(from) && to_reg >= 0
&& ((from_insn->type == IR_FLOAT && from_insn->val.f == 0) ||
(from_insn->type == IR_DOUBLE && from_insn->val.d == 0))) {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvxorp%c ", suffix);
ir_emit_reg(to_reg, type);
fprintf(stderr, ", ");
} else {
fprintf(stderr, "\txorp%c ", suffix);
}
ir_emit_reg(to_reg, type);
fprintf(stderr, ", ");
ir_emit_reg(to_reg, type);
fprintf(stderr, "\n");
} else {
if (to_reg >= 0 && from_reg >= 0) {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovap%c ", suffix);
} else {
fprintf(stderr, "\tmovap%c ", suffix);
}
} else {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovs%c ", suffix);
} else {
fprintf(stderr, "\tmovs%c ", suffix);
}
}
if (IR_IS_CONST_REF(from)) {
fprintf(stderr, ".LLC%d(%%rip)", -from);
from_insn->emit_const = 1;
} else if (from_reg >= 0) {
ir_emit_reg(from_reg, type);
} else {
ir_emit_vreg(ctx, from);
}
fprintf(stderr, ", ");
if (to_reg >= 0) {
ir_emit_reg(to_reg, type);
} else {
ir_emit_vreg(ctx, to);
}
fprintf(stderr, "\n");
}
if (to_reg != dst_reg) {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovs%c ", suffix);
} else {
fprintf(stderr, "\tmovs%c ", suffix);
}
ir_emit_reg(to_reg, type);
fprintf(stderr, ", ");
ir_emit_vreg(ctx, to);
fprintf(stderr, "\n");
}
#endif
}
return 1;
}
static void ir_emit_param_move(ir_ctx *ctx, uint8_t type, int from_reg, int to, ir_ref name)
{
#if IR_DASM
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_live_interval *ival = ctx->live_intervals[to];
ir_reg to_reg = ival->reg;
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_RBP : IR_REG_RSP;
int32_t offset;
if (IR_IS_TYPE_INT(type)) {
if (from_reg >= 0) {
if (to_reg >= 0) {
| ASM_REG_REG_OP mov, type, to_reg, from_reg
} else {
IR_ASSERT(ctx->live_intervals[to]->stack_spill_pos);
offset = (ctx->flags & IR_USE_FRAME_POINTER) ?
-ival->stack_spill_pos : data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_REG_OP mov, type, [Ra(fp)+offset], from_reg
}
} else {
offset = (ctx->flags & IR_USE_FRAME_POINTER) ? -8 : -8; // TODO:
if (to_reg >= 0) {
| ASM_REG_MEM_OP mov, type, to_reg, [Ra(fp)+offset]
} else {
| ASM_REG_MEM_OP mov, type, IR_REG_R0, [Ra(fp)+offset] // TODO: tmp
offset = (ctx->flags & IR_USE_FRAME_POINTER) ?
-ival->stack_spill_pos : data->stack_frame_size - ival->stack_spill_pos;
| ASM_MEM_REG_OP mov, type, [Ra(fp)+offset], IR_REG_R0 // TODO: tmp
}
}
} else {
if (from_reg >= 0) {
if (to_reg >= 0) {
| ASM_FP_REG_REG_OP movaps, movapd, vmovaps, vmovapd, type, from_reg, to_reg
} else {
IR_ASSERT(ctx->live_intervals[to]->stack_spill_pos);
offset = (ctx->flags & IR_USE_FRAME_POINTER) ?
-ival->stack_spill_pos : data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP movss, movsd, vmovss, vmovsd, type, [Ra(fp)+offset], from_reg
}
} else {
offset = (ctx->flags & IR_USE_FRAME_POINTER) ? -8 : -8; // TODO:
if (to_reg >= 0) {
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, to_reg, [Ra(fp)+offset]
} else {
| ASM_FP_REG_MEM_OP movss, movsd, vmovss, vmovsd, type, IR_REG_R0, [Ra(fp)+offset] // TODO: tmp
offset = (ctx->flags & IR_USE_FRAME_POINTER) ?
-ival->stack_spill_pos : data->stack_frame_size - ival->stack_spill_pos;
| ASM_FP_MEM_REG_OP movss, movsd, vmovss, vmovsd, type, [Ra(fp)+offset], IR_REG_R0 // TODO: tmp
}
}
}
#else
char suffix;
if (IR_IS_TYPE_INT(type)) {
suffix = ir_int_suffix(type);
fprintf(stderr, "\tmov%c ", suffix);
} else {
suffix = ir_fp_suffix(type);
if (from_reg >= 0 && ctx->live_intervals[to]->reg >= 0) {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovap%c ", suffix);
} else {
fprintf(stderr, "\tmovap%c ", suffix);
}
} else {
if (ctx->flags & IR_AVX) {
fprintf(stderr, "\tvmovs%c ", suffix);
} else {
fprintf(stderr, "\tmovs%c ", suffix);
}
}
}
if (from_reg >= 0) {
ir_emit_reg(from_reg, type);
} else {
fprintf(stderr, "%s", ir_get_str(ctx, name)); // TODO: replace param name by stack slot
}
fprintf(stderr, ", ");
ir_emit_vreg(ctx, to);
fprintf(stderr, " # %s\n", ir_get_str(ctx, name));
#endif
}
static void ir_emit_load_params(ir_ctx *ctx)
{
ir_use_list *use_list = &ctx->use_lists[1];
ir_insn *insn;
ir_ref i, n, *p, use;
int int_param_num = 0;
int fp_param_num = 0;
int src_reg; /* negative values represent arguments on CPU stack */
int dst_reg;
// TODO: Calling convention specific
int int_reg_params_count = IR_REG_INT_ARGS;
int fp_reg_params_count = IR_REG_FP_ARGS;
const int8_t *int_reg_params = _ir_int_reg_params;
const int8_t *fp_reg_params = _ir_fp_reg_params;
n = use_list->count;
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
use = *p;
insn = &ctx->ir_base[use];
if (insn->op == IR_PARAM && ctx->vregs[use]) {
if (IR_IS_TYPE_INT(insn->type)) {
if (int_param_num < int_reg_params_count) {
src_reg = int_reg_params[int_param_num];
} else {
// TODO: replace IR_REG_NONE by stack slot
src_reg = IR_REG_NONE;
}
int_param_num++;
} else {
if (fp_param_num < fp_reg_params_count) {
src_reg = fp_reg_params[fp_param_num];
} else {
// TODO: replace IR_REG_NONE by stack slot
src_reg = IR_REG_NONE;
}
fp_param_num++;
}
dst_reg = ctx->live_intervals[ctx->vregs[use]]->reg;
if (src_reg != dst_reg) {
// TODO: DO parallel move
ir_emit_param_move(ctx, insn->type, src_reg, ctx->vregs[use], insn->op2);
}
}
}
}
static void ir_allocate_unique_spill_slots(ir_ctx *ctx)
{
int b;
ir_block *bb;
ir_insn *insn;
ir_ref i, n;
uint32_t flags, *rule;
ir_backend_data *data = ctx->data;
ctx->live_intervals = ir_mem_calloc(ctx->vregs_count + 1, sizeof(ir_live_interval*));
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
bb->flags &= ~IR_BB_MAY_SKIP;
flags = IR_BB_MAY_SKIP;
if (bb->successors_count != 1 ||
ctx->cfg_edges[bb->successors] != b + 1 ||
(bb->flags & IR_BB_DESSA_MOVES)) {
flags = 0;
}
for (i = bb->start, insn = ctx->ir_base + i, rule = ctx->rules + i; i <= bb->end;) {
switch (ctx->rules ? *rule : insn->op) {
case IR_START:
case IR_BEGIN:
case IR_END:
case IR_IF_TRUE:
case IR_IF_FALSE:
case IR_CASE_VAL:
case IR_CASE_DEFAULT:
case IR_MERGE:
case IR_LOOP_BEGIN:
case IR_LOOP_END:
case IR_LOOP_EXIT:
case IR_SKIP:
break;
default:
flags = 0;
case IR_PARAM:
case IR_VAR:
case IR_PHI:
case IR_PI:
case IR_SKIP_REG:
/* skip */
if (ctx->vregs[i]) {
if (!ctx->live_intervals[ctx->vregs[i]]) {
ir_live_interval *ival = ir_mem_malloc(sizeof(ir_live_interval));
ctx->live_intervals[ctx->vregs[i]] = ival;
ival->type = insn->type;
ival->reg = IR_REG_NONE;
ival->range.start = 0;
ival->range.end = 0;
ival->range.next = NULL;
// if (insn->op == IR_PARAM) {
// fprintf(f, "\t%s d_%d = %s;\n", ir_type_cname[insn->type], ctx->vregs[i], ir_get_str(ctx, insn->op2));
// } else {
data->stack_frame_size += 8; // ir_type_size[insn->type]; // TODO: alignment
ival->stack_spill_pos = data->stack_frame_size;
// fprintf(f, "\t%s d_%d;\n", ir_type_cname[insn->type], ctx->vregs[i]);
// }
} else if (insn->op == IR_PARAM) {
IR_ASSERT(0 && "unexpected PARAM");
return;
}
}
break;
}
n = ir_operands_count(ctx, insn);
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
i += n;
insn += n;
rule += n;
}
bb->flags |= flags;
}
}
static void ir_mark_empty_blocks(ir_ctx *ctx)
{
int b;
ir_block *bb;
ir_insn *insn;
ir_ref i, n;
uint32_t flags, *rule;
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
bb->flags &= ~IR_BB_MAY_SKIP;
if (bb->successors_count == 1
&& ctx->cfg_edges[bb->successors] == b + 1
&& !(bb->flags & IR_BB_DESSA_MOVES)) {
flags = IR_BB_MAY_SKIP;
for (i = bb->start, insn = ctx->ir_base + i, rule = ctx->rules + i; i < bb->end;) {
if (*rule != IR_SKIP && *rule != IR_SKIP_REG) {
flags = 0;
break;
}
n = ir_operands_count(ctx, insn);
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
i += n;
insn += n;
rule += n;
}
bb->flags |= flags;
}
}
}
static void ir_cals_stack_frame_size(ir_ctx *ctx, ir_backend_data *data)
{
int i;
ir_live_interval **p, *ival;
uint32_t additional_size = 0;
for (i = 1, p = ctx->live_intervals + i; i <= ctx->vregs_count; i++, p++) {
ival = *p;
if (ival) {
if (ival->stack_spill_pos) {
if (ival->stack_spill_pos > data->stack_frame_size) {
data->stack_frame_size = ival->stack_spill_pos;
}
}
if (ival->reg >= 0) {
if (!IR_REGSET_IN(data->used_preserved_regs, ival->reg)
&& IR_REGSET_IN(IR_REGSET_PRESERVED, ival->reg)) {
IR_REGSET_INCL(data->used_preserved_regs, ival->reg);
additional_size += 8;
}
}
}
}
data->stack_frame_size += additional_size;
}
void *ir_emit(ir_ctx *ctx, size_t *size)
{
int b, n, target;
ir_block *bb;
ir_ref i;
ir_insn *insn;
uint32_t *rule;
ir_backend_data data;
#if IR_DASM
dasm_State **Dst;
int ret;
void *entry;
#endif
ctx->data = &data;
data.stack_frame_size = 0;
data.used_preserved_regs = 0;
if (!ctx->live_intervals) {
ir_allocate_unique_spill_slots(ctx);
} else {
ir_mark_empty_blocks(ctx);
ir_cals_stack_frame_size(ctx, &data);
}
#if IR_DASM
Dst = &data.dasm_state;
data.dasm_state = NULL;
dasm_init(&data.dasm_state, DASM_MAXSECTION);
// dasm_setupglobal(&data.dasm_state, dasm_labels, ir_lb_MAX);
dasm_setup(&data.dasm_state, dasm_actions);
dasm_growpc(&data.dasm_state, ctx->cfg_blocks_count + 1 + ctx->consts_count + 1);
#endif
if (ctx->flags & IR_FUNCTION) {
ir_emit_prologue(ctx);
ir_emit_load_params(ctx);
}
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
// if (bb->flags & IR_BB_MAY_SKIP) {
// continue;
// }
// if (ir_needs_block_label(ctx, b)) {
#if IR_DASM
|=>b:
#else
fprintf(stderr, ".LL%d:\n", b);
#endif
// }
for (i = bb->start, insn = ctx->ir_base + i, rule = ctx->rules + i; i <= bb->end;) {
switch (*rule) {
case IR_SKIP:
case IR_SKIP_REG:
break;
#if IR_DASM
case IR_LEA_OB:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
int32_t offset = ctx->ir_base[insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0);
if (insn->op == IR_SUB) {
offset = -offset;
}
| lea Ra(def_reg), aword [Ra(op1_reg)+offset]
}
break;
case IR_LEA_SI:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
int32_t scale = ctx->ir_base[insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0);
if (scale == 2) {
| lea Ra(def_reg), aword [Ra(op1_reg)*2]
} else if (scale == 4) {
| lea Ra(def_reg), aword [Ra(op1_reg)*4]
} else if (scale == 8) {
| lea Ra(def_reg), aword [Ra(op1_reg)*8]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_SIB:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
int32_t scale = ctx->ir_base[insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0);
if (scale == 3) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op1_reg)*2]
} else if (scale == 5) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op1_reg)*4]
} else if (scale == 9) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op1_reg)*8]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_IB:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)]
}
break;
case IR_LEA_OB_I:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
int32_t offset = ctx->ir_base[op1_insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (op1_insn->op == IR_SUB) {
offset = -offset;
}
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)+offset]
}
break;
case IR_LEA_I_OB:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_insn *op2_insn = &ctx->ir_base[insn->op2];
ir_reg op2_reg = ir_ref_reg(ctx, op2_insn->op2);
int32_t offset = ctx->ir_base[op2_insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (op2_insn->op == IR_SUB) {
offset = -offset;
}
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)+offset]
}
break;
case IR_LEA_SI_O:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
int32_t scale = ctx->ir_base[op1_insn->op2].val.i32;
int32_t offset = ctx->ir_base[insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0);
if (insn->op == IR_SUB) {
offset = -offset;
}
if (scale == 2) {
| lea Ra(def_reg), aword [Ra(op1_reg)*2+offset]
} else if (scale == 4) {
| lea Ra(def_reg), aword [Ra(op1_reg)*4+offset]
} else if (scale == 8) {
| lea Ra(def_reg), aword [Ra(op1_reg)*8+offset]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_SIB_O:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
int32_t scale = ctx->ir_base[op1_insn->op2].val.i32;
int32_t offset = ctx->ir_base[insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0);
if (insn->op == IR_SUB) {
offset = -offset;
}
if (scale == 3) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op1_reg)*2+offset]
} else if (scale == 5) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op1_reg)*4+offset]
} else if (scale == 6) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op1_reg)*8+offset]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_IB_O:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, op1_insn->op2);
int32_t offset = ctx->ir_base[insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (insn->op == IR_SUB) {
offset = -offset;
}
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)+offset]
}
break;
case IR_LEA_OB_SI:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_insn *op2_insn = &ctx->ir_base[insn->op2];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
int32_t offset = ctx->ir_base[op1_insn->op2].val.i32;
ir_reg op2_reg = ir_ref_reg(ctx, op2_insn->op1);
int32_t scale = ctx->ir_base[op2_insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (op1_insn->op == IR_SUB) {
offset = -offset;
}
if (scale == 2) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)*2+offset]
} else if (scale == 4) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)*4+offset]
} else if (scale == 8) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)*8+offset]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_SI_OB:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_insn *op2_insn = &ctx->ir_base[insn->op2];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
int32_t scale = ctx->ir_base[op1_insn->op2].val.i32;
ir_reg op2_reg = ir_ref_reg(ctx, op2_insn->op1);
int32_t offset = ctx->ir_base[op2_insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (op1_insn->op == IR_SUB) {
offset = -offset;
}
if (scale == 2) {
| lea Ra(def_reg), aword [Ra(op2_reg)+Ra(op1_reg)*2+offset]
} else if (scale == 4) {
| lea Ra(def_reg), aword [Ra(op2_reg)+Ra(op1_reg)*4+offset]
} else if (scale == 8) {
| lea Ra(def_reg), aword [Ra(op2_reg)+Ra(op1_reg)*8+offset]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_B_SI:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op2_insn = &ctx->ir_base[insn->op2];
ir_reg op1_reg = ir_ref_reg(ctx, insn->op1);
ir_reg op2_reg = ir_ref_reg(ctx, op2_insn->op1);
int32_t scale = ctx->ir_base[op2_insn->op2].val.i32;
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (scale == 2) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)*2]
} else if (scale == 4) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)*4]
} else if (scale == 8) {
| lea Ra(def_reg), aword [Ra(op1_reg)+Ra(op2_reg)*8]
} else {
IR_ASSERT(0);
}
}
break;
case IR_LEA_SI_B:
{
ir_reg def_reg = ir_ref_reg(ctx, i);
ir_insn *op1_insn = &ctx->ir_base[insn->op1];
ir_reg op1_reg = ir_ref_reg(ctx, op1_insn->op1);
int32_t scale = ctx->ir_base[op1_insn->op2].val.i32;
ir_reg op2_reg = ir_ref_reg(ctx, insn->op2);
IR_ASSERT(def_reg >= 0 && op1_reg >= 0 && op2_reg >= 0);
if (scale == 2) {
| lea Ra(def_reg), aword [Ra(op2_reg)+Ra(op1_reg)*2]
} else if (scale == 4) {
| lea Ra(def_reg), aword [Ra(op2_reg)+Ra(op1_reg)*4]
} else if (scale == 8) {
| lea Ra(def_reg), aword [Ra(op2_reg)+Ra(op1_reg)*8]
} else {
IR_ASSERT(0);
}
}
break;
case IR_INC:
case IR_DEC:
ir_emit_incdec(ctx, *rule, i, insn);
break;
case IR_MUL_2:
ir_emit_mul2(ctx, i, insn);
break;
case IR_MUL_PWR2:
ir_emit_mul_pwr2(ctx, i, insn);
break;
case IR_DIV_PWR2:
ir_emit_div_pwr2(ctx, i, insn);
break;
case IR_MOD_PWR2:
ir_emit_mod_pwr2(ctx, i, insn);
break;
case IR_SHIFT:
ir_emit_shift(ctx, i, insn);
break;
case IR_SHIFT_CONST:
ir_emit_shift_const(ctx, i, insn);
break;
case IR_OP_INT:
ir_emit_op_int(ctx, i, insn);
break;
#endif
case IR_BINOP_INT:
ir_emit_binop_int(ctx, i, insn);
break;
case IR_BINOP_SSE2:
ir_emit_binop_sse2(ctx, i, insn);
break;
case IR_BINOP_AVX:
ir_emit_binop_avx(ctx, i, insn);
break;
case IR_MUL_INT:
case IR_DIV_INT:
case IR_MOD_INT:
ir_emit_mul_div_mod(ctx, i, insn);
break;
case IR_CMP_INT:
ir_emit_cmp_int(ctx, i, insn);
break;
case IR_CMP_FP:
ir_emit_cmp_fp(ctx, i, insn);
break;
case IR_COPY_INT:
ir_emit_copy_int(ctx, i, insn);
break;
case IR_COPY_FP:
ir_emit_copy_fp(ctx, i, insn);
break;
case IR_CMP_AND_BRANCH_INT:
ir_emit_cmp_and_branch_int(ctx, b, i, insn);
break;
case IR_CMP_AND_BRANCH_FP:
ir_emit_cmp_and_branch_fp(ctx, b, i, insn);
break;
case IR_IF_INT:
ir_emit_if_int(ctx, b, i, insn);
break;
case IR_END:
case IR_LOOP_END:
if (bb->flags & IR_BB_DESSA_MOVES) {
ir_gen_dessa_moves(ctx, b, ir_emit_dessa_move);
}
IR_ASSERT(bb->successors_count == 1);
target = ir_skip_empty_blocks(ctx, ctx->cfg_edges[bb->successors]);
if (target != ir_skip_empty_blocks(ctx, b + 1)) {
#if IR_DASM
| jmp =>target
#else
fprintf(stderr, "\tjmp .LL%d\n", target);
#endif
}
break;
case IR_RETURN_VOID:
ir_emit_return_void(ctx);
break;
case IR_RETURN_INT:
ir_emit_return_int(ctx, insn);
break;
case IR_RETURN_FP:
ir_emit_return_fp(ctx, insn);
break;
default:
IR_ASSERT(0 && "NIY rule/insruction");
break;
}
n = ir_operands_count(ctx, insn);
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
i += n;
insn += n;
rule += n;
}
}
bool has_rodata = 0;
for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
if (insn->emit_const) {
if (IR_IS_TYPE_FP(insn->type)) {
#if IR_DASM
int label = ctx->cfg_blocks_count + i;
if (!has_rodata) {
|.rodata
has_rodata = 1;
}
if (insn->type == IR_DOUBLE) {
|.align 8
|=>label:
|.dword insn->val.u32, insn->val.u32_hi
} else if (insn->type == IR_FLOAT) {
|.align 4
|=>label:
|.dword insn->val.u32
} else {
IR_ASSERT(0);
}
#else
if (!has_rodata) {
fprintf(stderr, "\t.section .rodata\n");
has_rodata = 1;
}
fprintf(stderr, "\t.align %d\n", ir_type_size[insn->type]);
fprintf(stderr, ".LLC%d:\n", i);
if (insn->type == IR_DOUBLE) {
fprintf(stderr, "\t.long %d\n", insn->val.u32);
fprintf(stderr, "\t.long %d\n", insn->val.u32_hi);
} else if (insn->type == IR_FLOAT) {
fprintf(stderr, "\t.long %d\n", insn->val.u32);
} else {
IR_ASSERT(0);
}
#endif
} else {
IR_ASSERT(0);
}
}
}
if (has_rodata) {
#if IR_DASM
|.code
#else
fprintf(stderr, "\t.text\n");
#endif
}
#if IR_DASM
ret = dasm_link(&data.dasm_state, size);
if (ret != DASM_S_OK) {
return 0;
}
entry = ir_mem_mmap(4096);
ir_mem_unprotect(entry, 4096);
ret = dasm_encode(&data.dasm_state, entry);
if (ret != DASM_S_OK) {
IR_ASSERT(0);
ir_mem_unmap(entry, 4096);
return NULL;
}
dasm_free(&data.dasm_state);
ir_mem_flush(entry, 4096);
ir_mem_protect(entry, 4096);
return entry;
#else
return NULL;
#endif
}