// File: /workspace/github_c_makefile_star2_O2/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
leaq 0x11062(%rip), %rdi     # 0x155f8
callq 0x543c
movq %rax, %rbx
testq %rax, %rax
je 0x45d1
movq %rbx, %rdi
callq 0x6420
movq %rax, %r14
pushq $0x4
popq %rax
xorl %r12d, %r12d
movq %r14, 0x18(%rsp)
cmpq $0xf, %rax
je 0x45f4
cmpl $0x7, (%rbx,%rax,4)
leaq 0x1(%rax), %rax
jne 0x45b9
decq %r14
xorl %r12d, %r12d
jmp 0x45f7
leaq 0x11020(%rip), %rdi     # 0x155f8
callq 0x5478
testq %rax, %rax
je 0x46ff
movq %rax, %r12
movq 0x30(%rax), %r14
movq %r14, 0x18(%rsp)
jmp 0x45f7
xorl %r14d, %r14d
xorl %r13d, %r13d
pushq $0x2e
popq %r15
movq 0x10f83(%rip), %rax     # 0x15588
xorl %ebp, %ebp
cmpq 0x3000(%rax), %rbp
jae 0x4635
cmpb $0x1, -0x2(%rax,%r15)
jne 0x462c
movzbl (%rax,%r15), %edi
callq 0x54ce
incl %r13d
movq 0x10f5c(%rip), %rax     # 0x15588
incq %rbp
addq $0x30, %r15
jmp 0x4607
cmpl $0x1, 0x10f6c(%rip)     # 0x155a8
jne 0x4643
xorl %r15d, %r15d
jmp 0x465c
xorl %r15d, %r15d
pushq $0x4
popq %rbp
callq 0x52f0
incq %r15
movl %ebp, %edi
callq 0x4fe3
testb %al, %al
jne 0x4649
leaq 0x3c4a(%rip), %rsi      # 0x82ad
pushq $0x1
popq %rdi
callq 0x52f5
cmpq %r14, %r15
jbe 0x467c
leaq 0x3bcd(%rip), %rdi      # 0x8244
jmp 0x4706
jae 0x4687
leaq 0x3bd2(%rip), %rdi      # 0x8257
jmp 0x4706
testq %rbx, %rbx
je 0x471c
cmpq $0x0, 0x18(%rsp)
je 0x4884
cmpl $0x1, 0x10(%rbx)
jne 0x4736
movq %r14, %rdi
callq 0x4110
movq %rax, %r12
movq %r14, 0x28(%rsp)
movslq %r14d, %r15
testq %r15, %r15
jle 0x4743
decq %r15
callq 0x54e1
movl %eax, %ebp
testl %eax, %eax
js 0x46e3
movb %bpl, (%r12,%r15)
movzbl %bpl, %edi
callq 0x5551
testl %ebp, %ebp
jns 0x46b9
jmp 0x4743
callq 0x5510
xorl %r14d, %r14d
cmpq %r15, %r14
jge 0x46dd
movzbl (%r12,%r14), %edi
callq 0x551c
incq %r14
jmp 0x46eb
leaq 0x3b13(%rip), %rdi      # 0x8219
addq $0xb8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x52e1
movswl 0x28(%r12), %ebx
pushq $0x29
popq %rdi
callq 0x5561
movl %ebx, %edi
callq 0x5633
jmp 0x4884
movzwl (%rbx), %edi
callq 0x5561
jmp 0x4884
movq $0x0, 0x10(%rsp)
movq $0x0, 0x20(%rsp)
xorl %ebp, %ebp
movq 0x18(%rsp), %r15
cmpq %rbp, %r15
je 0x4835
movl 0x14(%rbx,%rbp,4), %ecx
cmpl $0xb, %ecx
ja 0x478d
cmpl $0x7, %ecx
jne 0x4783
testb $0x1, 0x10(%rsp)
je 0x47d9
leaq 0x3aeb(%rip), %rdi      # 0x826c
jmp 0x47ab
movl $0xc64, %eax            # imm = 0xC64
btl %ecx, %eax
jb 0x47b5
movl $0x80, %esi
leaq 0x30(%rsp), %r14
movq %r14, %rdi
leaq 0x3ae8(%rip), %rdx      # 0x8289
xorl %eax, %eax
callq 0x40b0
movq %r14, %rdi
callq 0x52e1
incq %rbp
jmp 0x475c
pushq $0x48
popq %rdi
callq 0x5561
movq 0x20(%rsp), %r14
movzbl (%r12,%r14), %edi
callq 0x55a9
incq %r14
movq %r14, 0x20(%rsp)
incq %rbp
jmp 0x475c
callq 0x54e1
testl %eax, %eax
jns 0x47f6
incq %rbp
callq 0x5510
movb $0x1, %al
movq %rax, 0x10(%rsp)
jmp 0x475c
movl %eax, %r14d
pushq $0x4a
popq %rdi
callq 0x5561
movb %r14b, 0x30(%rsp)
movq 0x10d93(%rip), %rdi     # 0x155a0
movq 0x10dd4(%rip), %rcx     # 0x155e8
pushq $0x1
popq %rsi
leaq 0x30(%rsp), %rdx
callq 0x42a1
incq %rbp
movb $0x1, %al
movq %rax, 0x10(%rsp)
movb %r14b, 0xf(%rsp)
jmp 0x475c
xorl %r15d, %r15d
movq 0x28(%rsp), %r14
cmpq %r15, %r14
je 0x4851
movzbl (%r12,%r15), %edi
callq 0x551c
incq %r15
jmp 0x483d
movzwl (%rbx), %edi
callq 0x5561
testb $0x1, 0x10(%rsp)
je 0x4875
movzbl 0xf(%rsp), %ebx
movl %ebx, %edi
callq 0x54ce
movl %ebx, %edi
callq 0x551c
jmp 0x487c
xorl %edi, %edi
callq 0x55ca
movq %r12, %rdi
callq 0x4030
movl %r13d, %eax
imulq $0x30, %rax, %rbx
addq $-0x2, %rbx
testl %r13d, %r13d
jle 0x48ad
movq 0x10ced(%rip), %rax     # 0x15588
movzbl (%rax,%rbx), %edi
callq 0x5551
decl %r13d
addq $-0x30, %rbx
jmp 0x488f
addq $0xb8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq