// File: /workspace/github_c_makefile_star2_O0/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
movq %rsp, %rbp
subq $0x110, %rsp            # imm = 0x110
movb %dil, %al
andb $0x1, %al
movb %al, -0x1(%rbp)
movq $0x0, -0x10(%rbp)
movq $0x0, -0x18(%rbp)
movq $0x0, -0x20(%rbp)
leaq 0x14cf6(%rip), %rdi     # 0x195a8
addq $0x50, %rdi
callq 0x5a10
movq %rax, -0x28(%rbp)
cmpq $0x0, -0x28(%rbp)
je 0x4911
movq -0x28(%rbp), %rdi
callq 0x7d20
movq %rax, -0x10(%rbp)
movq $0x0, -0x30(%rbp)
cmpq $0xb, -0x30(%rbp)
jae 0x490f
movq -0x28(%rbp), %rax
movq -0x30(%rbp), %rcx
cmpl $0x7, 0x10(%rax,%rcx,4)
jne 0x48ff
movq -0x10(%rbp), %rax
subq $0x1, %rax
movq %rax, -0x18(%rbp)
jmp 0x490f
jmp 0x4901
movq -0x30(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x30(%rbp)
jmp 0x48db
jmp 0x494d
leaq 0x14c90(%rip), %rdi     # 0x195a8
addq $0x50, %rdi
callq 0x5a80
movq %rax, -0x20(%rbp)
cmpq $0x0, -0x20(%rbp)
jne 0x493d
leaq 0x78ca(%rip), %rdi      # 0xc1fd
callq 0x57b0
jmp 0x4c98
movq -0x20(%rbp), %rax
movq 0x30(%rax), %rax
movq %rax, -0x10(%rbp)
movq %rax, -0x18(%rbp)
movl $0x0, -0x34(%rbp)
movq $0x0, -0x40(%rbp)
movq -0x40(%rbp), %rax
movq 0x14c21(%rip), %rcx     # 0x19588
cmpq 0x3000(%rcx), %rax
jae 0x49b6
movq 0x14c11(%rip), %rax     # 0x19588
imulq $0x30, -0x40(%rbp), %rcx
addq %rcx, %rax
testb $0x1, 0x2c(%rax)
je 0x49a6
movq 0x14bfc(%rip), %rax     # 0x19588
imulq $0x30, -0x40(%rbp), %rcx
addq %rcx, %rax
movzbl 0x2e(%rax), %edi
callq 0x5b30
movl -0x34(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x34(%rbp)
jmp 0x49a8
movq -0x40(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x40(%rbp)
jmp 0x495c
callq 0x5b60
movq %rax, -0x48(%rbp)
movq -0x48(%rbp), %rax
cmpq -0x18(%rbp), %rax
jbe 0x49da
leaq 0x7858(%rip), %rdi      # 0xc228
callq 0x57b0
jmp 0x4c98
movq -0x48(%rbp), %rax
cmpq -0x18(%rbp), %rax
jae 0x49f5
leaq 0x7850(%rip), %rdi      # 0xc23b
callq 0x57b0
jmp 0x4c98
jmp 0x49f7
cmpq $0x0, -0x28(%rbp)
je 0x4c47
cmpq $0x0, -0x10(%rbp)
jbe 0x4c45
movq -0x28(%rbp), %rax
cmpl $0x1, 0x10(%rax)
jne 0x4c37
movq -0x18(%rbp), %rdi
callq 0x4120
movq %rax, -0x50(%rbp)
movq -0x18(%rbp), %rax
subl $0x1, %eax
movl %eax, -0x54(%rbp)
cmpl $0x0, -0x54(%rbp)
jl 0x4aa4
callq 0x5bc0
movl %eax, -0x58(%rbp)
cmpl $0x0, -0x58(%rbp)
jge 0x4a78
callq 0x5c30
movl $0x0, -0x5c(%rbp)
movl -0x5c(%rbp), %eax
cmpl -0x54(%rbp), %eax
jge 0x4a76
movq -0x50(%rbp), %rax
movslq -0x5c(%rbp), %rcx
movzbl (%rax,%rcx), %edi
callq 0x5c50
movl -0x5c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x5c(%rbp)
jmp 0x4a52
jmp 0x4aa4
movl -0x58(%rbp), %eax
movb %al, %dl
movq -0x50(%rbp), %rax
movslq -0x54(%rbp), %rcx
movb %dl, (%rax,%rcx)
movq -0x50(%rbp), %rax
movslq -0x54(%rbp), %rcx
movzbl (%rax,%rcx), %edi
callq 0x5cb0
movl -0x54(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0x54(%rbp)
jmp 0x4a32
movb $0x0, -0x5d(%rbp)
movq $0x0, -0x68(%rbp)
movq $0x0, -0x70(%rbp)
movq -0x70(%rbp), %rax
cmpq -0x10(%rbp), %rax
jae 0x4bbf
movq -0x28(%rbp), %rax
movq -0x70(%rbp), %rcx
movl 0x14(%rax,%rcx,4), %eax
movl %eax, -0x74(%rbp)
movl -0x74(%rbp), %eax
movl %eax, -0x110(%rbp)
subl $0x2, %eax
je 0x4b12
jmp 0x4ae5
movl -0x110(%rbp), %eax
addl $-0x5, %eax
subl $0x2, %eax
jb 0x4b12
jmp 0x4af5
movl -0x110(%rbp), %eax
subl $0x7, %eax
je 0x4b3b
jmp 0x4b02
movl -0x110(%rbp), %eax
addl $-0xa, %eax
subl $0x1, %eax
ja 0x4b83
jmp 0x4b12
movl $0x48, %edi
callq 0x5ce0
movq -0x50(%rbp), %rax
movq -0x68(%rbp), %rcx
movzbl (%rax,%rcx), %edi
callq 0x5d80
movq -0x68(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x68(%rbp)
jmp 0x4bac
testb $0x1, -0x5d(%rbp)
je 0x4b4f
leaq 0x7708(%rip), %rdi      # 0xc250
callq 0x57b0
jmp 0x4bac
movb $0x1, -0x5d(%rbp)
callq 0x5bc0
movl %eax, -0x78(%rbp)
cmpl $0x0, -0x78(%rbp)
jge 0x4b68
callq 0x5c30
jmp 0x4bac
movl -0x78(%rbp), %eax
movb %al, -0x5e(%rbp)
movl $0x4a, %edi
callq 0x5ce0
movzbl -0x5e(%rbp), %edi
callq 0x5dc0
jmp 0x4bac
leaq -0x100(%rbp), %rdi
movl -0x74(%rbp), %ecx
movl $0x80, %esi
leaq 0x76d4(%rip), %rdx      # 0xc26d
movb $0x0, %al
callq 0x40a0
leaq -0x100(%rbp), %rdi
callq 0x57b0
jmp 0x4bae
movq -0x70(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x70(%rbp)
jmp 0x4ab8
movq $0x0, -0x108(%rbp)
movq -0x108(%rbp), %rax
cmpq -0x18(%rbp), %rax
jae 0x4bff
movq -0x50(%rbp), %rax
movq -0x108(%rbp), %rcx
movzbl (%rax,%rcx), %edi
callq 0x5c50
movq -0x108(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x108(%rbp)
jmp 0x4bca
movq -0x28(%rbp), %rax
movzwl (%rax), %edi
callq 0x5ce0
testb $0x1, -0x5d(%rbp)
je 0x4c25
movzbl -0x5e(%rbp), %edi
callq 0x5b30
movzbl -0x5e(%rbp), %edi
callq 0x5c50
jmp 0x4c2c
xorl %edi, %edi
callq 0x5e00
movq -0x50(%rbp), %rdi
callq 0x4030
jmp 0x4c43
movq -0x28(%rbp), %rax
movzwl (%rax), %edi
callq 0x5ce0
jmp 0x4c45
jmp 0x4c54
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdi
callq 0x5e50
movl -0x34(%rbp), %eax
subl $0x1, %eax
movl %eax, -0x10c(%rbp)
cmpl $0x0, -0x10c(%rbp)
jl 0x4c98
movq 0x14918(%rip), %rax     # 0x19588
movslq -0x10c(%rbp), %rcx
imulq $0x30, %rcx, %rcx
addq %rcx, %rax
movzbl 0x2e(%rax), %edi
callq 0x5cb0
movl -0x10c(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0x10c(%rbp)
jmp 0x4c60
addq $0x110, %rsp            # imm = 0x110
popq %rbp
retq
nopw %cs:(%rax,%rax)