// File: /workspace/github_c_makefile_star2_O1/jtuu[P]quesler/src/compiler.c
// Function: call
static void call(bool can_assign) {
    UNUSED(can_assign);

    size_t arity = 0;
    size_t effective_arity = 0;
    Function* func = NULL;
    const OpcodeDefinition* opcode = get_opcode_definition_from_token(&parser.previous2);
    
    if (opcode) {
        arity = get_opcode_arity(opcode);

        for (size_t i = 0; i < OPCODE_MAX_ARITY; i++) {
            if (opcode->parameters[i] == T_BREG) {
                effective_arity = arity - 1;
                break;
            }
        }
    } else {
        func = resolve_function(&parser.previous2);

        if (!func) {
            error("Call target is not a function or an opcode");
            return;
        }

        effective_arity = arity = func->arity;
    }
    
    // Save locals
    int num_saved_locals = 0;
    for (size_t i = 0; i < current->local_count; i++) {
        if (current->locals[i].initialized) {
            emit_stack_push(current->locals[i].reg);
            num_saved_locals++;
        }
    }

    size_t arg_count = argument_list();

    if (arg_count > effective_arity) {
        error("Too many arguments");
        return;
    } else if (arg_count < effective_arity) {
        error("Not enough arguments");
        return;
    }

    if (opcode) {
        if (arity > 0) {
            if (opcode->parameters[0] == T_ARGS) {
                // Put args on the arg stack

                uint8_t* regs = malloc(effective_arity);

                // Take args from the stack and put them in registers
                for (int i = (int) effective_arity - 1; i >= 0; i--) {
                    int ret = alloc_register();

                    if (ret < 0) {
                        error_alloc_register();

                        for (int j = 0; j < i; j++) {
                            free_register(regs[j]);
                        }

                        break;
                    }

                    regs[i] = (uint8_t) ret;
                    emit_stack_pop(regs[i]);
                }

                bool has_output = false;
                uint8_t output_reg;
                size_t arg_reg_i = 0;

                // Put args into the arg stack from registers
                for (size_t i = 0; i < arity; i++) {
                    ParameterType param_type = opcode->parameters[i + 1];

                    switch (param_type) {
                        case T_REG:
                        case T_DWORD:
                        case T_FLOAT:
                        case T_FUNC:
                        case T_FUNC2:
                            emit_opcode(OP_ARG_PUSHR);
                            emit_register(regs[arg_reg_i]);
                            arg_reg_i++;
                            break;
                        case T_BREG:
                            // A register reference is likely to be the output argument
                            if (has_output) {
                                error("Opcode with multiple outputs");
                                break;
                            } else {
                                has_output = true;
                                
                                int ret = alloc_register();

                                if (ret < 0) {
                                    error_alloc_register();
                                    break;
                                }

                                output_reg = (uint8_t) ret;

                                emit_opcode(OP_ARG_PUSHB);
                                emit_byte(output_reg);
                            }
                            break;
                        default: {
                            char str[128];
                            snprintf(str, 128, "Unsupported opcode argument type %d", param_type);
                            error(str);
                            break;
                        }
                    }
                }

                for (size_t i = 0; i < effective_arity; i++) {
                    free_register(regs[i]);
                }

                emit_opcode(opcode->opcode);

                if (has_output) {
                    emit_stack_push(output_reg);
                    free_register(output_reg);
                } else {
                    emit_stack_push_value(0);
                }

                free(regs);
            } else {
                // TODO: Inline arguments need a completely different way of expression parsing
                emit_opcode(opcode->opcode);
            }
        }
    } else {
        emit_call(func->label);
    }

    // Restore locals
    for (int i = num_saved_locals - 1; i >= 0; i--) {
        emit_stack_pop(current->locals[i].reg);
    }
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xb8, %rsp
movq 0x12d8a(%rip), %rax     # 0x17608
cmpq $0x3f, %rax
movl $0x3f, %ebx
cmovbq %rax, %rbx
movq 0x12d6e(%rip), %rsi     # 0x17600
leaq 0x30(%rsp), %r14
movq %r14, %rdi
movq %rbx, %rdx
callq 0x4050
movb $0x0, 0x30(%rsp,%rbx)
movq %r14, %rdi
callq 0x77e0
movq %rax, %rbx
testq %rax, %rax
je 0x48df
movq %rbx, %rdi
callq 0x7847
movl $0x4, %ecx
xorl %r12d, %r12d
movq %rax, 0x8(%rsp)
cmpl $0x7, (%rbx,%rcx,4)
je 0x4902
incq %rcx
cmpq $0xf, %rcx
jne 0x48cc
xorl %ebp, %ebp
jmp 0x490b
leaq 0x12d12(%rip), %rdi     # 0x175f8
callq 0x611d
testq %rax, %rax
je 0x4cf3
movq %rax, %r12
movq 0x30(%rax), %rbp
movq %rbp, 0x8(%rsp)
jmp 0x490b
movq %rax, %rbp
decq %rbp
xorl %r12d, %r12d
movq 0x12c76(%rip), %rax     # 0x17588
cmpq $0x0, 0x3000(%rax)
je 0x4956
xorl %r14d, %r14d
movl $0x2e, %r13d
xorl %r15d, %r15d
cmpb $0x1, -0x2(%rax,%r13)
jne 0x493d
movzbl (%rax,%r13), %edi
callq 0x6180
incl %r15d
incq %r14
movq 0x12c41(%rip), %rax     # 0x17588
addq $0x30, %r13
cmpq 0x3000(%rax), %r14
jb 0x4928
jmp 0x4959
xorl %r15d, %r15d
cmpl $0x1, 0x12c48(%rip)     # 0x175a8
jne 0x4975
xorl %r14d, %r14d
cmpl $0x1, 0x12c3c(%rip)     # 0x175a8
jne 0x49a2
callq 0x5b04
jmp 0x49b5
movl $0x1, %edi
callq 0x606f
movl $0x1, %r14d
cmpl $0x4, 0x12c1c(%rip)     # 0x175a8
jne 0x4965
callq 0x5b04
movl $0x1, %edi
callq 0x606f
incq %r14
jmp 0x4985
leaq 0x12bff(%rip), %rdi     # 0x175a8
leaq 0x58fd(%rip), %rsi      # 0xa2ad
callq 0x6108
cmpq %rbp, %r14
jbe 0x49c6
leaq 0x5883(%rip), %rsi      # 0xa244
jmp 0x4cfa
jae 0x49d4
leaq 0x5888(%rip), %rsi      # 0xa257
jmp 0x4cfa
testq %rbx, %rbx
je 0x4d18
cmpq $0x0, 0x8(%rsp)
je 0x4d7e
cmpl $0x1, 0x10(%rbx)
jne 0x4d63
movq %rbx, 0x28(%rsp)
movq %rbp, %rdi
callq 0x4110
movq %rax, 0x10(%rsp)
movq %rbp, 0x20(%rsp)
movslq %ebp, %r12
leaq 0x12b94(%rip), %r14     # 0x175a8
testq %r12, %r12
jle 0x4ac3
movq %r12, %rbp
decq %r12
xorl %r13d, %r13d
cmpb $0x1, 0x7a(%r13,%r14)
je 0x4a42
incq %r13
cmpq $0x100, %r13            # imm = 0x100
jne 0x4a26
movl $0xffffffff, %r13d      # imm = 0xFFFFFFFF
jmp 0x4a4e
movb $0x0, 0x7a(%r13,%r14)
incl 0x12b4a(%rip)           # 0x17598
testl %r13d, %r13d
js 0x4a67
movq 0x10(%rsp), %rax
movb %r13b, (%rax,%r12)
movzbl %r13b, %edi
callq 0x61cc
jmp 0x4aba
leaq 0x12b62(%rip), %rdi     # 0x175d0
leaq 0x5863(%rip), %rsi      # 0xa2d8
callq 0x6108
cmpq $0x1, %rbp
je 0x4aba
xorl %ebp, %ebp
movq 0x10(%rsp), %rax
movzbl (%rax,%rbp), %ebx
cmpb $0x1, 0x7a(%rbx,%r14)
jne 0x4aa6
leaq 0x12b36(%rip), %rdi     # 0x175d0
leaq 0x5853(%rip), %rsi      # 0xa2f4
callq 0x6108
decl 0x12aec(%rip)           # 0x17598
movb $0x1, 0x7a(%rbx,%r14)
incq %rbp
cmpq %r12, %rbp
jl 0x4a82
testl %r13d, %r13d
jns 0x4a14
movq 0x8(%rsp), %rbp
cmpq $0x1, %rbp
adcq $0x0, %rbp
movq $0x0, 0x18(%rsp)
xorl %r13d, %r13d
movq $0x0, 0x8(%rsp)
movq 0x28(%rsp), %rax
movl 0x14(%rax,%r13,4), %ecx
cmpl $0xb, %ecx
ja 0x4b7c
movl $0xc64, %eax            # imm = 0xC64
btl %ecx, %eax
jae 0x4b60
movb $0x48, 0x30(%rsp)
movq 0x12a92(%rip), %rdi     # 0x175a0
movq 0x12ad3(%rip), %rcx     # 0x175e8
movl $0x1, %esi
leaq 0x30(%rsp), %r12
movq %r12, %rdx
callq 0x4574
movq 0x10(%rsp), %rax
movq 0x8(%rsp), %rbx
movb (%rax,%rbx), %al
movb %al, 0x30(%rsp)
movq 0x12a61(%rip), %rdi     # 0x175a0
movq 0x12aa2(%rip), %rcx     # 0x175e8
movl $0x1, %esi
movq %r12, %rdx
callq 0x4574
incq %rbx
movq %rbx, 0x8(%rsp)
jmp 0x4c4b
cmpl $0x7, %ecx
jne 0x4b7c
testb $0x1, 0x18(%rsp)
je 0x4bab
leaq 0x12a5d(%rip), %rdi     # 0x175d0
leaq 0x56f2(%rip), %rsi      # 0xa26c
jmp 0x4ba1
movl $0x80, %esi
leaq 0x30(%rsp), %r12
movq %r12, %rdi
leaq 0x56f9(%rip), %rdx      # 0xa289
xorl %eax, %eax
callq 0x40a0
leaq 0x12a32(%rip), %rdi     # 0x175d0
movq %r12, %rsi
callq 0x6108
jmp 0x4c4b
movq %rbp, %r12
xorl %ebp, %ebp
cmpb $0x1, 0x7a(%rbp,%r14)
je 0x4bcb
incq %rbp
cmpq $0x100, %rbp            # imm = 0x100
jne 0x4bb0
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
jmp 0x4bd7
movb $0x0, 0x7a(%rbp,%r14)
incl 0x129c1(%rip)           # 0x17598
testl %ebp, %ebp
js 0x4c2e
movb $0x4a, 0x30(%rsp)
movq 0x129b9(%rip), %rdi     # 0x175a0
movq 0x129fa(%rip), %rcx     # 0x175e8
movl $0x1, %esi
leaq 0x30(%rsp), %rbx
movq %rbx, %rdx
callq 0x4574
movb %bpl, 0x30(%rsp)
movq 0x12994(%rip), %rdi     # 0x175a0
movq 0x129d5(%rip), %rcx     # 0x175e8
movl $0x1, %esi
movq %rbx, %rdx
callq 0x4574
movb $0x1, %al
movq %rax, 0x18(%rsp)
movb %bpl, 0x7(%rsp)
jmp 0x4c48
leaq 0x1299b(%rip), %rdi     # 0x175d0
leaq 0x569c(%rip), %rsi      # 0xa2d8
callq 0x6108
movb $0x1, %al
movq %rax, 0x18(%rsp)
movq %r12, %rbp
incq %r13
cmpq %rbp, %r13
jne 0x4ae5
cmpq $0x0, 0x20(%rsp)
je 0x4ca3
xorl %r12d, %r12d
leaq 0x12967(%rip), %rbp     # 0x175d0
leaq 0x5684(%rip), %r13      # 0xa2f4
movq 0x10(%rsp), %rax
movzbl (%rax,%r12), %ebx
cmpb $0x1, 0x7a(%rbx,%r14)
jne 0x4c8d
movq %rbp, %rdi
movq %r13, %rsi
callq 0x6108
decl 0x12905(%rip)           # 0x17598
movb $0x1, 0x7a(%rbx,%r14)
incq %r12
cmpq %r12, 0x20(%rsp)
jne 0x4c70
movq 0x28(%rsp), %rax
movzwl (%rax), %edi
callq 0x6218
testb $0x1, 0x18(%rsp)
je 0x4d6d
movzbl 0x7(%rsp), %ebx
movl %ebx, %edi
callq 0x6180
cmpb $0x1, 0x7a(%rbx,%r14)
jne 0x4ce2
leaq 0x128fa(%rip), %rdi     # 0x175d0
leaq 0x5617(%rip), %rsi      # 0xa2f4
callq 0x6108
movb $0x1, 0x7a(%rbx,%r14)
decl 0x128aa(%rip)           # 0x17598
jmp 0x4d74
leaq 0x551f(%rip), %rsi      # 0xa219
leaq 0x128cf(%rip), %rdi     # 0x175d0
callq 0x6108
addq $0xb8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movzwl 0x28(%r12), %ebp
leaq 0x30(%rsp), %rbx
movb $0x29, (%rbx)
movq 0x12873(%rip), %rdi     # 0x175a0
movq 0x128b4(%rip), %rcx     # 0x175e8
movl $0x1, %esi
movq %rbx, %rdx
callq 0x4574
movw %bp, (%rbx)
movq 0x12855(%rip), %rdi     # 0x175a0
movq 0x12896(%rip), %rcx     # 0x175e8
leaq 0x30(%rsp), %rdx
movl $0x2, %esi
callq 0x4574
jmp 0x4d7e
movzwl (%rbx), %edi
callq 0x6218
jmp 0x4d7e
xorl %edi, %edi
callq 0x6266
movq 0x10(%rsp), %rdi
callq 0x4030
testl %r15d, %r15d
jle 0x4d06
movl %r15d, %ebx
leaq (%rbx,%rbx,2), %r14
incq %rbx
shlq $0x4, %r14
addq $-0x2, %r14
movq 0x127ec(%rip), %rax     # 0x17588
movzbl (%rax,%r14), %edi
callq 0x61cc
decq %rbx
addq $-0x30, %r14
cmpq $0x1, %rbx
jg 0x4d95
jmp 0x4d06