mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Use std::deque<std::vector> for free space (#1323)
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "link/assign.hpp"
|
#include "link/assign.hpp"
|
||||||
#include "link/section.hpp"
|
#include "link/section.hpp"
|
||||||
@@ -27,11 +28,10 @@ struct MemoryLocation {
|
|||||||
struct FreeSpace {
|
struct FreeSpace {
|
||||||
uint16_t address;
|
uint16_t address;
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
FreeSpace *next, *prev;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Table of free space for each bank
|
// Table of free space for each bank
|
||||||
FreeSpace *memory[SECTTYPE_INVALID];
|
std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID];
|
||||||
|
|
||||||
uint64_t nbSectionsToAssign;
|
uint64_t nbSectionsToAssign;
|
||||||
|
|
||||||
@@ -39,20 +39,12 @@ uint64_t nbSectionsToAssign;
|
|||||||
static void initFreeSpace()
|
static void initFreeSpace()
|
||||||
{
|
{
|
||||||
for (enum SectionType type : EnumSeq(SECTTYPE_INVALID)) {
|
for (enum SectionType type : EnumSeq(SECTTYPE_INVALID)) {
|
||||||
memory[type] = (FreeSpace *)malloc(sizeof(*memory[type]) * nbbanks(type));
|
memory[type].resize(nbbanks(type));
|
||||||
if (!memory[type])
|
for (std::deque<FreeSpace> &bankMem : memory[type]) {
|
||||||
err("Failed to init free space for region %d", type);
|
bankMem.push_back({
|
||||||
|
.address = sectionTypeInfo[type].startAddr,
|
||||||
for (uint32_t bank = 0; bank < nbbanks(type); bank++) {
|
.size = sectionTypeInfo[type].size
|
||||||
memory[type][bank].next =
|
});
|
||||||
(FreeSpace *)malloc(sizeof(*memory[type][0].next));
|
|
||||||
if (!memory[type][bank].next)
|
|
||||||
err("Failed to init free space for region %d bank %" PRIu32,
|
|
||||||
type, bank);
|
|
||||||
memory[type][bank].next->address = sectionTypeInfo[type].startAddr;
|
|
||||||
memory[type][bank].next->size = sectionTypeInfo[type].size;
|
|
||||||
memory[type][bank].next->next = nullptr;
|
|
||||||
memory[type][bank].next->prev = &memory[type][bank];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +80,7 @@ static void assignSection(Section *section, MemoryLocation const *location)
|
|||||||
* @param location The location to attempt placing the section at
|
* @param location The location to attempt placing the section at
|
||||||
* @return True if the location is suitable, false otherwise.
|
* @return True if the location is suitable, false otherwise.
|
||||||
*/
|
*/
|
||||||
static bool isLocationSuitable(Section const *section, FreeSpace const *freeSpace,
|
static bool isLocationSuitable(Section const *section, FreeSpace const &freeSpace,
|
||||||
MemoryLocation const *location)
|
MemoryLocation const *location)
|
||||||
{
|
{
|
||||||
if (section->isAddressFixed && section->org != location->address)
|
if (section->isAddressFixed && section->org != location->address)
|
||||||
@@ -97,20 +89,23 @@ static bool isLocationSuitable(Section const *section, FreeSpace const *freeSpac
|
|||||||
if (section->isAlignFixed && ((location->address - section->alignOfs) & section->alignMask))
|
if (section->isAlignFixed && ((location->address - section->alignOfs) & section->alignMask))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (location->address < freeSpace->address)
|
if (location->address < freeSpace.address)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return location->address + section->size <= freeSpace->address + freeSpace->size;
|
return location->address + section->size <= freeSpace.address + freeSpace.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finds a suitable location to place a section at.
|
* Finds a suitable location to place a section at.
|
||||||
* @param section The section to be placed
|
* @param section The section to be placed
|
||||||
* @param location A pointer to a memory location that will be filled
|
* @param location A pointer to a memory location that will be filled
|
||||||
* @return A pointer to the free space encompassing the location, or `nullptr` if none was found
|
* @return The index into `memory[section->type]` of the free space encompassing the location,
|
||||||
|
* or -1 if none was found
|
||||||
*/
|
*/
|
||||||
static FreeSpace *getPlacement(Section const *section, MemoryLocation *location)
|
static ssize_t getPlacement(Section const *section, MemoryLocation *location)
|
||||||
{
|
{
|
||||||
|
SectionTypeInfo const &typeInfo = sectionTypeInfo[section->type];
|
||||||
|
|
||||||
static uint16_t curScrambleROM = 0;
|
static uint16_t curScrambleROM = 0;
|
||||||
static uint8_t curScrambleWRAM = 0;
|
static uint8_t curScrambleWRAM = 0;
|
||||||
static int8_t curScrambleSRAM = 0;
|
static int8_t curScrambleSRAM = 0;
|
||||||
@@ -131,22 +126,22 @@ static FreeSpace *getPlacement(Section const *section, MemoryLocation *location)
|
|||||||
curScrambleSRAM = scrambleSRAM;
|
curScrambleSRAM = scrambleSRAM;
|
||||||
location->bank = curScrambleSRAM--;
|
location->bank = curScrambleSRAM--;
|
||||||
} else {
|
} else {
|
||||||
location->bank = sectionTypeInfo[section->type].firstBank;
|
location->bank = typeInfo.firstBank;
|
||||||
}
|
}
|
||||||
FreeSpace *space;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Switch to the beginning of the next bank
|
// Switch to the beginning of the next bank
|
||||||
#define BANK_INDEX (location->bank - sectionTypeInfo[section->type].firstBank)
|
std::deque<FreeSpace> &bankMem = memory[section->type][location->bank - typeInfo.firstBank];
|
||||||
space = memory[section->type][BANK_INDEX].next;
|
size_t spaceIdx = 0;
|
||||||
if (space)
|
|
||||||
location->address = space->address;
|
if (spaceIdx < bankMem.size())
|
||||||
|
location->address = bankMem[spaceIdx].address;
|
||||||
|
|
||||||
// Process locations in that bank
|
// Process locations in that bank
|
||||||
while (space) {
|
while (spaceIdx < bankMem.size()) {
|
||||||
// If that location is OK, return it
|
// If that location is OK, return it
|
||||||
if (isLocationSuitable(section, space, location))
|
if (isLocationSuitable(section, bankMem[spaceIdx], location))
|
||||||
return space;
|
return spaceIdx;
|
||||||
|
|
||||||
// Go to the next *possible* location
|
// Go to the next *possible* location
|
||||||
if (section->isAddressFixed) {
|
if (section->isAddressFixed) {
|
||||||
@@ -156,8 +151,7 @@ static FreeSpace *getPlacement(Section const *section, MemoryLocation *location)
|
|||||||
if (location->address < section->org)
|
if (location->address < section->org)
|
||||||
location->address = section->org;
|
location->address = section->org;
|
||||||
else
|
else
|
||||||
// Try again in next bank
|
break; // Try again in next bank
|
||||||
space = nullptr;
|
|
||||||
} else if (section->isAlignFixed) {
|
} else if (section->isAlignFixed) {
|
||||||
// Move to next aligned location
|
// Move to next aligned location
|
||||||
// Move back to alignment boundary
|
// Move back to alignment boundary
|
||||||
@@ -168,15 +162,16 @@ static FreeSpace *getPlacement(Section const *section, MemoryLocation *location)
|
|||||||
location->address += section->alignMask + 1 + section->alignOfs;
|
location->address += section->alignMask + 1 + section->alignOfs;
|
||||||
} else {
|
} else {
|
||||||
// Any location is fine, so, next free block
|
// Any location is fine, so, next free block
|
||||||
space = space->next;
|
spaceIdx++;
|
||||||
if (space)
|
if (spaceIdx < bankMem.size())
|
||||||
location->address = space->address;
|
location->address = bankMem[spaceIdx].address;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If that location is past the current block's end,
|
// If that location is past the current block's end,
|
||||||
// go forwards until that is no longer the case.
|
// go forwards until that is no longer the case.
|
||||||
while (space && location->address >= space->address + space->size)
|
while (spaceIdx < bankMem.size() && location->address >=
|
||||||
space = space->next;
|
bankMem[spaceIdx].address + bankMem[spaceIdx].size)
|
||||||
|
spaceIdx++;
|
||||||
|
|
||||||
// Try again with the new location/free space combo
|
// Try again with the new location/free space combo
|
||||||
}
|
}
|
||||||
@@ -185,34 +180,33 @@ static FreeSpace *getPlacement(Section const *section, MemoryLocation *location)
|
|||||||
// Try scrambled banks in descending order until no bank in the scrambled range is available.
|
// Try scrambled banks in descending order until no bank in the scrambled range is available.
|
||||||
// Otherwise, try in ascending order.
|
// Otherwise, try in ascending order.
|
||||||
if (section->isBankFixed) {
|
if (section->isBankFixed) {
|
||||||
return nullptr;
|
return -1;
|
||||||
} else if (scrambleROMX && section->type == SECTTYPE_ROMX && location->bank <= scrambleROMX) {
|
} else if (scrambleROMX && section->type == SECTTYPE_ROMX && location->bank <= scrambleROMX) {
|
||||||
if (location->bank > sectionTypeInfo[section->type].firstBank)
|
if (location->bank > typeInfo.firstBank)
|
||||||
location->bank--;
|
location->bank--;
|
||||||
else if (scrambleROMX < sectionTypeInfo[section->type].lastBank)
|
else if (scrambleROMX < typeInfo.lastBank)
|
||||||
location->bank = scrambleROMX + 1;
|
location->bank = scrambleROMX + 1;
|
||||||
else
|
else
|
||||||
return nullptr;
|
return -1;
|
||||||
} else if (scrambleWRAMX && section->type == SECTTYPE_WRAMX && location->bank <= scrambleWRAMX) {
|
} else if (scrambleWRAMX && section->type == SECTTYPE_WRAMX && location->bank <= scrambleWRAMX) {
|
||||||
if (location->bank > sectionTypeInfo[section->type].firstBank)
|
if (location->bank > typeInfo.firstBank)
|
||||||
location->bank--;
|
location->bank--;
|
||||||
else if (scrambleWRAMX < sectionTypeInfo[section->type].lastBank)
|
else if (scrambleWRAMX < typeInfo.lastBank)
|
||||||
location->bank = scrambleWRAMX + 1;
|
location->bank = scrambleWRAMX + 1;
|
||||||
else
|
else
|
||||||
return nullptr;
|
return -1;
|
||||||
} else if (scrambleSRAM && section->type == SECTTYPE_SRAM && location->bank <= scrambleSRAM) {
|
} else if (scrambleSRAM && section->type == SECTTYPE_SRAM && location->bank <= scrambleSRAM) {
|
||||||
if (location->bank > sectionTypeInfo[section->type].firstBank)
|
if (location->bank > typeInfo.firstBank)
|
||||||
location->bank--;
|
location->bank--;
|
||||||
else if (scrambleSRAM < sectionTypeInfo[section->type].lastBank)
|
else if (scrambleSRAM < typeInfo.lastBank)
|
||||||
location->bank = scrambleSRAM + 1;
|
location->bank = scrambleSRAM + 1;
|
||||||
else
|
else
|
||||||
return nullptr;
|
return -1;
|
||||||
} else if (location->bank < sectionTypeInfo[section->type].lastBank) {
|
} else if (location->bank < typeInfo.lastBank) {
|
||||||
location->bank++;
|
location->bank++;
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return -1;
|
||||||
}
|
}
|
||||||
#undef BANK_INDEX
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,49 +236,35 @@ static void placeSection(Section *section)
|
|||||||
|
|
||||||
// Place section using first-fit decreasing algorithm
|
// Place section using first-fit decreasing algorithm
|
||||||
// https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm
|
// https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm
|
||||||
FreeSpace *freeSpace = getPlacement(section, &location);
|
if (ssize_t spaceIdx = getPlacement(section, &location); spaceIdx != -1) {
|
||||||
|
std::deque<FreeSpace> &bankMem = memory[section->type][location.bank -
|
||||||
|
sectionTypeInfo[section->type].firstBank];
|
||||||
|
FreeSpace &freeSpace = bankMem[spaceIdx];
|
||||||
|
|
||||||
if (freeSpace) {
|
|
||||||
assignSection(section, &location);
|
assignSection(section, &location);
|
||||||
|
|
||||||
// Split the free space
|
// Update the free space
|
||||||
bool noLeftSpace = freeSpace->address == section->org;
|
bool noLeftSpace = freeSpace.address == section->org;
|
||||||
bool noRightSpace = freeSpace->address + freeSpace->size
|
bool noRightSpace = freeSpace.address + freeSpace.size == section->org + section->size;
|
||||||
== section->org + section->size;
|
|
||||||
if (noLeftSpace && noRightSpace) {
|
if (noLeftSpace && noRightSpace) {
|
||||||
// The free space is entirely deleted
|
// The free space is entirely deleted
|
||||||
freeSpace->prev->next = freeSpace->next;
|
bankMem.erase(bankMem.begin() + spaceIdx);
|
||||||
if (freeSpace->next)
|
|
||||||
freeSpace->next->prev = freeSpace->prev;
|
|
||||||
// If the space is the last one on the list, set its
|
|
||||||
// size to 0 so it doesn't get picked, but don't free()
|
|
||||||
// it as it will be freed when cleaning up
|
|
||||||
free(freeSpace);
|
|
||||||
} else if (!noLeftSpace && !noRightSpace) {
|
} else if (!noLeftSpace && !noRightSpace) {
|
||||||
// The free space is split in two
|
// The free space is split in two
|
||||||
FreeSpace *newSpace = (FreeSpace *)malloc(sizeof(*newSpace));
|
// Append the new space after the original one
|
||||||
|
bankMem.insert(bankMem.begin() + spaceIdx + 1, {
|
||||||
if (!newSpace)
|
.address = (uint16_t)(section->org + section->size),
|
||||||
err("Failed to split new free space");
|
.size = (uint16_t)(freeSpace.address + freeSpace.size -
|
||||||
// Append the new space after the chosen one
|
section->org - section->size)
|
||||||
newSpace->prev = freeSpace;
|
});
|
||||||
newSpace->next = freeSpace->next;
|
// Resize the original space (address is unmodified)
|
||||||
if (freeSpace->next)
|
freeSpace.size = section->org - freeSpace.address;
|
||||||
freeSpace->next->prev = newSpace;
|
|
||||||
freeSpace->next = newSpace;
|
|
||||||
// Set its parameters
|
|
||||||
newSpace->address = section->org + section->size;
|
|
||||||
newSpace->size = freeSpace->address + freeSpace->size -
|
|
||||||
newSpace->address;
|
|
||||||
// Set the original space's new parameters
|
|
||||||
freeSpace->size = section->org - freeSpace->address;
|
|
||||||
// address is unmodified
|
|
||||||
} else {
|
} else {
|
||||||
// The amount of free spaces doesn't change: resize!
|
// The amount of free spaces doesn't change: resize!
|
||||||
freeSpace->size -= section->size;
|
freeSpace.size -= section->size;
|
||||||
if (noLeftSpace)
|
if (noLeftSpace)
|
||||||
// The free space is moved *and* resized
|
// The free space is moved *and* resized
|
||||||
freeSpace->address += section->size;
|
freeSpace.address += section->size;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -417,21 +397,3 @@ max_out:
|
|||||||
|
|
||||||
unreachable_();
|
unreachable_();
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign_Cleanup()
|
|
||||||
{
|
|
||||||
for (enum SectionType type : EnumSeq(SECTTYPE_INVALID)) {
|
|
||||||
for (uint32_t bank = 0; bank < nbbanks(type); bank++) {
|
|
||||||
FreeSpace *ptr = memory[type][bank].next;
|
|
||||||
|
|
||||||
while (ptr) {
|
|
||||||
FreeSpace *next = ptr->next;
|
|
||||||
|
|
||||||
free(ptr);
|
|
||||||
ptr = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(memory[type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -470,7 +470,6 @@ int main(int argc, char *argv[])
|
|||||||
reportErrors();
|
reportErrors();
|
||||||
assign_AssignSections();
|
assign_AssignSections();
|
||||||
obj_CheckAssertions();
|
obj_CheckAssertions();
|
||||||
assign_Cleanup();
|
|
||||||
|
|
||||||
// and finally output the result.
|
// and finally output the result.
|
||||||
patch_ApplyPatches();
|
patch_ApplyPatches();
|
||||||
|
|||||||
Reference in New Issue
Block a user