Use musttail attribute to guarantee tail recursion (#1849)

This commit is contained in:
Rangi
2025-10-20 15:56:22 -04:00
committed by GitHub
parent 1badba03d8
commit 2873e0b8c8
3 changed files with 25 additions and 9 deletions

View File

@@ -60,4 +60,13 @@
#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

@@ -744,7 +744,7 @@ static int peek() {
lexerState->expansionScanDistance += str->length(); lexerState->expansionScanDistance += str->length();
} }
return peek(); // Tail recursion MUSTTAIL return peek();
} else if (c == '{') { } else if (c == '{') {
// If character is an open brace, do symbol interpolation // If character is an open brace, do symbol interpolation
shiftChar(); shiftChar();
@@ -752,7 +752,7 @@ static int peek() {
beginExpansion(interp.second, interp.first->name); beginExpansion(interp.second, interp.first->name);
} }
return peek(); // Tail recursion MUSTTAIL return peek();
} else { } else {
return c; return c;
} }
@@ -1695,7 +1695,7 @@ static Token yylex_NORMAL() {
return Token(nextToken); return Token(nextToken);
} }
for (;; lexerState->atLineStart = false) { for (;;) {
int c = bumpChar(); int c = bumpChar();
switch (c) { switch (c) {
@@ -1707,7 +1707,7 @@ static Token yylex_NORMAL() {
case ' ': case ' ':
case '\t': case '\t':
continue; break;
// Handle unambiguous single-char tokens // Handle unambiguous single-char tokens
@@ -1759,7 +1759,7 @@ static Token yylex_NORMAL() {
if (peek() == '*') { if (peek() == '*') {
shiftChar(); shiftChar();
discardBlockComment(); discardBlockComment();
continue; break;
} }
return oneOrTwo('=', T_(POP_DIVEQ), T_(OP_DIV)); return oneOrTwo('=', T_(POP_DIVEQ), T_(OP_DIV));
@@ -1897,7 +1897,7 @@ static Token yylex_NORMAL() {
// Macro args were handled by `peek`, and character escapes do not exist // Macro args were handled by `peek`, and character escapes do not exist
// outside of string literals, so this must be a line continuation. // outside of string literals, so this must be a line continuation.
discardLineContinuation(); discardLineContinuation();
continue; break;
// Handle raw strings... or fall through if '#' is not followed by '"' // Handle raw strings... or fall through if '#' is not followed by '"'
@@ -1918,7 +1918,7 @@ static Token yylex_NORMAL() {
c = bumpChar(); c = bumpChar();
} else if (!startsIdentifier(c)) { } else if (!startsIdentifier(c)) {
reportGarbageCharacters(c); reportGarbageCharacters(c);
continue; break;
} }
Token token = readIdentifier(c, raw); Token token = readIdentifier(c, raw);
@@ -1943,7 +1943,9 @@ 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);
return yylex_NORMAL(); // Tail recursion // 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
} }
} }
@@ -1968,6 +1970,10 @@ static Token yylex_NORMAL() {
return token; return token;
} }
// If we exited the switch, i.e. read some characters without yet returning a token,
// we can't be at the start of the line
lexerState->atLineStart = false;
} }
} }

View File

@@ -15,6 +15,7 @@
#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"
@@ -203,7 +204,7 @@ static std::optional<size_t> getPlacement(Section const &section, MemoryLocation
return std::nullopt; return std::nullopt;
} }
return getPlacement(section, location); // Tail recursion MUSTTAIL return getPlacement(section, location);
} }
static std::string getSectionDescription(Section const &section) { static std::string getSectionDescription(Section const &section) {