mirror of
https://github.com/danog/ir.git
synced 2024-12-02 17:55:40 +01:00
1080 lines
30 KiB
C
1080 lines
30 KiB
C
/*
|
|
* IR - Lightweight JIT Compilation Framework
|
|
* (LLVM code generator)
|
|
* Copyright (C) 2022 Zend by Perforce.
|
|
* Authors: Dmitry Stogov <dmitry@php.net>
|
|
*/
|
|
|
|
#include "ir.h"
|
|
#include "ir_private.h"
|
|
|
|
#include <math.h>
|
|
|
|
static const char *ir_type_llvm_name[IR_LAST_TYPE] = {
|
|
"void", // IR_VOID
|
|
"i1", // IR_BOOL
|
|
"i8", // IR_U8
|
|
"i16", // IR_U16
|
|
"i32", // IR_U32
|
|
"i64", // IR_U63
|
|
"ptr", // IR_ADDR
|
|
"i8", // IR_CHAR
|
|
"i8", // IR_I8
|
|
"i16", // IR_I16
|
|
"i32", // IR_I32
|
|
"i64", // IR_I64
|
|
"double", // IR_DOUBLE
|
|
"float", // IR_FLOAT
|
|
};
|
|
|
|
static void ir_emit_ref(ir_ctx *ctx, FILE *f, ir_ref ref)
|
|
{
|
|
if (IR_IS_CONST_REF(ref)) {
|
|
ir_insn *insn = &ctx->ir_base[ref];
|
|
if (insn->op == IR_FUNC || insn->op == IR_SYM) {
|
|
fprintf(f, "@%s", ir_get_str(ctx, insn->val.i32));
|
|
} else if (insn->op == IR_STR) {
|
|
fprintf(f, "@.str%d", -ref);
|
|
} else if (insn->op == IR_ADDR) {
|
|
if (insn->val.addr == 0) {
|
|
fprintf(f, "null");
|
|
} else if (insn->op == IR_ADDR) {
|
|
fprintf(f, "u0x%" PRIxPTR, insn->val.addr);
|
|
// fprintf(f, "inttoptr(i64 u0x%" PRIxPTR " to ptr)", insn->val.addr);
|
|
}
|
|
} else if (IR_IS_TYPE_FP(insn->type)) {
|
|
if (insn->type == IR_DOUBLE) {
|
|
double d = insn->val.d;
|
|
if (isnan(d)) {
|
|
fprintf(f, "nan");
|
|
} else if (d == 0.0) {
|
|
fprintf(f, "0.0");
|
|
} else {
|
|
double e = log10(d);
|
|
if (e < -4 || e >= 6) {
|
|
fprintf(f, "%e", d);
|
|
} else if (round(d) == d) {
|
|
fprintf(f, "%.0f.0", d);
|
|
} else {
|
|
fprintf(f, "%g", d);
|
|
}
|
|
}
|
|
} else {
|
|
IR_ASSERT(insn->type == IR_FLOAT);
|
|
double d = insn->val.f;
|
|
if (isnan(d)) {
|
|
fprintf(f, "nan");
|
|
} else if (d == 0.0) {
|
|
fprintf(f, "0.0");
|
|
} else {
|
|
double e = log10(d);
|
|
if (e < -4 || e >= 6) {
|
|
fprintf(f, "%e", d);
|
|
} else if (round(d) == d) {
|
|
fprintf(f, "%.0f.0", d);
|
|
} else {
|
|
fprintf(f, "%g", d);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ir_print_const(ctx, &ctx->ir_base[ref], f, true);
|
|
}
|
|
} else {
|
|
fprintf(f, "%%d%d", ref);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_def_ref(ir_ctx *ctx, FILE *f, ir_ref def)
|
|
{
|
|
fprintf(f, "\t%%d%d = ", def);
|
|
}
|
|
|
|
static void ir_emit_phi(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, ir_block *bb)
|
|
{
|
|
bool first = 1;
|
|
uint32_t j, n, *p;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "phi %s ", ir_type_llvm_name[insn->type]);
|
|
n = insn->inputs_count;
|
|
p = ctx->cfg_edges + bb->predecessors;
|
|
for (j = 2; j <= n; j++) {
|
|
if (first) {
|
|
first = 0;
|
|
} else {
|
|
fprintf(f, ", ");
|
|
}
|
|
fprintf(f, "[");
|
|
ir_emit_ref(ctx, f, ir_insn_op(insn, j));
|
|
fprintf(f, ", %%l%d", *p);
|
|
p++;
|
|
fprintf(f, "]");
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static void ir_emit_unary_neg(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
|
|
{
|
|
ir_type type = insn->type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "fneg %s ", ir_type_llvm_name[type]);
|
|
} else {
|
|
fprintf(f, "sub %s 0, ", ir_type_llvm_name[type]);
|
|
}
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static void ir_emit_unary_not(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
if (insn->type == IR_BOOL) {
|
|
if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "fcmp oeq %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", 0.0\n");
|
|
} else {
|
|
fprintf(f, "icmp eq %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", 0\n");
|
|
}
|
|
} else {
|
|
IR_ASSERT(IR_IS_TYPE_INT(type) && type == insn->type);
|
|
fprintf(f, "xor %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", -1\n");
|
|
}
|
|
}
|
|
|
|
static void ir_emit_binary_op(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *op, const char *uop, const char *fop)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
|
|
if (insn->type == IR_ADDR) {
|
|
type = sizeof(void*) == 8 ? IR_U64 : IR_U32;
|
|
if (!IR_IS_CONST_REF(insn->op1) && ctx->ir_base[insn->op1].type != IR_I64 && ctx->ir_base[insn->op1].type != IR_U64) {
|
|
fprintf(f, "\t%%t%d_1 = ptrtoint ptr ", def);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, " to i64\n");
|
|
}
|
|
if (!IR_IS_CONST_REF(insn->op2) && ctx->ir_base[insn->op2].type != IR_I64 && ctx->ir_base[insn->op2].type != IR_U64) {
|
|
fprintf(f, "\t%%t%d_2 = ptrtoint ptr ", def);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, " to i64\n");
|
|
}
|
|
fprintf(f, "\t%%t%d = %s %s ", def, uop ? uop : op, ir_type_llvm_name[type]);
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
fprintf(f, "%" PRIu64 ", ", ctx->ir_base[insn->op1].val.u64);
|
|
} else if (ctx->ir_base[insn->op1].type == IR_I64 || ctx->ir_base[insn->op1].type == IR_U64) {
|
|
fprintf(f, "%%d%d, ", insn->op1);
|
|
} else {
|
|
fprintf(f, "%%t%d_1, ", def);
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
fprintf(f, "%" PRIu64 "\n", ctx->ir_base[insn->op2].val.u64);
|
|
} else if (ctx->ir_base[insn->op2].type == IR_I64 || ctx->ir_base[insn->op2].type == IR_U64) {
|
|
fprintf(f, "%%d%d\n", insn->op2);
|
|
} else {
|
|
fprintf(f, "%%t%d_2\n", def);
|
|
}
|
|
fprintf(f, "\t%%d%d = inttoptr i64 %%t%d to ptr\n", def, def);
|
|
return;
|
|
}
|
|
ir_emit_def_ref(ctx, f, def);
|
|
if (fop && IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "%s ", fop);
|
|
} else {
|
|
IR_ASSERT(IR_IS_TYPE_INT(type));
|
|
if (uop && IR_IS_TYPE_UNSIGNED(type)) {
|
|
fprintf(f, "%s ", uop);
|
|
} else {
|
|
fprintf(f, "%s ", op);
|
|
}
|
|
}
|
|
fprintf(f, "%s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", ");
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static void ir_emit_binary_overflow_op(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *op, const char *uop)
|
|
{
|
|
ir_type type = insn->type;
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(type));
|
|
fprintf(f, "\t%%t%d = call {%s, i1} @llvm.%s.with.overflow.%s(%s ", def, ir_type_llvm_name[type],
|
|
IR_IS_TYPE_UNSIGNED(type) ? uop : op,
|
|
ir_type_llvm_name[type], ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ")\n");
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "extractvalue {%s, i1} %%t%d, 0\n", ir_type_llvm_name[type], def);
|
|
}
|
|
|
|
static void ir_emit_rol_ror(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *op)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "call %s @llvm.%s.%s(%s ",
|
|
ir_type_llvm_name[type], op, ir_type_llvm_name[type], ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ")\n");
|
|
}
|
|
|
|
static void ir_emit_bitop(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *name, bool poison)
|
|
{
|
|
ir_type type = insn->type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "call %s @llvm.%s.%s(%s ",
|
|
ir_type_llvm_name[type], name, ir_type_llvm_name[type], ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
if (poison) {
|
|
fprintf(f, ", 0");
|
|
}
|
|
fprintf(f, ")\n");
|
|
}
|
|
|
|
static void ir_emit_conv(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *op)
|
|
{
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "%s %s ", op, ir_type_llvm_name[ctx->ir_base[insn->op1].type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, " to %s\n", ir_type_llvm_name[insn->type]);
|
|
}
|
|
|
|
static void ir_emit_minmax_op(ir_ctx *ctx, FILE *f, int def, ir_insn *insn, const char *op, const char *uop, const char *fop)
|
|
{
|
|
ir_type type = insn->type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "call %s @llvm.%s.%s(%s ", ir_type_llvm_name[type],
|
|
fop, type == IR_DOUBLE ? "f64" : "f32",
|
|
ir_type_llvm_name[type]);
|
|
} else {
|
|
IR_ASSERT(IR_IS_TYPE_INT(type));
|
|
if (IR_IS_TYPE_UNSIGNED(type)) {
|
|
fprintf(f, "call %s @llvm.%s.%s(%s ", ir_type_llvm_name[type],
|
|
uop, ir_type_llvm_name[type], ir_type_llvm_name[type]);
|
|
} else {
|
|
fprintf(f, "call %s @llvm.%s.%s(%s ", ir_type_llvm_name[type],
|
|
op, ir_type_llvm_name[type], ir_type_llvm_name[type]);
|
|
}
|
|
}
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ")\n");
|
|
}
|
|
|
|
static void ir_emit_conditional_op(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
if (type == IR_BOOL) {
|
|
fprintf(f, "select i1 ");
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "\t%%t%d = fcmp une %s ", def, ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", 0.0\n");
|
|
fprintf(f, "select i1 %%t%d", def);
|
|
} else {
|
|
fprintf(f, "\t%%t%d = icmp ne %s ", def, ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", 0\n");
|
|
fprintf(f, "select i1 %%t%d", def);
|
|
}
|
|
|
|
fprintf(f, ", %s ", ir_type_llvm_name[insn->type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ", %s ", ir_type_llvm_name[insn->type]);
|
|
ir_emit_ref(ctx, f, insn->op3);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static void ir_emit_abs(ir_ctx *ctx, FILE *f, int def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
|
|
ir_emit_def_ref(ctx, f, def);
|
|
if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "call %s @llvm.fabs.%s(%s ",
|
|
ir_type_llvm_name[type], type == IR_DOUBLE ? "f64" : "f32", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ")\n");
|
|
} else {
|
|
fprintf(f, "call %s @llvm.abs.%s(%s ",
|
|
ir_type_llvm_name[type], ir_type_llvm_name[type], ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, ", i1 0)\n");
|
|
}
|
|
}
|
|
|
|
static void ir_emit_if(ir_ctx *ctx, FILE *f, uint32_t b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
uint32_t true_block = 0, false_block = 0, next_block;
|
|
|
|
ir_get_true_false_blocks(ctx, b, &true_block, &false_block, &next_block);
|
|
|
|
// TODO: i1 @llvm.expect.i1(i1 <val>, i1 <expected_val>) ???
|
|
|
|
if (type == IR_BOOL) {
|
|
fprintf(f, "\tbr i1 ");
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "\t%%t%d = fcmp une %s ", def, ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ", 0.0\n");
|
|
fprintf(f, "\tbr i1 %%t%d", def);
|
|
} else {
|
|
fprintf(f, "\t%%t%d = icmp ne %s ", def, ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ", 0\n");
|
|
fprintf(f, "\tbr i1 %%t%d", def);
|
|
}
|
|
fprintf(f, ", label %%l%d, label %%l%d\n", true_block, false_block);
|
|
}
|
|
|
|
static void ir_emit_guard(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
|
|
// TODO: i1 @llvm.expect.i1(i1 <val>, i1 <expected_val>) ???
|
|
if (type == IR_BOOL) {
|
|
fprintf(f, "\tbr i1 ");
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
fprintf(f, "\t%%t%d = fcmp une %s ", def, ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ", 0.0\n");
|
|
fprintf(f, "\tbr i1 %%t%d", def);
|
|
} else {
|
|
fprintf(f, "\t%%t%d = icmp ne %s ", def, ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ", 0\n");
|
|
fprintf(f, "\tbr i1 %%t%d", def);
|
|
}
|
|
fprintf(f, ", label %%l%d_true, label %%l%d_false\n", def, def);
|
|
fprintf(f, "l%d_%s:\n", def, insn->op == IR_GUARD ? "false" : "true");
|
|
fprintf(f, "\tindirectbr ptr ");
|
|
if (IR_IS_CONST_REF(insn->op3) && ctx->ir_base[insn->op3].op == IR_ADDR) {
|
|
fprintf(f, "inttoptr(i64 u0x%" PRIxPTR " to ptr)", ctx->ir_base[insn->op3].val.addr);
|
|
} else {
|
|
ir_emit_ref(ctx, f, insn->op3);
|
|
}
|
|
fprintf(f, ", []\n");
|
|
fprintf(f, "l%d_%s:\n", def, insn->op == IR_GUARD ? "true" : "false");
|
|
}
|
|
|
|
static void ir_emit_switch(ir_ctx *ctx, FILE *f, uint32_t b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
ir_block *bb;
|
|
uint32_t n, *p, use_block;
|
|
ir_insn *use_insn;
|
|
|
|
// TODO: i1 @llvm.expect.i1(i1 <val>, i1 <expected_val>) ???
|
|
|
|
fprintf(f, "\tswitch %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
|
|
bb = &ctx->cfg_blocks[b];
|
|
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_CASE_DEFAULT) {
|
|
fprintf(f, ", label %%l%d", ir_skip_empty_target_blocks(ctx, use_block));
|
|
break;
|
|
} else {
|
|
IR_ASSERT(use_insn->op == IR_CASE_VAL);
|
|
}
|
|
}
|
|
fprintf(f, " [\n");
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_CASE_VAL) {
|
|
fprintf(f, "\t\t%s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, use_insn->op2);
|
|
fprintf(f, ", label %%l%d\n", ir_skip_empty_target_blocks(ctx, use_block));
|
|
} else {
|
|
IR_ASSERT(use_insn->op == IR_CASE_DEFAULT);
|
|
}
|
|
}
|
|
fprintf(f, "\t]\n");
|
|
}
|
|
|
|
static const char *ir_builtin_func_name(const char *name, ir_ref *last_arg)
|
|
{
|
|
if (strcmp(name, "memset")) {
|
|
*last_arg = IR_FALSE;
|
|
return (IR_SIZE_T == IR_I32) ? "llvm.memset.p0.i32" : "llvm.memset.p0.i64";
|
|
} else if (strcmp(name, "memcpy")) {
|
|
*last_arg = IR_FALSE;
|
|
return (IR_SIZE_T == IR_I32) ? "llvm.memcpy.p0.p0.i32" : "llvm.memcpy.p0.p0.i64";
|
|
} else if (strcmp(name, "memmove")) {
|
|
*last_arg = IR_FALSE;
|
|
return (IR_SIZE_T == IR_I32) ? "llvm.memmove.p0.p0.i32" : "llvm.memmve.p0.p0.i64";
|
|
} else if (strcmp(name, "sqrt")) {
|
|
return "llvm.sqrt.f64";
|
|
} else if (strcmp(name, "sqrtf")) {
|
|
return "llvm.sqrt.f32";
|
|
} else if (strcmp(name, "sin")) {
|
|
return "llvm.sin.f64";
|
|
} else if (strcmp(name, "sinf")) {
|
|
return "llvm.sin.f32";
|
|
} else if (strcmp(name, "cos")) {
|
|
return "llvm.cos.f64";
|
|
} else if (strcmp(name, "cosf")) {
|
|
return "llvm.cos.f32";
|
|
} else if (strcmp(name, "pow")) {
|
|
return "llvm.pow.f64";
|
|
} else if (strcmp(name, "powf")) {
|
|
return "llvm.pow.f32";
|
|
} else if (strcmp(name, "exp")) {
|
|
return "llvm.exp.f64";
|
|
} else if (strcmp(name, "expf")) {
|
|
return "llvm.exp.f32";
|
|
} else if (strcmp(name, "exp2")) {
|
|
return "llvm.exp2.f64";
|
|
} else if (strcmp(name, "exp2f")) {
|
|
return "llvm.exp2.f32";
|
|
} else if (strcmp(name, "exp10")) {
|
|
return "llvm.exp10.f64";
|
|
} else if (strcmp(name, "exp10f")) {
|
|
return "llvm.exp10.f32";
|
|
} else if (strcmp(name, "ldexp")) {
|
|
return "llvm.ldexp.f64.i32";
|
|
} else if (strcmp(name, "ldexpf")) {
|
|
return "llvm.ldexp.f32.i32";
|
|
} else if (strcmp(name, "frexp")) {
|
|
return "llvm.frexp.f64.i32";
|
|
} else if (strcmp(name, "frexpf")) {
|
|
return "llvm.frexp.f32.i32";
|
|
} else if (strcmp(name, "log")) {
|
|
return "llvm.log.f64";
|
|
} else if (strcmp(name, "logf")) {
|
|
return "llvm.log.f32";
|
|
} else if (strcmp(name, "log2")) {
|
|
return "llvm.log2.f64";
|
|
} else if (strcmp(name, "log2f")) {
|
|
return "llvm.log2.f32";
|
|
} else if (strcmp(name, "log10")) {
|
|
return "llvm.log10.f64";
|
|
} else if (strcmp(name, "log10f")) {
|
|
return "llvm.log10.f32";
|
|
} else if (strcmp(name, "copysign")) {
|
|
return "llvm.copysign.f64";
|
|
} else if (strcmp(name, "copysignf")) {
|
|
return "llvm.copysign.f32";
|
|
} else if (strcmp(name, "floor")) {
|
|
return "llvm.floor.f64";
|
|
} else if (strcmp(name, "floorf")) {
|
|
return "llvm.floor.f32";
|
|
} else if (strcmp(name, "ceil")) {
|
|
return "llvm.ceil.f64";
|
|
} else if (strcmp(name, "ceilf")) {
|
|
return "llvm.ceil.f32";
|
|
} else if (strcmp(name, "trunc")) {
|
|
return "llvm.trunc.f64";
|
|
} else if (strcmp(name, "truncf")) {
|
|
return "llvm.trunc.f32";
|
|
} else if (strcmp(name, "round")) {
|
|
return "llvm.round.f64";
|
|
} else if (strcmp(name, "roundf")) {
|
|
return "llvm.round.f32";
|
|
}
|
|
return name;
|
|
}
|
|
|
|
static void ir_emit_call(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn)
|
|
{
|
|
int j, k, n;
|
|
ir_ref last_arg = IR_UNUSED;
|
|
|
|
if (insn->type != IR_VOID) {
|
|
ir_emit_def_ref(ctx, f, def);
|
|
} else {
|
|
fprintf(f, "\t");
|
|
}
|
|
if (insn->op == IR_TAILCALL) {
|
|
fprintf(f, "tail ");
|
|
}
|
|
fprintf(f, "call ");
|
|
if (ir_is_fastcall(ctx, insn)) {
|
|
fprintf(f, "x86_fastcallcc ");
|
|
}
|
|
fprintf(f, "%s ", ir_type_llvm_name[insn->type]);
|
|
if (ir_is_vararg(ctx, insn)) {
|
|
fprintf(f, "(...) ");
|
|
}
|
|
|
|
// TODO: function prototype ???
|
|
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
const char *name = ir_get_str(ctx, ctx->ir_base[insn->op2].val.i32);
|
|
if (ctx->ir_base[insn->op2].const_flags & IR_CONST_BUILTIN_FUNC) {
|
|
name = ir_builtin_func_name(name, &last_arg);
|
|
}
|
|
fprintf(f, "@%s", name);
|
|
} else {
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
}
|
|
fprintf(f, "(");
|
|
n = insn->inputs_count;
|
|
for (j = 3; j <= n; j++) {
|
|
if (j != 3) {
|
|
fprintf(f, ", ");
|
|
}
|
|
k = ir_insn_op(insn, j);
|
|
fprintf(f, "%s ", ir_type_llvm_name[ctx->ir_base[k].type]);
|
|
ir_emit_ref(ctx, f, k);
|
|
}
|
|
if (last_arg) {
|
|
fprintf(f, ", ");
|
|
fprintf(f, "%s ", ir_type_llvm_name[ctx->ir_base[last_arg].type]);
|
|
ir_emit_ref(ctx, f, last_arg);
|
|
}
|
|
fprintf(f, ")\n");
|
|
if (insn->op == IR_TAILCALL) {
|
|
if (insn->type != IR_VOID) {
|
|
fprintf(f, "\tret %s", ir_type_llvm_name[insn->type]);
|
|
fprintf(f, " ");
|
|
ir_emit_ref(ctx, f, def);
|
|
fprintf(f, "\n");
|
|
} else {
|
|
fprintf(f, "\tunreachable\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_ijmp(ir_ctx *ctx, FILE *f, ir_insn *insn)
|
|
{
|
|
fprintf(f, "\tindirectbr ptr ");
|
|
if (IR_IS_CONST_REF(insn->op2) && ctx->ir_base[insn->op2].op == IR_ADDR) {
|
|
fprintf(f, "inttoptr(i64 u0x%" PRIxPTR " to ptr)", ctx->ir_base[insn->op2].val.addr);
|
|
} else {
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
}
|
|
fprintf(f, ", []\n");
|
|
}
|
|
|
|
static void ir_emit_alloca(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "alloca i8, %s ", ir_type_llvm_name[ctx->ir_base[insn->op2].type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, ", align 16\n");
|
|
}
|
|
|
|
static void ir_emit_vaddr(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "bitcast ptr ");
|
|
ir_emit_ref(ctx, f, insn->op1);
|
|
fprintf(f, " to ptr\n");
|
|
}
|
|
|
|
static void ir_emit_load(ir_ctx *ctx, FILE *f, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_emit_def_ref(ctx, f, def);
|
|
fprintf(f, "load %s, ptr ", ir_type_llvm_name[insn->type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static void ir_emit_store(ir_ctx *ctx, FILE *f, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op3].type;
|
|
|
|
fprintf(f, "\tstore %s ", ir_type_llvm_name[type]);
|
|
ir_emit_ref(ctx, f, insn->op3);
|
|
fprintf(f, ", ptr ");
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static bool may_be_used_by_phi(ir_ctx *ctx, ir_block *bb)
|
|
{
|
|
if (bb->successors_count == 1) {
|
|
bb = &ctx->cfg_blocks[ctx->cfg_edges[bb->successors]];
|
|
if (bb->predecessors_count > 1) {
|
|
ir_use_list *use_list = &ctx->use_lists[bb->start];
|
|
ir_ref i, n, *p;
|
|
|
|
n = use_list->count;
|
|
if (n > 1) {
|
|
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
|
|
if (ctx->ir_base[*p].op == IR_PHI) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ir_emit_func(ir_ctx *ctx, const char *name, FILE *f)
|
|
{
|
|
ir_ref i, n, *p, use;
|
|
ir_insn *insn;
|
|
ir_use_list *use_list;
|
|
bool first;
|
|
uint32_t b, target;
|
|
ir_block *bb;
|
|
|
|
/* Emit function prototype */
|
|
fprintf(f, "define ");
|
|
if (ctx->flags & IR_STATIC) {
|
|
fprintf(f, "internal ");
|
|
}
|
|
if (ctx->flags & IR_FASTCALL_FUNC) {
|
|
// TODO:
|
|
}
|
|
fprintf(f, "%s", ir_type_llvm_name[ctx->ret_type != (ir_type)-1 ? ctx->ret_type : IR_VOID]);
|
|
fprintf(f, " @%s(", name);
|
|
use_list = &ctx->use_lists[1];
|
|
n = use_list->count;
|
|
first = 1;
|
|
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) {
|
|
if (first) {
|
|
first = 0;
|
|
} else {
|
|
fprintf(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++) {
|
|
IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
|
|
if ((bb->flags & (IR_BB_START|IR_BB_ENTRY|IR_BB_EMPTY)) == IR_BB_EMPTY) {
|
|
continue;
|
|
}
|
|
if (bb->predecessors_count > 0 || may_be_used_by_phi(ctx, bb)) {
|
|
fprintf(f, "l%d:\n", b);
|
|
}
|
|
for (i = bb->start, insn = ctx->ir_base + i; i <= bb->end;) {
|
|
switch (insn->op) {
|
|
case IR_START:
|
|
case IR_BEGIN:
|
|
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_PARAM:
|
|
/* skip */
|
|
break;
|
|
case IR_PHI:
|
|
ir_emit_phi(ctx, f, i, insn, bb);
|
|
break;
|
|
case IR_VAR:
|
|
use_list = &ctx->use_lists[i];
|
|
if (use_list->count > 0) {
|
|
fprintf(f, "\t%%d%d = alloca %s\n", i, ir_type_llvm_name[insn->type]);
|
|
}
|
|
break;
|
|
case IR_EQ:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp eq", NULL, "fcmp oeq");
|
|
break;
|
|
case IR_NE:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp ne", NULL, "fcmp une");
|
|
break;
|
|
case IR_LT:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp slt", "icmp ult", "fcmp olt");
|
|
break;
|
|
case IR_GE:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp sge", "icmp uge", "fcmp oge");
|
|
break;
|
|
case IR_LE:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp sle", "icmp ule", "fcmp ole");
|
|
break;
|
|
case IR_GT:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp sgt", "icmp ugt", "fcmp ogt");
|
|
break;
|
|
case IR_ULT:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp ult", NULL, "fcmp ult");
|
|
break;
|
|
case IR_UGE:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp uge", NULL, "fcmp uge");
|
|
break;
|
|
case IR_ULE:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp ule", NULL, "fcmp ule");
|
|
break;
|
|
case IR_UGT:
|
|
ir_emit_binary_op(ctx, f, i, insn, "icmp ugt", NULL, "fcmp ugt");
|
|
break;
|
|
case IR_ADD:
|
|
ir_emit_binary_op(ctx, f, i, insn, "add", NULL, "fadd");
|
|
break;
|
|
case IR_SUB:
|
|
ir_emit_binary_op(ctx, f, i, insn, "sub", NULL, "fsub");
|
|
break;
|
|
case IR_MUL:
|
|
ir_emit_binary_op(ctx, f, i, insn, "mul", NULL, "fmul");
|
|
break;
|
|
case IR_DIV:
|
|
ir_emit_binary_op(ctx, f, i, insn, "sdiv", "udiv", "fdiv");
|
|
break;
|
|
case IR_MOD:
|
|
ir_emit_binary_op(ctx, f, i, insn, "srem", "urem", NULL);
|
|
break;
|
|
case IR_NEG:
|
|
ir_emit_unary_neg(ctx, f, i, insn);
|
|
break;
|
|
case IR_NOT:
|
|
ir_emit_unary_not(ctx, f, i, insn);
|
|
break;
|
|
case IR_OR:
|
|
ir_emit_binary_op(ctx, f, i, insn, "or", NULL, NULL);
|
|
break;
|
|
case IR_AND:
|
|
ir_emit_binary_op(ctx, f, i, insn, "and", NULL, NULL);
|
|
break;
|
|
case IR_XOR:
|
|
ir_emit_binary_op(ctx, f, i, insn, "xor", NULL, NULL);
|
|
break;
|
|
case IR_MIN:
|
|
ir_emit_minmax_op(ctx, f, i, insn, "smin", "umin", "minnum");
|
|
break;
|
|
case IR_MAX:
|
|
ir_emit_minmax_op(ctx, f, i, insn, "smax", "umax", "maxnum");
|
|
break;
|
|
case IR_COND:
|
|
ir_emit_conditional_op(ctx, f, i, insn);
|
|
break;
|
|
case IR_ABS:
|
|
ir_emit_abs(ctx, f, i, insn);
|
|
break;
|
|
case IR_SHL:
|
|
ir_emit_binary_op(ctx, f, i, insn, "shl", NULL, NULL);
|
|
break;
|
|
case IR_SHR:
|
|
ir_emit_binary_op(ctx, f, i, insn, "lshr", NULL, NULL);
|
|
break;
|
|
case IR_SAR:
|
|
ir_emit_binary_op(ctx, f, i, insn, "ashr", NULL, NULL);
|
|
break;
|
|
case IR_ROL:
|
|
ir_emit_rol_ror(ctx, f, i, insn, "fshl");
|
|
break;
|
|
case IR_ROR:
|
|
ir_emit_rol_ror(ctx, f, i, insn, "fshr");
|
|
break;
|
|
case IR_BSWAP:
|
|
ir_emit_bitop(ctx, f, i, insn, "bswap", 0);
|
|
break;
|
|
case IR_CTPOP:
|
|
ir_emit_bitop(ctx, f, i, insn, "ctpop", 0);
|
|
break;
|
|
case IR_CTLZ:
|
|
ir_emit_bitop(ctx, f, i, insn, "ctlz", 1);
|
|
break;
|
|
case IR_CTTZ:
|
|
ir_emit_bitop(ctx, f, i, insn, "cttz", 1);
|
|
break;
|
|
case IR_SEXT:
|
|
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type));
|
|
IR_ASSERT(ir_type_size[insn->type] > ir_type_size[ctx->ir_base[insn->op1].type]);
|
|
ir_emit_conv(ctx, f, i, insn, "sext");
|
|
break;
|
|
case IR_ZEXT:
|
|
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type));
|
|
IR_ASSERT(ir_type_size[insn->type] > ir_type_size[ctx->ir_base[insn->op1].type]);
|
|
ir_emit_conv(ctx, f, i, insn, "zext");
|
|
break;
|
|
case IR_TRUNC:
|
|
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type));
|
|
IR_ASSERT(ir_type_size[insn->type] < ir_type_size[ctx->ir_base[insn->op1].type]);
|
|
ir_emit_conv(ctx, f, i, insn, "trunc");
|
|
break;
|
|
case IR_BITCAST:
|
|
if (insn->type == IR_ADDR
|
|
&& IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)
|
|
&& ctx->ir_base[insn->op1].type != IR_ADDR) {
|
|
ir_emit_conv(ctx, f, i, insn, "inttoptr");
|
|
} else if (ctx->ir_base[insn->op1].type == IR_ADDR
|
|
&& IR_IS_TYPE_INT(insn->type)
|
|
&& insn->type != IR_ADDR) {
|
|
ir_emit_conv(ctx, f, i, insn, "ptrtoint");
|
|
} else {
|
|
ir_emit_conv(ctx, f, i, insn, "bitcast");
|
|
}
|
|
break;
|
|
case IR_INT2FP:
|
|
IR_ASSERT(IR_IS_TYPE_FP(insn->type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type));
|
|
ir_emit_conv(ctx, f, i, insn, IR_IS_TYPE_UNSIGNED(ctx->ir_base[insn->op1].type) ? "uitofp" : "sitofp");
|
|
break;
|
|
case IR_FP2INT:
|
|
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
|
|
IR_ASSERT(IR_IS_TYPE_FP(ctx->ir_base[insn->op1].type));
|
|
ir_emit_conv(ctx, f, i, insn, IR_IS_TYPE_UNSIGNED(insn->type) ? "fptoui" : "fptosi");
|
|
break;
|
|
case IR_FP2FP:
|
|
IR_ASSERT(IR_IS_TYPE_FP(insn->type));
|
|
IR_ASSERT(IR_IS_TYPE_FP(ctx->ir_base[insn->op1].type));
|
|
ir_emit_conv(ctx, f, i, insn, insn->type == IR_DOUBLE ? "fpext" : "fptrunc");
|
|
break;
|
|
case IR_COPY:
|
|
ir_emit_conv(ctx, f, i, insn, "bitcast");
|
|
break;
|
|
case IR_ADD_OV:
|
|
ir_emit_binary_overflow_op(ctx, f, i, insn, "sadd", "uadd");
|
|
break;
|
|
case IR_SUB_OV:
|
|
ir_emit_binary_overflow_op(ctx, f, i, insn, "ssub", "usub");
|
|
break;
|
|
case IR_MUL_OV:
|
|
ir_emit_binary_overflow_op(ctx, f, i, insn, "smul", "umul");
|
|
break;
|
|
case IR_OVERFLOW:
|
|
ir_emit_def_ref(ctx, f, i);
|
|
fprintf(f, "extractvalue {%s, i1} %%t%d, 1\n", ir_type_llvm_name[ctx->ir_base[insn->op1].type], insn->op1);
|
|
break;
|
|
case IR_RETURN:
|
|
IR_ASSERT(bb->successors_count == 0);
|
|
if (!insn->op2) {
|
|
fprintf(f, "\tret void\n");
|
|
} else {
|
|
fprintf(f, "\tret %s ", ir_type_llvm_name[ctx->ir_base[insn->op2].type]);
|
|
ir_emit_ref(ctx, f, insn->op2);
|
|
fprintf(f, "\n");
|
|
}
|
|
break;
|
|
case IR_END:
|
|
case IR_LOOP_END:
|
|
IR_ASSERT(bb->successors_count == 1);
|
|
target = ir_skip_empty_target_blocks(ctx, ctx->cfg_edges[bb->successors]);
|
|
fprintf(f, "\tbr label %%l%d\n", target);
|
|
break;
|
|
case IR_IF:
|
|
ir_emit_if(ctx, f, b, i, insn);
|
|
break;
|
|
case IR_SWITCH:
|
|
ir_emit_switch(ctx, f, b, i, insn);
|
|
break;
|
|
case IR_CALL:
|
|
case IR_TAILCALL:
|
|
ir_emit_call(ctx, f, i, insn);
|
|
if (insn->op == IR_TAILCALL) {
|
|
i = bb->end + 1;
|
|
continue;
|
|
}
|
|
break;
|
|
case IR_IJMP:
|
|
ir_emit_ijmp(ctx, f, insn);
|
|
break;
|
|
case IR_ALLOCA:
|
|
ir_emit_alloca(ctx, f, i, insn);
|
|
break;
|
|
case IR_VADDR:
|
|
ir_emit_vaddr(ctx, f, i, insn);
|
|
break;
|
|
case IR_LOAD:
|
|
case IR_VLOAD:
|
|
ir_emit_load(ctx, f, i, insn);
|
|
break;
|
|
case IR_STORE:
|
|
case IR_VSTORE:
|
|
ir_emit_store(ctx, f, insn);
|
|
break;
|
|
case IR_FRAME_ADDR:
|
|
ir_emit_def_ref(ctx, f, i);
|
|
fprintf(f, "\tcall ptr @llvm.frameaddress.p0(i32 0)\n");
|
|
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;
|
|
case IR_GUARD:
|
|
case IR_GUARD_NOT:
|
|
ir_emit_guard(ctx, f, i, insn);
|
|
break;
|
|
case IR_UNREACHABLE:
|
|
fprintf(f, "\tunreachable\n");
|
|
break;
|
|
case IR_RLOAD:
|
|
case IR_RSTORE:
|
|
case IR_TLS:
|
|
default:
|
|
IR_ASSERT(0 && "NIY instruction");
|
|
ctx->status = IR_ERROR_UNSUPPORTED_CODE_RULE;
|
|
return 0;
|
|
}
|
|
n = ir_insn_len(insn);
|
|
i += n;
|
|
insn += n;
|
|
}
|
|
}
|
|
|
|
fprintf(f, "}\n");
|
|
|
|
for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
|
|
if (insn->op == IR_FUNC) {
|
|
const char *name = ir_get_str(ctx, insn->val.i32);
|
|
if (insn->const_flags & IR_CONST_BUILTIN_FUNC) {
|
|
ir_ref dummy;
|
|
name = ir_builtin_func_name(name, &dummy);
|
|
}
|
|
// TODO: function prototype ???
|
|
fprintf(f, "declare void @%s()\n", name);
|
|
} else if (insn->op == IR_SYM) {
|
|
// TODO: symbol "global" or "constant" ???
|
|
// TODO: symbol type ???
|
|
fprintf(f, "@%s = external global ptr\n", ir_get_str(ctx, insn->val.i32));
|
|
} else if (insn->op == IR_STR) {
|
|
const char *str = ir_get_str(ctx, insn->val.i32);
|
|
// TODO: strlen != size ???
|
|
int len = strlen(str);
|
|
int j;
|
|
|
|
fprintf(f, "@.str%d = private unnamed_addr constant [%d x i8] c\"", i, len + 1);
|
|
for (j = 0; j < len; j++) {
|
|
char c = str[j];
|
|
|
|
if (c == '\\') {
|
|
if (str[j+1] == '\\') {
|
|
j++;
|
|
c = '\\';
|
|
} else if (str[j+1] == '\'') {
|
|
j++;
|
|
c = '\'';
|
|
} else if (str[j+1] == '"') {
|
|
j++;
|
|
c = '"';
|
|
} else if (str[j+1] == 'a') {
|
|
j++;
|
|
c = '\a';
|
|
} else if (str[j+1] == 'b') {
|
|
j++;
|
|
c = '\b';
|
|
} else if (str[j+1] == 'e') {
|
|
j++;
|
|
c = 27; /* '\e'; */
|
|
} else if (str[j+1] == 'f') {
|
|
j++;
|
|
c = '\f';
|
|
} else if (str[j+1] == 'n') {
|
|
j++;
|
|
c = '\n';
|
|
} else if (str[j+1] == 'r') {
|
|
j++;
|
|
c = '\r';
|
|
} else if (str[j+1] == 't') {
|
|
j++;
|
|
c = '\t';
|
|
} else if (str[j+1] == 'v') {
|
|
j++;
|
|
c = '\v';
|
|
} else if (str[j+1] == '?') {
|
|
j++;
|
|
c = 0x3f;
|
|
}
|
|
}
|
|
if (c < ' ' || c >= 127) {
|
|
char c1 = c >> 8;
|
|
char c2 = c & 15;
|
|
c1 = (c1 < 10) ? (c1 + '0') : (c1 - 10 + 'A');
|
|
c2 = (c2 < 10) ? (c2 + '0') : (c2 - 10 + 'A');
|
|
fprintf(f, "\\%c%c", c1, c2);
|
|
} else {
|
|
fprintf(f, "%c", c);
|
|
}
|
|
}
|
|
fprintf(f, "\\00\"\n");
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ir_emit_llvm(ir_ctx *ctx, const char *name, FILE *f)
|
|
{
|
|
return ir_emit_func(ctx, name, f);
|
|
}
|
|
|
|
void ir_emit_llvm_func_decl(const char *name, uint32_t flags, ir_type ret_type, uint32_t params_count, ir_type *param_types, FILE *f)
|
|
{
|
|
fprintf(f, "declare %s @%s(", ir_type_llvm_name[ret_type], name);
|
|
if (params_count) {
|
|
ir_type *p = param_types;
|
|
|
|
fprintf(f, "%s", ir_type_llvm_name[*p]);
|
|
p++;
|
|
while (--params_count) {
|
|
fprintf(f, ", %s", ir_type_llvm_name[*p]);
|
|
p++;
|
|
}
|
|
if (flags & IR_VARARG_FUNC) {
|
|
fprintf(f, ", ...");
|
|
}
|
|
} else if (flags & IR_VARARG_FUNC) {
|
|
fprintf(f, "...");
|
|
} else {
|
|
fprintf(f, "void");
|
|
}
|
|
fprintf(f, ")\n");
|
|
}
|