Add VA_ARG nodes (JIT code generation is not supported yet)

This commit is contained in:
Dmitry Stogov 2023-11-16 00:29:24 +03:00
parent e5b4c686df
commit 507175b228
8 changed files with 132 additions and 4 deletions

2
TODO
View File

@ -1,4 +1,3 @@
- va_arg nodes
? BSTART, BEND nodes (to free data allocated by ALLOCA) (now it's possible to do this through AFREE)
- Full support for function prototypes ???
(now we may only set "fastcall" calling convention for constants or for variables through BITCAST)
@ -27,6 +26,7 @@
- Try to avoid live-interval construction, see "Efficient Global Register Allocation" Ian Rogers
? code generation
- VA_START, VA_END, VA_COPY, VA_ARG nodes
- COND optimization
- TAILCALL with stack arguments (tests/x86/tailcall_001.itr)
- 32-bit x86 back-end 64-bit integers support

25
ir.c
View File

@ -201,6 +201,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
#define ir_op_flag_s3 (ir_op_flag_s | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x2 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x3 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_xN (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | IR_OP_FLAG_VAR_INPUTS)
#define ir_op_flag_a2 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_ALLOC | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
@ -2322,3 +2323,27 @@ check_aliasing:
}
ctx->control = ir_emit3(ctx, IR_STORE, ctx->control, addr, val);
}
void _ir_VA_START(ir_ctx *ctx, ir_ref list)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit2(ctx, IR_VA_START, ctx->control, list);
}
void _ir_VA_END(ir_ctx *ctx, ir_ref list)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit2(ctx, IR_VA_END, ctx->control, list);
}
void _ir_VA_COPY(ir_ctx *ctx, ir_ref dst, ir_ref src)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_VA_COPY, ctx->control, dst, src);
}
ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list);
}

6
ir.h
View File

@ -302,6 +302,12 @@ typedef enum _ir_type {
_(TRAP, x1, src, ___, ___) /* DebugBreak */ \
/* memory reference ops (A, H, U, S, TMP, STR, NEW, X, V) ??? */ \
\
/* va_args */ \
_(VA_START, x2, src, def, ___) /* va_start(va_list) */ \
_(VA_END, x2, src, def, ___) /* va_end(va_list) */ \
_(VA_COPY, x3, src, def, def) /* va_copy(dst, stc) */ \
_(VA_ARG, x2, src, def, ___) /* va_arg(va_list) */ \
\
/* guards */ \
_(GUARD, c3, src, def, def) /* IF without second successor */ \
_(GUARD_NOT , c3, src, def, def) /* IF without second successor */ \

View File

@ -539,6 +539,11 @@ extern "C" {
#define ir_TLS(_index, _offset) _ir_TLS(_ir_CTX, (_index), (_offset))
#define ir_TRAP() do {_ir_CTX->control = ir_emit1(_ir_CTX, IR_TRAP, _ir_CTX->control);} while (0)
#define ir_VA_START(_list) _ir_VA_START(_ir_CTX, _list)
#define ir_VA_END(_list) _ir_VA_END(_ir_CTX, _list)
#define ir_VA_COPY(_dst, _src) _ir_VA_COPY(_ir_CTX, _dst, _src)
#define ir_VA_ARG(_list, _type) _ir_VA_ARG(_ir_CTX, _type, _list)
#define ir_START() _ir_START(_ir_CTX)
#define ir_ENTRY(_src, _num) _ir_ENTRY(_ir_CTX, (_src), (_num))
#define ir_BEGIN(_src) _ir_BEGIN(_ir_CTX, (_src))
@ -603,6 +608,10 @@ ir_ref _ir_RLOAD(ir_ctx *ctx, ir_type type, ir_ref reg);
void _ir_RSTORE(ir_ctx *ctx, ir_ref reg, ir_ref val);
ir_ref _ir_LOAD(ir_ctx *ctx, ir_type type, ir_ref addr);
void _ir_STORE(ir_ctx *ctx, ir_ref addr, ir_ref val);
void _ir_VA_START(ir_ctx *ctx, ir_ref list);
void _ir_VA_END(ir_ctx *ctx, ir_ref list);
void _ir_VA_COPY(ir_ctx *ctx, ir_ref dst, ir_ref src);
ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list);
void _ir_START(ir_ctx *ctx);
void _ir_ENTRY(ir_ctx *ctx, ir_ref src, ir_ref num);
void _ir_BEGIN(ir_ctx *ctx, ir_ref src);

View File

@ -719,6 +719,17 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
fprintf(f, "%s %s", ir_type_cname[insn->type], ir_get_str(ctx, insn->op2));
}
}
if (ctx->flags & IR_VARARG_FUNC) {
if (first) {
first = 0;
} else {
fprintf(f, ", ");
}
fprintf(f, "...");
}
if (first) {
fprintf(f, "void");
}
fprintf(f, ")\n{\n");
/* Emit declarations for local variables */
@ -968,6 +979,29 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
case IR_STORE:
ir_emit_store(ctx, f, insn);
break;
case IR_VA_START:
fprintf(f, "\tva_start(");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ");\n");
break;
case IR_VA_END:
fprintf(f, "\tva_end(");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ");\n");
break;
case IR_VA_COPY:
fprintf(f, "\tva_copy(");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ", ");
ir_emit_ref(ctx, f, insn->op3);
fprintf(f, ");\n");
break;
case IR_VA_ARG:
ir_emit_def_ref(ctx, f, i);
fprintf(f, "va_arg(");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ", %s)\n", ir_type_cname[insn->type]);
break;
default:
IR_ASSERT(0 && "NIY instruction");
ctx->status = IR_ERROR_UNSUPPORTED_CODE_RULE;

View File

@ -667,6 +667,14 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
fprintf(f, "%s %%d%d", ir_type_llvm_name[insn->type], use);
}
}
if (ctx->flags & IR_VARARG_FUNC) {
if (first) {
first = 0;
} else {
fprintf(f, ", ");
}
fprintf(f, "...");
}
fprintf(f, ")\n{\n");
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
@ -898,6 +906,29 @@ static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
case IR_VSTORE:
ir_emit_store(ctx, f, insn);
break;
case IR_VA_START:
fprintf(f, "\tcall void @llvm.va_start(ptr ");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ")\n");
break;
case IR_VA_END:
fprintf(f, "\tcall void @llvm.va_end(ptr ");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ")\n");
break;
case IR_VA_COPY:
fprintf(f, "\tcall void @llvm.va_copy(ptr");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ", ptr ");
ir_emit_ref(ctx, f, insn->op3);
fprintf(f, ")\n");
break;
case IR_VA_ARG:
ir_emit_def_ref(ctx, f, i);
fprintf(f, "va_arg ptr ");
ir_emit_ref(ctx, f, insn->op2);
fprintf(f, ", %s\n", ir_type_cname[insn->type]);
break;
case IR_TRAP:
fprintf(f, "\tcall void @llvm.debugtrap()\n");
break;

View File

@ -471,6 +471,13 @@ static ir_type llvm2ir_overflow_type(LLVMTypeRef stype)
return type;
}
static void llvm2ir_va_arg(ir_ctx *ctx, LLVMValueRef insn)
{
ir_type type = llvm2ir_type(LLVMTypeOf(insn));
ir_ref ref = ir_VA_ARG(type, llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type));
ir_addrtab_add(ctx->binding, (uintptr_t)insn, ref);
}
#define STR_START(name, name_len, str) (name_len >= strlen(str) && memcmp(name, str, strlen(str)) == 0)
#define STR_EQUAL(name, name_len, str) (name_len == strlen(str) && memcmp(name, str, strlen(str)) == 0)
@ -751,11 +758,19 @@ static ir_ref llvm2ir_intrinsic(ir_ctx *ctx, LLVMValueRef insn, LLVMTypeRef ftyp
llvm2ir_op(ctx, LLVMGetOperand(insn, 0), type),
llvm2ir_op(ctx, LLVMGetOperand(insn, 2), type));
} else if (STR_EQUAL(name, name_len, "llvm.va_start")) {
// TODO:
IR_ASSERT(count == 1);
ir_VA_START(llvm2ir_op(ctx, LLVMGetOperand(insn, 0), IR_ADDR));
return IR_NULL;
} else if (STR_EQUAL(name, name_len, "llvm.va_end")) {
// TODO:
IR_ASSERT(count == 1);
ir_VA_END(llvm2ir_op(ctx, LLVMGetOperand(insn, 0), IR_ADDR));
return IR_NULL;
} else if (STR_EQUAL(name, name_len, "llvm.va_copy")) {
// TODO:
IR_ASSERT(count == 2);
ir_VA_COPY(
llvm2ir_op(ctx, LLVMGetOperand(insn, 0), IR_ADDR),
llvm2ir_op(ctx, LLVMGetOperand(insn, 1), IR_ADDR));
return IR_NULL;
} else if (STR_START(name, name_len, "llvm.ctpop.")) {
// TODO:
} else if (STR_START(name, name_len, "llvm.ctlz.")) {
@ -1687,6 +1702,9 @@ static int llvm2ir_func(ir_ctx *ctx, LLVMValueRef func)
case LLVMFreeze:
llvm2ir_freeze(ctx, insn);
break;
case LLVMVAArg:
llvm2ir_va_arg(ctx, insn);
break;
case LLVMExtractValue:
if (llvm2ir_extract(ctx, insn)) {
break;

View File

@ -546,6 +546,11 @@ static bool ir_loader_func_process(ir_loader *loader, ir_ctx *ctx, const char *n
fprintf(l->dump_file, ", %s", ir_type_cname[insn->type]);
insn++;;
}
if (ctx->flags & IR_VARARG_FUNC) {
fprintf(l->dump_file, ", ...");
}
} else if (ctx->flags & IR_VARARG_FUNC) {
fprintf(l->dump_file, "...");
} else {
fprintf(l->dump_file, "void");
}