// File: /workspace/github_c_makefile_star2_O0/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:
subq $0x98, %rsp
movq %rdi, 0x90(%rsp)
movl %esi, 0x8c(%rsp)
cmpl $0x2, 0x8c(%rsp)
je 0x168ef
movq 0x90(%rsp), %rdi
leaq 0x147ec(%rip), %rdx     # 0x2b04f
movl $0x47, %esi
callq 0x160a0
movq 0x90(%rsp), %rax
movq 0x38(%rax), %rax
movq %rax, 0x80(%rsp)
movq 0x90(%rsp), %rax
movl 0x40(%rax), %eax
movl %eax, 0x7c(%rsp)
movq 0x90(%rsp), %rdi
movq 0x80(%rsp), %rsi
movl 0x7c(%rsp), %edx
callq 0x16dd0
movl %edx, 0x68(%rsp)
movq %rax, 0x60(%rsp)
movq 0x60(%rsp), %rax
movq %rax, 0x70(%rsp)
movl 0x68(%rsp), %eax
movl %eax, 0x78(%rsp)
cmpl $0x0, 0x70(%rsp)
je 0x168ed
movq 0x90(%rsp), %rdi
movl 0x7c(%rsp), %edx
movq 0x80(%rsp), %rcx
leaq 0x14783(%rip), %rsi     # 0x2b069
movb $0x0, %al
callq 0x15f90
jmp 0x1690f
leaq 0x1478f(%rip), %rax     # 0x2b085
movq %rax, 0x80(%rsp)
movq 0x80(%rsp), %rdi
callq 0x2130
movl %eax, 0x7c(%rsp)
movq 0x90(%rsp), %rax
movq (%rax), %rdi
movq 0x80(%rsp), %rsi
movl 0x7c(%rsp), %edx
movq 0x90(%rsp), %rax
movq 0x18b0(%rax), %rcx
cmpl $0x0, 0x8c(%rsp)
sete %al
movzbl %al, %r8d
andl $0x1, %r8d
xorl %eax, %eax
movl %eax, %r9d
callq 0x4a80
movq %rax, 0x58(%rsp)
movq 0x90(%rsp), %rax
movq 0x18b0(%rax), %rax
movl 0x48(%rax), %eax
subl $0x1, %eax
movl %eax, 0x54(%rsp)
cmpl $0x100, 0x54(%rsp)      # imm = 0x100
jne 0x16995
movq 0x90(%rsp), %rdi
leaq 0x14709(%rip), %rsi     # 0x2b092
movl $0x100, %edx            # imm = 0x100
movb $0x0, %al
callq 0x15f90
movq 0x90(%rsp), %rdi
movq 0x58(%rsp), %rdx
movl 0x54(%rsp), %ecx
leaq 0x30(%rsp), %rsi
callq 0x16fe0
movl $0x0, 0x2c(%rsp)
movq 0x90(%rsp), %rdi
callq 0x17050
movq 0x90(%rsp), %rdi
movl $0x9, %esi
callq 0x15f50
testb $0x1, %al
jne 0x169e0
jmp 0x16b5c
movq 0x90(%rsp), %rdi
movl $0xa, %esi
callq 0x15f50
testb $0x1, %al
jne 0x16b5c
jmp 0x169fc
movq 0x90(%rsp), %rdi
callq 0x15f40
movq 0x90(%rsp), %rdi
movl $0x47, %esi
leaq 0x146a3(%rip), %rdx     # 0x2b0c0
callq 0x160a0
movl 0x2c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x2c(%rsp)
movq 0x90(%rsp), %rax
movq 0x38(%rax), %rax
movq %rax, 0x20(%rsp)
movq 0x90(%rsp), %rax
movl 0x40(%rax), %eax
movl %eax, 0x1c(%rsp)
movb $0x0, 0x1b(%rsp)
movq 0x90(%rsp), %rax
movl 0x18a8(%rax), %eax
subl $0x1, %eax
movl %eax, 0x14(%rsp)
cmpl $0x0, 0x14(%rsp)
jl 0x16aeb
movq 0x90(%rsp), %rax
addq $0xa8, %rax
movslq 0x14(%rsp), %rcx
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x8(%rsp)
movq 0x90(%rsp), %rax
movl 0xa0(%rax), %eax
movq 0x8(%rsp), %rcx
cmpl 0xc(%rcx), %eax
je 0x16aa7
jmp 0x16aeb
movq 0x8(%rsp), %rax
movl 0x8(%rax), %eax
cmpl 0x1c(%rsp), %eax
jne 0x16ad9
movq 0x8(%rsp), %rax
movq (%rax), %rdi
movq 0x20(%rsp), %rsi
movl 0x1c(%rsp), %eax
movl %eax, %edx
callq 0x2080
cmpl $0x0, %eax
jne 0x16ad9
movb $0x1, 0x1b(%rsp)
jmp 0x16aeb
jmp 0x16adb
movl 0x14(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x14(%rsp)
jmp 0x16a67
testb $0x1, 0x1b(%rsp)
je 0x16b08
movq 0x90(%rsp), %rdi
leaq 0x145da(%rip), %rsi     # 0x2b0db
movb $0x0, %al
callq 0x15f90
movq 0x90(%rsp), %rdi
movq 0x20(%rsp), %rsi
movl 0x1c(%rsp), %edx
movq 0x90(%rsp), %rax
movl 0x44(%rax), %ecx
callq 0x17070
movq 0x90(%rsp), %rdi
movl $0x5, %esi
callq 0x15f50
testb $0x1, %al
jne 0x169fc
movq 0x90(%rsp), %rdi
movl $0xa, %esi
leaq 0x145a8(%rip), %rdx     # 0x2b0ff
callq 0x160a0
movl 0x2c(%rsp), %ecx
movq 0x58(%rsp), %rax
movl %ecx, 0x20(%rax)
movq 0x90(%rsp), %rdi
movl 0x2c(%rsp), %esi
callq 0x16760
cmpl $0x0, 0x8c(%rsp)
je 0x16da8
movq 0x90(%rsp), %rdi
xorl %esi, %esi
callq 0x171e0
movq 0x90(%rsp), %rax
cmpq $0x0, 0x98(%rax)
je 0x16d73
movq 0x90(%rsp), %rax
movq 0x98(%rax), %rax
testb $0x1, (%rax)
jne 0x16d73
movq 0x90(%rsp), %rax
testb $0x1, 0x38ce(%rax)
je 0x16d71
jmp 0x16bdb
movq 0x90(%rsp), %rax
movq 0x18c0(%rax), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rax
cmpl $0x3, 0x8(%rax)
jae 0x16c32
movq 0x1c3f9(%rip), %rax     # 0x32ff8
movq (%rax), %rdi
leaq 0x13547(%rip), %rsi     # 0x2a150
leaq 0x13654(%rip), %rdx     # 0x2a264
leaq 0x1450b(%rip), %rcx     # 0x2b122
leaq 0x143b9(%rip), %r8      # 0x2afd7
movl $0x837, %r9d            # imm = 0x837
movb $0x0, %al
callq 0x2240
ud2
callq 0x2040
jmp 0x16c34
jmp 0x16c36
movq 0x90(%rsp), %rax
movq 0x18c0(%rax), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rax
movq (%rax), %rax
movq 0x90(%rsp), %rcx
movq 0x18c0(%rcx), %rcx
movq 0x8(%rcx), %rcx
movq 0x38(%rcx), %rcx
movl 0x8(%rcx), %ecx
subl $0x1, %ecx
movl %ecx, %ecx
movzbl (%rax,%rcx), %eax
cmpl $0x25, %eax
je 0x16cb2
movq 0x1c379(%rip), %rax     # 0x32ff8
movq (%rax), %rdi
leaq 0x134c7(%rip), %rsi     # 0x2a150
leaq 0x135d4(%rip), %rdx     # 0x2a264
leaq 0x1448b(%rip), %rcx     # 0x2b122
leaq 0x14339(%rip), %r8      # 0x2afd7
movl $0x838, %r9d            # imm = 0x838
movb $0x0, %al
callq 0x2240
ud2
callq 0x2040
jmp 0x16cb4
jmp 0x16cb6
movq 0x90(%rsp), %rax
movq 0x18c0(%rax), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rax
movq (%rax), %rax
movq 0x90(%rsp), %rcx
movq 0x18c0(%rcx), %rcx
movq 0x8(%rcx), %rcx
movq 0x38(%rcx), %rcx
movl 0x8(%rcx), %ecx
subl $0x3, %ecx
movl %ecx, %ecx
movzbl (%rax,%rcx), %eax
cmpl $0x27, %eax
je 0x16d32
movq 0x1c2f9(%rip), %rax     # 0x32ff8
movq (%rax), %rdi
leaq 0x13447(%rip), %rsi     # 0x2a150
leaq 0x13554(%rip), %rdx     # 0x2a264
leaq 0x1440b(%rip), %rcx     # 0x2b122
leaq 0x142b9(%rip), %r8      # 0x2afd7
movl $0x839, %r9d            # imm = 0x839
movb $0x0, %al
callq 0x2240
ud2
callq 0x2040
jmp 0x16d34
movq 0x90(%rsp), %rax
movq 0x18c0(%rax), %rax
movq 0x8(%rax), %rax
movq 0x38(%rax), %rax
movq (%rax), %rax
movq 0x90(%rsp), %rcx
movq 0x18c0(%rcx), %rcx
movq 0x8(%rcx), %rcx
movq 0x38(%rcx), %rcx
movl 0x8(%rcx), %ecx
subl $0x3, %ecx
movl %ecx, %ecx
movb $0x28, (%rax,%rcx)
jmp 0x16d73
movq 0x90(%rsp), %rdi
movl $0x35, %esi
leaq 0x143ab(%rip), %rdx     # 0x2b132
callq 0x160a0
movq 0x90(%rsp), %rdi
callq 0x17300
movq 0x90(%rsp), %rdi
callq 0x16240
jmp 0x16db5
movq 0x90(%rsp), %rdi
callq 0x17300
movq 0x90(%rsp), %rdi
callq 0x173c0
movl 0x54(%rsp), %eax
addq $0x98, %rsp
retq
nop