// File: /workspace/github_c_makefile_star2_O2/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 0x5340
movq %rsi, %r15
movq %rdi, %rbx
leaq 0x1b56(%rip), %rcx      # 0x69d0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl 0x10(%r15), %edx
leal -0x2(%rdx), %eax
cmpl $0x4, %eax
ja 0x539d
movq 0x8(%r15), %r14
leaq 0x1b42(%rip), %rcx      # 0x69e0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x20(%r15), %rsi
movl 0x18(%r15), %edx
movq %rbx, %rdi
callq 0x53d4
movq 0x10(%rax), %rcx
movq %rcx, 0x10(%rsp)
movups (%rax), %xmm0
jmp 0x5301
movq 0x8(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x53d4
movq %rax, %r14
movq (%rbx), %rdi
callq 0x2982
pushq $0x6
popq %rbp
movl %ebp, (%rax)
movl $0x2, 0x8(%rax)
pushq $0x3
popq %r12
movl %r12d, 0x10(%rax)
andl $0x0, 0x18(%rax)
movups (%r14), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r14), %rcx
movq %rcx, 0x38(%rax)
movq (%rbx), %rdi
callq 0x2982
movl %ebp, (%rax)
movl $0x8, 0x8(%rax)
movl %r12d, 0x10(%rax)
andl $0x0, 0x18(%rax)
movl $0x1, 0x28(%rax)
andl $0x0, 0x30(%rax)
movq 0x30(%rbx), %rdi
movq 0x18(%r15), %rax
movq (%rax), %rsi
callq 0x57f9
testq %rax, %rax
je 0x535f
movq (%rax), %r14
movq (%rbx), %rdi
callq 0x2982
movl $0x6, (%rax)
movl $0xa, 0x8(%rax)
andl $0x0, 0x10(%rax)
movq %r14, 0x18(%rax)
movq 0x30(%rbx), %rdi
movq 0x10(%r15), %rax
movq (%rax), %rsi
callq 0x57f9
testq %rax, %rax
jne 0x4fb3
leaq 0x2009(%rip), %rdi      # 0x6f8b
leaq 0x1b32(%rip), %rsi      # 0x6abb
leaq 0x1dfe(%rip), %rcx      # 0x6d8e
movl $0x349, %edx            # imm = 0x349
callq 0x10d0
movq 0x30(%rbx), %rdi
movq 0x8(%r15), %rax
movq (%rax), %rsi
callq 0x57f9
testq %rax, %rax
je 0x537e
movq (%rax), %r14
movq (%rbx), %rdi
callq 0x2982
movl $0x6, (%rax)
movl $0x9, 0x8(%rax)
andl $0x0, 0x10(%rax)
movq %r14, 0x18(%rax)
jmp 0x5312
movq 0x8(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x53d4
movq %rax, %r14
movq (%rbx), %rdi
callq 0x2982
pushq $0x6
popq %rbp
movl %ebp, (%rax)
pushq $0x2
popq %r15
movl %r15d, 0x8(%rax)
pushq $0x3
popq %r12
movl %r12d, 0x10(%rax)
andl $0x0, 0x18(%rax)
movups (%r14), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r14), %rcx
movq %rcx, 0x38(%rax)
movq (%rbx), %rdi
callq 0x2982
movl %ebp, (%rax)
movl %r15d, 0x8(%rax)
movl %r12d, 0x10(%rax)
movl $0x4, 0x18(%rax)
movl %r12d, 0x28(%rax)
pushq $0x5
popq %r14
movl %r14d, 0x30(%rax)
movq (%rbx), %rdi
callq 0x2982
movl %ebp, (%rax)
movl $0x1, 0x8(%rax)
movl %r12d, 0x10(%rax)
movl %r14d, 0x18(%rax)
movq (%rbx), %rdi
callq 0x2982
movl %ebp, (%rax)
movl $0xb, 0x8(%rax)
jmp 0x5312
movq 0x18(%r15), %rbp
xorl %r13d, %r13d
movq %rbx, %rdi
movq %rbp, %rsi
xorl %edx, %edx
callq 0x53d4
movq %r14, %r12
movq %rax, %r14
movq 0x5f49(%rip), %rax      # 0xafd8
movq (%rax), %rdi
leaq 0x1cbb(%rip), %rsi      # 0x6d54
movq %rbp, %rdx
xorl %eax, %eax
callq 0x1120
movq 0x5f2e(%rip), %rax      # 0xafd8
movq (%rax), %rdi
movq %r14, 0x30(%rsp)
movq %r14, %rsi
movq %r12, %r14
callq 0x4b2f
movq 0x5f14(%rip), %rax      # 0xafd8
movq (%rax), %rsi
pushq $0xa
popq %rdi
callq 0x10f0
leaq 0x1dba(%rip), %r12      # 0x6e90
movq 0x20(%r15), %rdi
callq 0x29db
cmpq %rax, %r13
jae 0x527c
cmpq $0x4, %r13
je 0x5321
movq 0x20(%r15), %rdi
movq %r13, %rsi
callq 0x29c6
movq (%rax), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x53d4
movq %rax, %rbp
movq (%rbx), %rdi
callq 0x2982
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
addq $0x4, %r12
jmp 0x50d6
movl $0x1, 0x18(%rsp)
movl 0x18(%r15), %eax
movl %eax, 0x20(%rsp)
jmp 0x52f2
movq 0x20(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x53d4
movq %rax, %r12
movq 0x28(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x53d4
movq %rax, %r15
movq (%rbx), %rdi
callq 0x2982
pushq $0x6
popq %r13
movl %r13d, (%rax)
movl $0x2, 0x8(%rax)
pushq $0x3
popq %rbp
movl %ebp, 0x10(%rax)
andl $0x0, 0x18(%rax)
movups (%r12), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r12), %rcx
movq %rcx, 0x38(%rax)
movq (%rbx), %rdi
callq 0x2982
movl %r13d, (%rax)
pushq $0x4
popq %r12
movl %r12d, 0x8(%rax)
movl %ebp, 0x10(%rax)
andl $0x0, 0x18(%rax)
movups (%r15), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r15), %rcx
movq %rcx, 0x38(%rax)
movq %rbx, %rdi
movq %r14, %rsi
callq 0x5469
movl %r12d, 0x18(%rsp)
andq $0x0, 0x20(%rsp)
movl %eax, 0x28(%rsp)
movl %r12d, 0x2c(%rsp)
movq (%rbx), %rdi
callq 0x2982
movl %r13d, (%rax)
movl %r12d, 0x8(%rax)
movups 0x18(%rsp), %xmm0
movups %xmm0, 0x10(%rax)
movq 0x28(%rsp), %rcx
movq %rcx, 0x20(%rax)
movl %ebp, 0x28(%rax)
jmp 0x52ee
movq 0x18(%r15), %rsi
movq %rbx, %rdi
xorl %edx, %edx
callq 0x53d4
movq %rax, %r15
movq (%rbx), %rdi
callq 0x2982
pushq $0x6
popq %rcx
movl %ecx, (%rax)
movl %ecx, 0x8(%rax)
movups 0x17ab(%rip), %xmm0     # 0x69f8
movups %xmm0, 0x10(%rax)
andq $0x0, 0x20(%rax)
movups (%r15), %xmm0
movups %xmm0, 0x28(%rax)
movq 0x10(%r15), %rcx
movq %rcx, 0x38(%rax)
movups 0x178b(%rip), %xmm0     # 0x69f8
movups %xmm0, (%rsp)
andq $0x0, 0x10(%rsp)
jmp 0x5305
movq (%rbx), %rdi
callq 0x2982
pushq $0x6
popq %rbp
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 0x5469
pushq $0x4
popq %rcx
movl %ecx, 0x18(%rsp)
andq $0x0, 0x20(%rsp)
movl %eax, 0x28(%rsp)
movl %ecx, 0x2c(%rsp)
movq (%rbx), %rdi
callq 0x2982
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)
andl $0x0, 0x30(%rax)
movq 0x28(%rsp), %rax
movq %rax, 0x10(%rsp)
movups 0x18(%rsp), %xmm0
movups %xmm0, (%rsp)
movq %rbx, %rdi
movq %r14, %rsi
xorl %edx, %edx
callq 0x4c98
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x1a3c(%rip), %rdi      # 0x6d64
leaq 0x178c(%rip), %rsi      # 0x6abb
leaq 0x1a58(%rip), %rcx      # 0x6d8e
movl $0x2ba, %edx            # imm = 0x2BA
callq 0x10d0
leaq 0x14f7(%rip), %rdi      # 0x683e
leaq 0x176d(%rip), %rsi      # 0x6abb
leaq 0x1a39(%rip), %rcx      # 0x6d8e
movl $0x36e, %edx            # imm = 0x36E
callq 0x10d0
leaq 0x1c25(%rip), %rdi      # 0x6f8b
leaq 0x174e(%rip), %rsi      # 0x6abb
leaq 0x1a1a(%rip), %rcx      # 0x6d8e
movl $0x33c, %edx            # imm = 0x33C
callq 0x10d0
leaq 0x1c06(%rip), %rdi      # 0x6f8b
leaq 0x172f(%rip), %rsi      # 0x6abb
leaq 0x19fb(%rip), %rcx      # 0x6d8e
movl $0x35a, %edx            # imm = 0x35A
callq 0x10d0
movq 0x5c54(%rip), %rax      # 0xaff8
movq (%rax), %rdi
leaq 0x17ab(%rip), %rsi      # 0x6b59
xorl %eax, %eax
callq 0x1120
leaq 0x1482(%rip), %rdi      # 0x683e
leaq 0x16f8(%rip), %rsi      # 0x6abb
leaq 0x19c4(%rip), %rcx      # 0x6d8e
movl $0x2ed, %edx            # imm = 0x2ED
callq 0x10d0