// File: /workspace/github_cpp_makefile_star2_O2/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 $0x38, %rsp
testq %rdx, %rdx
je 0x767b
movq %rcx, %r12
movq %rdx, %rbp
movl %edi, %r15d
pushq $0x1
popq %rdi
callq 0x71e7
movq %r15, %rdi
movl %eax, %esi
callq 0x722b
orq %rax, %rbp
orq %rax, %r12
movq %r12, 0x8(%rsp)
imulq $0x30, %r15, %r14
leaq 0xf18f(%rip), %r13      # 0x16430
leaq (%r14,%r13), %r12
movq %rbp, %rbx
shrq $0x6, %rbx
leaq 0x20(%rsp), %rsi
movq %rbx, (%rsi)
movq %r12, %rdi
callq 0x8b44
leaq (%r14,%r13), %rcx
addq $0x8, %rcx
cmpq %rcx, %rax
je 0x72cf
addq $0x28, %rax
jmp 0x72ee
leaq 0x20(%rsp), %rsi
movq %rbx, (%rsi)
andl $0x0, 0x8(%rsi)
movq %r12, %rdi
callq 0x8b9a
leaq 0xf186(%rip), %rax      # 0x16470
leaq (%rax,%r15,8), %rax
incq (%rax)
leaq 0xf0a8(%rip), %rdi      # 0x163a0
leaq 0x8(%rsp), %rsi
callq 0x8b44
leaq 0xf09f(%rip), %r14      # 0x163a8
cmpq %r14, %rax
je 0x731a
leaq 0xf033(%rip), %r12      # 0x16348
jmp 0x7609
cmpq $0x100000, 0xf153(%rip) # imm = 0x100000
movq %rbp, 0x10(%rsp)
jae 0x7353
cmpq $0x0, 0xf134(%rip)      # 0x16468
je 0x74f6
movq 0xf11f(%rip), %rax      # 0x16460
incq %rax
movq %rax, 0xf115(%rip)      # 0x16460
xorl %r14d, %r14d
jmp 0x7505
movq %r15, 0x18(%rsp)
movq %rsp, %r12
andq $0x0, (%r12)
movq 0xf051(%rip), %r15      # 0x163b8
leaq 0xf092(%rip), %r13      # 0x16400
leaq 0xf093(%rip), %rbx      # 0x16408
cmpq %r14, %r15
je 0x765c
movq 0x20(%r15), %rax
movq %rax, (%rsp)
movq %r13, %rdi
movq %r12, %rsi
callq 0x8b44
cmpq %rbx, %rax
je 0x73a3
movq %r15, %rdi
callq 0x2180
movq %rax, %r15
jmp 0x7375
movq 0x28(%r15), %rbx
leaq 0x30(%rsp), %r12
movq %rbx, (%r12)
leaq 0xefe9(%rip), %r13      # 0x163a0
movq %r13, %rdi
movq %r15, %rsi
callq 0x8d22
movq 0x8(%rsp), %rax
leaq 0x20(%rsp), %rsi
movq %rax, (%rsi)
movq %rbx, 0x8(%rsi)
movq %r13, %rdi
callq 0x8d6e
leaq 0xefee(%rip), %rdi      # 0x163d0
movq %r12, %rsi
callq 0x8b44
leaq 0xefe7(%rip), %rcx      # 0x163d8
cmpq %rcx, %rax
je 0x76b9
leaq 0x8(%rsp), %r15
movq (%r15), %rcx
movq %rcx, 0x28(%rax)
leaq 0xef43(%rip), %r12      # 0x16350
movq %r12, %rdi
callq 0x8e04
movq %r12, %rdi
movq %r15, %rsi
callq 0x8e54
imulq $0x64c80, 0x18(%rsp), %rbx # imm = 0x64C80
leaq 0x10e00(%rip), %r14     # 0x18230
leaq (%r14,%rbx), %rdi
addq $0x3690, %rdi           # imm = 0x3690
movq (%rsp), %rsi
callq 0x462c
leaq (%r14,%rbx), %rdi
addq $0x13a38, %rdi          # imm = 0x13A38
movq (%rsp), %rsi
callq 0x462c
leaq (%r14,%rbx), %rdi
addq $0x23de0, %rdi          # imm = 0x23DE0
movq (%rsp), %rsi
callq 0x462c
leaq (%r14,%rbx), %r15
addq $0x34188, %r15          # imm = 0x34188
leaq (%r14,%rbx), %r12
addq $0x44530, %r12          # imm = 0x44530
leaq (%r14,%rbx), %r13
addq $0x548d8, %r13          # imm = 0x548D8
xorl %ebx, %ebx
leaq 0x75a3a(%rip), %rbp     # 0x7ced0
cmpq $0x40, %rbx
je 0x74d9
movq 0x30(%rsp), %r14
shlq $0x6, %r14
addq %rbx, %r14
movq %r15, %rdi
movq %r14, %rsi
callq 0x462c
movq %r12, %rdi
movq %r14, %rsi
callq 0x462c
movq %r13, %rdi
movq %r14, %rsi
callq 0x462c
movq %rbp, %rdi
movq %r14, %rsi
callq 0x462c
incq %rbx
jmp 0x7496
leaq 0xefb0(%rip), %rbx      # 0x16490
leaq 0xee65(%rip), %r12      # 0x1634c
movq 0x10(%rsp), %rbp
movq 0x18(%rsp), %r15
jmp 0x75fe
leaq 0xef9b(%rip), %rdi      # 0x16498
callq 0x8630
movb $0x1, %r14b
leaq 0xeec4(%rip), %r12      # 0x163d0
movq %rsp, %r13
leaq 0xeec2(%rip), %rbx      # 0x163d8
leaq 0xef7b(%rip), %rbp      # 0x16498
movq %rax, (%rsp)
movq %r12, %rdi
movq %r13, %rsi
callq 0x8b44
cmpq %rbx, %rax
je 0x754e
cmpq $0x0, 0xef2f(%rip)      # 0x16468
movzbl %r14b, %r14d
pushq $0x1
popq %rax
cmovnel %eax, %r14d
movq %rbp, %rdi
callq 0x8630
jmp 0x751d
leaq 0x8(%rsp), %r13
movq (%r13), %rax
movq (%rsp), %rcx
leaq 0x20(%rsp), %r12
movq %rax, (%r12)
movq %rcx, 0x8(%r12)
leaq 0xee30(%rip), %rdi      # 0x163a0
movq %r12, %rsi
callq 0x8d6e
movq (%rsp), %rax
movq (%r13), %rcx
movq %rax, (%r12)
movq %rcx, 0x8(%r12)
leaq 0xee40(%rip), %rdi      # 0x163d0
leaq 0x20(%rsp), %rsi
callq 0x8d6e
leaq 0xedaf(%rip), %rdi      # 0x16350
movq %r13, %rsi
callq 0x8e54
decq 0xeeb8(%rip)            # 0x16468
leaq 0xeec9(%rip), %rax      # 0x16480
incq (%rax,%r15,8)
movq (%rsp), %rax
incq 0xeeb2(%rip)            # 0x16478
movq %rax, 0xee93(%rip)      # 0x16460
leaq 0xeeb4(%rip), %rbx      # 0x16488
leaq 0xed6d(%rip), %r12      # 0x16348
testb %r14b, %r14b
je 0x75f9
callq 0x2090
pushq $0xa
popq %rcx
cltd
idivl %ecx
movl %edx, %ecx
pushq $0x1
popq %rax
shll %cl, %eax
movq %rax, 0xee6f(%rip)      # 0x16468
movq 0x10(%rsp), %rbp
leaq 0xeda3(%rip), %r14      # 0x163a8
incq (%rbx,%r15,8)
leaq 0xed90(%rip), %rdi      # 0x163a0
leaq 0x8(%rsp), %rsi
callq 0x8b44
cmpq %r14, %rax
je 0x769a
movq 0x28(%rax), %rax
leaq 0x75886(%rip), %rcx     # 0x7ceb0
movl (%r12), %edx
addq (%rcx,%r15,8), %rdx
leaq 0x7587f(%rip), %rcx     # 0x7ceb8
movq %rdx, (%rcx,%r15,8)
shlq $0xc, %rax
andl $0xfff, %ebp            # imm = 0xFFF
orq %rax, %rbp
movq %rbp, %rax
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x8a04(%rip), %rdi      # 0x10067
leaq 0x9051(%rip), %rsi      # 0x106bb
leaq 0x90ff(%rip), %rcx      # 0x10770
movl $0x180, %edx            # imm = 0x180
callq 0x2160
leaq 0x89e5(%rip), %rdi      # 0x10067
leaq 0x9032(%rip), %rsi      # 0x106bb
leaq 0x90e0(%rip), %rcx      # 0x10770
movl $0x153, %edx            # imm = 0x153
callq 0x2160
leaq 0x89c6(%rip), %rdi      # 0x10067
leaq 0x9013(%rip), %rsi      # 0x106bb
leaq 0x90c1(%rip), %rcx      # 0x10770
movl $0x1ed, %edx            # imm = 0x1ED
callq 0x2160
leaq 0x89a7(%rip), %rdi      # 0x10067
leaq 0x8ff4(%rip), %rsi      # 0x106bb
leaq 0x90a2(%rip), %rcx      # 0x10770
movl $0x192, %edx            # imm = 0x192
callq 0x2160