// File: /workspace/github_c_makefile_star2_O3/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), %rsi     # 0x17600
movq 0x12d8b(%rip), %rax     # 0x17608
cmpq $0x3f, %rax
movl $0x3f, %ebx
cmovbq %rax, %rbx
leaq 0x30(%rsp), %r14
movq %r14, %rdi
movq %rbx, %rdx
callq 0x4050
movb $0x0, 0x30(%rsp,%rbx)
movq %r14, %rdi
callq 0x76c0
movq %rax, %rbx
testq %rax, %rax
je 0x48d9
movq %rbx, %rdi
callq 0x7727
movl $0x4, %ecx
xorl %r12d, %r12d
cmpl $0x7, (%rbx,%rcx,4)
je 0x4903
incq %rcx
cmpq $0xf, %rcx
jne 0x48bf
movq $0x0, 0x10(%rsp)
jmp 0x4911
movq 0x12d20(%rip), %rdi     # 0x17600
movq 0x12d21(%rip), %rsi     # 0x17608
callq 0x60a3
testq %rax, %rax
je 0x4a77
movq %rax, %r12
movq 0x30(%rax), %rax
movq %rax, 0x10(%rsp)
jmp 0x4911
movq %rax, %rcx
decq %rcx
movq %rcx, 0x10(%rsp)
xorl %r12d, %r12d
movq %rax, 0x28(%rsp)
movq 0x12c6b(%rip), %rax     # 0x17588
cmpq $0x0, 0x3000(%rax)
je 0x495e
xorl %r15d, %r15d
movl $0x2e, %ebp
xorl %r13d, %r13d
cmpb $0x1, -0x2(%rax,%rbp)
jne 0x494c
movzbl (%rax,%rbp), %edi
callq 0x6104
incl %r13d
movq 0x12c3c(%rip), %rax     # 0x17588
incq %r15
addq $0x30, %rbp
cmpq 0x3000(%rax), %r15
jb 0x4932
jmp 0x4961
xorl %r13d, %r13d
cmpl $0x1, 0x12c40(%rip)     # 0x175a8
jne 0x496f
xorl %r15d, %r15d
jmp 0x4999
movl $0x1, %r15d
movl $0x1, %edi
callq 0x5ff5
movl 0x12c23(%rip), %eax     # 0x175a8
cmpl $0x4, %eax
jne 0x4994
callq 0x5af3
incq %r15
jmp 0x4975
cmpl $0x1, %eax
jne 0x49a0
callq 0x5af3
jmp 0x49b3
leaq 0x12c01(%rip), %rdi     # 0x175a8
leaq 0x5923(%rip), %rsi      # 0xa2d1
callq 0x608e
cmpq 0x10(%rsp), %r15
jbe 0x49c6
leaq 0x58a7(%rip), %rsi      # 0xa268
jmp 0x4a7e
jae 0x49d4
leaq 0x58ac(%rip), %rsi      # 0xa27b
jmp 0x4a7e
testq %rbx, %rbx
je 0x4a9c
cmpq $0x0, 0x28(%rsp)
je 0x4d82
cmpl $0x1, 0x10(%rbx)
jne 0x4aea
movq 0x10(%rsp), %r14
movq %r14, %rdi
callq 0x4110
movq %rax, 0x18(%rsp)
testl %r14d, %r14d
jle 0x4b59
movq 0x10(%rsp), %rax
movl %eax, %r12d
andl $0x7fffffff, %r12d      # imm = 0x7FFFFFFF
leal -0x1(%rax), %ebp
leaq 0x12b81(%rip), %r15     # 0x175a8
leaq -0x1(%r12), %rcx
xorl %eax, %eax
cmpb $0x0, 0x7a(%rax,%r15)
jne 0x4a46
incq %rax
cmpq $0x100, %rax            # imm = 0x100
jne 0x4a2e
jmp 0x4af7
movb $0x0, 0x7a(%rax,%r15)
incl 0x12b46(%rip)           # 0x17598
movq %rcx, %r14
movq 0x18(%rsp), %rcx
movb %al, -0x1(%rcx,%r12)
movzbl %al, %edi
callq 0x6150
decl %ebp
cmpq $0x1, %r12
movq %r14, %r12
jg 0x4a27
jmp 0x4b59
leaq 0x57bf(%rip), %rsi      # 0xa23d
leaq 0x12b4b(%rip), %rdi     # 0x175d0
callq 0x608e
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 0x12aef(%rip), %rdi     # 0x175a0
movq 0x12b30(%rip), %rcx     # 0x175e8
movl $0x1, %esi
movq %rbx, %rdx
callq 0x4564
movw %bp, (%rbx)
movq 0x12ad1(%rip), %rdi     # 0x175a0
movq 0x12b12(%rip), %rcx     # 0x175e8
leaq 0x30(%rsp), %rdx
movl $0x2, %esi
callq 0x4564
jmp 0x4d82
movzwl (%rbx), %edi
callq 0x619c
jmp 0x4d82
leaq 0x12ad2(%rip), %rdi     # 0x175d0
leaq 0x57f7(%rip), %rsi      # 0xa2fc
callq 0x608e
cmpq $0x1, %r12
jle 0x4b59
movl %ebp, %ecx
xorl %ebp, %ebp
movq 0x18(%rsp), %rax
movzbl (%rax,%rbp), %r12d
cmpb $0x1, 0x7a(%r12,%r15)
jne 0x4b45
leaq 0x12aa3(%rip), %rdi     # 0x175d0
leaq 0x57e4(%rip), %rsi      # 0xa318
movq %r15, %r14
movq %rcx, %r15
callq 0x608e
movq %r15, %rcx
movq %r14, %r15
decl 0x12a4d(%rip)           # 0x17598
movb $0x1, 0x7a(%r12,%r15)
incq %rbp
cmpq %rbp, %rcx
jne 0x4b14
movq $0x0, 0x20(%rsp)
xorl %r12d, %r12d
xorl %ebp, %ebp
movq 0x28(%rsp), %r15
movl 0x14(%rbx,%r12,4), %ecx
cmpl $0xb, %ecx
ja 0x4bf0
movl $0xc64, %eax            # imm = 0xC64
btl %ecx, %eax
jae 0x4bd4
movb $0x48, 0x30(%rsp)
movq 0x12a14(%rip), %rdi     # 0x175a0
movq 0x12a55(%rip), %rcx     # 0x175e8
movl $0x1, %esi
leaq 0x30(%rsp), %r14
movq %r14, %rdx
callq 0x4564
movq 0x18(%rsp), %rax
movb (%rax,%rbp), %al
movb %al, 0x30(%rsp)
movq 0x129e8(%rip), %rdi     # 0x175a0
movq 0x12a29(%rip), %rcx     # 0x175e8
movl $0x1, %esi
movq %r14, %rdx
callq 0x4564
incq %rbp
jmp 0x4cbb
cmpl $0x7, %ecx
jne 0x4bf0
testb $0x1, 0x20(%rsp)
je 0x4c1f
leaq 0x129e9(%rip), %rdi     # 0x175d0
leaq 0x56a2(%rip), %rsi      # 0xa290
jmp 0x4c15
movl $0x80, %esi
leaq 0x30(%rsp), %r14
movq %r14, %rdi
leaq 0x56a9(%rip), %rdx      # 0xa2ad
xorl %eax, %eax
callq 0x40a0
leaq 0x129be(%rip), %rdi     # 0x175d0
movq %r14, %rsi
callq 0x608e
jmp 0x4cbb
xorl %r15d, %r15d
leaq 0x1297f(%rip), %rax     # 0x175a8
cmpb $0x0, 0x7a(%r15,%rax)
jne 0x4c59
incq %r15
cmpq $0x100, %r15            # imm = 0x100
jne 0x4c29
leaq 0x1298c(%rip), %rdi     # 0x175d0
leaq 0x56b1(%rip), %rsi      # 0xa2fc
callq 0x608e
movb $0x1, %al
movq %rax, 0x20(%rsp)
jmp 0x4cb6
incl 0x12939(%rip)           # 0x17598
movb $0x0, 0x7a(%r15,%rax)
movb $0x4a, 0x30(%rsp)
movq 0x1292f(%rip), %rdi     # 0x175a0
movq 0x12970(%rip), %rcx     # 0x175e8
movl $0x1, %esi
leaq 0x30(%rsp), %r14
movq %r14, %rdx
callq 0x4564
movb %r15b, 0x30(%rsp)
movq 0x1290a(%rip), %rdi     # 0x175a0
movq 0x1294b(%rip), %rcx     # 0x175e8
movl $0x1, %esi
movq %r14, %rdx
callq 0x4564
movb $0x1, %al
movq %rax, 0x20(%rsp)
movb %r15b, 0xf(%rsp)
movq 0x28(%rsp), %r15
incq %r12
cmpq %r15, %r12
jne 0x4b6c
cmpq $0x0, 0x10(%rsp)
leaq 0x128d4(%rip), %rcx     # 0x175a8
je 0x4d21
xorl %r15d, %r15d
leaq 0x128f0(%rip), %r14     # 0x175d0
leaq 0x5631(%rip), %rbp      # 0xa318
movq 0x18(%rsp), %rax
movzbl (%rax,%r15), %r12d
cmpb $0x1, 0x7a(%r12,%rcx)
jne 0x4d0b
movq %r14, %rdi
movq %rbp, %rsi
callq 0x608e
leaq 0x1289d(%rip), %rcx     # 0x175a8
decl 0x12887(%rip)           # 0x17598
movb $0x1, 0x7a(%r12,%rcx)
incq %r15
cmpq %r15, 0x10(%rsp)
jne 0x4ce7
movzwl (%rbx), %edi
callq 0x619c
testb $0x1, 0x20(%rsp)
je 0x4d71
movzbl 0xf(%rsp), %ebx
movl %ebx, %edi
callq 0x6104
leaq 0x12865(%rip), %rax     # 0x175a8
cmpb $0x1, 0x7a(%rbx,%rax)
jne 0x4d64
leaq 0x1287f(%rip), %rdi     # 0x175d0
leaq 0x55c0(%rip), %rsi      # 0xa318
callq 0x608e
leaq 0x12844(%rip), %rax     # 0x175a8
movb $0x1, 0x7a(%rbx,%rax)
decl 0x12829(%rip)           # 0x17598
jmp 0x4d78
xorl %edi, %edi
callq 0x61ea
movq 0x18(%rsp), %rdi
callq 0x4030
testl %r13d, %r13d
jle 0x4a8a
movl %r13d, %ebx
leaq (%rbx,%rbx,2), %r14
incq %rbx
shlq $0x4, %r14
addq $-0x2, %r14
movq 0x127e4(%rip), %rax     # 0x17588
movzbl (%rax,%r14), %edi
callq 0x6150
decq %rbx
addq $-0x30, %r14
cmpq $0x1, %rbx
ja 0x4d9d
jmp 0x4a8a