Compare commits

..

2 Commits

Author SHA1 Message Date
Rangi42
df5162edca Use loops instead of tail calls and musttail
gcc 15.2.1 20250813 complains "address of automatic variable can
escape to `musttail` call" from `-Wmaybe-musttail-local-addr`,
and guaranteeing tail-call optimization cross-platform is more
trouble than it's worth.
2025-10-27 12:05:27 -04:00
Rangi42
2519d1e698 Mention REDEF and FOR regarding EQUS expansion
Fixes #1851
2025-10-27 10:54:16 -04:00
4 changed files with 116 additions and 124 deletions

View File

@@ -60,13 +60,4 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#endif #endif
// gcc and clang have their own `musttail` attributes for tail recursion
#if defined(__clang__) && __has_cpp_attribute(clang::musttail)
#define MUSTTAIL [[clang::musttail]]
#elif defined(__GNUC__) && __has_cpp_attribute(gnu::musttail)
#define MUSTTAIL [[gnu::musttail]]
#else
#define MUSTTAIL
#endif
#endif // RGBDS_PLATFORM_HPP #endif // RGBDS_PLATFORM_HPP

View File

@@ -154,6 +154,8 @@ of string constants:
will be expanded in all of will be expanded in all of
.Ql DEF({name}) , .Ql DEF({name}) ,
.Ql DEF {name} EQU/=/EQUS/etc ... , .Ql DEF {name} EQU/=/EQUS/etc ... ,
.Ql REDEF {name} EQU/=/EQUS/etc ... ,
.Ql FOR {name}, ... ,
.Ql PURGE {name} , .Ql PURGE {name} ,
and and
.Ql MACRO {name} , .Ql MACRO {name} ,
@@ -1556,6 +1558,8 @@ in the C programming language.
This expansion is disabled in a few contexts: This expansion is disabled in a few contexts:
.Ql DEF(name) , .Ql DEF(name) ,
.Ql DEF name EQU/=/EQUS/etc ... , .Ql DEF name EQU/=/EQUS/etc ... ,
.Ql REDEF name EQU/=/EQUS/etc ... ,
.Ql FOR name, ... ,
.Ql PURGE name , .Ql PURGE name ,
and and
.Ql MACRO name .Ql MACRO name

View File

@@ -716,45 +716,44 @@ int LexerState::peekCharAhead() {
static std::pair<Symbol const *, std::shared_ptr<std::string>> readInterpolation(size_t depth); static std::pair<Symbol const *, std::shared_ptr<std::string>> readInterpolation(size_t depth);
static int peek() { static int peek() {
int c = lexerState->peekChar(); for (;;) {
int c = lexerState->peekChar();
if (lexerState->expansionScanDistance > 0) { if (lexerState->expansionScanDistance > 0) {
return c;
}
++lexerState->expansionScanDistance; // Do not consider again
if (lexerState->disableExpansions) {
return c;
} else if (c == '\\') {
// If character is a backslash, check for a macro arg
++lexerState->expansionScanDistance;
if (!isMacroChar(lexerState->peekCharAhead())) {
return c; return c;
} }
// If character is a macro arg char, do macro arg expansion ++lexerState->expansionScanDistance; // Do not consider again
shiftChar();
if (std::shared_ptr<std::string> str = readMacroArg(); str) {
beginExpansion(str, std::nullopt);
// Mark the entire macro arg expansion as "painted blue" if (lexerState->disableExpansions) {
// so that macro args can't be recursive return c;
// https://en.wikipedia.org/wiki/Painted_blue } else if (c == '\\') {
lexerState->expansionScanDistance += str->length(); // If character is a backslash, check for a macro arg
++lexerState->expansionScanDistance;
if (!isMacroChar(lexerState->peekCharAhead())) {
return c;
}
// If character is a macro arg char, do macro arg expansion
shiftChar();
if (std::shared_ptr<std::string> str = readMacroArg(); str) {
beginExpansion(str, std::nullopt);
// Mark the entire macro arg expansion as "painted blue"
// so that macro args can't be recursive
// https://en.wikipedia.org/wiki/Painted_blue
lexerState->expansionScanDistance += str->length();
}
// Continue in the next iteration
} else if (c == '{') {
// If character is an open brace, do symbol interpolation
shiftChar();
if (auto interp = readInterpolation(0); interp.first && interp.second) {
beginExpansion(interp.second, interp.first->name);
}
// Continue in the next iteration
} else {
return c;
} }
MUSTTAIL return peek();
} else if (c == '{') {
// If character is an open brace, do symbol interpolation
shiftChar();
if (auto interp = readInterpolation(0); interp.first && interp.second) {
beginExpansion(interp.second, interp.first->name);
}
MUSTTAIL return peek();
} else {
return c;
} }
} }
@@ -1943,8 +1942,6 @@ static Token yylex_NORMAL() {
if (Symbol const *sym = sym_FindExactSymbol(std::get<std::string>(token.value)); if (Symbol const *sym = sym_FindExactSymbol(std::get<std::string>(token.value));
sym && sym->type == SYM_EQUS) { sym && sym->type == SYM_EQUS) {
beginExpansion(sym->getEqus(), sym->name); beginExpansion(sym->getEqus(), sym->name);
// We cannot do `MUSTTAIL return yylex_NORMAL();` because tail call optimization
// requires the return value to be "trivially destructible", and `Token` is not.
continue; // Restart, reading from the new buffer continue; // Restart, reading from the new buffer
} }
} }

View File

@@ -15,7 +15,6 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "itertools.hpp" #include "itertools.hpp"
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "platform.hpp" // MUSTTAIL
#include "verbosity.hpp" #include "verbosity.hpp"
#include "link/main.hpp" #include "link/main.hpp"
@@ -119,92 +118,93 @@ static MemoryLocation getStartLocation(Section const &section) {
static std::optional<size_t> getPlacement(Section const &section, MemoryLocation &location) { static std::optional<size_t> getPlacement(Section const &section, MemoryLocation &location) {
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type]; SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
// Switch to the beginning of the next bank for (;;) {
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank - typeInfo.firstBank]; // Switch to the beginning of the next bank
size_t spaceIdx = 0; std::deque<FreeSpace> &bankMem = memory[section.type][location.bank - typeInfo.firstBank];
size_t spaceIdx = 0;
if (spaceIdx < bankMem.size()) { if (spaceIdx < bankMem.size()) {
location.address = bankMem[spaceIdx].address;
}
// Process locations in that bank
while (spaceIdx < bankMem.size()) {
// If that location is OK, return it
if (isLocationSuitable(section, bankMem[spaceIdx], location)) {
return spaceIdx;
}
// Go to the next *possible* location
if (section.isAddressFixed) {
// If the address is fixed, there can be only one candidate block per bank;
// if we already reached it, give up.
if (location.address < section.org) {
location.address = section.org;
} else {
break; // Try again in next bank
}
} else if (section.isAlignFixed) {
// Move to next aligned location
// Move back to alignment boundary
location.address -= section.alignOfs;
// Ensure we're there (e.g. on first check)
location.address &= ~section.alignMask;
// Go to next align boundary and add offset
location.address += section.alignMask + 1 + section.alignOfs;
} else if (++spaceIdx < bankMem.size()) {
// Any location is fine, so, next free block
location.address = bankMem[spaceIdx].address; location.address = bankMem[spaceIdx].address;
} }
// If that location is past the current block's end, // Process locations in that bank
// go forwards until that is no longer the case. while (spaceIdx < bankMem.size()) {
while (spaceIdx < bankMem.size() // If that location is OK, return it
&& location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size) { if (isLocationSuitable(section, bankMem[spaceIdx], location)) {
++spaceIdx; return spaceIdx;
}
// Go to the next *possible* location
if (section.isAddressFixed) {
// If the address is fixed, there can be only one candidate block per bank;
// if we already reached it, give up and try again in the next bank.
if (location.address >= section.org) {
break;
}
location.address = section.org;
} else if (section.isAlignFixed) {
// Move to next aligned location
// Move back to alignment boundary
location.address -= section.alignOfs;
// Ensure we're there (e.g. on first check)
location.address &= ~section.alignMask;
// Go to next align boundary and add offset
location.address += section.alignMask + 1 + section.alignOfs;
} else if (++spaceIdx < bankMem.size()) {
// Any location is fine, so, next free block
location.address = bankMem[spaceIdx].address;
}
// If that location is past the current block's end,
// go forwards until that is no longer the case.
while (spaceIdx < bankMem.size()
&& location.address >= 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 // Try again in the next bank, if one is available.
// Try scrambled banks in descending order until no bank in the scrambled range is
// available. Otherwise, try in ascending order.
if (section.isBankFixed) {
return std::nullopt;
} else if (options.scrambleROMX && section.type == SECTTYPE_ROMX
&& location.bank <= options.scrambleROMX) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleROMX < typeInfo.lastBank) {
location.bank = options.scrambleROMX + 1;
} else {
return std::nullopt;
}
} else if (options.scrambleWRAMX && section.type == SECTTYPE_WRAMX
&& location.bank <= options.scrambleWRAMX) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleWRAMX < typeInfo.lastBank) {
location.bank = options.scrambleWRAMX + 1;
} else {
return std::nullopt;
}
} else if (options.scrambleSRAM && section.type == SECTTYPE_SRAM
&& location.bank <= options.scrambleSRAM) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleSRAM < typeInfo.lastBank) {
location.bank = options.scrambleSRAM + 1;
} else {
return std::nullopt;
}
} else if (location.bank < typeInfo.lastBank) {
++location.bank;
} else {
return std::nullopt;
}
// Try again in the next iteration.
} }
// Try again in the next bank, if one is available.
// Try scrambled banks in descending order until no bank in the scrambled range is
// available. Otherwise, try in ascending order.
if (section.isBankFixed) {
return std::nullopt;
} else if (options.scrambleROMX && section.type == SECTTYPE_ROMX
&& location.bank <= options.scrambleROMX) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleROMX < typeInfo.lastBank) {
location.bank = options.scrambleROMX + 1;
} else {
return std::nullopt;
}
} else if (options.scrambleWRAMX && section.type == SECTTYPE_WRAMX
&& location.bank <= options.scrambleWRAMX) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleWRAMX < typeInfo.lastBank) {
location.bank = options.scrambleWRAMX + 1;
} else {
return std::nullopt;
}
} else if (options.scrambleSRAM && section.type == SECTTYPE_SRAM
&& location.bank <= options.scrambleSRAM) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleSRAM < typeInfo.lastBank) {
location.bank = options.scrambleSRAM + 1;
} else {
return std::nullopt;
}
} else if (location.bank < typeInfo.lastBank) {
++location.bank;
} else {
return std::nullopt;
}
MUSTTAIL return getPlacement(section, location);
} }
static std::string getSectionDescription(Section const &section) { static std::string getSectionDescription(Section const &section) {