root/usr/src/tools/smatch/src/sparse-llvm.c
/*
 * Example usage:
 *      ./sparse-llvm hello.c | llc | as -o hello.o
 */

#include <llvm-c/Core.h>
#include <llvm-c/BitWriter.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/Target.h>

#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include "symbol.h"
#include "expression.h"
#include "linearize.h"
#include "flow.h"

struct function {
        LLVMBuilderRef                  builder;
        LLVMValueRef                    fn;
        LLVMModuleRef                   module;
};

static LLVMTypeRef symbol_type(struct symbol *sym);

static LLVMTypeRef func_return_type(struct symbol *sym)
{
        return symbol_type(sym->ctype.base_type);
}

static LLVMTypeRef sym_func_type(struct symbol *sym)
{
        int n_arg = symbol_list_size(sym->arguments);
        LLVMTypeRef *arg_type = calloc(n_arg, sizeof(LLVMTypeRef));
        LLVMTypeRef ret_type = func_return_type(sym);
        struct symbol *arg;
        int idx = 0;

        FOR_EACH_PTR(sym->arguments, arg) {
                struct symbol *arg_sym = arg->ctype.base_type;

                arg_type[idx++] = symbol_type(arg_sym);
        } END_FOR_EACH_PTR(arg);

        return LLVMFunctionType(ret_type, arg_type, n_arg, sym->variadic);
}

static LLVMTypeRef sym_array_type(struct symbol *sym)
{
        LLVMTypeRef elem_type;
        struct symbol *base_type;

        base_type = sym->ctype.base_type;
        /* empty struct is undefined [6.7.2.1(8)] */
        assert(base_type->bit_size > 0);

        elem_type = symbol_type(base_type);
        if (!elem_type)
                return NULL;

        return LLVMArrayType(elem_type, sym->bit_size / base_type->bit_size);
}

#define MAX_STRUCT_MEMBERS 64

static LLVMTypeRef sym_struct_type(struct symbol *sym)
{
        LLVMTypeRef elem_types[MAX_STRUCT_MEMBERS];
        struct symbol *member;
        char buffer[256];
        LLVMTypeRef ret;
        unsigned nr = 0;

        snprintf(buffer, sizeof(buffer), "struct.%s", sym->ident ? sym->ident->name : "anno");
        ret = LLVMStructCreateNamed(LLVMGetGlobalContext(), buffer);
        /* set ->aux to avoid recursion */
        sym->aux = ret;

        FOR_EACH_PTR(sym->symbol_list, member) {
                LLVMTypeRef member_type;

                assert(nr < MAX_STRUCT_MEMBERS);

                member_type = symbol_type(member);

                elem_types[nr++] = member_type; 
        } END_FOR_EACH_PTR(member);

        LLVMStructSetBody(ret, elem_types, nr, 0 /* packed? */); 
        return ret;
}

static LLVMTypeRef sym_union_type(struct symbol *sym)
{
        LLVMTypeRef elements;
        unsigned union_size;

        /*
         * There's no union support in the LLVM API so we treat unions as
         * opaque structs. The downside is that we lose type information on the
         * members but as LLVM doesn't care, neither do we.
         */
        union_size = sym->bit_size / 8;

        elements = LLVMArrayType(LLVMInt8Type(), union_size);

        return LLVMStructType(&elements, 1, 0 /* packed? */);
}

static LLVMTypeRef sym_ptr_type(struct symbol *sym)
{
        LLVMTypeRef type;

        /* 'void *' is treated like 'char *' */
        if (is_void_type(sym->ctype.base_type))
                type = LLVMInt8Type();
        else
                type = symbol_type(sym->ctype.base_type);

        return LLVMPointerType(type, 0);
}

static LLVMTypeRef sym_basetype_type(struct symbol *sym)
{
        LLVMTypeRef ret = NULL;

        if (is_float_type(sym)) {
                switch (sym->bit_size) {
                case 32:
                        ret = LLVMFloatType();
                        break;
                case 64:
                        ret = LLVMDoubleType();
                        break;
                case 80:
                        ret = LLVMX86FP80Type();
                        break;
                default:
                        die("invalid bit size %d for type %d", sym->bit_size, sym->type);
                        break;
                }
        } else {
                switch (sym->bit_size) {
                case -1:
                        ret = LLVMVoidType();
                        break;
                case 1:
                        ret = LLVMInt1Type();
                        break;
                case 8:
                        ret = LLVMInt8Type();
                        break;
                case 16:
                        ret = LLVMInt16Type();
                        break;
                case 32:
                        ret = LLVMInt32Type();
                        break;
                case 64:
                        ret = LLVMInt64Type();
                        break;
                default:
                        die("invalid bit size %d for type %d", sym->bit_size, sym->type);
                        break;
                }
        }

        return ret;
}

static LLVMTypeRef symbol_type(struct symbol *sym)
{
        LLVMTypeRef ret = NULL;

        /* don't cache the result for SYM_NODE */
        if (sym->type == SYM_NODE)
                return symbol_type(sym->ctype.base_type);

        if (sym->aux)
                return sym->aux;

        switch (sym->type) {
        case SYM_BITFIELD:
                ret = LLVMIntType(sym->bit_size);
                break;
        case SYM_RESTRICT:
        case SYM_ENUM:
                ret = symbol_type(sym->ctype.base_type);
                break;
        case SYM_BASETYPE:
                ret = sym_basetype_type(sym);
                break;
        case SYM_PTR:
                ret = sym_ptr_type(sym);
                break;
        case SYM_UNION:
                ret = sym_union_type(sym);
                break;
        case SYM_STRUCT:
                ret = sym_struct_type(sym);
                break;
        case SYM_ARRAY:
                ret = sym_array_type(sym);
                break;
        case SYM_FN:
                ret = sym_func_type(sym);
                break;
        default:
                assert(0);
        }

        /* cache the result */
        sym->aux = ret;
        return ret;
}

static LLVMTypeRef insn_symbol_type(struct instruction *insn)
{
        if (insn->type)
                return symbol_type(insn->type);

        switch (insn->size) {
                case 8:         return LLVMInt8Type();
                case 16:        return LLVMInt16Type();
                case 32:        return LLVMInt32Type();
                case 64:        return LLVMInt64Type();

                default:
                        die("invalid bit size %d", insn->size);
                        break;
        }

        return NULL;    /* not reached */
}

static LLVMLinkage data_linkage(struct symbol *sym)
{
        if (sym->ctype.modifiers & MOD_STATIC)
                return LLVMPrivateLinkage;

        return LLVMExternalLinkage;
}

static LLVMLinkage function_linkage(struct symbol *sym)
{
        if (sym->ctype.modifiers & MOD_STATIC)
                return LLVMInternalLinkage;

        return LLVMExternalLinkage;
}

#define MAX_PSEUDO_NAME 64

static const char *pseudo_name(pseudo_t pseudo, char *buf)
{
        switch (pseudo->type) {
        case PSEUDO_REG:
                snprintf(buf, MAX_PSEUDO_NAME, "R%d.", pseudo->nr);
                break;
        case PSEUDO_PHI:
                snprintf(buf, MAX_PSEUDO_NAME, "PHI%d.", pseudo->nr);
                break;
        case PSEUDO_SYM:
        case PSEUDO_VAL:
        case PSEUDO_ARG:
        case PSEUDO_VOID:
                buf[0] = '\0';
                break;
        case PSEUDO_UNDEF:
                assert(0);
                break;
        default:
                assert(0);
        }

        return buf;
}

static LLVMValueRef get_sym_value(LLVMModuleRef module, struct symbol *sym)
{
        const char *name = show_ident(sym->ident);
        LLVMTypeRef type = symbol_type(sym);
        LLVMValueRef result = NULL;
        struct expression *expr;

        assert(sym->bb_target == NULL);

        expr = sym->initializer;
        if (expr && !sym->ident) {
                switch (expr->type) {
                case EXPR_STRING: {
                        const char *s = expr->string->data;
                        LLVMValueRef indices[] = { LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt64Type(), 0, 0) };
                        LLVMValueRef data;

                        data = LLVMAddGlobal(module, LLVMArrayType(LLVMInt8Type(), strlen(s) + 1), ".str");
                        LLVMSetLinkage(data, LLVMPrivateLinkage);
                        LLVMSetGlobalConstant(data, 1);
                        LLVMSetInitializer(data, LLVMConstString(strdup(s), strlen(s) + 1, true));

                        result = LLVMConstGEP(data, indices, ARRAY_SIZE(indices));
                        return result;
                }
                default:
                        break;
                }
        }

        if (LLVMGetTypeKind(type) == LLVMFunctionTypeKind) {
                result = LLVMGetNamedFunction(module, name);
                if (!result)
                        result = LLVMAddFunction(module, name, type);
        } else {
                result = LLVMGetNamedGlobal(module, name);
                if (!result)
                        result = LLVMAddGlobal(module, type, name);
        }

        return result;
}

static LLVMValueRef constant_value(unsigned long long val, LLVMTypeRef dtype)
{
        LLVMValueRef result;

        switch (LLVMGetTypeKind(dtype)) {
        case LLVMPointerTypeKind:
                if (val != 0) {  // for example: ... = (void*) 0x123;
                        LLVMTypeRef itype = LLVMIntType(bits_in_pointer);
                        result = LLVMConstInt(itype, val, 1);
                        result = LLVMConstIntToPtr(result, dtype);
                } else {
                        result = LLVMConstPointerNull(dtype);
                }
                break;
        case LLVMIntegerTypeKind:
                result = LLVMConstInt(dtype, val, 1);
                break;
        case LLVMArrayTypeKind:
        case LLVMStructTypeKind:
                if (val != 0)
                        return NULL;
                result = LLVMConstNull(dtype);
                break;
        default:
                return NULL;
        }
        return result;
}

static LLVMValueRef val_to_value(unsigned long long val, struct symbol *ctype)
{
        LLVMValueRef result;
        LLVMTypeRef dtype;

        assert(ctype);
        dtype = symbol_type(ctype);
        result = constant_value(val, dtype);
        if (result)
                return result;
        sparse_error(ctype->pos, "no value possible for %s", show_typename(ctype));
        return LLVMGetUndef(symbol_type(ctype));
}

static LLVMValueRef pseudo_to_value(struct function *fn, struct symbol *ctype, pseudo_t pseudo)
{
        LLVMValueRef result = NULL;

        switch (pseudo->type) {
        case PSEUDO_REG:
                result = pseudo->priv;
                break;
        case PSEUDO_SYM:
                result = get_sym_value(fn->module, pseudo->sym);
                break;
        case PSEUDO_VAL:
                result = val_to_value(pseudo->value, ctype);
                break;
        case PSEUDO_ARG: {
                result = LLVMGetParam(fn->fn, pseudo->nr - 1);
                break;
        }
        case PSEUDO_PHI:
                result = pseudo->priv;
                break;
        case PSEUDO_VOID:
                result = NULL;
                break;
        case PSEUDO_UNDEF:
                result = LLVMGetUndef(symbol_type(ctype));
                break;
        default:
                assert(0);
        }

        return result;
}

static LLVMValueRef pseudo_to_rvalue(struct function *fn, struct symbol *ctype, pseudo_t pseudo)
{
        LLVMValueRef val = pseudo_to_value(fn, ctype, pseudo);
        LLVMTypeRef dtype = symbol_type(ctype);
        char name[MAX_PSEUDO_NAME];

        pseudo_name(pseudo, name);
        return LLVMBuildBitCast(fn->builder, val, dtype, name);
}

static LLVMValueRef value_to_ivalue(struct function *fn, struct symbol *ctype, LLVMValueRef val)
{
        const char *name = LLVMGetValueName(val);
        LLVMTypeRef dtype = symbol_type(ctype);

        if (LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMPointerTypeKind) {
                LLVMTypeRef dtype = LLVMIntType(bits_in_pointer);
                val = LLVMBuildPtrToInt(fn->builder, val, dtype, name);
        }
        if (ctype && is_int_type(ctype)) {
                val = LLVMBuildIntCast(fn->builder, val, dtype, name);
        }
        return val;
}

static LLVMValueRef value_to_pvalue(struct function *fn, struct symbol *ctype, LLVMValueRef val)
{
        const char *name = LLVMGetValueName(val);
        LLVMTypeRef dtype = symbol_type(ctype);

        assert(is_ptr_type(ctype));
        switch (LLVMGetTypeKind(LLVMTypeOf(val))) {
        case LLVMIntegerTypeKind:
                val = LLVMBuildIntToPtr(fn->builder, val, dtype, name);
                break;
        case LLVMPointerTypeKind:
                val = LLVMBuildBitCast(fn->builder, val, dtype, name);
                break;
        default:
                break;
        }
        return val;
}

static LLVMValueRef adjust_type(struct function *fn, struct symbol *ctype, LLVMValueRef val)
{
        if (is_int_type(ctype))
                return value_to_ivalue(fn, ctype, val);
        if (is_ptr_type(ctype))
                return value_to_pvalue(fn, ctype, val);
        return val;
}

/*
 * Get the LLVMValue corresponding to the pseudo
 * and force the type corresponding to ctype.
 */
static LLVMValueRef get_operand(struct function *fn, struct symbol *ctype, pseudo_t pseudo)
{
        LLVMValueRef target = pseudo_to_value(fn, ctype, pseudo);
        return adjust_type(fn, ctype, target);
}

/*
 * Get the LLVMValue corresponding to the pseudo
 * and force the type corresponding to ctype but
 * map all pointers to intptr_t.
 */
static LLVMValueRef get_ioperand(struct function *fn, struct symbol *ctype, pseudo_t pseudo)
{
        LLVMValueRef target = pseudo_to_value(fn, ctype, pseudo);
        return value_to_ivalue(fn, ctype, target);
}

static LLVMValueRef calc_gep(LLVMBuilderRef builder, LLVMValueRef base, LLVMValueRef off)
{
        LLVMTypeRef type = LLVMTypeOf(base);
        unsigned int as = LLVMGetPointerAddressSpace(type);
        LLVMTypeRef bytep = LLVMPointerType(LLVMInt8Type(), as);
        LLVMValueRef addr;
        const char *name = LLVMGetValueName(off);

        /* convert base to char* type */
        base = LLVMBuildPointerCast(builder, base, bytep, name);
        /* addr = base + off */
        addr = LLVMBuildInBoundsGEP(builder, base, &off, 1, name);
        /* convert back to the actual pointer type */
        addr = LLVMBuildPointerCast(builder, addr, type, name);
        return addr;
}

static LLVMRealPredicate translate_fop(int opcode)
{
        static const LLVMRealPredicate trans_tbl[] = {
                [OP_FCMP_ORD]   = LLVMRealORD,
                [OP_FCMP_OEQ]   = LLVMRealOEQ,
                [OP_FCMP_ONE]   = LLVMRealONE,
                [OP_FCMP_OLE]   = LLVMRealOLE,
                [OP_FCMP_OGE]   = LLVMRealOGE,
                [OP_FCMP_OLT]   = LLVMRealOLT,
                [OP_FCMP_OGT]   = LLVMRealOGT,
                [OP_FCMP_UEQ]   = LLVMRealUEQ,
                [OP_FCMP_UNE]   = LLVMRealUNE,
                [OP_FCMP_ULE]   = LLVMRealULE,
                [OP_FCMP_UGE]   = LLVMRealUGE,
                [OP_FCMP_ULT]   = LLVMRealULT,
                [OP_FCMP_UGT]   = LLVMRealUGT,
                [OP_FCMP_UNO]   = LLVMRealUNO,
        };

        return trans_tbl[opcode];
}

static LLVMIntPredicate translate_op(int opcode)
{
        static const LLVMIntPredicate trans_tbl[] = {
                [OP_SET_EQ]     = LLVMIntEQ,
                [OP_SET_NE]     = LLVMIntNE,
                [OP_SET_LE]     = LLVMIntSLE,
                [OP_SET_GE]     = LLVMIntSGE,
                [OP_SET_LT]     = LLVMIntSLT,
                [OP_SET_GT]     = LLVMIntSGT,
                [OP_SET_B]      = LLVMIntULT,
                [OP_SET_A]      = LLVMIntUGT,
                [OP_SET_BE]     = LLVMIntULE,
                [OP_SET_AE]     = LLVMIntUGE,
        };

        return trans_tbl[opcode];
}

static void output_op_binary(struct function *fn, struct instruction *insn)
{
        LLVMValueRef lhs, rhs, target;
        char target_name[64];

        lhs = get_ioperand(fn, insn->type, insn->src1);
        rhs = get_ioperand(fn, insn->type, insn->src2);

        pseudo_name(insn->target, target_name);

        switch (insn->opcode) {
        /* Binary */
        case OP_ADD:
                target = LLVMBuildAdd(fn->builder, lhs, rhs, target_name);
                break;
        case OP_SUB:
                target = LLVMBuildSub(fn->builder, lhs, rhs, target_name);
                break;
        case OP_MUL:
                target = LLVMBuildMul(fn->builder, lhs, rhs, target_name);
                break;
        case OP_DIVU:
                target = LLVMBuildUDiv(fn->builder, lhs, rhs, target_name);
                break;
        case OP_DIVS:
                assert(!is_float_type(insn->type));
                target = LLVMBuildSDiv(fn->builder, lhs, rhs, target_name);
                break;
        case OP_MODU:
                assert(!is_float_type(insn->type));
                target = LLVMBuildURem(fn->builder, lhs, rhs, target_name);
                break;
        case OP_MODS:
                assert(!is_float_type(insn->type));
                target = LLVMBuildSRem(fn->builder, lhs, rhs, target_name);
                break;
        case OP_SHL:
                assert(!is_float_type(insn->type));
                target = LLVMBuildShl(fn->builder, lhs, rhs, target_name);
                break;
        case OP_LSR:
                assert(!is_float_type(insn->type));
                target = LLVMBuildLShr(fn->builder, lhs, rhs, target_name);
                break;
        case OP_ASR:
                assert(!is_float_type(insn->type));
                target = LLVMBuildAShr(fn->builder, lhs, rhs, target_name);
                break;

        /* floating-point */
        case OP_FADD:
                target = LLVMBuildFAdd(fn->builder, lhs, rhs, target_name);
                break;
        case OP_FSUB:
                target = LLVMBuildFSub(fn->builder, lhs, rhs, target_name);
                break;
        case OP_FMUL:
                target = LLVMBuildFMul(fn->builder, lhs, rhs, target_name);
                break;
        case OP_FDIV:
                target = LLVMBuildFDiv(fn->builder, lhs, rhs, target_name);
                break;
        
        /* Logical */
        case OP_AND:
                assert(!is_float_type(insn->type));
                target = LLVMBuildAnd(fn->builder, lhs, rhs, target_name);
                break;
        case OP_OR:
                assert(!is_float_type(insn->type));
                target = LLVMBuildOr(fn->builder, lhs, rhs, target_name);
                break;
        case OP_XOR:
                assert(!is_float_type(insn->type));
                target = LLVMBuildXor(fn->builder, lhs, rhs, target_name);
                break;
        default:
                assert(0);
                break;
        }

        target = adjust_type(fn, insn->type, target);
        insn->target->priv = target;
}

static void output_op_compare(struct function *fn, struct instruction *insn)
{
        LLVMValueRef lhs, rhs, target;
        char target_name[64];

        lhs = pseudo_to_value(fn, NULL, insn->src1);
        if (insn->src2->type == PSEUDO_VAL)
                rhs = constant_value(insn->src2->value, LLVMTypeOf(lhs));
        else
                rhs = pseudo_to_value(fn, NULL, insn->src2);
        if (!rhs)
                rhs = LLVMGetUndef(symbol_type(insn->type));

        pseudo_name(insn->target, target_name);

        LLVMTypeRef dst_type = insn_symbol_type(insn);

        switch  (LLVMGetTypeKind(LLVMTypeOf(lhs))) {
        case LLVMPointerTypeKind:
                lhs = value_to_pvalue(fn, &ptr_ctype, lhs);
                rhs = value_to_pvalue(fn, &ptr_ctype, rhs);
                /* fall through */

        case LLVMIntegerTypeKind: {
                LLVMIntPredicate op = translate_op(insn->opcode);

                if (LLVMGetTypeKind(LLVMTypeOf(rhs)) == LLVMPointerTypeKind) {
                        LLVMTypeRef ltype = LLVMTypeOf(lhs);
                        rhs = LLVMBuildPtrToInt(fn->builder, rhs, ltype, "");
                }
                target = LLVMBuildICmp(fn->builder, op, lhs, rhs, target_name);
                break;
        }
        case LLVMHalfTypeKind:
        case LLVMFloatTypeKind:
        case LLVMDoubleTypeKind:
        case LLVMX86_FP80TypeKind:
        case LLVMFP128TypeKind:
        case LLVMPPC_FP128TypeKind: {
                LLVMRealPredicate op = translate_fop(insn->opcode);

                target = LLVMBuildFCmp(fn->builder, op, lhs, rhs, target_name);
                break;
        }
        default:
                assert(0);
        }

        target = LLVMBuildZExt(fn->builder, target, dst_type, target_name);

        insn->target->priv = target;
}

static void output_op_ret(struct function *fn, struct instruction *insn)
{
        pseudo_t pseudo = insn->src;

        if (pseudo && pseudo != VOID) {
                LLVMValueRef result = get_operand(fn, insn->type, pseudo);
                LLVMBuildRet(fn->builder, result);
        } else
                LLVMBuildRetVoid(fn->builder);
}

static LLVMValueRef calc_memop_addr(struct function *fn, struct instruction *insn)
{
        LLVMTypeRef int_type, addr_type;
        LLVMValueRef src, off, addr;
        unsigned int as;

        /* int type large enough to hold a pointer */
        int_type = LLVMIntType(bits_in_pointer);
        off = LLVMConstInt(int_type, insn->offset, 0);

        /* convert src to the effective pointer type */
        src = pseudo_to_value(fn, insn->type, insn->src);
        as = LLVMGetPointerAddressSpace(LLVMTypeOf(src));
        addr_type = LLVMPointerType(insn_symbol_type(insn), as);
        src = LLVMBuildPointerCast(fn->builder, src, addr_type, LLVMGetValueName(src));

        /* addr = src + off */
        addr = calc_gep(fn->builder, src, off);
        return addr;
}


static void output_op_load(struct function *fn, struct instruction *insn)
{
        LLVMValueRef addr, target;
        char name[MAX_PSEUDO_NAME];

        addr = calc_memop_addr(fn, insn);

        /* perform load */
        pseudo_name(insn->target, name);
        target = LLVMBuildLoad(fn->builder, addr, name);

        insn->target->priv = target;
}

static void output_op_store(struct function *fn, struct instruction *insn)
{
        LLVMValueRef addr, target_in;

        addr = calc_memop_addr(fn, insn);

        target_in = pseudo_to_rvalue(fn, insn->type, insn->target);

        /* perform store */
        LLVMBuildStore(fn->builder, target_in, addr);
}

static LLVMValueRef bool_value(struct function *fn, LLVMValueRef value)
{
        if (LLVMTypeOf(value) != LLVMInt1Type())
                value = LLVMBuildIsNotNull(fn->builder, value, LLVMGetValueName(value));

        return value;
}

static void output_op_cbr(struct function *fn, struct instruction *br)
{
        LLVMValueRef cond = bool_value(fn,
                        pseudo_to_value(fn, NULL, br->cond));

        LLVMBuildCondBr(fn->builder, cond,
                        br->bb_true->priv,
                        br->bb_false->priv);
}

static void output_op_br(struct function *fn, struct instruction *br)
{
        LLVMBuildBr(fn->builder, br->bb_true->priv);
}

static void output_op_sel(struct function *fn, struct instruction *insn)
{
        LLVMValueRef target, src1, src2, src3;
        char name[MAX_PSEUDO_NAME];

        src1 = bool_value(fn, pseudo_to_value(fn, NULL, insn->src1));
        src2 = get_operand(fn, insn->type, insn->src2);
        src3 = get_operand(fn, insn->type, insn->src3);

        pseudo_name(insn->target, name);
        target = LLVMBuildSelect(fn->builder, src1, src2, src3, name);

        insn->target->priv = adjust_type(fn, insn->type, target);
}

static void output_op_switch(struct function *fn, struct instruction *insn)
{
        LLVMValueRef sw_val, target;
        struct basic_block *def = NULL;
        struct multijmp *jmp;
        int n_jmp = 0;

        FOR_EACH_PTR(insn->multijmp_list, jmp) {
                if (jmp->begin <= jmp->end) {
                        n_jmp += (jmp->end - jmp->begin) + 1;
                } else                                  /* default case */
                        def = jmp->target;
        } END_FOR_EACH_PTR(jmp);

        sw_val = get_ioperand(fn, insn->type, insn->cond);
        target = LLVMBuildSwitch(fn->builder, sw_val,
                                 def ? def->priv : NULL, n_jmp);

        FOR_EACH_PTR(insn->multijmp_list, jmp) {
                long long val;

                for (val = jmp->begin; val <= jmp->end; val++) {
                        LLVMValueRef Val = val_to_value(val, insn->type);
                        LLVMAddCase(target, Val, jmp->target->priv);
                }
        } END_FOR_EACH_PTR(jmp);
}

static void output_op_call(struct function *fn, struct instruction *insn)
{
        LLVMValueRef target, func;
        struct symbol *ctype;
        int n_arg = 0, i;
        struct pseudo *arg;
        LLVMValueRef *args;
        char name[64];

        n_arg = pseudo_list_size(insn->arguments);
        args = calloc(n_arg, sizeof(LLVMValueRef));

        PREPARE_PTR_LIST(insn->fntypes, ctype);
        if (insn->func->type == PSEUDO_REG || insn->func->type == PSEUDO_PHI)
                func = get_operand(fn, ctype, insn->func);
        else
                func = pseudo_to_value(fn, ctype, insn->func);
        i = 0;
        FOR_EACH_PTR(insn->arguments, arg) {
                NEXT_PTR_LIST(ctype);
                args[i++] = pseudo_to_rvalue(fn, ctype, arg);
        } END_FOR_EACH_PTR(arg);
        FINISH_PTR_LIST(ctype);

        pseudo_name(insn->target, name);
        target = LLVMBuildCall(fn->builder, func, args, n_arg, name);

        insn->target->priv = target;
}

static void output_op_phisrc(struct function *fn, struct instruction *insn)
{
        LLVMValueRef v;
        struct instruction *phi;

        assert(insn->target->priv == NULL);

        /* target = src */
        v = get_operand(fn, insn->type, insn->phi_src);

        FOR_EACH_PTR(insn->phi_users, phi) {
                LLVMValueRef load, ptr;

                assert(phi->opcode == OP_PHI);
                /* phi must be load from alloca */
                load = phi->target->priv;
                assert(LLVMGetInstructionOpcode(load) == LLVMLoad);
                ptr = LLVMGetOperand(load, 0);
                /* store v to alloca */
                LLVMBuildStore(fn->builder, v, ptr);
        } END_FOR_EACH_PTR(phi);
}

static void output_op_phi(struct function *fn, struct instruction *insn)
{
        LLVMValueRef load = insn->target->priv;

        /* forward load */
        assert(LLVMGetInstructionOpcode(load) == LLVMLoad);
        /* forward load has no parent block */
        assert(!LLVMGetInstructionParent(load));
        /* finalize load in current block  */
        LLVMInsertIntoBuilder(fn->builder, load);
}

static void output_op_ptrcast(struct function *fn, struct instruction *insn)
{
        LLVMValueRef src, target;
        LLVMTypeRef dtype;
        struct symbol *otype = insn->orig_type;
        LLVMOpcode op;
        char target_name[64];

        src = get_operand(fn, otype, insn->src);
        pseudo_name(insn->target, target_name);

        dtype = symbol_type(insn->type);
        switch (insn->opcode) {
        case OP_UTPTR:
        case OP_SEXT:                   // FIXME
                assert(is_int_type(otype));
                assert(is_ptr_type(insn->type));
                op = LLVMIntToPtr;
                break;
        case OP_PTRTU:
                assert(is_ptr_type(otype));
                assert(is_int_type(insn->type));
                op = LLVMPtrToInt;
                break;
        case OP_PTRCAST:
        case OP_ZEXT:                   // FIXME
                assert(is_ptr_type(otype));
                assert(is_ptr_type(insn->type));
                op = LLVMBitCast;
                break;
        default:
                assert(0);
        }

        target = LLVMBuildCast(fn->builder, op, src, dtype, target_name);
        insn->target->priv = target;
}

static void output_op_cast(struct function *fn, struct instruction *insn, LLVMOpcode op)
{
        LLVMValueRef src, target;
        LLVMTypeRef dtype;
        struct symbol *otype = insn->orig_type;
        char target_name[64];

        if (is_ptr_type(insn->type))    // cast to void* is OP_CAST ...
                return output_op_ptrcast(fn, insn);

        assert(is_int_type(insn->type));

        src = get_operand(fn, otype, insn->src);
        pseudo_name(insn->target, target_name);

        dtype = symbol_type(insn->type);
        if (is_ptr_type(otype)) {
                op = LLVMPtrToInt;
        } else if (is_float_type(otype)) {
                assert(op == LLVMFPToUI || op == LLVMFPToSI);
        } else if (is_int_type(otype)) {
                unsigned int width = otype->bit_size;
                if (insn->size < width)
                        op = LLVMTrunc;
                else if (insn->size == width)
                        op = LLVMBitCast;
        } else {
                assert(0);
        }

        target = LLVMBuildCast(fn->builder, op, src, dtype, target_name);
        insn->target->priv = target;
}

static void output_op_fpcast(struct function *fn, struct instruction *insn)
{
        LLVMTypeRef dtype = symbol_type(insn->type);
        LLVMValueRef src, target;
        struct symbol *otype = insn->orig_type;
        char name[64];

        assert(is_float_type(insn->type));

        pseudo_name(insn->target, name);
        src = get_operand(fn, otype, insn->src);
        switch (insn->opcode) {
        case OP_FCVTF:
                target = LLVMBuildFPCast(fn->builder, src, dtype, name);
                break;
        case OP_SCVTF:
                target = LLVMBuildSIToFP(fn->builder, src, dtype, name);
                break;
        case OP_UCVTF:
                target = LLVMBuildUIToFP(fn->builder, src, dtype, name);
                break;
        default:
                assert(0);
        }
        insn->target->priv = target;
}

static void output_op_setval(struct function *fn, struct instruction *insn)
{
        struct expression *val = insn->val;
        LLVMValueRef target;

        switch (val->type) {
        case EXPR_LABEL:
                target = LLVMBlockAddress(fn->fn, val->symbol->bb_target->priv);
                break;
        default:
                assert(0);
        }

        insn->target->priv = target;
}

static void output_op_setfval(struct function *fn, struct instruction *insn)
{
        LLVMTypeRef dtype = symbol_type(insn->type);
        LLVMValueRef target;

        target = LLVMConstReal(dtype, insn->fvalue);
        insn->target->priv = target;
}

static void output_insn(struct function *fn, struct instruction *insn)
{
        switch (insn->opcode) {
        case OP_RET:
                output_op_ret(fn, insn);
                break;
        case OP_BR:
                output_op_br(fn, insn);
                break;
        case OP_CBR:
                output_op_cbr(fn, insn);
                break;
        case OP_SYMADDR:
                assert(0);
                break;
        case OP_SETVAL:
                output_op_setval(fn, insn);
                break;
        case OP_SETFVAL:
                output_op_setfval(fn, insn);
                break;
        case OP_SWITCH:
                output_op_switch(fn, insn);
                break;
        case OP_COMPUTEDGOTO:
                assert(0);
                break;
        case OP_PHISOURCE:
                output_op_phisrc(fn, insn);
                break;
        case OP_PHI:
                output_op_phi(fn, insn);
                break;
        case OP_LOAD:
                output_op_load(fn, insn);
                break;
        case OP_STORE:
                output_op_store(fn, insn);
                break;
        case OP_INLINED_CALL:
                break;
        case OP_CALL:
                output_op_call(fn, insn);
                break;
        case OP_ZEXT:
                output_op_cast(fn, insn, LLVMZExt);
                break;
        case OP_SEXT:
                output_op_cast(fn, insn, LLVMSExt);
                break;
        case OP_TRUNC:
                output_op_cast(fn, insn, LLVMTrunc);
                break;
        case OP_FCVTU:
                output_op_cast(fn, insn, LLVMFPToUI);
                break;
        case OP_FCVTS:
                output_op_cast(fn, insn, LLVMFPToSI);
                break;
        case OP_UCVTF: case OP_SCVTF:
        case OP_FCVTF:
                output_op_fpcast(fn, insn);
                break;
        case OP_UTPTR:
        case OP_PTRTU:
        case OP_PTRCAST:
                output_op_ptrcast(fn, insn);
                break;
        case OP_BINARY ... OP_BINARY_END:
                output_op_binary(fn, insn);
                break;
        case OP_FPCMP ... OP_BINCMP_END:
                output_op_compare(fn, insn);
                break;
        case OP_SEL:
                output_op_sel(fn, insn);
                break;
        case OP_SLICE:
                assert(0);
                break;
        case OP_NOT: {
                LLVMValueRef src, target;
                char target_name[64];

                src = pseudo_to_value(fn, insn->type, insn->src);

                pseudo_name(insn->target, target_name);

                target = LLVMBuildNot(fn->builder, src, target_name);

                insn->target->priv = target;
                break;
        }
        case OP_FNEG:
        case OP_NEG: {
                LLVMValueRef src, target;
                char target_name[64];

                src = pseudo_to_value(fn, insn->type, insn->src);

                pseudo_name(insn->target, target_name);

                if (insn->opcode == OP_FNEG)
                        target = LLVMBuildFNeg(fn->builder, src, target_name);
                else
                        target = LLVMBuildNeg(fn->builder, src, target_name);

                insn->target->priv = target;
                break;
        }
        case OP_CONTEXT:
                assert(0);
                break;
        case OP_RANGE:
                assert(0);
                break;
        case OP_NOP:
                assert(0);
                break;
        case OP_DEATHNOTE:
                break;
        case OP_ASM:
                assert(0);
                break;
        case OP_COPY:
                assert(0);
                break;
        default:
                break;
        }
}

static void output_bb(struct function *fn, struct basic_block *bb)
{
        struct instruction *insn;

        FOR_EACH_PTR(bb->insns, insn) {
                if (!insn->bb)
                        continue;

                output_insn(fn, insn);
        }
        END_FOR_EACH_PTR(insn);
}

#define MAX_ARGS        64

static void output_fn(LLVMModuleRef module, struct entrypoint *ep)
{
        struct symbol *sym = ep->name;
        struct symbol *base_type = sym->ctype.base_type;
        struct function function = { .module = module };
        struct basic_block *bb;
        int nr_args = 0;
        int i;

        function.fn = get_sym_value(module, sym);
        LLVMSetFunctionCallConv(function.fn, LLVMCCallConv);
        LLVMSetLinkage(function.fn, function_linkage(sym));

        function.builder = LLVMCreateBuilder();

        /* give a name to each argument */
        nr_args = symbol_list_size(base_type->arguments);
        for (i = 0; i < nr_args; i++) {
                char name[MAX_PSEUDO_NAME];
                LLVMValueRef arg;

                arg = LLVMGetParam(function.fn, i);
                snprintf(name, sizeof(name), "ARG%d.", i+1);
                LLVMSetValueName(arg, name);
        }

        /* create the BBs */
        FOR_EACH_PTR(ep->bbs, bb) {
                static int nr_bb;
                LLVMBasicBlockRef bbr;
                char bbname[32];
                struct instruction *insn;

                sprintf(bbname, "L%d", nr_bb++);
                bbr = LLVMAppendBasicBlock(function.fn, bbname);

                bb->priv = bbr;

                /* allocate alloca for each phi */
                FOR_EACH_PTR(bb->insns, insn) {
                        LLVMBasicBlockRef entrybbr;
                        LLVMTypeRef phi_type;
                        LLVMValueRef ptr;

                        if (!insn->bb || insn->opcode != OP_PHI)
                                continue;
                        /* insert alloca into entry block */
                        entrybbr = LLVMGetEntryBasicBlock(function.fn);
                        LLVMPositionBuilderAtEnd(function.builder, entrybbr);
                        phi_type = insn_symbol_type(insn);
                        ptr = LLVMBuildAlloca(function.builder, phi_type, "");
                        /* emit forward load for phi */
                        LLVMClearInsertionPosition(function.builder);
                        insn->target->priv = LLVMBuildLoad(function.builder, ptr, "phi");
                } END_FOR_EACH_PTR(insn);
        }
        END_FOR_EACH_PTR(bb);

        FOR_EACH_PTR(ep->bbs, bb) {
                LLVMPositionBuilderAtEnd(function.builder, bb->priv);

                output_bb(&function, bb);
        }
        END_FOR_EACH_PTR(bb);
}

static LLVMValueRef output_data(LLVMModuleRef module, struct symbol *sym)
{
        struct expression *initializer = sym->initializer;
        LLVMValueRef initial_value;
        LLVMValueRef data;
        const char *name;

        if (initializer) {
                switch (initializer->type) {
                case EXPR_VALUE:
                        initial_value = LLVMConstInt(symbol_type(sym), initializer->value, 1);
                        break;
                case EXPR_FVALUE:
                        initial_value = LLVMConstReal(symbol_type(sym), initializer->fvalue);
                        break;
                case EXPR_SYMBOL: {
                        struct symbol *sym = initializer->symbol;

                        initial_value = LLVMGetNamedGlobal(module, show_ident(sym->ident));
                        if (!initial_value)
                                initial_value = output_data(module, sym);
                        break;
                }
                case EXPR_STRING: {
                        const char *s = initializer->string->data;

                        initial_value = LLVMConstString(strdup(s), strlen(s) + 1, true);
                        break;
                }
                default:
                        warning(initializer->pos, "can't initialize type: %s", show_typename(sym));
                        initial_value = NULL;
                        break;
                }
        } else {
                LLVMTypeRef type = symbol_type(sym);

                initial_value = LLVMConstNull(type);
        }

        if (!initial_value)
                return NULL;

        name = sym->ident ? show_ident(sym->ident) : "" ;

        data = LLVMAddGlobal(module, LLVMTypeOf(initial_value), name);

        LLVMSetLinkage(data, data_linkage(sym));
        if (sym->ctype.modifiers & MOD_CONST)
                LLVMSetGlobalConstant(data, 1);
        if (sym->ctype.modifiers & MOD_TLS)
                LLVMSetThreadLocal(data, 1);
        if (sym->ctype.alignment)
                LLVMSetAlignment(data, sym->ctype.alignment);

        if (!(sym->ctype.modifiers & MOD_EXTERN))
                LLVMSetInitializer(data, initial_value);

        return data;
}

static int is_prototype(struct symbol *sym)
{
        if (sym->type == SYM_NODE)
                sym = sym->ctype.base_type;
        return sym && sym->type == SYM_FN && !sym->stmt;
}

static int compile(LLVMModuleRef module, struct symbol_list *list)
{
        struct symbol *sym;

        FOR_EACH_PTR(list, sym) {
                struct entrypoint *ep;
                expand_symbol(sym);

                if (is_prototype(sym)) {
                        // this will do the LLVMAddFunction() we want
                        get_sym_value(module, sym);
                        continue;
                }

                ep = linearize_symbol(sym);
                if (ep)
                        output_fn(module, ep);
                else
                        output_data(module, sym);
        }
        END_FOR_EACH_PTR(sym);

        return 0;
}

#ifndef LLVM_DEFAULT_TARGET_TRIPLE
#define LLVM_DEFAULT_TARGET_TRIPLE LLVM_HOSTTRIPLE
#endif

#define X86_LINUX_LAYOUT \
        "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" \
        "i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-" \
        "a0:0:64-f80:32:32-n8:16:32-S128"

#define X86_64_LINUX_LAYOUT \
        "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" \
        "i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-" \
        "a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"

static void set_target(LLVMModuleRef module)
{
        char target[] = LLVM_DEFAULT_TARGET_TRIPLE;
        const char *arch, *vendor, *os, *env, *layout = NULL;
        char triple[256];

        arch = strtok(target, "-");
        vendor = strtok(NULL, "-");
        os = strtok(NULL, "-");
        env = strtok(NULL, "-");

        if (!os)
                return;
        if (!env)
                env = "unknown";

        if (!strcmp(arch, "x86_64") && !strcmp(os, "linux")) {
                if (arch_m64) {
                        layout = X86_64_LINUX_LAYOUT;
                } else {
                        arch = "i386";
                        layout = X86_LINUX_LAYOUT;
                }
        }

        /* unsupported target */
        if (!layout)
                return;

        snprintf(triple, sizeof(triple), "%s-%s-%s-%s", arch, vendor, os, env);
        LLVMSetTarget(module, triple);
        LLVMSetDataLayout(module, layout);
}

int main(int argc, char **argv)
{
        struct string_list *filelist = NULL;
        struct symbol_list *symlist;
        LLVMModuleRef module;
        char *file;

        symlist = sparse_initialize(argc, argv, &filelist);

        module = LLVMModuleCreateWithName("sparse");
        set_target(module);

        compile(module, symlist);

        /* need ->phi_users */
        dbg_dead = 1;
        FOR_EACH_PTR(filelist, file) {
                symlist = sparse(file);
                if (die_if_error)
                        return 1;
                compile(module, symlist);
        } END_FOR_EACH_PTR(file);

        LLVMVerifyModule(module, LLVMPrintMessageAction, NULL);

        LLVMWriteBitcodeToFD(module, STDOUT_FILENO, 0, 0);

        LLVMDisposeModule(module);

        report_stats();
        return 0;
}