not64/r4300/Recomp-Cache-Heap.c
Extrems d7ca5e913f Added GC Loader 2.0.0 write support.
Added M.2 Loader support.
Added directory structure creation.
Added USB storage device name compatibility.
Fixed Wii Remote extension controller hotplug.
Fixed LoadButtonSlot argument.
Fixed RSP DMA memory alignment.
Fixed viewport flipping.
Other minor fixes.
2021-08-16 22:52:09 -04:00

344 lines
9.3 KiB
C

/**
* Wii64 - Recomp-Cache-Heap.c
* Copyright (C) 2009, 2010 Mike Slegeir
*
* Cache for recompiled code using a heap to maintain LRU order
*
* Wii64 homepage: http://www.emulatemii.com
* email address: tehpola@gmail.com
*
*
* This program is free software; you can redistribute it and/
* or modify it under the terms of the GNU General Public Li-
* cence as published by the Free Software Foundation; either
* version 2 of the Licence, or any later version.
*
* This program is distributed in the hope that it will be use-
* ful, but WITHOUT ANY WARRANTY; without even the implied war-
* ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public Licence for more details.
*
**/
#include <ogc/lwp_heap.h>
#include <stdlib.h>
#include <malloc.h>
#include "../gc_memory/MEM2.h"
#include "r4300.h"
#include "ppc/Recompile.h"
#include "ppc/Wrappers.h"
#include "Invalid_Code.h"
#include "Recomp-Cache.h"
#include "ARAM-blocks.h"
typedef struct _meta_node {
unsigned int addr;
PowerPC_func* func;
unsigned int size;
} CacheMetaNode;
unsigned int nextLRU = 0;
static heap_cntrl* cache = NULL, * meta_cache = NULL;
static int cacheSize = 0;
#define HEAP_CHILD1(i) ((i<<1)+1)
#define HEAP_CHILD2(i) ((i<<1)+2)
#define HEAP_PARENT(i) ((i-1)>>2)
#define INITIAL_HEAP_SIZE (64)
static unsigned int heapSize = 0;
static unsigned int maxHeapSize = 0;
static CacheMetaNode** cacheHeap = NULL;
static void heapSwap(int i, int j){
CacheMetaNode* t = cacheHeap[i];
cacheHeap[i] = cacheHeap[j];
cacheHeap[j] = t;
}
static void heapUp(int i){
// While the given element is out of order
while(i && cacheHeap[i]->func->lru < cacheHeap[HEAP_PARENT(i)]->func->lru){
// Swap the child with its parent
heapSwap(i, HEAP_PARENT(i));
// Consider its new position
i = HEAP_PARENT(i);
}
}
static void heapDown(int i){
// While the given element is out of order
while(1){
unsigned int lru = cacheHeap[i]->func->lru;
CacheMetaNode* c1 = (HEAP_CHILD1(i) < heapSize) ?
cacheHeap[HEAP_CHILD1(i)] : NULL;
CacheMetaNode* c2 = (HEAP_CHILD2(i) < heapSize) ?
cacheHeap[HEAP_CHILD2(i)] : NULL;
// Check against the children, swapping with the min if parent isn't
if(c1 && lru > c1->func->lru &&
(!c2 || c1->func->lru < c2->func->lru)){
heapSwap(i, HEAP_CHILD1(i));
i = HEAP_CHILD1(i);
} else if(c2 && lru > c2->func->lru){
heapSwap(i, HEAP_CHILD2(i));
i = HEAP_CHILD2(i);
} else break;
}
}
static void heapify(void){
int i;
for(i=1; i<heapSize; ++i) heapUp(i);
}
static void heapPush(CacheMetaNode* node){
if(heapSize == maxHeapSize){
maxHeapSize = 3*maxHeapSize/2 + 10;
cacheHeap = realloc(cacheHeap, maxHeapSize*sizeof(void*));
}
// Simply add it to the end of the heap
// No need to heapUp since its the most recently used
cacheHeap[heapSize++] = node;
}
static CacheMetaNode* heapPop(void){
heapSwap(0, --heapSize);
heapDown(0);
return cacheHeap[heapSize];
}
static void unlink_func(PowerPC_func* func){
// start_section(UNLINK_SECTION);
// Remove any incoming links to this func
PowerPC_func_link_node* link, * next_link;
for(link = func->links_in; link != NULL; link = next_link){
next_link = link->next;
GEN_ORI(*(link->branch-10), 0, 0, 0);
GEN_ORI(*(link->branch-9), 0, 0, 0);
GEN_BLR(*link->branch, 1); // Set the linking branch to blrl
DCFlushRange(link->branch-10, 11*sizeof(PowerPC_instr));
ICInvalidateRange(link->branch-10, 11*sizeof(PowerPC_instr));
remove_func(&link->func->links_out, func);
MetaCache_Free(link);
}
func->links_in = NULL;
// Remove any references to outgoing links from this func
void remove_outgoing_links(PowerPC_func_node** node){
if(!*node) return;
if((*node)->left) remove_outgoing_links(&(*node)->left);
if((*node)->right) remove_outgoing_links(&(*node)->right);
// Remove any links this function has which point in the code
PowerPC_func_link_node** link, ** next;
for(link = &(*node)->function->links_in; *link != NULL; link = next){
next = &(*link)->next;
if((*link)->func == func){
PowerPC_func_link_node* tmp = (*link)->next;
MetaCache_Free(*link);
*link = tmp;
next = link;
}
}
MetaCache_Free(*node); // Free the PowerPC_func_node*
}
remove_outgoing_links(&func->links_out);
func->links_out = NULL;
// end_section(UNLINK_SECTION);
}
static void free_func(PowerPC_func* func, unsigned int addr){
// Free the code associated with the func
__lwp_heap_free(cache, func->code);
MetaCache_Free(func->code_addr);
// Remove any holes into this func
PowerPC_func_hole_node* hole, * next_hole;
for(hole = func->holes; hole != NULL; hole = next_hole){
next_hole = hole->next;
free(hole);
}
// Remove any pointers to this code
// Remove the func from the block
PowerPC_block* block = blocks_get(addr>>12);
remove_func(&block->funcs, func);
// Remove func links
unlink_func(func);
free(func);
}
static inline void update_lru(PowerPC_func* func){
func->lru = nextLRU++;
if(!nextLRU || nextLRU>0x80000000){
// Handle nextLRU overflows
// By heap-sorting and assigning new LRUs
heapify();
// Since you can't do an in-place min-heap ascending-sort
// I have to create a new heap
CacheMetaNode** newHeap = malloc(maxHeapSize * sizeof(CacheMetaNode*));
int i, savedSize = heapSize;
for(i=0; heapSize > 0; ++i){
newHeap[i] = heapPop();
newHeap[i]->func->lru = i;
}
free(cacheHeap);
cacheHeap = newHeap;
nextLRU = heapSize = savedSize;
}
}
static void release(int minNeeded){
// Frees alloc'ed blocks so that at least minNeeded bytes are available
int toFree = minNeeded * 2; // Free 2x what is needed
// Restore the heap properties to pop the LRU
heapify();
// Release nodes' memory until we've freed enough
while(toFree > 0 && cacheSize){
// Pop the LRU to be freed
CacheMetaNode* n = heapPop();
// Free the function it contains
free_func(n->func, n->addr);
toFree -= n->size;
cacheSize -= n->size;
// And the cache node itself
free(n);
}
}
void RecompCache_Alloc(unsigned int size, unsigned int address, PowerPC_func* func){
// Allocate new memory for this code
void* code = __lwp_heap_allocate(cache, size);
while(!code){
release(size);
code = __lwp_heap_allocate(cache, size);
}
int num_instrs = (func->end_addr - func->start_addr) >> 2;
void* code_addr = MetaCache_Alloc(num_instrs * sizeof(void*));
CacheMetaNode* newBlock = malloc(sizeof(CacheMetaNode));
newBlock->addr = address;
newBlock->size = size;
newBlock->func = func;
cacheSize += size;
newBlock->func->code = code;
newBlock->func->code_addr = code_addr;
// Add it to the heap
heapPush(newBlock);
// Make this function the LRU
update_lru(func);
}
void RecompCache_Realloc(PowerPC_func* func, unsigned int new_size){
// There should be no need for the code to be preserved
__lwp_heap_free(cache, func->code);
func->code = __lwp_heap_allocate(cache, new_size);
while(!func->code){
release(new_size);
func->code = __lwp_heap_allocate(cache, new_size);
}
// Update the size for the cache
int i;
for(i=heapSize-1; i>=0; --i){
if(cacheHeap[i]->func == func){
cacheSize += new_size - cacheHeap[i]->size;
cacheHeap[i]->size = new_size;
break;
}
}
// Remove any func links since the code has changed
unlink_func(func);
}
void RecompCache_Free(unsigned int addr){
int i;
CacheMetaNode* n = NULL;
// Find the corresponding node
for(i=heapSize-1; i>=0; --i){
if(cacheHeap[i]->addr == addr){
n = cacheHeap[i];
// Remove from the heap
heapSwap(i, --heapSize);
// Free n's func
free_func(n->func, addr);
cacheSize -= n->size;
// Free the cache node
free(n);
return;
}
}
}
void RecompCache_Update(PowerPC_func* func){
update_lru(func);
}
void RecompCache_Link(PowerPC_func* src_func, PowerPC_instr* src_instr,
PowerPC_func* dst_func, PowerPC_instr* dst_instr){
// start_section(LINK_SECTION);
// Setup book-keeping
// Create the incoming link info
PowerPC_func_link_node* fln =
MetaCache_Alloc(sizeof(PowerPC_func_link_node));
fln->branch = src_instr;
fln->func = src_func;
fln->next = dst_func->links_in;
dst_func->links_in = fln;
// Create the outgoing link info
insert_func(&src_func->links_out, dst_func);
// Actually link the funcs
GEN_LIS(*(src_instr-10), DYNAREG_FUNC, (unsigned int)dst_func>>16);
GEN_ORI(*(src_instr-9), DYNAREG_FUNC,DYNAREG_FUNC, (unsigned int)dst_func);
GEN_B(*src_instr, (PowerPC_instr*)dst_instr-src_instr, 0, 0);
DCFlushRange(src_instr-10, 11*sizeof(PowerPC_instr));
ICInvalidateRange(src_instr-10, 11*sizeof(PowerPC_instr));
// end_section(LINK_SECTION);
}
void RecompCache_Init(void){
if(!cache){
cache = malloc(sizeof(heap_cntrl));
__lwp_heap_init(cache, memalign(32,RECOMP_CACHE_SIZE),
RECOMP_CACHE_SIZE, 32);
}
if(!meta_cache){
meta_cache = malloc(sizeof(heap_cntrl));
#ifdef HW_RVL
__lwp_heap_init(meta_cache, RECOMPMETA_LO,
RECOMPMETA_SIZE, 32);
#else
__lwp_heap_init(meta_cache, memalign(32,1*1024*1024),
1*1024*1024, 32);
#endif
}
}
void* MetaCache_Alloc(unsigned int size){
void* ptr = __lwp_heap_allocate(meta_cache, size);
// While there's no room to allocate, call release
while(!ptr){
release(size);
ptr = __lwp_heap_allocate(meta_cache, size);
}
return ptr;
}
void MetaCache_Free(void* ptr){
__lwp_heap_free(meta_cache, ptr);
}