// File: /workspace/github_cpp_makefile_star2_O2/NishadSaraf[P]Championship-Simulator/src/ooo_cpu.cc
// Function: O3_CPU::handle_branch()
void O3_CPU::handle_branch()
{
    // actual processors do not work like this but for easier implementation,
    // we read instruction traces and virtually add them in the ROB
    // note that these traces are not yet translated and fetched 

    uint8_t continue_reading = 1;
    uint32_t num_reads = 0;
    instrs_to_read_this_cycle = FETCH_WIDTH;

    // first, read PIN trace
    while (continue_reading) {

        size_t instr_size = knob_cloudsuite ? sizeof(cloudsuite_instr) : sizeof(input_instr);

        if (knob_cloudsuite) {
            if (!fread(&current_cloudsuite_instr, instr_size, 1, trace_file)) {
                // reached end of file for this trace
                cout << "*** Reached end of trace for Core: " << cpu << " Repeating trace: " << trace_string << endl; 

                // close the trace file and re-open it
                pclose(trace_file);
                trace_file = popen(gunzip_command, "r");
                if (trace_file == NULL) {
                    cerr << endl << "*** CANNOT REOPEN TRACE FILE: " << trace_string << " ***" << endl;
                    assert(0);
                }
            } else { // successfully read the trace

                // copy the instruction into the performance model's instruction format
                ooo_model_instr arch_instr;
                int num_reg_ops = 0, num_mem_ops = 0;

                arch_instr.instr_id = instr_unique_id;
                arch_instr.ip = current_cloudsuite_instr.ip;
                arch_instr.is_branch = current_cloudsuite_instr.is_branch;
                arch_instr.branch_taken = current_cloudsuite_instr.branch_taken;

                arch_instr.asid[0] = current_cloudsuite_instr.asid[0];
                arch_instr.asid[1] = current_cloudsuite_instr.asid[1];

                for (uint32_t i=0; i<MAX_INSTR_DESTINATIONS; i++) {
                    arch_instr.destination_registers[i] = current_cloudsuite_instr.destination_registers[i];
                    arch_instr.destination_memory[i] = current_cloudsuite_instr.destination_memory[i];
                    arch_instr.destination_virtual_address[i] = current_cloudsuite_instr.destination_memory[i];

                    if (arch_instr.destination_registers[i])
                        num_reg_ops++;
                    if (arch_instr.destination_memory[i]) {
                        num_mem_ops++;

                        // update STA, this structure is required to execute store instructios properly without deadlock
                        if (num_mem_ops > 0) {
#ifdef SANITY_CHECK
                            if (STA[STA_tail] < UINT64_MAX) {
                                if (STA_head != STA_tail)
                                    assert(0);
                            }
#endif
                            STA[STA_tail] = instr_unique_id;
                            STA_tail++;

                            if (STA_tail == STA_SIZE)
                                STA_tail = 0;
                        }
                    }
                }

                for (int i=0; i<NUM_INSTR_SOURCES; i++) {
                    arch_instr.source_registers[i] = current_cloudsuite_instr.source_registers[i];
                    arch_instr.source_memory[i] = current_cloudsuite_instr.source_memory[i];
                    arch_instr.source_virtual_address[i] = current_cloudsuite_instr.source_memory[i];

                    if (arch_instr.source_registers[i])
                        num_reg_ops++;
                    if (arch_instr.source_memory[i])
                        num_mem_ops++;
                }

                arch_instr.num_reg_ops = num_reg_ops;
                arch_instr.num_mem_ops = num_mem_ops;
                if (num_mem_ops > 0) 
                    arch_instr.is_memory = 1;

                // virtually add this instruction to the ROB
                if (ROB.occupancy < ROB.SIZE) {
                    uint32_t rob_index = add_to_rob(&arch_instr);
                    num_reads++;

                    // branch prediction
                    if (arch_instr.is_branch) {

                        DP( if (warmup_complete[cpu]) {
                        cout << "[BRANCH] instr_id: " << instr_unique_id << " ip: " << hex << arch_instr.ip << dec << " taken: " << +arch_instr.branch_taken << endl; });

                        num_branch++;

                        /*
                        uint8_t branch_prediction;
                        // for faster simulation, force perfect prediction during the warmup
                        // note that branch predictor is still learning with real branch results
                        if (all_warmup_complete == 0)
                            branch_prediction = arch_instr.branch_taken; 
                        else
                            branch_prediction = predict_branch(arch_instr.ip);
                        */
                        uint8_t branch_prediction = predict_branch(arch_instr.ip);
                        
                        if (arch_instr.branch_taken != branch_prediction) {
                            branch_mispredictions++;

                            DP( if (warmup_complete[cpu]) {
                            cout << "[BRANCH] MISPREDICTED instr_id: " << instr_unique_id << " ip: " << hex << arch_instr.ip << dec;
                            cout << " taken: " << +arch_instr.branch_taken << " predicted: " << +branch_prediction << endl; });

                            // halt any further fetch this cycle
                            instrs_to_read_this_cycle = 0;

                            // and stall any additional fetches until the branch is executed
                            fetch_stall = 1; // FIXME

                            ROB.entry[rob_index].branch_mispredicted = 1;
                        }
                        else {
                            if (branch_prediction == 1) {
                                // if we are accurately predicting a branch to be taken, then we can't possibly fetch down that path this cycle,
                                // so we have to wait until the next cycle to fetch those
                                instrs_to_read_this_cycle = 0;
                            }

                            DP( if (warmup_complete[cpu]) {
                            cout << "[BRANCH] PREDICTED    instr_id: " << instr_unique_id << " ip: " << hex << arch_instr.ip << dec;
                            cout << " taken: " << +arch_instr.branch_taken << " predicted: " << +branch_prediction << endl; });
                        }

                        last_branch_result(arch_instr.ip, arch_instr.branch_taken);
                    }

                    //if ((num_reads == FETCH_WIDTH) || (ROB.occupancy == ROB.SIZE))
                    if ((num_reads >= instrs_to_read_this_cycle) || (ROB.occupancy == ROB.SIZE))
                        continue_reading = 0;
                }
                instr_unique_id++;
            }
        } else {
            if (!fread(&current_instr, instr_size, 1, trace_file)) {
                // reached end of file for this trace
                cout << "*** Reached end of trace for Core: " << cpu << " Repeating trace: " << trace_string << endl; 

                // close the trace file and re-open it
                pclose(trace_file);
                trace_file = popen(gunzip_command, "r");
                if (trace_file == NULL) {
                    cerr << endl << "*** CANNOT REOPEN TRACE FILE: " << trace_string << " ***" << endl;
                    assert(0);
                }
            } else { // successfully read the trace

                // copy the instruction into the performance model's instruction format
                ooo_model_instr arch_instr;
                int num_reg_ops = 0, num_mem_ops = 0;

                arch_instr.instr_id = instr_unique_id;
                arch_instr.ip = current_instr.ip;
                arch_instr.is_branch = current_instr.is_branch;
                arch_instr.branch_taken = current_instr.branch_taken;

                arch_instr.asid[0] = cpu;
                arch_instr.asid[1] = cpu;

                for (uint32_t i=0; i<MAX_INSTR_DESTINATIONS; i++) {
                    arch_instr.destination_registers[i] = current_instr.destination_registers[i];
                    arch_instr.destination_memory[i] = current_instr.destination_memory[i];
                    arch_instr.destination_virtual_address[i] = current_instr.destination_memory[i];

                    if (arch_instr.destination_registers[i])
                        num_reg_ops++;
                    if (arch_instr.destination_memory[i]) {
                        num_mem_ops++;

                        // update STA, this structure is required to execute store instructios properly without deadlock
                        if (num_mem_ops > 0) {
#ifdef SANITY_CHECK
                            if (STA[STA_tail] < UINT64_MAX) {
                                if (STA_head != STA_tail)
                                    assert(0);
                            }
#endif
                            STA[STA_tail] = instr_unique_id;
                            STA_tail++;

                            if (STA_tail == STA_SIZE)
                                STA_tail = 0;
                        }
                    }
                }

                for (int i=0; i<NUM_INSTR_SOURCES; i++) {
                    arch_instr.source_registers[i] = current_instr.source_registers[i];
                    arch_instr.source_memory[i] = current_instr.source_memory[i];
                    arch_instr.source_virtual_address[i] = current_instr.source_memory[i];

                    if (arch_instr.source_registers[i])
                        num_reg_ops++;
                    if (arch_instr.source_memory[i])
                        num_mem_ops++;
                }

                arch_instr.num_reg_ops = num_reg_ops;
                arch_instr.num_mem_ops = num_mem_ops;
                if (num_mem_ops > 0) 
                    arch_instr.is_memory = 1;

                // virtually add this instruction to the ROB
                if (ROB.occupancy < ROB.SIZE) {
                    uint32_t rob_index = add_to_rob(&arch_instr);
                    num_reads++;

                    // branch prediction
                    if (arch_instr.is_branch) {

                        DP( if (warmup_complete[cpu]) {
                        cout << "[BRANCH] instr_id: " << instr_unique_id << " ip: " << hex << arch_instr.ip << dec << " taken: " << +arch_instr.branch_taken << endl; });

                        num_branch++;

                        /*
                        uint8_t branch_prediction;
                        // for faster simulation, force perfect prediction during the warmup
                        // note that branch predictor is still learning with real branch results
                        if (all_warmup_complete == 0)
                            branch_prediction = arch_instr.branch_taken; 
                        else
                            branch_prediction = predict_branch(arch_instr.ip);
                        */
                        uint8_t branch_prediction = predict_branch(arch_instr.ip);
                        
                        if (arch_instr.branch_taken != branch_prediction) {
                            branch_mispredictions++;

                            DP( if (warmup_complete[cpu]) {
                            cout << "[BRANCH] MISPREDICTED instr_id: " << instr_unique_id << " ip: " << hex << arch_instr.ip << dec;
                            cout << " taken: " << +arch_instr.branch_taken << " predicted: " << +branch_prediction << endl; });

                            // halt any further fetch this cycle
                            instrs_to_read_this_cycle = 0;

                            // and stall any additional fetches until the branch is executed
                            fetch_stall = 1; // FIXME

                            ROB.entry[rob_index].branch_mispredicted = 1;
                        }
                        else {
                            if (branch_prediction == 1) {
                                // if we are accurately predicting a branch to be taken, then we can't possibly fetch down that path this cycle,
                                // so we have to wait until the next cycle to fetch those
                                instrs_to_read_this_cycle = 0;
                            }

                            DP( if (warmup_complete[cpu]) {
                            cout << "[BRANCH] PREDICTED    instr_id: " << instr_unique_id << " ip: " << hex << arch_instr.ip << dec;
                            cout << " taken: " << +arch_instr.branch_taken << " predicted: " << +branch_prediction << endl; });
                        }

                        last_branch_result(arch_instr.ip, arch_instr.branch_taken);
                    }

                    //if ((num_reads == FETCH_WIDTH) || (ROB.occupancy == ROB.SIZE))
                    if ((num_reads >= instrs_to_read_this_cycle) || (ROB.occupancy == ROB.SIZE))
                        continue_reading = 0;
                }
                instr_unique_id++;
            }
        }
    }

    //instrs_to_fetch_this_cycle = num_reads;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x778, %rsp            # imm = 0x778
movq %rdi, %r14
movq $0x4, 0x900(%rdi)
leaq 0x850(%rdi), %rax
movq %rax, 0x18(%rsp)
leaq 0x85a(%rdi), %r13
leaq 0x888(%rdi), %rbp
leaq 0x10(%rdi), %rbx
leaq 0x410(%rdi), %r12
leaq 0x810(%rdi), %rax
movq %rax, 0x10(%rsp)
leaq 0x81a(%rdi), %r15
movq $0x0, 0x8(%rsp)
movb $0x1, %cl
testb %cl, %cl
je 0xa221
leaq 0xc57a(%rip), %rax      # 0x16335
cmpb $0x0, (%rax)
movq 0x8(%r14), %rcx
je 0x9e4b
movq 0x18(%rsp), %rdi
pushq $0x60
popq %rsi
pushq $0x1
popq %rdx
callq 0x2300
testq %rax, %rax
jne 0x9ece
movq 0xc1e8(%rip), %rdi      # 0x15fd0
leaq 0x6da9(%rip), %rsi      # 0x10b98
callq 0x2230
movl (%r14), %esi
movq %rax, %rdi
callq 0x2170
movq %rax, %rdi
leaq 0x6db3(%rip), %rsi      # 0x10bbc
callq 0x2230
movq %rax, %rdi
movq %rbx, %rsi
callq 0x2230
movq %rax, %rdi
callq 0x2110
movq 0x8(%r14), %rdi
callq 0x22e0
movq %r12, %rdi
leaq 0x72ac(%rip), %rsi      # 0x110e0
callq 0x20c0
movq %rax, 0x8(%r14)
testq %rax, %rax
jne 0x9db4
jmp 0xa233
movq 0x10(%rsp), %rdi
pushq $0x40
popq %rsi
pushq $0x1
popq %rdx
callq 0x2300
testq %rax, %rax
jne 0xa008
movq 0xc165(%rip), %rdi      # 0x15fd0
leaq 0x6d26(%rip), %rsi      # 0x10b98
callq 0x2230
movl (%r14), %esi
movq %rax, %rdi
callq 0x2170
movq %rax, %rdi
leaq 0x6d30(%rip), %rsi      # 0x10bbc
callq 0x2230
movq %rax, %rdi
movq %rbx, %rsi
callq 0x2230
movq %rax, %rdi
callq 0x2110
movq 0x8(%r14), %rdi
callq 0x22e0
movq %r12, %rdi
leaq 0x7229(%rip), %rsi      # 0x110e0
callq 0x20c0
movq %rax, 0x8(%r14)
testq %rax, %rax
jne 0x9db4
jmp 0xa289
leaq 0x20(%rsp), %rdi
callq 0xd550
movq 0x8b0(%r14), %rcx
movq %rcx, 0x20(%rsp)
movq 0x850(%r14), %rax
movq %rax, 0x28(%rsp)
movb 0x858(%r14), %al
movb %al, 0x68(%rsp)
movb 0x859(%r14), %al
movb %al, 0x6a(%rsp)
movb 0x8a8(%r14), %al
movb %al, 0x7b(%rsp)
movb 0x8a9(%r14), %al
movb %al, 0x7c(%rsp)
leaq 0xc2dd(%rip), %r10      # 0x16200
movb (%r10), %dil
xorl %edx, %edx
xorl %esi, %esi
xorl %eax, %eax
movzbl %dil, %r8d
cmpq %r8, %rdx
jae 0x9fca
movb (%r13,%rdx), %r8b
movb %r8b, 0x9c(%rsp,%rdx)
movq 0xe(%r13,%rdx,8), %r9
movq %r9, 0x5c8(%rsp,%rdx,8)
movq %r9, 0x608(%rsp,%rdx,8)
cmpb $0x1, %r8b
sbbl $-0x1, %eax
testq %r9, %r9
je 0x9fbc
leal 0x1(%rsi), %r8d
testl %esi, %esi
js 0x9fbf
movq 0x2a38(%r14), %rsi
cmpq $-0x1, 0xa30(%r14,%rsi,8)
je 0x9f8e
cmpq %rsi, 0x2a30(%r14)
jne 0xa2e5
movq %rcx, 0xa30(%r14,%rsi,8)
movq 0x2a38(%r14), %rsi
incq %rsi
cmpq $0x400, %rsi            # imm = 0x400
movl $0x0, %edi
cmovneq %rsi, %rdi
movq %rdi, 0x2a38(%r14)
movb (%r10), %dil
jmp 0x9fbf
movl %esi, %r8d
incq %rdx
movl %r8d, %esi
jmp 0x9f2c
xorl %ecx, %ecx
cmpq $0x4, %rcx
je 0xa139
movb -0x2a(%rbp,%rcx), %dl
movb %dl, 0xa0(%rsp,%rcx)
movq (%rbp,%rcx,8), %rdi
movq %rdi, 0x5e8(%rsp,%rcx,8)
movq %rdi, 0x628(%rsp,%rcx,8)
cmpb $0x1, %dl
sbbl $-0x1, %eax
cmpq $0x1, %rdi
sbbl $-0x1, %esi
incq %rcx
jmp 0x9fcc
leaq 0x20(%rsp), %rdi
callq 0xd550
movq 0x8b0(%r14), %rcx
movq %rcx, 0x20(%rsp)
movq 0x810(%r14), %rax
movq %rax, 0x28(%rsp)
movb 0x818(%r14), %al
movb %al, 0x68(%rsp)
movb 0x819(%r14), %al
movb %al, 0x6a(%rsp)
movb (%r14), %al
movb %al, 0x7b(%rsp)
movb %al, 0x7c(%rsp)
leaq 0xc1ae(%rip), %r10      # 0x16200
movb (%r10), %dil
xorl %edx, %edx
xorl %eax, %eax
xorl %esi, %esi
movzbl %dil, %r8d
cmpq %r8, %rdx
jae 0xa0f8
movb (%r15,%rdx), %r8b
movb %r8b, 0x9c(%rsp,%rdx)
movq 0x6(%r15,%rdx,8), %r9
movq %r9, 0x5c8(%rsp,%rdx,8)
movq %r9, 0x608(%rsp,%rdx,8)
cmpb $0x1, %r8b
sbbl $-0x1, %eax
testq %r9, %r9
je 0xa0ea
leal 0x1(%rsi), %r8d
testl %esi, %esi
js 0xa0ed
movq 0x2a38(%r14), %rsi
cmpq $-0x1, 0xa30(%r14,%rsi,8)
je 0xa0bc
cmpq %rsi, 0x2a30(%r14)
jne 0xa302
movq %rcx, 0xa30(%r14,%rsi,8)
movq 0x2a38(%r14), %rsi
incq %rsi
cmpq $0x400, %rsi            # imm = 0x400
movl $0x0, %edi
cmovneq %rsi, %rdi
movq %rdi, 0x2a38(%r14)
movb (%r10), %dil
jmp 0xa0ed
movl %esi, %r8d
incq %rdx
movl %r8d, %esi
jmp 0xa05b
xorl %ecx, %ecx
cmpq $0x4, %rcx
je 0xa139
movb 0x81c(%r14,%rcx), %dl
movb %dl, 0xa0(%rsp,%rcx)
movq 0x830(%r14,%rcx,8), %rdi
movq %rdi, 0x5e8(%rsp,%rcx,8)
movq %rdi, 0x628(%rsp,%rcx,8)
cmpb $0x1, %dl
sbbl $-0x1, %eax
cmpq $0x1, %rdi
sbbl $-0x1, %esi
incq %rcx
jmp 0xa0fa
movl %eax, 0x8c(%rsp)
movl %esi, 0x90(%rsp)
testl %esi, %esi
jle 0xa150
movb $0x1, 0x69(%rsp)
movl 0x960(%r14), %eax
movb $0x1, %cl
cmpl 0x950(%r14), %eax
jae 0xa215
movq %r14, %rdi
leaq 0x20(%rsp), %rsi
callq 0xa322
cmpb $0x0, 0x68(%rsp)
je 0xa1e8
movl %eax, 0x4(%rsp)
incq 0x3680(%r14)
movq 0x28(%rsp), %rsi
movq %r14, %rdi
callq 0xfaec
movb 0x6a(%rsp), %cl
cmpb %al, %cl
jne 0xa1a8
cmpb $0x1, %al
jne 0xa1d8
andq $0x0, 0x900(%r14)
jmp 0xa1d8
incq 0x3688(%r14)
andq $0x0, 0x900(%r14)
movb $0x1, %sil
movb %sil, 0x3678(%r14)
movq 0x9b8(%r14), %rax
movl 0x4(%rsp), %edx
imulq $0x758, %rdx, %rdx      # imm = 0x758
movb %sil, 0x4b(%rax,%rdx)
movq 0x28(%rsp), %rsi
movzbl %cl, %edx
movq %r14, %rdi
callq 0xfb14
movq 0x8(%rsp), %rax
incl %eax
movq %rax, 0x8(%rsp)
movl %eax, %eax
cmpq %rax, 0x900(%r14)
jbe 0xa20f
movl 0x960(%r14), %eax
cmpl 0x950(%r14), %eax
jne 0xa213
xorl %ecx, %ecx
jmp 0xa215
movb $0x1, %cl
incq 0x8b0(%r14)
jmp 0x9dac
addq $0x778, %rsp            # imm = 0x778
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0xbdb6(%rip), %rdi      # 0x15ff0
callq 0x2110
leaq 0x6989(%rip), %rsi      # 0x10bcf
movq %rax, %rdi
callq 0x2230
movq %rax, %rdi
movq %rbx, %rsi
callq 0x2230
leaq 0x6577(%rip), %rsi      # 0x107d7
movq %rax, %rdi
callq 0x2230
movq %rax, %rdi
callq 0x2110
leaq 0x5df0(%rip), %rdi      # 0x10067
leaq 0x6970(%rip), %rsi      # 0x10bee
leaq 0x6978(%rip), %rcx      # 0x10bfd
pushq $0x26
jmp 0xa2fc
movq 0xbd60(%rip), %rdi      # 0x15ff0
callq 0x2110
leaq 0x6933(%rip), %rsi      # 0x10bcf
movq %rax, %rdi
callq 0x2230
movq %rax, %rdi
movq %rbx, %rsi
callq 0x2230
leaq 0x6521(%rip), %rsi      # 0x107d7
movq %rax, %rdi
callq 0x2230
movq %rax, %rdi
callq 0x2110
leaq 0x5d9a(%rip), %rdi      # 0x10067
leaq 0x691a(%rip), %rsi      # 0x10bee
leaq 0x6922(%rip), %rcx      # 0x10bfd
movl $0xa7, %edx
callq 0x2160
leaq 0x5d7b(%rip), %rdi      # 0x10067
leaq 0x68fb(%rip), %rsi      # 0x10bee
leaq 0x6903(%rip), %rcx      # 0x10bfd
pushq $0x45
popq %rdx
callq 0x2160
leaq 0x5d5e(%rip), %rdi      # 0x10067
leaq 0x68de(%rip), %rsi      # 0x10bee
leaq 0x68e6(%rip), %rcx      # 0x10bfd
movl $0xc6, %edx
callq 0x2160
nop