// File: /workspace/github_cpp_makefile_star2_O1/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 0x9790
movq %rdx, %r13
movl %edi, %ebp
orq %rbp, %r13
orq %rbp, %rcx
movq %rcx, (%rsp)
leaq (,%rbp,2), %rax
addq %rbp, %rax
shlq $0x4, %rax
leaq 0x111f4(%rip), %rdi     # 0x1a440
movq %r13, %rcx
shrq $0x6, %rcx
leaq (%rax,%rdi), %rdx
addq $0x8, %rdx
movq 0x8(%rdx), %r8
movq %rdx, %rsi
testq %r8, %r8
je 0x9283
movq %rdx, %rsi
xorl %r9d, %r9d
cmpq %rcx, 0x20(%r8)
setb %r9b
cmovaeq %r8, %rsi
movq 0x10(%r8,%r9,8), %r8
testq %r8, %r8
jne 0x926a
movq %rdx, %r8
cmpq %rdx, %rsi
je 0x9296
cmpq 0x20(%rsi), %rcx
cmovbq %rdx, %rsi
movq %rsi, %r8
cmpq %rdx, %r8
je 0x92a1
addq $0x28, %r8
jmp 0x92c3
addq %rax, %rdi
leaq 0x18(%rsp), %rsi
movq %rcx, (%rsi)
movl $0x0, 0x8(%rsi)
callq 0xb106
leaq 0x111c1(%rip), %rax     # 0x1a480
leaq (%rax,%rbp,8), %r8
incq (%r8)
movq 0x110f3(%rip), %rdx     # 0x1a3c0
leaq 0x110e4(%rip), %rcx     # 0x1a3b8
movq %rcx, %rax
testq %rdx, %rdx
je 0x92ff
leaq 0x110d5(%rip), %rax     # 0x1a3b8
movq (%rsp), %rsi
xorl %edi, %edi
cmpq %rsi, 0x20(%rdx)
setb %dil
cmovaeq %rdx, %rax
movq 0x10(%rdx,%rdi,8), %rdx
testq %rdx, %rdx
jne 0x92e7
cmpq %rcx, %rax
je 0x9312
movq (%rsp), %rcx
cmpq 0x20(%rax), %rcx
jae 0x94a3
cmpq $0x100000, 0x1116b(%rip) # imm = 0x100000
movq %r13, 0x10(%rsp)
movq %rbp, 0x8(%rsp)
jae 0x934f
cmpq $0x0, 0x11147(%rip)     # 0x1a478
je 0x94af
movq 0x11132(%rip), %r14     # 0x1a470
incq %r14
movq %r14, 0x11128(%rip)     # 0x1a470
xorl %ebx, %ebx
jmp 0x94ca
movq 0x11072(%rip), %rax     # 0x1a3c8
leaq 0x1105b(%rip), %rbx     # 0x1a3b8
cmpq %rbx, %rax
je 0x93b2
movq 0x110b7(%rip), %r15     # 0x1a420
leaq 0x110a8(%rip), %r12     # 0x1a418
movq 0x20(%rax), %r14
movq %r12, %rcx
testq %r15, %r15
je 0x939a
movq %r15, %rdx
movq %r12, %rcx
xorl %esi, %esi
cmpq %r14, 0x20(%rdx)
setb %sil
cmovaeq %rdx, %rcx
movq 0x10(%rdx,%rsi,8), %rdx
testq %rdx, %rdx
jne 0x9382
cmpq %r12, %rcx
je 0x93d1
cmpq 0x20(%rcx), %r14
jb 0x93d1
movq %rax, %rdi
callq 0x3190
cmpq %rbx, %rax
jne 0x9370
leaq 0xacae(%rip), %rdi      # 0x14067
leaq 0xb2eb(%rip), %rsi      # 0x146ab
leaq 0xb399(%rip), %rcx      # 0x14760
movl $0x180, %edx            # imm = 0x180
callq 0x3160
cmpq %rbx, %rax
je 0x97ce
movq 0x28(%rax), %rbx
leaq 0x10fcb(%rip), %r15     # 0x1a3b0
movq %r15, %rdi
movq %rax, %rsi
callq 0xb248
movq (%rsp), %rax
leaq 0x18(%rsp), %rsi
movq %rax, (%rsi)
movq %rbx, 0x8(%rsi)
movq %r15, %rdi
callq 0xb280
movq 0x10fe1(%rip), %rdx     # 0x1a3f0
leaq 0x10fd2(%rip), %rax     # 0x1a3e8
movq %rax, %rcx
testq %rdx, %rdx
je 0x943d
leaq 0x10fc3(%rip), %rcx     # 0x1a3e8
xorl %esi, %esi
cmpq %rbx, 0x20(%rdx)
setb %sil
cmovaeq %rdx, %rcx
movq 0x10(%rdx,%rsi,8), %rdx
testq %rdx, %rdx
jne 0x9425
movq %rax, %rdx
cmpq %rax, %rcx
je 0x9454
cmpq 0x20(%rcx), %rbx
leaq 0x10f98(%rip), %rdx     # 0x1a3e8
cmovaeq %rcx, %rdx
cmpq %rax, %rdx
je 0x97ed
movq (%rsp), %rax
movq %rax, 0x28(%rdx)
leaq 0x10ef4(%rip), %rdi     # 0x1a360
callq 0xb30c
movq 0x10f18(%rip), %rax     # 0x1a390
movq 0x10f21(%rip), %rcx     # 0x1a3a0
addq $-0x8, %rcx
cmpq %rcx, %rax
je 0x9624
movq (%rsp), %rcx
movq %rcx, (%rax)
addq $0x8, %rax
movq %rax, 0x10ef2(%rip)     # 0x1a390
jmp 0x9633
leaq 0x10eae(%rip), %r14     # 0x1a358
jmp 0x96fa
leaq 0x1237a(%rip), %rsi     # 0x1b830
leaq 0x12d3b(%rip), %rdi     # 0x1c1f8
movq %rdi, %rdx
callq 0xada8
movq %rax, %r14
movb $0x1, %bl
leaq 0x10f17(%rip), %r13     # 0x1a3e8
movl $0x1, %ebp
leaq 0x12353(%rip), %r15     # 0x1b830
leaq 0x12d14(%rip), %r12     # 0x1c1f8
movq 0x10f05(%rip), %rcx     # 0x1a3f0
movq %r13, %rax
testq %rcx, %rcx
je 0x950d
movq %r13, %rax
xorl %edx, %edx
cmpq %r14, 0x20(%rcx)
setb %dl
cmovaeq %rcx, %rax
movq 0x10(%rcx,%rdx,8), %rcx
testq %rcx, %rcx
jne 0x94f6
cmpq %r13, %rax
je 0x9539
cmpq 0x20(%rax), %r14
jb 0x9539
cmpq $0x0, 0x10f58(%rip)     # 0x1a478
movzbl %bl, %ebx
cmovnel %ebp, %ebx
movq %r12, %rdi
movq %r15, %rsi
movq %r12, %rdx
callq 0xada8
movq %rax, %r14
jmp 0x94e4
movq (%rsp), %rax
leaq 0x18(%rsp), %r15
movq %rax, (%r15)
movq %r14, 0x8(%r15)
leaq 0x10e60(%rip), %rdi     # 0x1a3b0
movq %r15, %rsi
callq 0xb280
movq (%rsp), %rax
movq %r14, (%r15)
movq %rax, 0x8(%r15)
leaq 0x10e76(%rip), %rdi     # 0x1a3e0
leaq 0x18(%rsp), %rsi
callq 0xb280
movq 0x10e15(%rip), %rax     # 0x1a390
movq 0x10e1e(%rip), %rcx     # 0x1a3a0
addq $-0x8, %rcx
cmpq %rcx, %rax
je 0x959f
movq (%rsp), %rcx
movq %rcx, (%rax)
addq $0x8, %rax
movq %rax, 0x10df3(%rip)     # 0x1a390
jmp 0x95ae
leaq 0x10dba(%rip), %rdi     # 0x1a360
movq %rsp, %rsi
callq 0xb356
movq 0x10(%rsp), %r13
movq 0x8(%rsp), %rbp
decq 0x10eb9(%rip)           # 0x1a478
leaq 0x10eca(%rip), %rax     # 0x1a490
incq (%rax,%rbp,8)
incq 0x10eb7(%rip)           # 0x1a488
movq %r14, 0x10e98(%rip)     # 0x1a470
leaq 0x10eb9(%rip), %r15     # 0x1a498
leaq 0x10d72(%rip), %r14     # 0x1a358
testb %bl, %bl
je 0x96f6
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, 0x10e59(%rip)     # 0x1a478
jmp 0x96f6
leaq 0x10d35(%rip), %rdi     # 0x1a360
movq %rsp, %rsi
callq 0xb356
imulq $0x64c80, %rbp, %r12    # imm = 0x64C80
leaq 0x12bff(%rip), %r13     # 0x1c240
leaq (%r12,%r13), %rdi
addq $0x3690, %rdi           # imm = 0x3690
movq %r14, %rsi
callq 0x5922
leaq (%r12,%r13), %rdi
addq $0x13a38, %rdi          # imm = 0x13A38
movq %r14, %rsi
callq 0x5922
leaq (%r12,%r13), %rdi
addq $0x23de0, %rdi          # imm = 0x23DE0
movq %r14, %rsi
callq 0x5922
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 0x5922
movq %r15, %rdi
movq %rbp, %rsi
callq 0x5922
movq %r12, %rdi
movq %rbp, %rsi
callq 0x5922
leaq 0x77813(%rip), %rdi     # 0x80ee0
movq %rbp, %rsi
callq 0x5922
incq %r13
cmpq $0x40, %r13
jne 0x96a1
movq 0x10(%rsp), %r13
movq 0x8(%rsp), %rbp
leaq 0x10c6d(%rip), %r14     # 0x1a35c
leaq 0x10daa(%rip), %r15     # 0x1a4a0
incq (%r15,%rbp,8)
movq 0x10cbf(%rip), %rdx     # 0x1a3c0
leaq 0x10cb0(%rip), %rax     # 0x1a3b8
movq %rax, %rcx
testq %rdx, %rdx
je 0x9733
leaq 0x10ca1(%rip), %rcx     # 0x1a3b8
movq (%rsp), %rsi
xorl %edi, %edi
cmpq %rsi, 0x20(%rdx)
setb %dil
cmovaeq %rdx, %rcx
movq 0x10(%rdx,%rdi,8), %rdx
testq %rdx, %rdx
jne 0x971b
movq %rax, %rdx
cmpq %rax, %rcx
je 0x974e
movq (%rsp), %rdx
cmpq 0x20(%rcx), %rdx
leaq 0x10c6e(%rip), %rdx     # 0x1a3b8
cmovaeq %rcx, %rdx
cmpq %rax, %rdx
je 0x97af
movq 0x28(%rdx), %rax
leaq 0x77762(%rip), %rcx     # 0x80ec0
leaq 0x77763(%rip), %rdx     # 0x80ec8
movl (%r14), %esi
addq (%rcx,%rbp,8), %rsi
movq %rsi, (%rdx,%rbp,8)
shlq $0xc, %rax
andl $0xfff, %r13d           # imm = 0xFFF
orq %rax, %r13
movq %r13, %rax
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0xa8d0(%rip), %rdi      # 0x14067
leaq 0xaf0d(%rip), %rsi      # 0x146ab
leaq 0xafbb(%rip), %rcx      # 0x14760
movl $0x153, %edx            # imm = 0x153
callq 0x3160
leaq 0xa8b1(%rip), %rdi      # 0x14067
leaq 0xaeee(%rip), %rsi      # 0x146ab
leaq 0xaf9c(%rip), %rcx      # 0x14760
movl $0x1ed, %edx            # imm = 0x1ED
callq 0x3160
leaq 0xa892(%rip), %rdi      # 0x14067
leaq 0xaecf(%rip), %rsi      # 0x146ab
leaq 0xaf7d(%rip), %rcx      # 0x14760
movl $0x183, %edx            # imm = 0x183
callq 0x3160
leaq 0xa873(%rip), %rdi      # 0x14067
leaq 0xaeb0(%rip), %rsi      # 0x146ab
leaq 0xaf5e(%rip), %rcx      # 0x14760
movl $0x192, %edx            # imm = 0x192
callq 0x3160