// File: /workspace/github_c_makefile_star2_O1/yutopp[P]cc/asm_x86_64.c
// Function: built_from_ir_inst
void built_from_ir_inst(ASM_X86_64 *a, IRInst* inst) {
    switch(inst->kind) {
    case IR_INST_KIND_LET:
    {
        IRSymbolID var_id = inst->value.let.id;
        IRInstValue* let_rhs = &inst->value.let.rhs;
        switch(let_rhs->kind) {
        case IR_INST_VALUE_KIND_REF:
        {
            ASM_X86_64_Value* ref_val =
                asm_x86_64_get_val(a, let_rhs->value.ref.sym, let_rhs->value.ref.is_global);

            asm_x86_64_set_val(a, var_id, *ref_val, 0);

            break;
        }

        case IR_INST_VALUE_KIND_ADDR_OF:
        {
            ASM_X86_64_Value* ref_val = asm_x86_64_get_val(a, let_rhs->value.addr_of.sym, 0);

            ASM_X86_64_Value dest = {
                .kind = ASM_X86_64_VALUE_KIND_REG,
                .value = {
                    .reg = ASM_X86_64_REG_RAX,
                },
            };

            // leaq RAX, 'ref_val'
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_LEAQ;
            inst->value.op.args[0] = dest;
            inst->value.op.args[1] = *ref_val;

            asm_x86_64_set_val(a, var_id, dest, 0);

            break;
        }

        case IR_INST_VALUE_KIND_IMM_INT:
        {
            ASM_X86_64_Value value = {
                .kind = ASM_X86_64_VALUE_KIND_IMM_INT,
                .value = {
                    .imm_int = let_rhs->value.imm_int, // TODO: fix size
                },
            };
            asm_x86_64_set_val(a, var_id, value, 0);

            break;
        }

        case IR_INST_VALUE_KIND_OP_BIN:
        {
            // TODO: !!! implement correctly !!!

            ASM_X86_64_Value* lhs_val = asm_x86_64_get_val(a, let_rhs->value.op_bin.lhs, 0);
            ASM_X86_64_Value* rhs_val = asm_x86_64_get_val(a, let_rhs->value.op_bin.rhs, 0);

            // Move value into RAX (TODO: fix size of data)
            {
                // movq rax, 'lhs_val'
                ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
                inst->kind = ASM_X86_64_INST_KIND_OP;
                inst->value.op.op = ASM_X86_64_OP_MOVQ;
                inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
                inst->value.op.args[0].value.reg = ASM_X86_64_REG_RAX;
                inst->value.op.args[1] = *lhs_val;
            }

            // Add value to RAX (TODO: fix size of data)
            {
                // addq rax, 'rhs_val'
                ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
                inst->kind = ASM_X86_64_INST_KIND_OP;
                inst->value.op.op = ASM_X86_64_OP_ADDQ;
                inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
                inst->value.op.args[0].value.reg = ASM_X86_64_REG_RAX;
                inst->value.op.args[1] = *rhs_val;
            }

            // Result is saved in RAX (TODO: fix size of data)
            size_t var_target_offset = asm_x86_64_get_local(a, var_id);
            ASM_X86_64_Value dest = {
                .kind = ASM_X86_64_VALUE_KIND_DISP_REG,
                .value = {
                    .disp_reg = {
                        .symbol = NULL,
                        .disp = (int)var_target_offset, // TODO: fix
                        .reg = ASM_X86_64_REG_RSP,
                    },
                },
            };
            {
                // movq 'var_target(RSP)', RAX
                ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
                inst->kind = ASM_X86_64_INST_KIND_OP;
                inst->value.op.op = ASM_X86_64_OP_ADDQ;
                inst->value.op.args[0] = dest;
                inst->value.op.args[1].kind = ASM_X86_64_VALUE_KIND_REG;
                inst->value.op.args[1].value.reg = ASM_X86_64_REG_RAX;
            }

            asm_x86_64_set_val(a, var_id, dest, 0);

            break;
        }

        case IR_INST_VALUE_KIND_CALL:
        {
            IRSymbolID lhs_id = let_rhs->value.call.lhs;
            ASM_X86_64_Value* lhs_val = asm_x86_64_get_val(a, lhs_id, 0);

            fprintf(DEBUGOUT, "CALL LHS %ld = ", lhs_id);
            fprint_value(DEBUGOUT, lhs_val);
            fprintf(DEBUGOUT, "\n");

            // TODO: support register passing
            for(size_t i=0; i<vector_len(let_rhs->value.call.args); ++i) {
                assert(i<sizeof(arg_regs)/sizeof(ASM_X86_64_Reg));

                IRSymbolID* arg = vector_at(let_rhs->value.call.args, i);
                ASM_X86_64_Value* arg_val = asm_x86_64_get_val(a, *arg, 0);

                // movq ARG_REG, 'arg_val'
                ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
                inst->kind = ASM_X86_64_INST_KIND_OP;
                inst->value.op.op = ASM_X86_64_OP_MOVQ;
                inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
                inst->value.op.args[0].value.reg = arg_regs[i];
                inst->value.op.args[1] = *arg_val;
            }

            {
                // call 'lhs_val'
                ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
                inst->kind = ASM_X86_64_INST_KIND_OP;
                inst->value.op.op = ASM_X86_64_OP_CALL;
                inst->value.op.args[0] = *lhs_val;
            }

            // Result is saved in RAX (TODO: fix size of data)
            size_t var_target_offset = asm_x86_64_get_local(a, var_id);
            ASM_X86_64_Value dest = {
                .kind = ASM_X86_64_VALUE_KIND_DISP_REG,
                .value = {
                    .disp_reg = {
                        .symbol = NULL,
                        .disp = (int)var_target_offset, // TODO: fix
                        .reg = ASM_X86_64_REG_RSP,
                    },
                },
            };
            {
                // movq 'var_target(RSP)', RAX
                ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
                inst->kind = ASM_X86_64_INST_KIND_OP;
                inst->value.op.op = ASM_X86_64_OP_MOVQ;
                inst->value.op.args[0] = dest;
                inst->value.op.args[1].kind = ASM_X86_64_VALUE_KIND_REG;
                inst->value.op.args[1].value.reg = ASM_X86_64_REG_RAX;
            }

            asm_x86_64_set_val(a, var_id, dest, 0);

            break;
        }

        default:
            fprintf(stderr, "Unexpected kind: %d\n", let_rhs->kind);
            assert(0); // TODO: error handling
        }
        break;
    }

    case IR_INST_KIND_RET:
    {
        IRSymbolID var_id = inst->value.ret.id;
        ASM_X86_64_Value* ret_val = asm_x86_64_get_val(a, var_id, 0);

        // Set a result (TODO: fix size of data)
        {
            // movq rax, 'ret_val'
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_MOVQ;
            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
            inst->value.op.args[0].value.reg = ASM_X86_64_REG_RAX;
            inst->value.op.args[1] = *ret_val;
        }

        {
            // movq rsp, rbp
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_MOVQ;
            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
            inst->value.op.args[0].value.reg = ASM_X86_64_REG_RSP;
            inst->value.op.args[1].kind = ASM_X86_64_VALUE_KIND_REG;
            inst->value.op.args[1].value.reg = ASM_X86_64_REG_RBP;
        }

        {
            // popq rbp
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_POPQ;
            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
            inst->value.op.args[0].value.reg = ASM_X86_64_REG_RBP;
        }

        {
            // ret
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_RET;
        }

        break;
    }

    case IR_INST_KIND_BRANCH:
    {
        ASM_X86_64_Value* cond_val = asm_x86_64_get_val(a, inst->value.branch.cond, 0);

        {
            // movq RAX, 'cond'
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_MOVQ;

            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
            inst->value.op.args[0].value.reg = ASM_X86_64_REG_RAX;
            inst->value.op.args[1] = *cond_val;
        }

        {
            // cmpq RAX, 0
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_CMPQ;
            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_REG;
            inst->value.op.args[0].value.reg = ASM_X86_64_REG_RAX;
            inst->value.op.args[1].kind = ASM_X86_64_VALUE_KIND_IMM_INT;
            inst->value.op.args[1].value.imm_int = 0;
        }

        {
            char const** m = uint_map_find(a->labels, inst->value.branch.else_bb->id);
            assert(m);
            char const* label_id = *m;

            // JE else_bb
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_JE;
            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_SYMBOL;
            inst->value.op.args[0].value.symbol = label_id;
        }

        {
            char const** m = uint_map_find(a->labels, inst->value.branch.then_bb->id);
            assert(m);
            char const* label_id = *m;

            // JMP then_bb
            ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
            inst->kind = ASM_X86_64_INST_KIND_OP;
            inst->value.op.op = ASM_X86_64_OP_JMP;
            inst->value.op.args[0].kind = ASM_X86_64_VALUE_KIND_SYMBOL;
            inst->value.op.args[0].value.symbol = label_id;
        }

        break;
    }

    case IR_INST_KIND_JUMP:
    {
        char const** m = uint_map_find(a->labels, inst->value.jump.next_bb->id);
        assert(m);
        char const* label_id = *m;

        ASM_X86_64_Value label = {
            .kind = ASM_X86_64_VALUE_KIND_SYMBOL,
            .value = {
                .symbol = label_id,
            },
        };

        // jmp 'label'
        ASM_X86_64_Inst* inst = (ASM_X86_64_Inst*)vector_append(a->insts);
        inst->kind = ASM_X86_64_INST_KIND_OP;
        inst->value.op.op = ASM_X86_64_OP_JMP;
        inst->value.op.args[0] = label;

        break;
    }

    default:
        assert(0); // TODO: error handling
    }
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movl (%rsi), %eax
cmpq $0x3, %rax
ja 0x5c98
movq %rsi, %r15
movq %rdi, %rbx
leaq 0x2221(%rip), %rcx      # 0x79c0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl 0x10(%r15), %edi
leal -0x2(%rdi), %eax
cmpl $0x4, %eax
ja 0x5cf5
movq 0x8(%r15), %r14
leaq 0x220d(%rip), %rcx      # 0x79d0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x20(%r15), %rsi
movl 0x18(%r15), %edx
movq %rbx, %rdi
callq 0x5cfa
movq 0x10(%rax), %rcx
movq %rcx, 0x10(%rsp)
movups (%rax), %xmm0
jmp 0x5c02
movq 0x8(%r15), %rsi
xorl %ebp, %ebp
movq %rbx, %rdi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %r14
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, %r12d
movl %r12d, (%rax)
movl $0x2, 0x8(%rax)
movl $0x3, %r13d
movl %r13d, 0x10(%rax)
movl %ebp, 0x18(%rax)
movups (%r14), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r14), %rcx
movq %rcx, 0x38(%rax)
movq (%rbx), %rdi
callq 0x31d0
movl %r12d, (%rax)
movl $0x8, 0x8(%rax)
movl %r13d, 0x10(%rax)
movl %ebp, 0x18(%rax)
movl $0x1, 0x28(%rax)
movl %ebp, 0x30(%rax)
movq 0x30(%rbx), %rdi
movq 0x18(%r15), %rax
movq (%rax), %rsi
callq 0x6166
testq %rax, %rax
je 0x5cb7
movq (%rax), %r14
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, (%rax)
movl $0xa, 0x8(%rax)
movl $0x0, 0x10(%rax)
movq %r14, 0x18(%rax)
movq 0x30(%rbx), %rdi
movq 0x10(%r15), %rax
movq (%rax), %rsi
callq 0x6166
testq %rax, %rax
jne 0x58e1
leaq 0x26cb(%rip), %rdi      # 0x7f7b
leaq 0x21f4(%rip), %rsi      # 0x7aab
leaq 0x24c0(%rip), %rcx      # 0x7d7e
movl $0x349, %edx            # imm = 0x349
callq 0x10d0
movq 0x30(%rbx), %rdi
movq 0x8(%r15), %rax
movq (%rax), %rsi
callq 0x6166
testq %rax, %rax
je 0x5cd6
movq (%rax), %r14
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, (%rax)
movl $0x9, 0x8(%rax)
movl $0x0, 0x10(%rax)
movq %r14, 0x18(%rax)
jmp 0x5c6a
movq 0x8(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %r14
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, %ebp
movl %ebp, (%rax)
movl $0x2, %r15d
movl %r15d, 0x8(%rax)
movl $0x3, %r12d
movl %r12d, 0x10(%rax)
movl $0x0, 0x18(%rax)
movups (%r14), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r14), %rcx
movq %rcx, 0x38(%rax)
movq (%rbx), %rdi
callq 0x31d0
movl %ebp, (%rax)
movl %r15d, 0x8(%rax)
movl %r12d, 0x10(%rax)
movl $0x4, 0x18(%rax)
movl %r12d, 0x28(%rax)
movl $0x5, %r14d
movl %r14d, 0x30(%rax)
movq (%rbx), %rdi
callq 0x31d0
movl %ebp, (%rax)
movl $0x1, 0x8(%rax)
movl %r12d, 0x10(%rax)
movl %r14d, 0x18(%rax)
movq (%rbx), %rdi
callq 0x31d0
movl %ebp, (%rax)
movl $0xb, 0x8(%rax)
jmp 0x5c6a
movq 0x18(%r15), %r13
movq %rbx, %rdi
movq %r13, %rsi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %rbp
movq 0x5613(%rip), %r12      # 0xafd8
movq (%r12), %rdi
leaq 0x2374(%rip), %rsi      # 0x7d44
movq %r13, %rdx
xorl %eax, %eax
callq 0x1110
movq (%r12), %rdi
movq %rbp, 0x30(%rsp)
movq %rbp, %rsi
callq 0x54a8
movq (%r12), %rsi
movl $0xa, %edi
callq 0x10f0
movq 0x20(%r15), %rdi
callq 0x322b
testq %rax, %rax
je 0x5a7f
leaq 0x2472(%rip), %r12      # 0x7e80
xorl %r13d, %r13d
cmpq $0x4, %r13
je 0x5c79
movq 0x20(%r15), %rdi
movq %r13, %rsi
callq 0x3216
movq (%rax), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %rbp
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, (%rax)
movl $0x2, 0x8(%rax)
movl $0x3, 0x10(%rax)
movl (%r12), %ecx
movl %ecx, 0x18(%rax)
movups (%rbp), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%rbp), %rcx
movq %rcx, 0x38(%rax)
incq %r13
movq 0x20(%r15), %rdi
callq 0x322b
addq $0x4, %r12
cmpq %rax, %r13
jb 0x5a11
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, %ebp
movl %ebp, (%rax)
movl $0x7, 0x8(%rax)
movq 0x30(%rsp), %rcx
movups (%rcx), %xmm0
movups %xmm0, 0x10(%rax)
movq 0x10(%rcx), %rcx
movq %rcx, 0x20(%rax)
movq %rbx, %rdi
movq %r14, %rsi
callq 0x5d8f
movl $0x4, %ecx
movl %ecx, 0x18(%rsp)
movq $0x0, 0x20(%rsp)
movl %eax, 0x28(%rsp)
movl %ecx, 0x2c(%rsp)
movq (%rbx), %rdi
callq 0x31d0
movl %ebp, (%rax)
movl $0x2, 0x8(%rax)
movups 0x18(%rsp), %xmm0
movups %xmm0, 0x10(%rax)
movq 0x28(%rsp), %rcx
movq %rcx, 0x20(%rax)
movl $0x3, 0x28(%rax)
movl $0x0, 0x30(%rax)
jmp 0x5bf3
movl $0x1, 0x18(%rsp)
movl 0x18(%r15), %eax
movl %eax, 0x20(%rsp)
jmp 0x5bf3
movq 0x20(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %r12
movq 0x28(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %r15
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, %ebp
movl %ebp, (%rax)
movl $0x2, 0x8(%rax)
movl $0x3, %ecx
movl %ecx, 0x10(%rax)
xorl %r13d, %r13d
movl %r13d, 0x18(%rax)
movups (%r12), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r12), %rcx
movq %rcx, 0x38(%rax)
movq (%rbx), %rdi
callq 0x31d0
movl %ebp, (%rax)
movl $0x4, %r12d
movl %r12d, 0x8(%rax)
movl $0x3, %ecx
movl %ecx, 0x10(%rax)
movl %r13d, 0x18(%rax)
xorl %r13d, %r13d
movups (%r15), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r15), %rcx
movq %rcx, 0x38(%rax)
movq %rbx, %rdi
movq %r14, %rsi
callq 0x5d8f
movl %r12d, 0x18(%rsp)
movq $0x0, 0x20(%rsp)
movl %eax, 0x28(%rsp)
movl %r12d, 0x2c(%rsp)
movq (%rbx), %rdi
callq 0x31d0
movl %ebp, (%rax)
movl %r12d, 0x8(%rax)
movups 0x18(%rsp), %xmm0
movups %xmm0, 0x10(%rax)
movq 0x28(%rsp), %rcx
movq %rcx, 0x20(%rax)
movl $0x3, %ecx
movl %ecx, 0x28(%rax)
movl %r13d, 0x30(%rax)
movq 0x28(%rsp), %rax
movq %rax, 0x10(%rsp)
movups 0x18(%rsp), %xmm0
movups %xmm0, (%rsp)
jmp 0x5c5d
movq 0x18(%r15), %rsi
xorl %r12d, %r12d
movq %rbx, %rdi
xorl %edx, %edx
callq 0x5cfa
movq %rax, %r15
movq (%rbx), %rdi
callq 0x31d0
movl $0x6, %ecx
movl %ecx, (%rax)
movl %ecx, 0x8(%rax)
movq %r12, 0x20(%rax)
movups 0x1daf(%rip), %xmm0     # 0x79e8
movups %xmm0, 0x10(%rax)
movups (%r15), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r15), %rcx
movq %rcx, 0x38(%rax)
movups 0x1d94(%rip), %xmm0     # 0x79e8
movups %xmm0, (%rsp)
movq %r12, 0x10(%rsp)
movq %rbx, %rdi
movq %r14, %rsi
xorl %edx, %edx
callq 0x55ae
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x20d4(%rip), %rdi      # 0x7d54
leaq 0x1e24(%rip), %rsi      # 0x7aab
leaq 0x20f0(%rip), %rcx      # 0x7d7e
movl $0x2ba, %edx            # imm = 0x2BA
callq 0x10d0
leaq 0x1b8f(%rip), %rdi      # 0x782e
leaq 0x1e05(%rip), %rsi      # 0x7aab
leaq 0x20d1(%rip), %rcx      # 0x7d7e
movl $0x36e, %edx            # imm = 0x36E
callq 0x10d0
leaq 0x22bd(%rip), %rdi      # 0x7f7b
leaq 0x1de6(%rip), %rsi      # 0x7aab
leaq 0x20b2(%rip), %rcx      # 0x7d7e
movl $0x33c, %edx            # imm = 0x33C
callq 0x10d0
leaq 0x229e(%rip), %rdi      # 0x7f7b
leaq 0x1dc7(%rip), %rsi      # 0x7aab
leaq 0x2093(%rip), %rcx      # 0x7d7e
movl $0x35a, %edx            # imm = 0x35A
callq 0x10d0
callq 0x1427