// File: /workspace/github_c_makefile_star2_O3/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 $0x48, %rsp
movl %esi, %r15d
movq %rdi, %r13
cmpl $0x2, %esi
jne 0x11070
leaq 0xbec8(%rip), %r14      # 0x1cf31
movl $0xc, %ebp
jmp 0x110b3
leaq 0xbe84(%rip), %rdx      # 0x1cefb
movq %r13, %rdi
movl $0x47, %esi
callq 0x10df5
movq 0x38(%r13), %r14
movl 0x40(%r13), %ebp
movq %r13, %rdi
movq %r14, %rsi
movl %ebp, %edx
callq 0x1132e
testl %eax, %eax
je 0x110b3
leaq 0xbe71(%rip), %rsi      # 0x1cf15
movq %r13, %rdi
movl %ebp, %edx
movq %r14, %rcx
xorl %eax, %eax
callq 0x10d4f
movq (%r13), %rdi
movq 0x18b0(%r13), %rcx
xorl %r8d, %r8d
testl %r15d, %r15d
sete %r8b
movq %r14, %rsi
movl %ebp, %edx
xorl %r9d, %r9d
callq 0x91e2
movq %rax, %r14
movq 0x18b0(%r13), %rax
movl 0x48(%rax), %ebp
decl %ebp
cmpl $0x100, %ebp            # imm = 0x100
jne 0x11109
leaq 0xbe4b(%rip), %rsi      # 0x1cf3e
movl $0x100, %ebx            # imm = 0x100
movq %r13, %rdi
movl $0x100, %edx            # imm = 0x100
xorl %eax, %eax
callq 0x10d4f
jmp 0x1110b
movl %ebp, %ebx
movq 0x18c0(%r13), %rax
leaq 0x28(%rsp), %rcx
movq %rax, 0x18(%rcx)
movq %r14, 0x8(%rcx)
movl 0xa0(%r13), %eax
movl %eax, (%rcx)
movl %ebx, 0x10(%rcx)
movq %rcx, 0x18c0(%r13)
incl %eax
movl %eax, 0xa0(%r13)
xorl %r12d, %r12d
cmpl $0x9, 0x50(%r13)
jne 0x11268
movq %r13, %rdi
callq 0x10771
cmpl $0xa, 0x50(%r13)
jne 0x11165
movq %r13, %rdi
callq 0x10771
jmp 0x11268
movq %r14, 0x18(%rsp)
movl %r15d, 0x10(%rsp)
movl %ebp, 0x14(%rsp)
leaq 0x9c(%r13), %rax
movq %rax, 0x20(%rsp)
xorl %r12d, %r12d
movq %r13, 0x8(%rsp)
movq %r13, %rdi
callq 0x11bcc
movq %r13, %rdi
movl $0x47, %esi
leaq 0xbdce(%rip), %rdx      # 0x1cf6c
callq 0x10df5
movq 0x38(%r13), %r14
movl 0x40(%r13), %r15d
movslq 0x18a8(%r13), %r13
testq %r13, %r13
jle 0x11218
movq 0x8(%rsp), %rax
movl 0xa0(%rax), %ebp
leaq (,%r13,2), %rax
addq %r13, %rax
incq %r13
movq 0x20(%rsp), %rcx
leaq (%rcx,%rax,8), %rbx
cmpl (%rbx), %ebp
jne 0x11218
cmpl %r15d, -0x4(%rbx)
jne 0x111f6
movq -0xc(%rbx), %rdi
movq %r14, %rsi
movq %r15, %rdx
callq 0x2090
testl %eax, %eax
je 0x11205
decq %r13
addq $-0x18, %rbx
cmpq $0x1, %r13
ja 0x111d9
jmp 0x11218
movq 0x8(%rsp), %rdi
leaq 0xbd76(%rip), %rsi      # 0x1cf87
xorl %eax, %eax
callq 0x10d4f
incl %r12d
movq 0x8(%rsp), %r13
movl 0x44(%r13), %ecx
movq %r13, %rdi
movq %r14, %rsi
movl %r15d, %edx
callq 0x11440
cmpl $0x5, 0x50(%r13)
jne 0x11246
movq %r13, %rdi
callq 0x10771
jmp 0x11187
leaq 0xbd5e(%rip), %rdx      # 0x1cfab
movq %r13, %rdi
movl $0xa, %esi
callq 0x10df5
movl 0x14(%rsp), %ebp
movl 0x10(%rsp), %r15d
movq 0x18(%rsp), %r14
movl %r12d, 0x20(%r14)
movq %r13, %rdi
movl %r12d, %esi
callq 0x11014
movq %r13, %rdi
testl %r15d, %r15d
je 0x112f7
xorl %esi, %esi
callq 0x114e1
movq 0x98(%r13), %rax
testq %rax, %rax
je 0x112d1
cmpb $0x0, (%rax)
jne 0x112d1
cmpb $0x1, 0x38ce(%r13)
jne 0x112d1
movq 0x18c0(%r13), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rcx
movl 0x8(%rcx), %eax
cmpl $0x2, %eax
jbe 0x1131f
movq (%rcx), %rcx
leal -0x1(%rax), %edx
cmpb $0x25, (%rcx,%rdx)
jne 0x11324
addl $-0x3, %eax
cmpb $0x27, (%rcx,%rax)
jne 0x11329
movb $0x28, (%rcx,%rax)
leaq 0xbd06(%rip), %rdx      # 0x1cfde
movq %r13, %rdi
movl $0x35, %esi
callq 0x10df5
movq %r13, %rdi
callq 0x11588
movq %r13, %rdi
callq 0x10e35
jmp 0x112fc
callq 0x11588
movq 0x18c0(%r13), %rax
movq 0x18(%rax), %rax
movq %rax, 0x18c0(%r13)
movl %ebp, %eax
addq $0x48, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x4c11
callq 0x4c47
callq 0x4c7d