// File: /workspace/github_cpp_makefile_star2_O2/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 $0x208, %rsp            # imm = 0x208
movq %rsi, %r12
movq %rdi, %r15
leaq 0x47(%rsp), %rdi
callq 0x3108
leaq 0x561d(%rip), %rdi      # 0x114dd
xorl %eax, %eax
callq 0x317b
movq 0x10(%r12), %rsi
movq %r15, %rdi
callq 0x8d10
cmpl $0x0, 0x4(%rax)
je 0xc4c5
leaq 0xcad3(%rip), %r14      # 0x189b8
movl 0x8(%r14), %ebp
movl 0x1c(%r14), %ecx
movl %ecx, 0x120(%rsp)
movups 0xc(%r14), %xmm0
movaps %xmm0, 0x110(%rsp)
movq 0x20(%r14), %xmm0
cmpq 0x8(%r15), %rax
movdqa %xmm0, 0x60(%rsp)
je 0xbf45
movl 0xc(%r12), %ebx
movq %rax, %rdi
callq 0x7572
leaq 0x55d5(%rip), %rsi      # 0x114fc
leaq 0x54bb(%rip), %rdx      # 0x113e9
leaq 0x3aac(%rip), %r8       # 0xf9e1
movl %ebx, %edi
movq %rax, %rcx
callq 0x7bee
movdqa 0x60(%rsp), %xmm0
movl %ebp, %eax
xorl $0x1, %eax
movd %xmm0, %ecx
orl %eax, %ecx
je 0xc4c5
movq 0x20(%r12), %rbx
testq %rbx, %rbx
jne 0xbf63
movq (%r14), %rbx
movq %rbx, 0x20(%r12)
andq $0x0, 0x50(%rsp)
andq $0x0, 0x58(%rsp)
callq 0x42c3
movq %rax, %r14
leaq 0x42aa(%rip), %rsi      # 0x1022d
leaq 0x50(%rsp), %rdi
movq %rax, %rdx
callq 0xcf6c
leaq 0x557e(%rip), %rsi      # 0x11515
leaq 0x58(%rsp), %rdi
movq %r14, %rdx
callq 0xcf6c
cmpl $0x1, %ebp
jne 0xbfbc
movq 0x50(%rsp), %rdi
callq 0x4dc4
leaq 0xc9fe(%rip), %r14      # 0x189b8
jmp 0xc01f
movl %ebp, 0x150(%rsp)
movaps 0x110(%rsp), %xmm0
movups %xmm0, 0x154(%rsp)
movl 0x120(%rsp), %eax
movl %eax, 0x164(%rsp)
movaps 0x60(%rsp), %xmm0
movlps %xmm0, 0x168(%rsp)
movq 0x50(%rsp), %rdi
movq 0x58(%rsp), %rsi
movdqu 0x150(%rsp), %xmm0
movups 0x160(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
callq 0x4d6f
leaq 0xc999(%rip), %r14      # 0x189b8
movq 0x50(%rsp), %rdi
callq 0x4d51
movq %r15, 0x90(%rsp)
movl %ebp, 0x74(%rsp)
movq 0x28(%r14), %rax
movups 0x18(%rax), %xmm0
movaps %xmm0, 0x170(%rsp)
movq 0x28(%rax), %rcx
movq %rcx, 0x180(%rsp)
movq 0x30(%rax), %rcx
movq %rcx, 0x60(%rsp)
movq %rcx, 0x188(%rsp)
movups 0x38(%rax), %xmm0
movaps %xmm0, 0xa0(%rsp)
movaps %xmm0, 0x190(%rsp)
movdqa 0x170(%rsp), %xmm0
movaps 0x180(%rsp), %xmm1
movaps 0x190(%rsp), %xmm2
movups %xmm2, 0x20(%rsp)
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
leaq 0x78(%rsp), %rdi
callq 0xbe4b
movq %rbx, 0x98(%rsp)
movq 0x18(%r12), %rdi
movq 0x88(%rsp), %rax
movq %rax, 0x10(%rsp)
movups 0x78(%rsp), %xmm0
movups %xmm0, (%rsp)
callq 0xbc16
movq %r12, 0x48(%rsp)
movq 0x10(%r12), %rdi
movq 0x88(%rsp), %rax
movq %rax, 0x10(%rsp)
movups 0x78(%rsp), %xmm0
movups %xmm0, (%rsp)
callq 0xbaec
xorps %xmm0, %xmm0
leaq 0xf0(%rsp), %rdi
movaps %xmm0, (%rdi)
andq $0x0, 0x10(%rdi)
movaps %xmm0, 0xb0(%rsp)
andq $0x0, 0xc0(%rsp)
movdqa 0xa0(%rsp), %xmm0
movq %xmm0, %r12
movq %r12, %rsi
callq 0xd108
leaq 0xb0(%rsp), %rdi
movq %r12, %rsi
callq 0xd108
leaq (,%r12,8), %rbp
xorl %ebx, %ebx
leaq 0xf0(%rsp), %r14
leaq 0x1e8(%rsp), %r15
leaq 0xb0(%rsp), %r13
cmpq %rbx, %rbp
je 0xc1bf
movq 0x60(%rsp), %rax
movq (%rax,%rbx), %rax
movups 0x10(%rax), %xmm0
movups 0x20(%rax), %xmm1
movups %xmm1, 0x10(%rsp)
movups %xmm0, (%rsp)
movq %r14, %rdi
callq 0xcea6
movq %r15, %rdi
xorl %esi, %esi
xorl %edx, %edx
xorl %ecx, %ecx
callq 0x42ea
movdqu 0x1e8(%rsp), %xmm0
movups 0x1f8(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
movq %r13, %rdi
callq 0xcea6
addq $0x8, %rbx
jmp 0xc161
movq 0x80(%rsp), %rbp
movq 0x88(%rsp), %r15
shlq $0x4, %rbp
xorl %r14d, %r14d
leaq 0xb0(%rsp), %r13
movq %r12, 0xa0(%rsp)
testq %rbp, %rbp
je 0xc289
cmpq %r14, %r12
je 0xc5d9
movb 0x8(%r15), %al
testb $0xf, %al
je 0xc279
movl %eax, %ecx
andb $0x3, %cl
shrb $0x2, %al
andb $0x3, %al
cmpb %cl, %al
je 0xc5f8
testb %cl, %cl
je 0xc279
testb %al, %al
sete %dl
cmpb %cl, %al
setae %al
orb %dl, %al
jne 0xc279
movq 0x60(%rsp), %rax
movq (%rax,%r14,8), %rbx
movq %r13, %rdi
movq %r14, %rsi
callq 0x549e
movl $0x2, (%rax)
callq 0x429c
movq %rax, %r12
movq %r13, %rdi
movq %r14, %rsi
callq 0x549e
movq %r12, 0x18(%rax)
movq %r13, %rdi
movq %r14, %rsi
callq 0x549e
movdqu (%rax), %xmm0
movups 0x10(%rax), %xmm1
movdqu %xmm0, 0x10(%rbx)
movups %xmm1, 0x20(%rbx)
movq 0xa0(%rsp), %r12
incq %r14
addq $0x10, %r15
addq $-0x10, %rbp
jmp 0xc1e6
movq 0x48(%rsp), %rax
cmpq $0x0, 0x18(%rax)
je 0xc617
leaq 0xc798(%rip), %rbx      # 0x18a38
movb 0x4(%rbx), %bpl
callq 0x429c
movq 0x90(%rsp), %r12
movq %rax, %r14
movq %rax, %rdi
callq 0x4294
callq 0x42c3
movq %rax, %r15
movq %rax, %rdi
callq 0x42b8
movb $0x0, 0x4(%rbx)
movq 0x48(%rsp), %rax
movq 0x18(%rax), %rdi
movq (%rdi), %rax
movq %r12, %rsi
callq *0x18(%rax)
movq 0x48(%rsp), %rax
movq 0x10(%rax), %rsi
movq %r12, %rdi
callq 0x8d10
movq 0x48(%rsp), %rax
movq 0x28(%rax), %rcx
testq %rcx, %rcx
jne 0xc30b
leaq 0xc6b0(%rip), %rcx      # 0x189b8
movq (%rcx), %rcx
movq %rcx, 0x60(%rsp)
movq %rcx, 0x28(%rax)
movq %r14, %rdi
callq 0x4294
movq %r15, %rdi
callq 0x42b8
movb %bpl, 0x4(%rbx)
movq 0x80(%rsp), %r15
movq 0x88(%rsp), %r12
shlq $0x4, %r15
xorl %r13d, %r13d
testq %r15, %r15
je 0xc431
movb 0x8(%r12), %al
movl %eax, %ecx
andb $0x3, %cl
je 0xc421
movq (%r12), %r14
shrb $0x2, %al
andb $0x3, %al
sete %dl
cmpb %cl, %al
setae %al
orb %dl, %al
jne 0xc3fe
leaq 0xb0(%rsp), %rdi
movq %r13, %rsi
callq 0x549e
movq 0x18(%rax), %rbx
movq 0x8(%r14), %rbp
leaq 0xf0(%rsp), %rdi
movq %r13, %rsi
callq 0x549e
movups 0x10(%r14), %xmm0
movups 0x20(%r14), %xmm1
movups %xmm1, 0x30(%rsp)
movups %xmm0, 0x20(%rsp)
movups (%rax), %xmm0
movups 0x10(%rax), %xmm1
movups %xmm1, 0x10(%rsp)
movups %xmm0, (%rsp)
leaq 0xd0(%rsp), %rdi
movq %rbp, %rsi
movq 0x98(%rsp), %rdx
movq 0x60(%rsp), %rcx
movq %rbx, %r8
callq 0x4ddf
addq $0x10, %r14
movdqu 0xd0(%rsp), %xmm0
movups 0xe0(%rsp), %xmm1
movups %xmm1, 0x10(%r14)
movdqu %xmm0, (%r14)
jmp 0xc421
leaq 0xf0(%rsp), %rdi
movq %r13, %rsi
callq 0x549e
movdqu (%rax), %xmm0
movups 0x10(%rax), %xmm1
movdqu %xmm0, 0x10(%r14)
movups %xmm1, 0x20(%r14)
incq %r13
addq $0x10, %r12
addq $-0x10, %r15
jmp 0xc33f
movq 0x48(%rsp), %r14
movq 0x18(%r14), %rdi
movq (%rdi), %rax
movq 0x90(%rsp), %rbx
movq %rbx, %rsi
callq *0x18(%rax)
movq 0x10(%r14), %rsi
movq %rbx, %rdi
callq 0x8d10
leaq 0xc55a(%rip), %rcx      # 0x189b8
movl 0x8(%rcx), %eax
movl %eax, 0x130(%rsp)
movups 0xc(%rcx), %xmm0
movups %xmm0, 0x134(%rsp)
movl 0x1c(%rcx), %eax
movl %eax, 0x144(%rsp)
movq 0x20(%rcx), %rax
movq %rax, 0x148(%rsp)
movq 0x50(%rsp), %rdi
movq 0x58(%rsp), %rsi
movdqu 0x130(%rsp), %xmm0
movups 0x140(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
callq 0x4d6f
movq 0x58(%rsp), %rdi
callq 0x4d51
cmpl $0x1, 0x74(%rsp)
jne 0xc4e1
leaq 0x47(%rsp), %rdi
callq 0x311c
addq $0x208, %rsp            # imm = 0x208
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x80(%rsp), %rbx
movq 0x88(%rsp), %r12
shlq $0x4, %rbx
xorl %r14d, %r14d
leaq 0xf0(%rsp), %r15
testq %rbx, %rbx
je 0xc4c5
movq (%r12), %r13
movb 0x8(%r12), %bpl
movq %r15, %rdi
movq %r14, %rsi
callq 0x549e
movups (%rax), %xmm0
movups 0x10(%rax), %xmm1
movaps %xmm0, 0xd0(%rsp)
movaps %xmm1, 0xe0(%rsp)
movdqu 0x10(%r13), %xmm0
movups 0x20(%r13), %xmm1
movaps %xmm1, 0x1b0(%rsp)
movdqa %xmm0, 0x1a0(%rsp)
testb $0x3, %bpl
je 0xc5c9
movq 0x8(%r13), %rsi
movaps 0x1a0(%rsp), %xmm0
movaps 0x1b0(%rsp), %xmm1
movups %xmm1, 0x30(%rsp)
movups %xmm0, 0x20(%rsp)
movdqa 0xd0(%rsp), %xmm0
movaps 0xe0(%rsp), %xmm1
movups %xmm1, 0x10(%rsp)
movdqu %xmm0, (%rsp)
leaq 0x1c8(%rsp), %rdi
movq 0x98(%rsp), %rdx
movq 0x60(%rsp), %rcx
pushq $-0x1
popq %r8
callq 0x4ddf
addq $0x10, %r13
movdqu 0x1c8(%rsp), %xmm0
movups 0x1d8(%rsp), %xmm1
movups %xmm1, 0x10(%r13)
movdqu %xmm0, (%r13)
incq %r14
addq $0x10, %r12
addq $-0x10, %rbx
jmp 0xc500
leaq 0x4f3f(%rip), %rdi      # 0x1151f
leaq 0x3e07(%rip), %rsi      # 0x103ee
leaq 0x4f40(%rip), %rcx      # 0x1152e
movl $0x88a, %edx            # imm = 0x88A
callq 0x20b0
leaq 0x4f62(%rip), %rdi      # 0x11561
leaq 0x3de8(%rip), %rsi      # 0x103ee
leaq 0x4f21(%rip), %rcx      # 0x1152e
movl $0x88d, %edx            # imm = 0x88D
callq 0x20b0
leaq 0x4f7a(%rip), %rdi      # 0x11598
leaq 0x3dc9(%rip), %rsi      # 0x103ee
leaq 0x4f02(%rip), %rcx      # 0x1152e
movl $0x89b, %edx            # imm = 0x89B
callq 0x20b0
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
jmp 0xc64e
movq %rax, %rbx
leaq 0x47(%rsp), %rdi
callq 0x311c
movq %rbx, %rdi
callq 0x21c0
nop