// File: /workspace/github_c_makefile_star2_O1/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, %rbx
cmpl $0x2, %esi
jne 0x1140d
leaq 0xcaef(%rip), %r14      # 0x1def5
movl $0xc, %ebp
jmp 0x1144f
leaq 0xcaab(%rip), %rdx      # 0x1debf
movq %rbx, %rdi
movl $0x47, %esi
callq 0x11171
movq 0x38(%rbx), %r14
movl 0x40(%rbx), %ebp
movq %rbx, %rdi
movq %r14, %rsi
movl %ebp, %edx
callq 0x116bc
testl %eax, %eax
je 0x1144f
leaq 0xca99(%rip), %rsi      # 0x1ded9
movq %rbx, %rdi
movl %ebp, %edx
movq %r14, %rcx
xorl %eax, %eax
callq 0x110cb
movq (%rbx), %rdi
movq 0x18b0(%rbx), %rcx
xorl %r8d, %r8d
testl %r15d, %r15d
sete %r8b
xorl %r12d, %r12d
movq %r14, %rsi
movl %ebp, %edx
xorl %r9d, %r9d
callq 0x929e
movq %rax, %r14
movq 0x18b0(%rbx), %rax
movl 0x48(%rax), %ebp
decl %ebp
cmpl $0x100, %ebp            # imm = 0x100
jne 0x114a0
leaq 0xca71(%rip), %rsi      # 0x1df02
movq %rbx, %rdi
movl $0x100, %edx            # imm = 0x100
xorl %eax, %eax
callq 0x110cb
movq 0x18c0(%rbx), %rax
leaq 0x28(%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)
cmpl $0x9, 0x50(%rbx)
jne 0x115f7
movq %rbx, %rdi
callq 0x10b76
cmpl $0xa, 0x50(%rbx)
jne 0x114f3
movq %rbx, %rdi
callq 0x10b76
jmp 0x115f7
movq %r14, 0x18(%rsp)
movl %r15d, 0x8(%rsp)
movl %ebp, 0xc(%rsp)
leaq 0x9c(%rbx), %rax
movq %rax, 0x20(%rsp)
xorl %r14d, %r14d
movq %rbx, %rdi
callq 0x11e66
movq %rbx, %rdi
movl $0x47, %esi
leaq 0xca09(%rip), %rdx      # 0x1df30
callq 0x11171
incl %r14d
movq %r14, 0x10(%rsp)
movq 0x38(%rbx), %r12
movl 0x40(%rbx), %r14d
movl 0x18a8(%rbx), %eax
leal 0x1(%rax), %ebp
leaq (%rax,%rax,2), %rax
movq 0x20(%rsp), %rcx
leaq (%rcx,%rax,8), %r15
xorl %r13d, %r13d
decl %ebp
testl %ebp, %ebp
jle 0x11590
movl 0xa0(%rbx), %eax
cmpl (%r15), %eax
jne 0x11586
cmpl %r14d, -0x4(%r15)
jne 0x1157f
movq -0xc(%r15), %rdi
movq %r12, %rsi
movq %r14, %rdx
callq 0x2090
testl %eax, %eax
je 0x11583
movb $0x1, %al
jmp 0x11588
movb $0x1, %r13b
xorl %eax, %eax
addq $-0x18, %r15
testb %al, %al
jne 0x11555
testb $0x1, %r13b
je 0x115a7
movq %rbx, %rdi
leaq 0xc9ab(%rip), %rsi      # 0x1df4b
xorl %eax, %eax
callq 0x110cb
movl 0x44(%rbx), %ecx
movq %rbx, %rdi
movq %r12, %rsi
movl %r14d, %edx
callq 0x11846
cmpl $0x5, 0x50(%rbx)
jne 0x115d0
movq %rbx, %rdi
callq 0x10b76
movq 0x10(%rsp), %r14
jmp 0x11510
leaq 0xc998(%rip), %rdx      # 0x1df6f
movq %rbx, %rdi
movl $0xa, %esi
callq 0x11171
movl 0xc(%rsp), %ebp
movl 0x8(%rsp), %r15d
movq 0x10(%rsp), %r12
movq 0x18(%rsp), %r14
movl %r12d, 0x20(%r14)
movq %rbx, %rdi
movl %r12d, %esi
callq 0x113b1
movq %rbx, %rdi
testl %r15d, %r15d
je 0x11685
xorl %esi, %esi
callq 0x11919
movq 0x98(%rbx), %rax
testq %rax, %rax
je 0x1165f
cmpb $0x0, (%rax)
jne 0x1165f
cmpb $0x1, 0x38ce(%rbx)
jne 0x1165f
movq 0x18c0(%rbx), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rcx
movl 0x8(%rcx), %eax
cmpl $0x2, %eax
jbe 0x116ad
movq (%rcx), %rcx
leal -0x1(%rax), %edx
cmpb $0x25, (%rcx,%rdx)
jne 0x116b2
addl $-0x3, %eax
cmpb $0x27, (%rcx,%rax)
jne 0x116b7
movb $0x28, (%rcx,%rax)
leaq 0xc93c(%rip), %rdx      # 0x1dfa2
movq %rbx, %rdi
movl $0x35, %esi
callq 0x11171
movq %rbx, %rdi
callq 0x11986
movq %rbx, %rdi
callq 0x111d2
jmp 0x1168a
callq 0x11986
movq 0x18c0(%rbx), %rax
movq 0x18(%rax), %rax
movq %rax, 0x18c0(%rbx)
movl %ebp, %eax
addq $0x48, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x4c47
callq 0x4c7d
callq 0x4cb3