Fix support for WIN64 calling convention

This commit is contained in:
Dmitry Stogov 2023-03-02 17:56:15 +03:00
parent c89a038fd3
commit 09d5ecc607
3 changed files with 103 additions and 2 deletions

1
ir.h
View File

@ -387,6 +387,7 @@ typedef union _ir_val {
/* IR constant flags */
#define IR_CONST_EMIT (1<<0)
#define IR_CONST_FASTCALL_FUNC (1<<1)
#define IR_CONST_VARARG_FUNC (1<<2)
/* IR Instruction */
typedef struct _ir_insn {

View File

@ -78,6 +78,18 @@ static bool ir_is_fastcall(ir_ctx *ctx, ir_insn *insn)
# define ir_is_fastcall(ctx, insn) 0
#endif
#ifdef _WIN64
static bool ir_is_vararg(ir_ctx *ctx, ir_insn *insn)
{
if (IR_IS_CONST_REF(insn->op2)) {
return (ctx->ir_base[insn->op2].const_flags & IR_CONST_VARARG_FUNC) != 0;
} else if (ctx->ir_base[insn->op2].op == IR_BITCAST) {
return (ctx->ir_base[insn->op2].op2 & IR_CONST_VARARG_FUNC) != 0;
}
return 0;
}
#endif
IR_ALWAYS_INLINE uint32_t ir_rule(ir_ctx *ctx, ir_ref ref)
{
IR_ASSERT(!IR_IS_CONST_REF(ref));
@ -119,6 +131,10 @@ static ir_reg ir_get_param_reg(ir_ctx *ctx, ir_ref ref)
}
}
int_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param++;
#endif
} else if (IR_IS_TYPE_FP(insn->type)) {
if (use == ref) {
if (fp_param < fp_reg_params_count) {
@ -128,6 +144,10 @@ static ir_reg ir_get_param_reg(ir_ctx *ctx, ir_ref ref)
}
}
fp_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param++;
#endif
} else {
IR_ASSERT(0);
}
@ -169,6 +189,10 @@ static int ir_get_args_regs(ir_ctx *ctx, ir_insn *insn, int8_t *regs)
regs[j] = IR_REG_NONE;
}
int_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param++;
#endif
} else if (IR_IS_TYPE_FP(type)) {
if (fp_param < fp_reg_params_count) {
regs[j] = fp_reg_params[fp_param];
@ -177,6 +201,10 @@ static int ir_get_args_regs(ir_ctx *ctx, ir_insn *insn, int8_t *regs)
regs[j] = IR_REG_NONE;
}
fp_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param++;
#endif
} else {
IR_ASSERT(0);
}

View File

@ -2064,7 +2064,11 @@ static void ir_emit_prologue(ir_ctx *ctx)
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
offset -= sizeof(void*);
| movsd qword [Ra(fp)+offset], xmm(i-IR_REG_FP_FIRST)
if (ctx->flags & IR_AVX) {
| vmovsd qword [Ra(fp)+offset], xmm(i-IR_REG_FP_FIRST)
} else {
| movsd qword [Ra(fp)+offset], xmm(i-IR_REG_FP_FIRST)
}
}
}
}
@ -2096,7 +2100,11 @@ static void ir_emit_epilogue(ir_ctx *ctx)
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
offset -= sizeof(void*);
| movsd xmm(i-IR_REG_FP_FIRST), qword [Ra(fp)+offset]
if (ctx->flags & IR_AVX) {
| vmovsd xmm(i-IR_REG_FP_FIRST), qword [Ra(fp)+offset]
} else {
| movsd xmm(i-IR_REG_FP_FIRST), qword [Ra(fp)+offset]
}
}
}
}
@ -5858,11 +5866,19 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn)
used_stack += IR_MAX(sizeof(void*), ir_type_size[type]);
}
int_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param++;
#endif
} else if (IR_IS_TYPE_FP(type)) {
if (fp_param >= fp_reg_params_count) {
used_stack += IR_MAX(sizeof(void*), ir_type_size[type]);
}
fp_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param++;
#endif
} else {
IR_ASSERT(0);
}
@ -5951,6 +5967,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg
dst_reg = IR_REG_NONE; /* pass argument through stack */
}
int_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param++;
#endif
} else if (IR_IS_TYPE_FP(type)) {
if (fp_param < fp_reg_params_count) {
dst_reg = fp_reg_params[fp_param];
@ -5958,6 +5978,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg
dst_reg = IR_REG_NONE; /* pass argument through stack */
}
fp_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param++;
#endif
} else {
IR_ASSERT(0);
}
@ -6016,6 +6040,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg
dst_reg = IR_REG_NONE; /* argument already passed through stack */
}
int_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param++;
#endif
} else if (IR_IS_TYPE_FP(type)) {
if (fp_param < fp_reg_params_count) {
dst_reg = fp_reg_params[fp_param];
@ -6023,6 +6051,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg
dst_reg = IR_REG_NONE; /* argument already passed through stack */
}
fp_param++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param++;
#endif
} else {
IR_ASSERT(0);
}
@ -6125,6 +6157,30 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg
}
}
}
#ifdef _WIN64
/* WIN64 calling convention requires duplcation of parameters passed in FP register into GP ones */
if (ir_is_vararg(ctx, insn)) {
n = IR_MIN(n, IR_MAX_REG_ARGS + 2);
for (j = 3; j <= n; j++) {
arg = ir_insn_op(insn, j);
arg_insn = &ctx->ir_base[arg];
type = arg_insn->type;
if (IR_IS_TYPE_FP(type)) {
src_reg = fp_reg_params[j-3];
dst_reg = int_reg_params[j-3];
|.if X64
if (ctx->flags & IR_AVX) {
| vmovd Rq(dst_reg), xmm(src_reg-IR_REG_FP_FIRST)
} else {
| movd Rq(dst_reg), xmm(src_reg-IR_REG_FP_FIRST)
}
|.endif
}
}
}
#endif
return used_stack;
}
@ -7237,6 +7293,10 @@ static void ir_emit_load_params(ir_ctx *ctx)
src_reg = IR_REG_NONE;
}
int_param_num++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param_num++;
#endif
} else {
if (fp_param_num < fp_reg_params_count) {
src_reg = fp_reg_params[fp_param_num];
@ -7244,6 +7304,10 @@ static void ir_emit_load_params(ir_ctx *ctx)
src_reg = IR_REG_NONE;
}
fp_param_num++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param_num++;
#endif
}
if (ctx->vregs[use]) {
dst_reg = IR_REG_NUM(ctx->regs[use][0]);
@ -7360,6 +7424,10 @@ static void ir_fix_param_spills(ir_ctx *ctx)
src_reg = IR_REG_NONE;
}
int_param_num++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
fp_param_num++;
#endif
} else {
if (fp_param_num < fp_reg_params_count) {
src_reg = fp_reg_params[fp_param_num];
@ -7367,6 +7435,10 @@ static void ir_fix_param_spills(ir_ctx *ctx)
src_reg = IR_REG_NONE;
}
fp_param_num++;
#ifdef _WIN64
/* WIN64 calling convention use common couter for int and fp registers */
int_param_num++;
#endif
}
if (src_reg == IR_REG_NONE) {
if (ctx->vregs[use]) {