// File: /workspace/github_cpp_makefile_star2_O3/NishadSaraf[P]Championship-Simulator/src/main.cc
// Function: va_to_pa(unsigned int, unsigned long, unsigned long, unsigned long)
uint64_t va_to_pa(uint32_t cpu, uint64_t instr_id, uint64_t va, uint64_t unique_vpage)
{
#ifdef SANITY_CHECK
    if (va == 0) 
        assert(0);
#endif

    uint8_t  swap = 0;
    uint64_t high_bit_mask = rotr64(cpu, lg2(NUM_CPUS)),
             unique_va = va | high_bit_mask;
    //uint64_t vpage = unique_va >> PAGE_SHIFT,
    uint64_t vpage = unique_vpage | high_bit_mask,
             voffset = unique_va & ((1<<PAGE_SHIFT) - 1);

    // smart random number generator
    uint64_t random_ppage;

    map <uint64_t, uint64_t>::iterator pr = page_table.begin();
    map <uint64_t, uint64_t>::iterator ppage_check = inverse_table.begin();

    // check unique cache line footprint
    map <uint64_t, uint64_t>::iterator cl_check = unique_cl[cpu].find(unique_va >> LOG2_BLOCK_SIZE);
    if (cl_check == unique_cl[cpu].end()) { // we've never seen this cache line before
        unique_cl[cpu].insert(make_pair(unique_va >> LOG2_BLOCK_SIZE, 0));
        num_cl[cpu]++;
    }
    else
        cl_check->second++;

    pr = page_table.find(vpage);
    if (pr == page_table.end()) { // no VA => PA translation found 

        if (allocated_pages >= DRAM_PAGES) { // not enough memory

            // TODO: elaborate page replacement algorithm
            // here, ChampSim randomly selects a page that is not recently used and we only track 32K recently accessed pages
            uint8_t  found_NRU = 0;
            uint64_t NRU_vpage = 0; // implement it
            map <uint64_t, uint64_t>::iterator pr2 = recent_page.begin();
            for (pr = page_table.begin(); pr != page_table.end(); pr++) {

                NRU_vpage = pr->first;
                if (recent_page.find(NRU_vpage) == recent_page.end()) {
                    found_NRU = 1;
                    break;
                }
            }
#ifdef SANITY_CHECK
            if (found_NRU == 0)
                assert(0);

            if (pr == page_table.end())
                assert(0);
#endif
            DP ( if (warmup_complete[cpu]) {
            cout << "[SWAP] update page table NRU_vpage: " << hex << pr->first << " new_vpage: " << vpage << " ppage: " << pr->second << dec << endl; });

            // update page table with new VA => PA mapping
            // since we cannot change the key value already inserted in a map structure, we need to erase the old node and add a new node
            uint64_t mapped_ppage = pr->second;
            page_table.erase(pr);
            page_table.insert(make_pair(vpage, mapped_ppage));

            // update inverse table with new PA => VA mapping
            ppage_check = inverse_table.find(mapped_ppage);
#ifdef SANITY_CHECK
            if (ppage_check == inverse_table.end())
                assert(0);
#endif
            ppage_check->second = vpage;

            DP ( if (warmup_complete[cpu]) {
            cout << "[SWAP] update inverse table NRU_vpage: " << hex << NRU_vpage << " new_vpage: ";
            cout << ppage_check->second << " ppage: " << ppage_check->first << dec << endl; });

            // update page_queue
            page_queue.pop();
            page_queue.push(vpage);

            // invalidate corresponding vpage and ppage from the cache hierarchy
            ooo_cpu[cpu].ITLB.invalidate_entry(NRU_vpage);
            ooo_cpu[cpu].DTLB.invalidate_entry(NRU_vpage);
            ooo_cpu[cpu].STLB.invalidate_entry(NRU_vpage);
            for (uint32_t i=0; i<BLOCK_SIZE; i++) {
                uint64_t cl_addr = (mapped_ppage << 6) | i;
                ooo_cpu[cpu].L1I.invalidate_entry(cl_addr);
                ooo_cpu[cpu].L1D.invalidate_entry(cl_addr);
                ooo_cpu[cpu].L2C.invalidate_entry(cl_addr);
                uncore.LLC.invalidate_entry(cl_addr);
            }

            // swap complete
            swap = 1;
        } else {
            uint8_t fragmented = 0;
            if (num_adjacent_page > 0)
                random_ppage = ++previous_ppage;
            else {
                random_ppage = champsim_rand.draw_rand();
                fragmented = 1;
            }

            // encoding cpu number 
            // this allows ChampSim to run homogeneous multi-programmed workloads without VA => PA aliasing
            // (e.g., cpu0: astar  cpu1: astar  cpu2: astar  cpu3: astar...)
            //random_ppage &= (~((NUM_CPUS-1)<< (32-PAGE_SHIFT)));
            //random_ppage |= (cpu<<(32-PAGE_SHIFT)); 

            while (1) { // try to find an empty physical page number
                ppage_check = inverse_table.find(random_ppage); // check if this page can be allocated 
                if (ppage_check != inverse_table.end()) { // random_ppage is not available
                    DP ( if (warmup_complete[cpu]) {
                    cout << "vpage: " << hex << ppage_check->first << " is already mapped to ppage: " << random_ppage << dec << endl; }); 
                    
                    if (num_adjacent_page > 0)
                        fragmented = 1;

                    // try one more time
                    random_ppage = champsim_rand.draw_rand();
                    
                    // encoding cpu number 
                    //random_ppage &= (~((NUM_CPUS-1)<<(32-PAGE_SHIFT)));
                    //random_ppage |= (cpu<<(32-PAGE_SHIFT)); 
                }
                else
                    break;
            }

            // insert translation to page tables
            //printf("Insert  num_adjacent_page: %u  vpage: %lx  ppage: %lx\n", num_adjacent_page, vpage, random_ppage);
            page_table.insert(make_pair(vpage, random_ppage));
            inverse_table.insert(make_pair(random_ppage, vpage));
            page_queue.push(vpage);
            previous_ppage = random_ppage;
            num_adjacent_page--;
            num_page[cpu]++;
            allocated_pages++;

            // try to allocate pages contiguously
            if (fragmented) {
                num_adjacent_page = 1 << (rand() % 10);
                DP ( if (warmup_complete[cpu]) {
                cout << "Recalculate num_adjacent_page: " << num_adjacent_page << endl; });
            }
        }

        if (swap)
            major_fault[cpu]++;
        else
            minor_fault[cpu]++;
    }
    else {
        //printf("Found  vpage: %lx  random_ppage: %lx\n", vpage, pr->second);
    }

    pr = page_table.find(vpage);
#ifdef SANITY_CHECK
    if (pr == page_table.end())
        assert(0);
#endif
    uint64_t ppage = pr->second;

    uint64_t pa = ppage << PAGE_SHIFT;
    pa |= voffset;

    DP ( if (warmup_complete[cpu]) {
    cout << "[PAGE_TABLE] instr_id: " << instr_id << " vpage: " << hex << vpage;
    cout << " => ppage: " << (pa >> PAGE_SHIFT) << " vadress: " << unique_va << " paddress: " << pa << dec << endl; });

    if (swap)
        stall_cycle[cpu] = current_core_cycle[cpu] + SWAP_LATENCY;
    else
        stall_cycle[cpu] = current_core_cycle[cpu] + PAGE_TABLE_LATENCY;

    //cout << "cpu: " << cpu << " allocated unique_vpage: " << hex << unique_vpage << " to ppage: " << ppage << dec << endl;

    return pa;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x28, %rsp
testq %rdx, %rdx
je 0x9828
movq %rdx, %rbp
movl %edi, %ebx
orq %rbx, %rbp
orq %rbx, %rcx
movq %rcx, (%rsp)
leaq (%rbx,%rbx,2), %rax
shlq $0x4, %rax
leaq 0x1118e(%rip), %rdi     # 0x1a440
movq %rbp, %rcx
shrq $0x6, %rcx
movq 0x10(%rax,%rdi), %rsi
testq %rsi, %rsi
je 0x92f6
leaq (%rax,%rdi), %r8
addq $0x8, %r8
movq %r8, %rdx
xorl %r9d, %r9d
cmpq %rcx, 0x20(%rsi)
setb %r9b
cmovaeq %rsi, %rdx
movq 0x10(%rsi,%r9,8), %rsi
testq %rsi, %rsi
jne 0x92ce
cmpq %r8, %rdx
je 0x92f6
cmpq 0x20(%rdx), %rcx
jae 0x944b
addq %rax, %rdi
leaq 0x18(%rsp), %rsi
movq %rcx, (%rsi)
movl $0x0, 0x8(%rsi)
callq 0xb178
leaq 0x1116c(%rip), %rax     # 0x1a480
leaq (%rax,%rbx,8), %rdx
incq (%rdx)
movq 0x1109e(%rip), %rdx     # 0x1a3c0
testq %rdx, %rdx
je 0x9360
leaq 0x1108a(%rip), %rax     # 0x1a3b8
movq (%rsp), %rcx
xorl %esi, %esi
cmpq %rcx, 0x20(%rdx)
setb %sil
cmovaeq %rdx, %rax
movq 0x10(%rdx,%rsi,8), %rdx
testq %rdx, %rdx
jne 0x9332
leaq 0x11067(%rip), %rdx     # 0x1a3b8
cmpq %rdx, %rax
je 0x9360
cmpq 0x20(%rax), %rcx
jae 0x9454
cmpq $0x100000, 0x1111d(%rip) # imm = 0x100000
movq %rbp, 0x10(%rsp)
movq %rbx, 0x8(%rsp)
jae 0x939d
cmpq $0x0, 0x110f9(%rip)     # 0x1a478
je 0x9460
movq 0x110e4(%rip), %r14     # 0x1a470
incq %r14
movq %r14, 0x110da(%rip)     # 0x1a470
xorl %ebp, %ebp
jmp 0x947c
movq 0x11024(%rip), %rbx     # 0x1a3c8
leaq 0x1100d(%rip), %r15     # 0x1a3b8
cmpq %r15, %rbx
je 0x9404
movq 0x11069(%rip), %r12     # 0x1a420
leaq 0x1105a(%rip), %r13     # 0x1a418
movq %rbx, %rax
movq 0x20(%rax), %r14
testq %r12, %r12
je 0x95dd
movq %r12, %rdx
movq %r13, %rcx
xorl %esi, %esi
cmpq %r14, 0x20(%rdx)
setb %sil
cmovaeq %rdx, %rcx
movq 0x10(%rdx,%rsi,8), %rdx
testq %rdx, %rdx
jne 0x93d4
cmpq %r13, %rcx
je 0x9423
cmpq 0x20(%rcx), %r14
jb 0x9423
movq %rax, %rdi
callq 0x3190
cmpq %r15, %rax
jne 0x93c1
leaq 0xac5c(%rip), %rdi      # 0x14067
leaq 0xb2d9(%rip), %rsi      # 0x146eb
leaq 0xb387(%rip), %rcx      # 0x147a0
movl $0x180, %edx            # imm = 0x180
callq 0x3160
cmpq %r15, %rax
jne 0x95e0
leaq 0xac34(%rip), %rdi      # 0x14067
leaq 0xb2b1(%rip), %rsi      # 0x146eb
leaq 0xb35f(%rip), %rcx      # 0x147a0
movl $0x183, %edx            # imm = 0x183
callq 0x3160
addq $0x28, %rdx
jmp 0x9318
leaq 0x10efd(%rip), %r14     # 0x1a358
jmp 0x976d
leaq 0x123c9(%rip), %rsi     # 0x1b830
leaq 0x12d8a(%rip), %rdi     # 0x1c1f8
movq %rdi, %rdx
callq 0xae0e
movq %rax, %r14
movb $0x1, %bpl
movq 0x10f6d(%rip), %rax     # 0x1a3f0
testq %rax, %rax
je 0x94f5
leaq 0x10f59(%rip), %rbx     # 0x1a3e8
movl $0x1, %r13d
leaq 0x12394(%rip), %r15     # 0x1b830
leaq 0x12d55(%rip), %r12     # 0x1c1f8
movq %rbx, %rcx
xorl %edx, %edx
cmpq %r14, 0x20(%rax)
setb %dl
cmovaeq %rax, %rcx
movq 0x10(%rax,%rdx,8), %rax
testq %rax, %rax
jne 0x94a6
cmpq %rbx, %rcx
je 0x94f5
cmpq 0x20(%rcx), %r14
jb 0x94f5
cmpq $0x0, 0x10fa8(%rip)     # 0x1a478
movzbl %bpl, %ebp
cmovnel %r13d, %ebp
movq %r12, %rdi
movq %r15, %rsi
movq %r12, %rdx
callq 0xae0e
movq %rax, %r14
movq 0x10f00(%rip), %rax     # 0x1a3f0
testq %rax, %rax
jne 0x94a3
movq (%rsp), %rax
leaq 0x18(%rsp), %r15
movq %rax, (%r15)
movq %r14, 0x8(%r15)
leaq 0x10ea4(%rip), %rdi     # 0x1a3b0
movq %r15, %rsi
callq 0xb2f2
movq (%rsp), %rax
movq %r14, (%r15)
movq %rax, 0x8(%r15)
leaq 0x10eba(%rip), %rdi     # 0x1a3e0
leaq 0x18(%rsp), %rsi
callq 0xb2f2
movq 0x10e59(%rip), %rax     # 0x1a390
movq 0x10e62(%rip), %rcx     # 0x1a3a0
addq $-0x8, %rcx
cmpq %rcx, %rax
je 0x955b
movq (%rsp), %rcx
movq %rcx, (%rax)
addq $0x8, %rax
movq %rax, 0x10e37(%rip)     # 0x1a390
jmp 0x956a
leaq 0x10dfe(%rip), %rdi     # 0x1a360
movq %rsp, %rsi
callq 0xb3c8
movq 0x8(%rsp), %rbx
decq 0x10f02(%rip)           # 0x1a478
leaq 0x10f13(%rip), %rax     # 0x1a490
incq (%rax,%rbx,8)
incq 0x10f00(%rip)           # 0x1a488
movq %r14, 0x10ee1(%rip)     # 0x1a470
leaq 0x10f02(%rip), %r15     # 0x1a498
leaq 0x10dbb(%rip), %r14     # 0x1a358
testb %bpl, %bpl
je 0x95d3
callq 0x30a0
movslq %eax, %rcx
imulq $0x66666667, %rcx, %rax # imm = 0x66666667
movq %rax, %rdx
shrq $0x3f, %rdx
shrq $0x22, %rax
addl %edx, %eax
addl %eax, %eax
leal (%rax,%rax,4), %eax
subl %eax, %ecx
movl $0x1, %eax
shll %cl, %eax
movq %rax, 0x10ea5(%rip)     # 0x1a478
movq 0x10(%rsp), %rbp
jmp 0x9769
movq %rbx, %rax
movq 0x28(%rax), %rbx
leaq 0x10dc5(%rip), %r15     # 0x1a3b0
movq %r15, %rdi
movq %rax, %rsi
callq 0xb2ba
movq (%rsp), %rax
leaq 0x18(%rsp), %rsi
movq %rax, (%rsi)
movq %rbx, 0x8(%rsi)
movq %r15, %rdi
callq 0xb2f2
movq 0x10ddb(%rip), %rcx     # 0x1a3f0
testq %rcx, %rcx
je 0x9809
leaq 0x10dc3(%rip), %rax     # 0x1a3e8
xorl %edx, %edx
cmpq %rbx, 0x20(%rcx)
setb %dl
cmovaeq %rcx, %rax
movq 0x10(%rcx,%rdx,8), %rcx
testq %rcx, %rcx
jne 0x9625
leaq 0x10da5(%rip), %rcx     # 0x1a3e8
cmpq %rcx, %rax
je 0x9809
cmpq 0x20(%rax), %rbx
jb 0x9809
movq (%rsp), %rcx
movq %rcx, 0x28(%rax)
leaq 0x10cfb(%rip), %rdi     # 0x1a360
callq 0xb37e
movq 0x10d1f(%rip), %rax     # 0x1a390
movq 0x10d28(%rip), %rcx     # 0x1a3a0
addq $-0x8, %rcx
cmpq %rcx, %rax
je 0x9695
movq (%rsp), %rcx
movq %rcx, (%rax)
addq $0x8, %rax
movq %rax, 0x10cfd(%rip)     # 0x1a390
jmp 0x96a4
leaq 0x10cc4(%rip), %rdi     # 0x1a360
movq %rsp, %rsi
callq 0xb3c8
imulq $0x64c80, 0x8(%rsp), %r12 # imm = 0x64C80
leaq 0x12b8c(%rip), %r13     # 0x1c240
leaq (%r12,%r13), %rdi
addq $0x3690, %rdi           # imm = 0x3690
movq %r14, %rsi
callq 0x5956
leaq (%r12,%r13), %rdi
addq $0x13a38, %rdi          # imm = 0x13A38
movq %r14, %rsi
callq 0x5956
leaq (%r12,%r13), %rdi
addq $0x23de0, %rdi          # imm = 0x23DE0
movq %r14, %rsi
callq 0x5956
shlq $0x6, %rbx
leaq (%r12,%r13), %r14
addq $0x34188, %r14          # imm = 0x34188
leaq (%r12,%r13), %r15
addq $0x44530, %r15          # imm = 0x44530
addq %r13, %r12
addq $0x548d8, %r12          # imm = 0x548D8
xorl %r13d, %r13d
leaq (%rbx,%r13), %rbp
movq %r14, %rdi
movq %rbp, %rsi
callq 0x5956
movq %r15, %rdi
movq %rbp, %rsi
callq 0x5956
movq %r12, %rdi
movq %rbp, %rsi
callq 0x5956
leaq 0x777a0(%rip), %rdi     # 0x80ee0
movq %rbp, %rsi
callq 0x5956
incq %r13
cmpq $0x40, %r13
jne 0x9714
movq 0x10(%rsp), %rbp
movq 0x8(%rsp), %rbx
leaq 0x10bfa(%rip), %r14     # 0x1a35c
leaq 0x10d37(%rip), %r15     # 0x1a4a0
incq (%r15,%rbx,8)
movq 0x10c4c(%rip), %rdx     # 0x1a3c0
testq %rdx, %rdx
je 0x97ea
leaq 0x10c38(%rip), %rax     # 0x1a3b8
movq (%rsp), %rcx
xorl %esi, %esi
cmpq %rcx, 0x20(%rdx)
setb %sil
cmovaeq %rdx, %rax
movq 0x10(%rdx,%rsi,8), %rdx
testq %rdx, %rdx
jne 0x9784
leaq 0x10c15(%rip), %rdx     # 0x1a3b8
cmpq %rdx, %rax
je 0x97ea
cmpq 0x20(%rax), %rcx
jb 0x97ea
movq 0x28(%rax), %rax
leaq 0x77707(%rip), %rcx     # 0x80ec0
movl (%r14), %edx
addq (%rcx,%rbx,8), %rdx
leaq 0x77701(%rip), %rcx     # 0x80ec8
movq %rdx, (%rcx,%rbx,8)
shlq $0xc, %rax
andl $0xfff, %ebp            # imm = 0xFFF
orq %rax, %rbp
movq %rbp, %rax
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0xa876(%rip), %rdi      # 0x14067
leaq 0xaef3(%rip), %rsi      # 0x146eb
leaq 0xafa1(%rip), %rcx      # 0x147a0
movl $0x1ed, %edx            # imm = 0x1ED
callq 0x3160
leaq 0xa857(%rip), %rdi      # 0x14067
leaq 0xaed4(%rip), %rsi      # 0x146eb
leaq 0xaf82(%rip), %rcx      # 0x147a0
movl $0x192, %edx            # imm = 0x192
callq 0x3160
leaq 0xa838(%rip), %rdi      # 0x14067
leaq 0xaeb5(%rip), %rsi      # 0x146eb
leaq 0xaf63(%rip), %rcx      # 0x147a0
movl $0x153, %edx            # imm = 0x153
callq 0x3160