// File: /workspace/github_cpp_makefile_star2_O3/baziotis[P]minijava-cpp/typecheck.cpp
// Function: MainTypeCheckVisitor::visit(WhileStatement*)
void MainTypeCheckVisitor::visit(WhileStatement *while_stmt) {
    LOG_SCOPE;
    debug_print("MainTypeCheck::WhileStatement\n");
    Type *cond = while_stmt->cond->accept(this);
    poison_stmt(cond);
    llvalue_t cond_val = __expr_context.llval;
    if (cond != this->type_table.bool_type) {
        typecheck_error(while_stmt->loc, "In while statement, the ",
                        "condition expression must have `boolean` type but has: `",
                        cond->name(), "`");
    }

    if (cond_val.kind == LLVALUE::CONST && cond_val.val == 0) {
        // Elide while body because of constant false condition.
        // Assume that the user does not even wants to typecheck it.
        return;
    }
    
    /// Codegen ///

    // Codegen loops as:
    /*
    predecessor:
        cond test code (branch to loop start or after_loop)
    loop_start:
        loop_body
        cond test code (again, branch to loop_start or after_loop)
    after_loop:

    That way, when we're in loop_body, we know whether we came from predecessor
    or from loop_start.
    Note: Here we have already generated the initial cond code when we
    typechecked it.
    */
    
    const char *pred_lbl_str = (while_stmt->pred_lbl == NULL) ?
                                __expr_context.curr_lbl.lbl : while_stmt->pred_lbl;
    while_stmt->pred_lbl = pred_lbl_str;
    llvm_label_t pred_lbl = {pred_lbl_str};
    llvm_label_t loop_lbl, after_loop_lbl;
    long lbl = gen_lbl();
    loop_lbl.construct("while", lbl);
    after_loop_lbl.construct("while_end", lbl);
    
    // Note: We have already tested if the `cond_val` is const with
    // false value. If we reach here and we have a const, then it's
    // definitely true in which case we don't generate the branch, we
    // just get to the loop.
    // However, note that we have to remember that for the while
    // so that we remember if we need to create a phi or not.
    bool generated_cond_branch = false;
    if (cond_val.kind != LLVALUE::CONST) {
        generated_cond_branch = true;
        llvm_branch_cond(cond_val, loop_lbl, after_loop_lbl);
    } else {
        llvm_branch(loop_lbl);
    }
    llvm_gen_lbl(loop_lbl);

    // There are 3 cases for a local inside a loop:
    // 1) No assignment to the local.
    // 2) We have at least one assignment and we have a use _after_ the _first_ assignment.
    // 3) We have at least one assignment and we have a use _before_ the _first_ assignment.

    // We want to insert a phi node for locals in case 3. To do that, we use the `track_buf`.
    // This keeps an `assigned` and `used` for every local. We then "visit" the whole
    // loop body and condition to track in what case we are. To do that, we use
    // an ordering in `assigned` and `used`. After finishing, either they're both
    // 0, or one is bigger than the other denoting the order.

    SerializedHashTable<Local*> locals = __expr_context.method->locals;
    FuncArr<WhileAssign> track_buf = get_track_buf(locals);

    // Track first the body and then the cond,
    // as the condition will be put on the bottom.
    track_while_assignment_for_stmt(while_stmt->body, track_buf);
    track_while_assignment_for_expr(while_stmt->cond, track_buf);

    /* -- Main algorithm for insert phi nodes --

    Assuming that we have an ordering now, we need to handle case 3) (from above)
    locals. For those we will insert a phi node at the start of the loop
    that decides between the previous value (the value before the loop) and
    the value inside the loop. Let's note some problems:
    1) We have to know the value inside the loop _before_ we actually generate
       the loop. This is kind of impossible so there are 2 ways:
       a) Generate the loop (say into a buffer), then read this IR, find the values
          and then regenerate the loop with the suitable phi nodes on the start.
       b) Generate the loop (but by disabling actual codegen, i.e. printing), find
          the values, generate the phi nodes and then regenerate the loop as it was
          never generated.
       We will do b) as it is of course way easier and faster. But that comes later.
       Before that we have to solve another problem:
    2) To do the first fake generation that was described 1b) above, we have to know
       the register that was assigned to the phi node, _before_ we can generate
       the phi node.
    
    To see both 1) and 2) into action, consider this:
        
       ```
       a = 3;
       while (a < 4) {
          a = a + 1;
       }
       ```
        
       That has to generate something like this:
       ```
           // ... (`a` has gotten constant value 3 here)
       while1:
           %1 = phi i32 [ 3, entry ], [ %2, while1 ]
           %2 = add i32 %1, 1
           %3 = icmp slt i32 %2, 4
           br i1 %3, label while1, label while_end1
       
       while_end1:
           // ...
       ```

       Note that to generate the phi node in %1, we have to know that `a`
       gets the llvalue %2. But, we can't know that _before_ we generate the body.
       So, let's say that we go to generate it. Well, to generate it, when we
       go to generate code for %2, we have to know that `a` has the llvalue %1.
       If we _don't_ somehow do that and do the generation normally, then when we
       generate code for %2, the value that will be assigned to `a` will be the
       constant value 3. And that's of course wrong code generation.

       To solve that we decide the register in which we will save the phi node
       before we actually. Because of the ordering (which remember, comes before all that),
       we know e.g. that we have to generate a phi node for `a`. At this point we can't
       generate the phi node, but we can decide that its value will be saved e.g. in %1.
       Then, we save the llvalue of `a` to be %1 (e.g. local->llval = ...). In that way,
       when we do the first fake generation of, when we go to generate %2, it uses %1
       as the llvalue of `a`. Then of course `a` gets as llvalue the %2.
       And _now_, after finishing the fake generation, we know that `a` got assigned
       as llvalue the %2. And so, we generate the phi node between the constant value
       3 (the previous value i.e. that before the loop) and %2 (the new value).

       Ok, to do all that we need 2 buffers:
       1) `previous_values` holds the values of the locals _before_ the loop.
       2) `new_assigned_values` holds the values that we assign a priori to the phi
          nodes that _will_ be generated.
    */

    FuncArr<llvalue_t> previous_values, new_assigned_values;
    previous_values.reserve(locals.len);
    new_assigned_values.reserve(locals.len);
    // Initialize the 2 buffers.
    for (Local *local : locals) {
        previous_values.push(local->llval);
        // DUMB VALUE
        new_assigned_values.push(const_llv(0, {}));
    }

    /// Decide for which locals we have to make a phi node
    /// and get a register for the phi node (that will be generated later).
    size_t i = 0;
    for (WhileAssign wa : track_buf) {
        assert(i < locals.len);
        // Either no use and no assignment or they are not equal (since
        // we should have ordering).
        assert((!wa.assigned && !wa.used) || (wa.used != wa.assigned));
        if (wa.assigned && wa.used && wa.used < wa.assigned) {
            Local *local = locals[i];
            new_assigned_values[i].kind = LLVALUE::REG;
            new_assigned_values[i].reg = gen_reg();
            local->llval = new_assigned_values[i];
        }
        ++i;
    }

    /// Do the fake generation (by disabling codegen).
    /// That requires some boilterplate to save and restore the state of registers
    /// and labels.

    assert(while_stmt->body);
    bool save_codegen = config.codegen;
    // TODO: We should have a save state / restore state.
    // Get the last register but we actually have to reset it immediately.
    long save_reg = gen_reg();
    set_reg(save_reg);
    long save_lbl = gen_lbl();
    set_lbl(save_lbl);
    config.codegen = false;
    while_stmt->body->accept(this);
    while_stmt->cond->accept(this);
    const char *last_lbl_str = (while_stmt->last_lbl == NULL) ?
                                __expr_context.curr_lbl.lbl : while_stmt->last_lbl;
    while_stmt->last_lbl = last_lbl_str;
    llvm_label_t last_lbl = {last_lbl_str};
    set_reg(save_reg);
    set_lbl(save_lbl);
    config.codegen = save_codegen;

    
    /// Now that we know the llvalue that the locals got in the loop,
    /// generate the phi nodes. Note that they're locals that might have been
    /// assigned but either never used or used after the assignment. In these
    /// cases we don't want to insert a phi node but we do want to restore their
    /// llvalue so that the actual code generation is correct.
    i = 0;
    for (WhileAssign wa : track_buf) {
        Local *local = wa.local;
        if (wa.assigned && wa.used && wa.used < wa.assigned) {
            long reg = new_assigned_values[i].reg;
            local->llval = while_phi_node(local->type, previous_values[i], local->llval,
                                          pred_lbl, last_lbl, reg);
        } else if (wa.assigned) {
            // Restore in the general case
            local->llval = previous_values[i];
        }
        ++i;
    }
    
    /// Do the actual code generation (note that we generate the cond on the
    /// bottom etc. - as described above on how while loops are generated / transformed).
    while_stmt->body->accept(this);
    while_stmt->cond->accept(this);
    cond_val = __expr_context.llval;
    llvm_branch_cond(cond_val, loop_lbl, after_loop_lbl);
    llvm_gen_lbl(after_loop_lbl);
    
    /// Last but not least, we want to generate a phi nodes for all
    /// the locals that were assigned inside the loop (regardless of the usage).
    /// Of course the phi will be between their before-loop-llvalue and the
    /// value they get assigned inside the loop.

    // Generate a phi node after the while. Do that only if the condition
    // was not known and so a conditional branch was inserted.
    if (generated_cond_branch) {
      i = 0;
      for (WhileAssign wa : track_buf) {
          Local *local = wa.local;
          llvalue_t prev_value = previous_values[i];
          llvalue_t new_value = local->llval;
          if (wa.assigned) {
              local->llval = llvm_phi_node(local->type, prev_value, new_value,
                                           pred_lbl, last_lbl);
          }
          ++i;
      }
    }
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x1c8, %rsp            # imm = 0x1C8
movq %rsi, %r15
movq %rdi, %r14
leaq 0x47(%rsp), %rdi
callq 0x34c8
leaq 0x5af9(%rip), %rdi      # 0x144ed
xorl %eax, %eax
callq 0x3539
movq 0x10(%r15), %rsi
movq %r14, %rdi
callq 0xb31c
cmpl $0x0, 0x4(%rax)
je 0xf11b
leaq 0xbfa0(%rip), %r12      # 0x1a9b8
movl 0x8(%r12), %ebp
movl 0x1c(%r12), %ecx
movl %ecx, 0x110(%rsp)
movups 0xc(%r12), %xmm0
movaps %xmm0, 0x100(%rsp)
movq 0x20(%r12), %xmm0
cmpq 0x8(%r14), %rax
movdqa %xmm0, 0xb0(%rsp)
je 0xea81
movl 0xc(%r15), %ebx
movq %rax, %rdi
callq 0x83da
leaq 0x5aac(%rip), %rsi      # 0x1450c
leaq 0x5992(%rip), %rdx      # 0x143f9
leaq 0x3ee3(%rip), %r8       # 0x12951
movl %ebx, %edi
movq %rax, %rcx
callq 0x8bb7
movdqa 0xb0(%rsp), %xmm0
movl %ebp, %eax
xorl $0x1, %eax
movd %xmm0, %ecx
orl %eax, %ecx
je 0xf11b
movq %r14, 0x60(%rsp)
movq 0x20(%r15), %r13
testq %r13, %r13
jne 0xeaa4
movq (%r12), %r13
movq %r13, 0x20(%r15)
xorl %eax, %eax
movq %rax, 0x58(%rsp)
movq %rax, 0x68(%rsp)
callq 0x476a
movq %rax, %r14
leaq 0x46da(%rip), %rsi      # 0x1319d
leaq 0x58(%rsp), %rdi
movq %rax, %rdx
callq 0xf93e
leaq 0x5a4e(%rip), %rsi      # 0x14525
leaq 0x68(%rsp), %rdi
movq %r14, %rdx
callq 0xf93e
cmpl $0x1, %ebp
movq %r15, 0x48(%rsp)
jne 0xeafa
movq 0x58(%rsp), %rdi
callq 0x54c9
jmp 0xeb59
movl %ebp, 0x140(%rsp)
movaps 0x100(%rsp), %xmm0
movups %xmm0, 0x144(%rsp)
movl 0x110(%rsp), %eax
movl %eax, 0x154(%rsp)
movaps 0xb0(%rsp), %xmm0
movlps %xmm0, 0x158(%rsp)
movq 0x58(%rsp), %rdi
movq 0x68(%rsp), %rsi
movdqu 0x140(%rsp), %xmm0
movups 0x150(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
callq 0x5448
movq 0x58(%rsp), %rdi
callq 0x542a
movl %ebp, 0xac(%rsp)
movq 0x28(%r12), %rax
movq 0x30(%rax), %rbx
movq 0x38(%rax), %r14
pxor %xmm0, %xmm0
leaq 0x90(%rsp), %rdi
movdqa %xmm0, (%rdi)
movq $0x0, 0x10(%rdi)
movq %r14, %rsi
callq 0x103be
movq %r14, 0x50(%rsp)
testq %r14, %r14
je 0xebd0
movq 0x50(%rsp), %rax
leaq (,%rax,8), %r15
xorl %r12d, %r12d
leaq 0x90(%rsp), %r14
movq (%rbx,%r12), %rsi
movq %r14, %rdi
xorl %edx, %edx
callq 0xfa28
addq $0x8, %r12
cmpq %r12, %r15
jne 0xebb9
movq %r13, 0xb0(%rsp)
movq 0x48(%rsp), %r14
movq 0x18(%r14), %rdi
movq 0xa0(%rsp), %rax
movq %rax, 0x10(%rsp)
movaps 0x90(%rsp), %xmm0
movups %xmm0, (%rsp)
callq 0xe724
movq 0x10(%r14), %rdi
movq 0xa0(%rsp), %rax
movq %rax, 0x10(%rsp)
movdqa 0x90(%rsp), %xmm0
movdqu %xmm0, (%rsp)
callq 0xe5f1
xorl %eax, %eax
leaq 0xc0(%rsp), %rdi
movq %rax, 0x10(%rdi)
pxor %xmm0, %xmm0
movdqa %xmm0, (%rdi)
movq %rax, 0x80(%rsp)
movdqa %xmm0, 0x70(%rsp)
movq 0x50(%rsp), %r14
movq %r14, %rsi
callq 0xfa92
leaq 0x70(%rsp), %rdi
movq %r14, %rsi
callq 0xfa92
testq %r14, %r14
je 0xece2
movq 0x50(%rsp), %rax
leaq (,%rax,8), %r15
xorl %r12d, %r12d
leaq 0xc0(%rsp), %r13
leaq 0x1a8(%rsp), %rbp
leaq 0x70(%rsp), %r14
movq (%rbx,%r12), %rax
movups 0x10(%rax), %xmm0
movups 0x20(%rax), %xmm1
movups %xmm1, 0x10(%rsp)
movups %xmm0, (%rsp)
movq %r13, %rdi
callq 0xf868
movq %rbp, %rdi
xorl %esi, %esi
xorl %edx, %edx
xorl %ecx, %ecx
callq 0x4791
movdqu 0x1a8(%rsp), %xmm0
movups 0x1b8(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
movq %r14, %rdi
callq 0xf868
addq $0x8, %r12
cmpq %r12, %r15
jne 0xec8b
movq 0x98(%rsp), %r14
testq %r14, %r14
movq 0x48(%rsp), %r12
movq 0x50(%rsp), %rsi
je 0xedc8
movq 0xa0(%rsp), %r13
shlq $0x4, %r14
xorl %ebp, %ebp
xorl %r15d, %r15d
cmpq %r15, %rsi
je 0xf156
movb 0x8(%r13,%rbp,2), %al
testb $0xf, %al
je 0xedb7
movl %eax, %ecx
andb $0x3, %cl
shrb $0x2, %al
andb $0x3, %al
cmpb %cl, %al
je 0xf175
testb %cl, %cl
je 0xedb7
testb %al, %al
sete %dl
cmpb %cl, %al
setae %al
orb %dl, %al
jne 0xedb7
cmpq %r15, 0x78(%rsp)
jbe 0xf137
movq (%rbx,%rbp), %r12
movq 0x80(%rsp), %rax
movl $0x2, (%rax,%rbp,4)
callq 0x4740
cmpq %r15, 0x78(%rsp)
jbe 0xf137
movq 0x80(%rsp), %rcx
movq %rax, 0x18(%rcx,%rbp,4)
cmpq %r15, 0x78(%rsp)
movq 0x50(%rsp), %rsi
jbe 0xf137
movq 0x80(%rsp), %rax
movdqu (%rax,%rbp,4), %xmm0
movups 0x10(%rax,%rbp,4), %xmm1
movups %xmm1, 0x20(%r12)
movdqu %xmm0, 0x10(%r12)
movq 0x48(%rsp), %r12
incq %r15
addq $0x8, %rbp
addq $-0x10, %r14
jne 0xed0e
cmpq $0x0, 0x18(%r12)
je 0xf194
leaq 0xbc5d(%rip), %rbx      # 0x1aa38
movb 0x4(%rbx), %r14b
callq 0x4740
movq %rax, %r13
movq %rax, %rdi
callq 0x4738
callq 0x476a
movq %rax, %rbp
movq %rax, %rdi
callq 0x475f
movb $0x0, 0x4(%rbx)
movq 0x18(%r12), %rdi
movq (%rdi), %rax
movq 0x60(%rsp), %rsi
callq *0x18(%rax)
movq 0x10(%r12), %rsi
movq 0x60(%rsp), %rdi
callq 0xb31c
movq 0x28(%r12), %r12
testq %r12, %r12
jne 0xee36
leaq 0xbb85(%rip), %rax      # 0x1a9b8
movq (%rax), %r12
movq 0x48(%rsp), %rax
movq %r12, 0x28(%rax)
movq %r13, %rdi
callq 0x4738
movq %rbp, %rdi
callq 0x475f
movb %r14b, 0x4(%rbx)
movq 0x98(%rsp), %rbx
testq %rbx, %rbx
je 0xef6f
movq 0xa0(%rsp), %r14
shlq $0x4, %rbx
addq %r14, %rbx
xorl %ebp, %ebp
xorl %r13d, %r13d
movb 0x8(%r14), %al
movl %eax, %ecx
andb $0x3, %cl
je 0xef5b
movq (%r14), %r15
shrb $0x2, %al
andb $0x3, %al
sete %dl
cmpb %cl, %al
setae %al
orb %dl, %al
jne 0xef30
cmpq %r13, 0x78(%rsp)
jbe 0xf137
cmpq %r13, 0xc8(%rsp)
jbe 0xf137
movq 0x80(%rsp), %rax
movq 0x18(%rax,%rbp), %r8
movq 0x8(%r15), %rsi
movq 0xd0(%rsp), %rax
movups 0x10(%r15), %xmm0
movups 0x20(%r15), %xmm1
movups %xmm1, 0x30(%rsp)
movups %xmm0, 0x20(%rsp)
movups (%rax,%rbp), %xmm0
movups 0x10(%rax,%rbp), %xmm1
movups %xmm1, 0x10(%rsp)
movups %xmm0, (%rsp)
leaq 0xe0(%rsp), %rdi
movq 0xb0(%rsp), %rdx
movq %r12, %rcx
callq 0x54ff
addq $0x10, %r15
movdqu 0xe0(%rsp), %xmm0
movups 0xf0(%rsp), %xmm1
movups %xmm1, 0x10(%r15)
movdqu %xmm0, (%r15)
jmp 0xef5b
cmpq %r13, 0xc8(%rsp)
jbe 0xf137
movq 0xd0(%rsp), %rax
movdqu (%rax,%rbp), %xmm0
movups 0x10(%rax,%rbp), %xmm1
movups %xmm1, 0x20(%r15)
movdqu %xmm0, 0x10(%r15)
incq %r13
addq $0x10, %r14
addq $0x20, %rbp
cmpq %rbx, %r14
jne 0xee78
movq 0x48(%rsp), %rax
movq 0x18(%rax), %rdi
movq (%rdi), %rax
movq 0x60(%rsp), %rsi
callq *0x18(%rax)
movq 0x48(%rsp), %rax
movq 0x10(%rax), %rsi
movq 0x60(%rsp), %rdi
callq 0xb31c
leaq 0xba1b(%rip), %rcx      # 0x1a9b8
movl 0x8(%rcx), %eax
movl %eax, 0x120(%rsp)
movups 0xc(%rcx), %xmm0
movups %xmm0, 0x124(%rsp)
movl 0x1c(%rcx), %eax
movl %eax, 0x134(%rsp)
movq 0x20(%rcx), %rax
movq %rax, 0x138(%rsp)
movq 0x58(%rsp), %rdi
movq 0x68(%rsp), %rsi
movdqu 0x120(%rsp), %xmm0
movups 0x130(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
callq 0x5448
movq 0x68(%rsp), %rdi
callq 0x542a
cmpl $0x1, 0xac(%rsp)
je 0xf11b
movq 0x98(%rsp), %rbx
testq %rbx, %rbx
je 0xf11b
movq 0xa0(%rsp), %r15
shlq $0x4, %rbx
addq %r15, %rbx
movq $-0x1, %r13
xorl %ebp, %ebp
incq %r13
cmpq %r13, 0xc8(%rsp)
jbe 0xf137
movq (%r15,%rbp), %r14
movb 0x8(%r15,%rbp), %al
movq 0xd0(%rsp), %rcx
movups (%rcx,%rbp,2), %xmm0
movups 0x10(%rcx,%rbp,2), %xmm1
movaps %xmm0, 0xe0(%rsp)
movaps %xmm1, 0xf0(%rsp)
movdqu 0x10(%r14), %xmm0
movups 0x20(%r14), %xmm1
movaps %xmm1, 0x170(%rsp)
movdqa %xmm0, 0x160(%rsp)
testb $0x3, %al
je 0xf106
movq 0x8(%r14), %rsi
movaps 0x160(%rsp), %xmm0
movaps 0x170(%rsp), %xmm1
movups %xmm1, 0x30(%rsp)
movups %xmm0, 0x20(%rsp)
movdqa 0xe0(%rsp), %xmm0
movaps 0xf0(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
leaq 0x188(%rsp), %rdi
movq 0xb0(%rsp), %rdx
movq %r12, %rcx
movq $-0x1, %r8
callq 0x54ff
addq $0x10, %r14
movdqu 0x188(%rsp), %xmm0
movups 0x198(%rsp), %xmm1
movups %xmm1, 0x10(%r14)
movdqu %xmm0, (%r14)
leaq (%r15,%rbp), %rax
addq $0x10, %rbp
addq $0x10, %rax
cmpq %rbx, %rax
jne 0xf034
leaq 0x47(%rsp), %rdi
callq 0x34dc
addq $0x1c8, %rsp            # imm = 0x1C8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x5ac5(%rip), %rdi      # 0x14c03
leaq 0x5719(%rip), %rsi      # 0x1485e
leaq 0x6017(%rip), %rcx      # 0x15163
movl $0x32, %edx
callq 0x20b0
leaq 0x53d2(%rip), %rdi      # 0x1452f
leaq 0x429a(%rip), %rsi      # 0x133fe
leaq 0x53d3(%rip), %rcx      # 0x1453e
movl $0x88a, %edx            # imm = 0x88A
callq 0x20b0
leaq 0x53f5(%rip), %rdi      # 0x14571
leaq 0x427b(%rip), %rsi      # 0x133fe
leaq 0x53b4(%rip), %rcx      # 0x1453e
movl $0x88d, %edx            # imm = 0x88D
callq 0x20b0
leaq 0x540d(%rip), %rdi      # 0x145a8
leaq 0x425c(%rip), %rsi      # 0x133fe
leaq 0x5395(%rip), %rcx      # 0x1453e
movl $0x89b, %edx            # imm = 0x89B
callq 0x20b0
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
jmp 0xf1cb
movq %rax, %rbx
leaq 0x47(%rsp), %rdi
callq 0x34dc
movq %rbx, %rdi
callq 0x21c0