// File: /workspace/github_c_makefile_star2_O2/atomlang[P]atomlang/src/pk_compiler.c
// Function: compileFunction
static int compileFunction(Compiler* compiler, FuncType fn_type) {

  const char* name;
  int name_length;

  if (fn_type != FN_LITERAL) {
    consume(compiler, TK_NAME, "Expected a function name.");
    name = compiler->previous.start;
    name_length = compiler->previous.length;
    NameSearchResult result = compilerSearchName(compiler, name, name_length);
    if (result.type != NAME_NOT_DEFINED) {
      parseError(compiler, "Name '%.*s' already exists.", name_length, name);
    }

  } else {
    name = LITERAL_FN_NAME;
    name_length = (int)strlen(name);
  }

  Function* func = newFunction(compiler->vm, name, name_length,
                               compiler->script, fn_type == FN_NATIVE, NULL);
  int fn_index = (int)compiler->script->functions.count - 1;
  if (fn_index == MAX_FUNCTIONS) {
    parseError(compiler, "A script should contain at most %d functions.",
               MAX_FUNCTIONS);
  }

  Func curr_fn;
  compilerPushFunc(compiler, &curr_fn, func, fn_index);

  int argc = 0;
  compilerEnterBlock(compiler); // Parameter depth.

  // Parameter list is optional.
  if (match(compiler, TK_LPARAN) && !match(compiler, TK_RPARAN)) {
    do {
      skipNewLines(compiler);

      consume(compiler, TK_NAME, "Expected a parameter name.");
      argc++;

      const char* param_name = compiler->previous.start;
      uint32_t param_len = compiler->previous.length;

      // TODO: move this to a functions.
      bool predefined = false;
      for (int i = compiler->local_count - 1; i >= 0; i--) {
        Local* local = &compiler->locals[i];
        if (compiler->scope_depth != local->depth) break;
        if (local->length == param_len &&
            strncmp(local->name, param_name, param_len) == 0) {
          predefined = true;
          break;
        }
      }
      if (predefined) {
        parseError(compiler, "Multiple definition of a parameter.");
      }

      compilerAddVariable(compiler, param_name, param_len,
                          compiler->previous.line);

    } while (match(compiler, TK_COMMA));

    consume(compiler, TK_RPARAN, "Expected ')' after parameter list.");
  }

  func->arity = argc;
  compilerChangeStack(compiler, argc);

  if (fn_type != FN_NATIVE) {
    compileBlockBody(compiler, BLOCK_FUNC);

    // Tail call optimization disabled at debug mode.
    if (compiler->options && !compiler->options->debug) {
      if (compiler->is_last_call) {
        ASSERT(_FN->opcodes.count >= 3, OOPS); // OP_CALL, argc, OP_POP
        ASSERT(_FN->opcodes.data[_FN->opcodes.count - 1] == OP_POP, OOPS);
        ASSERT(_FN->opcodes.data[_FN->opcodes.count - 3] == OP_CALL, OOPS);
        _FN->opcodes.data[_FN->opcodes.count - 3] = OP_TAIL_CALL;
      }
    }

    consume(compiler, TK_END, "Expected 'end' after function definition end.");
    compilerExitBlock(compiler); // Parameter depth.
    emitFunctionEnd(compiler);

  } else {
    compilerExitBlock(compiler); // Parameter depth.
  }

#if DEBUG_DUMP_COMPILED_CODE
  pkByteBuffer buff;
  pkByteBufferInit(&buff);
  dumpFunctionCode(compiler->vm, compiler->func->ptr, &buff);
  printf("%s", buff.data);
  pkByteBufferClear(&buff, compiler->vm);
#endif

  compilerPopFunc(compiler);

  return fn_index;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movl %esi, %r12d
movq %rdi, %rbx
cmpl $0x2, %esi
jne 0xc853
leaq 0xc6c3(%rip), %r14      # 0x18f11
pushq $0xc
popq %rbp
jmp 0xc893
leaq 0xc681(%rip), %rdx      # 0x18edb
pushq $0x47
popq %rsi
movq %rbx, %rdi
callq 0xc4fb
movq 0x38(%rbx), %r14
movl 0x40(%rbx), %ebp
movq %rbx, %rdi
movq %r14, %rsi
movl %ebp, %edx
callq 0xcb5c
testl %eax, %eax
je 0xc893
leaq 0xc671(%rip), %rsi      # 0x18ef5
movq %rbx, %rdi
movl %ebp, %edx
movq %r14, %rcx
xorl %eax, %eax
callq 0xc455
movq (%rbx), %rdi
movq 0x18b0(%rbx), %rcx
xorl %r8d, %r8d
testl %r12d, %r12d
sete %r8b
xorl %r15d, %r15d
movq %r14, %rsi
movl %ebp, %edx
xorl %r9d, %r9d
callq 0x37b0
movq %rax, %r14
movq 0x18b0(%rbx), %rax
movl 0x48(%rax), %ebp
decl %ebp
cmpl $0x100, %ebp            # imm = 0x100
jne 0xc8e4
leaq 0xc649(%rip), %rsi      # 0x18f1e
movq %rbx, %rdi
movl $0x100, %edx            # imm = 0x100
xorl %eax, %eax
callq 0xc455
movq 0x18c0(%rbx), %rax
leaq 0x18(%rsp), %rcx
movq %rax, 0x18(%rcx)
movq %r14, 0x8(%rcx)
movl 0xa0(%rbx), %eax
movl %eax, (%rcx)
movl %ebp, 0x10(%rcx)
movq %rcx, 0x18c0(%rbx)
incl %eax
movl %eax, 0xa0(%rbx)
pushq $0x9
popq %rsi
movq %rbx, %rdi
callq 0xc438
testb %al, %al
je 0xca0f
pushq $0xa
popq %rsi
movq %rbx, %rdi
callq 0xc438
testb %al, %al
jne 0xca0f
movq %r14, 0x8(%rsp)
movl %r12d, (%rsp)
movl %ebp, 0x4(%rsp)
leaq 0x9c(%rbx), %rax
movq %rax, 0x10(%rsp)
xorl %r15d, %r15d
movq %rbx, %rdi
callq 0xd399
movq %rbx, %rdi
pushq $0x47
popq %rsi
leaq 0xc5e3(%rip), %rdx      # 0x18f4c
callq 0xc4fb
incl %r15d
movq 0x38(%rbx), %rbp
movl 0x40(%rbx), %r13d
movl 0x18a8(%rbx), %r12d
imulq $0x18, %r12, %r14
addq 0x10(%rsp), %r14
testl %r12d, %r12d
jle 0xc9cc
movl 0xa0(%rbx), %eax
cmpl (%r14), %eax
jne 0xc9cc
cmpl %r13d, -0x4(%r14)
jne 0xc9b2
movq -0xc(%r14), %rdi
movq %rbp, %rsi
movq %r13, %rdx
callq 0x2090
testl %eax, %eax
je 0xc9bb
decl %r12d
addq $-0x18, %r14
jmp 0xc989
movq %rbx, %rdi
leaq 0xc5a2(%rip), %rsi      # 0x18f67
xorl %eax, %eax
callq 0xc455
movl 0x44(%rbx), %ecx
movq %rbx, %rdi
movq %rbp, %rsi
movl %r13d, %edx
callq 0xcc88
movq %rbx, %rdi
pushq $0x5
popq %rsi
callq 0xc438
testb %al, %al
jne 0xc954
leaq 0xc594(%rip), %rdx      # 0x18f8b
pushq $0xa
popq %rsi
movq %rbx, %rdi
callq 0xc4fb
movl 0x4(%rsp), %ebp
movl (%rsp), %r12d
movq 0x8(%rsp), %r14
movl %r15d, 0x20(%r14)
movq %rbx, %rdi
movl %r15d, %esi
callq 0xc7c9
movq %rbx, %rdi
testl %r12d, %r12d
je 0xcaa3
xorl %esi, %esi
callq 0xcd27
movq 0x98(%rbx), %rax
testq %rax, %rax
je 0xca7f
cmpb $0x0, (%rax)
jne 0xca7f
cmpb $0x1, 0x38ce(%rbx)
jne 0xca7f
movq 0x18c0(%rbx), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rcx
movl 0x8(%rcx), %eax
cmpl $0x2, %eax
jbe 0xcacb
movq (%rcx), %rcx
leal -0x1(%rax), %edx
cmpb $0x25, (%rcx,%rdx)
jne 0xcaf9
addl $-0x3, %eax
cmpb $0x27, (%rcx,%rax)
jne 0xcb27
movb $0x28, (%rcx,%rax)
leaq 0xc538(%rip), %rdx      # 0x18fbe
pushq $0x35
popq %rsi
movq %rbx, %rdi
callq 0xc4fb
movq %rbx, %rdi
callq 0xcdbf
movq %rbx, %rdi
callq 0xc55c
jmp 0xcaa8
callq 0xcdbf
movq 0x18c0(%rbx), %rax
movq 0x18(%rax), %rax
movq %rax, 0x18c0(%rbx)
movl %ebp, %eax
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x16526(%rip), %rax     # 0x22ff8
movq (%rax), %rdi
leaq 0xb614(%rip), %rsi      # 0x180f0
leaq 0xb721(%rip), %rdx      # 0x18204
leaq 0xc4c4(%rip), %rcx      # 0x18fae
leaq 0xc372(%rip), %r8       # 0x18e63
movl $0x837, %r9d            # imm = 0x837
jmp 0xcb53
movq 0x164f8(%rip), %rax     # 0x22ff8
movq (%rax), %rdi
leaq 0xb5e6(%rip), %rsi      # 0x180f0
leaq 0xb6f3(%rip), %rdx      # 0x18204
leaq 0xc496(%rip), %rcx      # 0x18fae
leaq 0xc344(%rip), %r8       # 0x18e63
movl $0x838, %r9d            # imm = 0x838
jmp 0xcb53
movq 0x164ca(%rip), %rax     # 0x22ff8
movq (%rax), %rdi
leaq 0xb5b8(%rip), %rsi      # 0x180f0
leaq 0xb6c5(%rip), %rdx      # 0x18204
leaq 0xc468(%rip), %rcx      # 0x18fae
leaq 0xc316(%rip), %r8       # 0x18e63
movl $0x839, %r9d            # imm = 0x839
xorl %eax, %eax
callq 0x2250
ud2