diff --git a/include/file.hpp b/include/file.hpp index 947753cb..e9d98e31 100644 --- a/include/file.hpp +++ b/include/file.hpp @@ -3,7 +3,6 @@ #ifndef RGBDS_FILE_HPP #define RGBDS_FILE_HPP -#include #include #include #include @@ -13,6 +12,7 @@ #include #include +#include "helpers.hpp" // assume #include "platform.hpp" #include "gfx/main.hpp" @@ -33,7 +33,7 @@ public: if (path != "-") { return _file.emplace().open(path, mode) ? this : nullptr; } else if (mode & std::ios_base::in) { - assert(!(mode & std::ios_base::out)); + assume(!(mode & std::ios_base::out)); _file.emplace(std::cin.rdbuf()); if (setmode(STDIN_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) { fatal( @@ -43,7 +43,7 @@ public: ); } } else { - assert(mode & std::ios_base::out); + assume(mode & std::ios_base::out); _file.emplace(std::cout.rdbuf()); } return this; diff --git a/include/helpers.hpp b/include/helpers.hpp index 5ad570d0..04f2fde6 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -3,12 +3,12 @@ #ifndef HELPERS_H #define HELPERS_H -// Ideally, we'd use `__has_attribute` and `__has_builtin`, but these were only introduced in GCC 9 +// Ideally we'd use `std::unreachable`, but it has insufficient compiler support #ifdef __GNUC__ // GCC or compatible - // In release builds, define "unreachable" as such, but trap in debug builds #ifdef NDEBUG #define unreachable_ __builtin_unreachable #else + // In release builds, define "unreachable" as such, but trap in debug builds #define unreachable_ __builtin_trap #endif #else @@ -18,32 +18,52 @@ } #endif -// Use builtins whenever possible, and shim them otherwise +// Ideally we'd use `[[assume()]]`, but it has insufficient compiler support +#ifdef NDEBUG + #ifdef _MSC_VER + #define assume(x) __assume(x) + #else + // `[[gnu::assume()]]` for GCC or compatible also has insufficient support (GCC 13+ only) + #define assume(x) \ + do { \ + if (!(x)) \ + unreachable_(); \ + } while (0) + #endif +#else + // In release builds, define "assume" as such, but `assert` in debug builds + #include + #define assume assert +#endif + +// Ideally we'd use `std::bit_width`, but it has insufficient compiler support #ifdef __GNUC__ // GCC or compatible #define ctz __builtin_ctz #define clz __builtin_clz #elif defined(_MSC_VER) - #include #include #pragma intrinsic(_BitScanReverse, _BitScanForward) + static inline int ctz(unsigned int x) { unsigned long cnt; - assert(x != 0); + assume(x != 0); _BitScanForward(&cnt, x); return cnt; } + static inline int clz(unsigned int x) { unsigned long cnt; - assert(x != 0); + assume(x != 0); _BitScanReverse(&cnt, x); return 31 - cnt; } #else #include + static inline int ctz(unsigned int x) { int cnt = 0; diff --git a/include/linkdefs.hpp b/include/linkdefs.hpp index a3c9430b..c9348f0d 100644 --- a/include/linkdefs.hpp +++ b/include/linkdefs.hpp @@ -3,10 +3,11 @@ #ifndef RGBDS_LINKDEFS_H #define RGBDS_LINKDEFS_H -#include #include #include +#include "helpers.hpp" // assume + #define RGBDS_OBJECT_VERSION_STRING "RGBA" #define RGBDS_OBJECT_REV 10U @@ -93,7 +94,7 @@ extern struct SectionTypeInfo { * @return `true` if the section's definition includes data */ static inline bool sect_HasData(SectionType type) { - assert(type != SECTTYPE_INVALID); + assume(type != SECTTYPE_INVALID); return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX; } diff --git a/src/asm/fstack.cpp b/src/asm/fstack.cpp index 93aade5a..4365de82 100644 --- a/src/asm/fstack.cpp +++ b/src/asm/fstack.cpp @@ -3,7 +3,6 @@ #include "asm/fstack.hpp" #include -#include #include #include #include @@ -50,28 +49,28 @@ static std::vector includePaths = {""}; static std::string preIncludeName; std::vector &FileStackNode::iters() { - assert(std::holds_alternative>(data)); + assume(std::holds_alternative>(data)); return std::get>(data); } std::vector const &FileStackNode::iters() const { - assert(std::holds_alternative>(data)); + assume(std::holds_alternative>(data)); return std::get>(data); } std::string &FileStackNode::name() { - assert(std::holds_alternative(data)); + assume(std::holds_alternative(data)); return std::get(data); } std::string const &FileStackNode::name() const { - assert(std::holds_alternative(data)); + assume(std::holds_alternative(data)); return std::get(data); } std::string const &FileStackNode::dump(uint32_t curLineNo) const { if (std::holds_alternative>(data)) { - assert(parent); // REPT nodes use their parent's name + assume(parent); // REPT nodes use their parent's name std::string const &lastName = parent->dump(lineNo); fputs(" -> ", stderr); fputs(lastName.c_str(), stderr); @@ -270,7 +269,7 @@ static void newMacroContext(Symbol const ¯o, std::shared_ptr macr fileInfoName.append(macro.name); auto fileInfo = std::make_shared(NODE_MACRO, fileInfoName); - assert(!contextStack.empty()); // The top level context cannot be a MACRO + assume(!contextStack.empty()); // The top level context cannot be a MACRO fileInfo->parent = oldContext.fileInfo; fileInfo->lineNo = lexer_GetLineNo(); @@ -295,7 +294,7 @@ static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint } auto fileInfo = std::make_shared(NODE_REPT, fileInfoIters); - assert(!contextStack.empty()); // The top level context cannot be a REPT + assume(!contextStack.empty()); // The top level context cannot be a REPT fileInfo->parent = oldContext.fileInfo; fileInfo->lineNo = reptLineNo; diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index 2511124d..3ca0728c 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -20,7 +19,7 @@ #include #endif -#include "helpers.hpp" // QUOTEDSTRLEN +#include "helpers.hpp" // assume, QUOTEDSTRLEN #include "util.hpp" #include "asm/fixpoint.hpp" @@ -477,14 +476,14 @@ LexerState::~LexerState() { // scheduled at EOF; `lexerStateEOL` thus becomes a (weak) ref to that lexer state... // It has been possible, due to a bug, that the corresponding fstack context gets popped // before EOL, deleting the associated state... but it would still be switched to at EOL. - // This assertion checks that this doesn't happen again. + // This assumption checks that this doesn't happen again. // It could be argued that deleting a state that's scheduled for EOF could simply clear // `lexerStateEOL`, but there's currently no situation in which this should happen. - assert(this != lexerStateEOL); + assume(this != lexerStateEOL); } bool Expansion::advance() { - assert(offset <= size()); + assume(offset <= size()); offset++; return offset > size(); } @@ -494,11 +493,11 @@ BufferedContent::~BufferedContent() { } void BufferedContent::advance() { - assert(offset < LEXER_BUF_SIZE); + assume(offset < LEXER_BUF_SIZE); offset++; if (offset == LEXER_BUF_SIZE) offset = 0; // Wrap around if necessary - assert(size > 0); + assume(size > 0); size--; } @@ -528,7 +527,7 @@ void BufferedContent::refill() { size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) { // This buffer overflow made me lose WEEKS of my life. Never again. - assert(startIndex + nbChars <= LEXER_BUF_SIZE); + assume(startIndex + nbChars <= LEXER_BUF_SIZE); ssize_t nbReadChars = read(fd, &buf[startIndex], nbChars); if (nbReadChars == -1) @@ -671,7 +670,7 @@ static std::shared_ptr readMacroArg(char name) { error("Invalid macro argument '\\0'\n"); return nullptr; } else { - assert(name > '0' && name <= '9'); + assume(name > '0' && name <= '9'); MacroArgs *macroArgs = fstk_GetCurrentMacroArgs(); if (!macroArgs) { @@ -698,11 +697,11 @@ int LexerState::peekChar() { if (view->offset < view->span.size) return (uint8_t)view->span.ptr[view->offset]; } else { - assert(std::holds_alternative(content)); + assume(std::holds_alternative(content)); auto &cbuf = std::get(content); if (cbuf.size == 0) cbuf.refill(); - assert(cbuf.offset < LEXER_BUF_SIZE); + assume(cbuf.offset < LEXER_BUF_SIZE); if (cbuf.size > 0) return (uint8_t)cbuf.buf[cbuf.offset]; } @@ -718,7 +717,7 @@ int LexerState::peekCharAhead() { for (Expansion &exp : expansions) { // An expansion that has reached its end will have `exp.offset` == `exp.size()`, // and `.peekCharAhead()` will continue with its parent - assert(exp.offset <= exp.size()); + assume(exp.offset <= exp.size()); if (exp.offset + distance < exp.size()) return (uint8_t)(*exp.contents)[exp.offset + distance]; distance -= exp.size() - exp.offset; @@ -728,9 +727,9 @@ int LexerState::peekCharAhead() { if (view->offset + distance < view->span.size) return (uint8_t)view->span.ptr[view->offset + distance]; } else { - assert(std::holds_alternative(content)); + assume(std::holds_alternative(content)); auto &cbuf = std::get(content); - assert(distance < LEXER_BUF_SIZE); + assume(distance < LEXER_BUF_SIZE); if (cbuf.size <= distance) cbuf.refill(); if (cbuf.size > distance) @@ -816,7 +815,7 @@ restart: if (auto *view = std::get_if(&lexerState->content); view) { view->offset++; } else { - assert(std::holds_alternative(lexerState->content)); + assume(std::holds_alternative(lexerState->content)); auto &cbuf = std::get(lexerState->content); cbuf.advance(); } @@ -1785,7 +1784,7 @@ static Token yylex_NORMAL() { return token; // `token` is either an `ID` or a `LOCAL_ID`, and both have a `std::string` value. - assert(std::holds_alternative(token.value)); + assume(std::holds_alternative(token.value)); // Local symbols cannot be string expansions if (token.type == T_(ID) && lexerState->expandStrings) { @@ -1795,7 +1794,7 @@ static Token yylex_NORMAL() { if (sym && sym->type == SYM_EQUS) { std::shared_ptr str = sym->getEqus(); - assert(str); + assume(str); beginExpansion(str, sym->name); continue; // Restart, reading from the new buffer } @@ -2172,7 +2171,7 @@ yy::parser::symbol_type yylex() { } else if (auto *strValue = std::get_if(&token.value); strValue) { return yy::parser::symbol_type(token.type, *strValue); } else { - assert(std::holds_alternative(token.value)); + assume(std::holds_alternative(token.value)); return yy::parser::symbol_type(token.type); } } @@ -2180,10 +2179,10 @@ yy::parser::symbol_type yylex() { static Capture startCapture() { // Due to parser internals, it reads the EOL after the expression before calling this. // Thus, we don't need to keep one in the buffer afterwards. - // The following assertion checks that. - assert(lexerState->atLineStart); + // The following assumption checks that. + assume(lexerState->atLineStart); - assert(!lexerState->capturing && lexerState->captureBuf == nullptr); + assume(!lexerState->capturing && lexerState->captureBuf == nullptr); lexerState->capturing = true; lexerState->captureSize = 0; @@ -2194,7 +2193,7 @@ static Capture startCapture() { .lineNo = lineNo, .span = {.ptr = view->makeSharedContentPtr(), .size = 0} }; } else { - assert(lexerState->captureBuf == nullptr); + assume(lexerState->captureBuf == nullptr); lexerState->captureBuf = std::make_shared>(); // `.span.ptr == nullptr`; indicates to retrieve the capture buffer when done capturing return { diff --git a/src/asm/output.cpp b/src/asm/output.cpp index ccba20a3..b6b0ccbc 100644 --- a/src/asm/output.cpp +++ b/src/asm/output.cpp @@ -2,7 +2,6 @@ #include "asm/output.hpp" -#include #include #include #include @@ -12,7 +11,7 @@ #include #include "error.hpp" -#include "helpers.hpp" // Defer +#include "helpers.hpp" // assume, Defer #include "asm/fstack.hpp" #include "asm/lexer.hpp" @@ -75,7 +74,7 @@ static uint32_t getSectIDIfAny(Section *sect) { // Write a patch to a file static void writepatch(Patch const &patch, FILE *file) { - assert(patch.src->ID != (uint32_t)-1); + assume(patch.src->ID != (uint32_t)-1); putlong(patch.src->ID, file); putlong(patch.lineNo, file); putlong(patch.offset, file); @@ -117,7 +116,7 @@ static void writesymbol(Symbol const &sym, FILE *file) { if (!sym.isDefined()) { putc(SYMTYPE_IMPORT, file); } else { - assert(sym.src->ID != (uint32_t)-1); + assume(sym.src->ID != (uint32_t)-1); putc(sym.isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, file); putlong(sym.src->ID, file); diff --git a/src/asm/parser.y b/src/asm/parser.y index 6a4dde76..745523d4 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -2542,7 +2542,7 @@ static std::string strfmt( } else if (auto *n = std::get_if(&args[argIndex]); n) { fmt.appendNumber(str, *n); } else { - assert(std::holds_alternative(args[argIndex])); + assume(std::holds_alternative(args[argIndex])); auto &s = std::get(args[argIndex]); fmt.appendString(str, s); } diff --git a/src/asm/rpn.cpp b/src/asm/rpn.cpp index a077a044..e2952df0 100644 --- a/src/asm/rpn.cpp +++ b/src/asm/rpn.cpp @@ -2,7 +2,6 @@ #include "asm/rpn.hpp" -#include #include #include #include @@ -10,6 +9,7 @@ #include #include +#include "helpers.hpp" // assume #include "opmath.hpp" #include "asm/output.hpp" @@ -20,7 +20,7 @@ using namespace std::literals; int32_t Expression::value() const { - assert(std::holds_alternative(data)); + assume(std::holds_alternative(data)); return std::get(data); } @@ -116,7 +116,7 @@ void Expression::makeBankSymbol(std::string const &symName) { data = 1; } else { sym = sym_Ref(symName); - assert(sym); // If the symbol didn't exist, it should have been created + assume(sym); // If the symbol didn't exist, it should have been created if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) { // Symbol's section is known and bank is fixed @@ -217,7 +217,7 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) { Symbol const &sym = lhsIsSymbol ? *lhsSymbol : *rhsSymbol; Expression const &expr = lhsIsSymbol ? rhs : lhs; // Opposite side of `sym` - assert(sym.isNumeric()); + assume(sym.isNumeric()); if (!expr.isKnown()) return -1; @@ -231,7 +231,7 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) { // `sym.getValue()` attempts to add the section's address, but that's "-1" // because the section is floating (otherwise we wouldn't be here) - assert(sect.org == (uint32_t)-1); + assume(sect.org == (uint32_t)-1); int32_t symbolOfs = sym.getValue() + 1; return (symbolOfs + sect.alignOfs) & ~unknownBits; @@ -502,8 +502,8 @@ void Expression::makeCheckRST() { // Checks that an RPN expression's value fits within N bits (signed or unsigned) void Expression::checkNBit(uint8_t n) const { - assert(n != 0); // That doesn't make sense - assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB + assume(n != 0); // That doesn't make sense + assume(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB if (isKnown()) { if (int32_t val = value(); val < -(1 << n) || val >= 1 << n) diff --git a/src/asm/section.cpp b/src/asm/section.cpp index b037333e..f14c28d6 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -3,7 +3,6 @@ #include "asm/section.hpp" #include -#include #include #include #include @@ -125,7 +124,7 @@ Section *sect_FindSectionByName(std::string const &name) { static unsigned int mergeSectUnion( Section §, SectionType type, uint32_t org, uint8_t alignment, uint16_t alignOffset ) { - assert(alignment < 16); // Should be ensured by the caller + assume(alignment < 16); // Should be ensured by the caller unsigned int nbSectErrors = 0; // Unionized sections only need "compatible" constraints, and they end up with the strictest @@ -177,7 +176,7 @@ static unsigned int mergeSectUnion( static unsigned int mergeFragments(Section §, uint32_t org, uint8_t alignment, uint16_t alignOffset) { - assert(alignment < 16); // Should be ensured by the caller + assume(alignment < 16); // Should be ensured by the caller unsigned int nbSectErrors = 0; // Fragments only need "compatible" constraints, and they end up with the strictest diff --git a/src/asm/symbol.cpp b/src/asm/symbol.cpp index b2c6f464..8d803a39 100644 --- a/src/asm/symbol.cpp +++ b/src/asm/symbol.cpp @@ -2,12 +2,12 @@ #include "asm/symbol.hpp" -#include #include #include #include #include "error.hpp" +#include "helpers.hpp" // assume #include "version.hpp" #include "asm/fstack.hpp" @@ -55,7 +55,7 @@ static int32_t CallbackPC() { } int32_t Symbol::getValue() const { - assert(std::holds_alternative(data) || std::holds_alternative(data)); + assume(std::holds_alternative(data) || std::holds_alternative(data)); if (auto *value = std::get_if(&data); value) { return type == SYM_LABEL ? *value + getSection()->org : *value; } @@ -73,12 +73,12 @@ int32_t Symbol::getOutputValue() const { } ContentSpan const &Symbol::getMacro() const { - assert((std::holds_alternative(data))); + assume((std::holds_alternative(data))); return std::get(data); } std::shared_ptr Symbol::getEqus() const { - assert(std::holds_alternative>(data)); + assume(std::holds_alternative>(data)); return std::get>(data); } @@ -361,7 +361,7 @@ Symbol *sym_AddVar(std::string const &symName, int32_t value) { * @return The created symbol */ static Symbol *addLabel(std::string const &symName) { - assert(!symName.starts_with('.')); // The symbol name must have been expanded prior + assume(!symName.starts_with('.')); // The symbol name must have been expanded prior Symbol *sym = sym_FindExactSymbol(symName); if (!sym) { @@ -390,11 +390,11 @@ static Symbol *addLabel(std::string const &symName) { // Add a local (`.name` or `Parent.name`) relocatable symbol Symbol *sym_AddLocalLabel(std::string const &symName) { // Assuming no dots in `labelScope` if defined - assert(!labelScope.has_value() || labelScope->find('.') == std::string::npos); + assume(!labelScope.has_value() || labelScope->find('.') == std::string::npos); size_t dotPos = symName.find('.'); - assert(dotPos != std::string::npos); // There should be at least one dot in `symName` + assume(dotPos != std::string::npos); // There should be at least one dot in `symName` // Check for something after the dot if (dotPos == symName.length() - 1) { diff --git a/src/fix/main.cpp b/src/fix/main.cpp index e2bb3174..ea506cbc 100644 --- a/src/fix/main.cpp +++ b/src/fix/main.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -774,7 +773,7 @@ static uint8_t maxTitleLen() { static ssize_t readBytes(int fd, uint8_t *buf, size_t len) { // POSIX specifies that lengths greater than SSIZE_MAX yield implementation-defined results - assert(len <= SSIZE_MAX); + assume(len <= SSIZE_MAX); ssize_t total = 0; @@ -799,7 +798,7 @@ static ssize_t readBytes(int fd, uint8_t *buf, size_t len) { static ssize_t writeBytes(int fd, uint8_t *buf, size_t len) { // POSIX specifies that lengths greater than SSIZE_MAX yield implementation-defined results - assert(len <= SSIZE_MAX); + assume(len <= SSIZE_MAX); ssize_t total = 0; @@ -869,9 +868,9 @@ static void overwriteBytes( static void processFile(int input, int output, char const *name, off_t fileSize) { // Both of these should be true for seekable files, and neither otherwise if (input == output) - assert(fileSize != 0); + assume(fileSize != 0); else - assert(fileSize == 0); + assume(fileSize == 0); uint8_t rom0[BANK_SIZE]; ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0)); @@ -1037,9 +1036,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize) } nbBanks = 2; } else { - assert(rom0Len == sizeof(rom0)); + assume(rom0Len == sizeof(rom0)); } - assert(nbBanks >= 2); + assume(nbBanks >= 2); // Alter number of banks to reflect required value // x&(x-1) is zero iff x is a power of 2, or 0; we know for sure it's non-zero, // so this is true (non-zero) when we don't have a power of 2 @@ -1063,7 +1062,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize) if (fixSpec & (FIX_GLOBAL_SUM | TRASH_GLOBAL_SUM)) { // Computation of the global checksum does not include the checksum bytes - assert(rom0Len >= 0x14E); + assume(rom0Len >= 0x14E); for (uint16_t i = 0; i < 0x14E; i++) globalSum += rom0[i]; for (uint16_t i = 0x150; i < rom0Len; i++) diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index d3a228a9..240cd129 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -3,7 +3,6 @@ #include "gfx/main.hpp" #include -#include #include #include #include @@ -17,6 +16,7 @@ #include "extern/getopt.hpp" #include "file.hpp" +#include "helpers.hpp" // assume #include "platform.hpp" #include "version.hpp" @@ -633,7 +633,7 @@ int main(int argc, char *argv[]) { if (musl_optind != curArgc) { // This happens if `--` is passed, process the remaining arg(s) as positional - assert(musl_optind < curArgc); + assume(musl_optind < curArgc); for (int i = musl_optind; i < curArgc; ++i) { registerInput(argv[i]); } @@ -845,7 +845,7 @@ int main(int argc, char *argv[]) { void Palette::addColor(uint16_t color) { for (size_t i = 0; true; ++i) { - assert(i < colors.size()); // The packing should guarantee this + assume(i < colors.size()); // The packing should guarantee this if (colors[i] == color) { // The color is already present break; } else if (colors[i] == UINT16_MAX) { // Empty slot diff --git a/src/gfx/pal_packing.cpp b/src/gfx/pal_packing.cpp index bbf78bb6..60cece66 100644 --- a/src/gfx/pal_packing.cpp +++ b/src/gfx/pal_packing.cpp @@ -3,7 +3,6 @@ #include "gfx/pal_packing.hpp" #include -#include #include #include #include @@ -107,7 +106,7 @@ private: return it; } reference operator*() const { - assert((*_iter).has_value()); + assume((*_iter).has_value()); return **_iter; } pointer operator->() const { @@ -308,7 +307,7 @@ static void decant( // Build up the "component"... colors.clear(); members.clear(); - assert(members.empty()); // Compiler optimization hint + assume(members.empty()); // Compiler optimization hint do { ProtoPalette const &protoPal = protoPalettes[attrs->protoPalIndex]; // If this is the first proto-pal, or if at least one color matches, add it diff --git a/src/gfx/pal_sorting.cpp b/src/gfx/pal_sorting.cpp index c6a35d81..dde98120 100644 --- a/src/gfx/pal_sorting.cpp +++ b/src/gfx/pal_sorting.cpp @@ -3,7 +3,6 @@ #include "gfx/pal_sorting.hpp" #include -#include #include "helpers.hpp" @@ -55,7 +54,7 @@ void grayscale( // This method is only applicable if there are at most as many colors as colors per palette, so // we should only have a single palette. - assert(palettes.size() == 1); + assume(palettes.size() == 1); Palette &palette = palettes[0]; std::fill(RANGE(palette.colors), Rgba::transparent); diff --git a/src/gfx/pal_spec.cpp b/src/gfx/pal_spec.cpp index 24974771..6e92e600 100644 --- a/src/gfx/pal_spec.cpp +++ b/src/gfx/pal_spec.cpp @@ -3,7 +3,6 @@ #include "gfx/pal_spec.hpp" #include -#include #include #include #include @@ -26,13 +25,13 @@ using namespace std::string_view_literals; constexpr uint8_t nibble(char c) { if (c >= 'a') { - assert(c <= 'f'); + assume(c <= 'f'); return c - 'a' + 10; } else if (c >= 'A') { - assert(c <= 'F'); + assume(c <= 'F'); return c - 'A' + 10; } else { - assert(c >= '0' && c <= '9'); + assume(c >= '0' && c <= '9'); return c - '0'; } } @@ -59,8 +58,8 @@ void parseInlinePalSpec(char const * const rawArg) { auto parseError = [&rawArg, &arg](size_type ofs, size_type len, char const *msg) { (void)arg; // With NDEBUG, `arg` is otherwise not used - assert(ofs <= arg.length()); - assert(len <= arg.length()); + assume(ofs <= arg.length()); + assume(len <= arg.length()); errorMessage(msg); fprintf( @@ -178,7 +177,7 @@ void parseInlinePalSpec(char const * const rawArg) { */ template static bool readMagic(std::filebuf &file, char const *magic) { - assert(strlen(magic) == n); + assume(strlen(magic) == n); char magicBuf[n]; return file.sgetn(magicBuf, n) == n && memcmp(magicBuf, magic, n); diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index b66fab2d..630facc4 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -3,7 +3,6 @@ #include "gfx/process.hpp" #include -#include #include #include #include @@ -48,7 +47,7 @@ public: if (!slot.has_value()) { slot.emplace(rgba); } else if (*slot != rgba) { - assert(slot->cgbColor() != UINT16_MAX); + assume(slot->cgbColor() != UINT16_MAX); return &*slot; } return nullptr; @@ -270,7 +269,7 @@ public: if (png_get_PLTE(png, info, &embeddedPal, &nbColors) != 0) { if (png_get_tRNS(png, info, &transparencyPal, &nbTransparentEntries, nullptr)) { - assert(nbTransparentEntries <= nbColors); + assume(nbTransparentEntries <= nbColors); } options.verbosePrint( @@ -326,16 +325,16 @@ public: // Update `info` with the transformations png_read_update_info(png, info); // These shouldn't have changed - assert(png_get_image_width(png, info) == width); - assert(png_get_image_height(png, info) == height); + assume(png_get_image_width(png, info) == width); + assume(png_get_image_height(png, info) == height); // These should have changed, however - assert(png_get_color_type(png, info) == PNG_COLOR_TYPE_RGBA); - assert(png_get_bit_depth(png, info) == 8); + assume(png_get_color_type(png, info) == PNG_COLOR_TYPE_RGBA); + assume(png_get_bit_depth(png, info) == 8); // Now that metadata has been read, we can process the image data size_t nbRowBytes = png_get_rowbytes(png, info); - assert(nbRowBytes != 0); + assume(nbRowBytes != 0); DefaultInitVec row(nbRowBytes); // Holds known-conflicting color pairs to avoid warning about them twice. // We don't need to worry about transitivity, as ImagePalette slots are immutable once @@ -394,7 +393,7 @@ public: } } } else { - assert(interlaceType == PNG_INTERLACE_ADAM7); + assume(interlaceType == PNG_INTERLACE_ADAM7); // For interlace to work properly, we must read the image `nbPasses` times for (int pass = 0; pass < PNG_INTERLACE_ADAM7_PASSES; ++pass) { @@ -549,7 +548,7 @@ static void generatePalSpec(Png const &png) { // Fill in the palette spec options.palSpec.emplace_back(); // A single palette, with `#00000000`s (transparent) - assert(options.palSpec.size() == 1); + assume(options.palSpec.size() == 1); if (embPalSize > options.maxOpaqueColors()) { // Ignore extraneous colors if they are unused embPalSize = options.maxOpaqueColors(); } @@ -568,7 +567,7 @@ static std::tuple, std::vector> // Run a "pagination" problem solver // TODO: allow picking one of several solvers? auto [mappings, nbPalettes] = packing::overloadAndRemove(protoPalettes); - assert(mappings.size() == protoPalettes.size()); + assume(mappings.size() == protoPalettes.size()); if (options.verbosity >= Options::VERB_INTERM) { fprintf( @@ -647,7 +646,7 @@ static std::tuple, std::vector> }); if (iter == palettes.end()) { - assert(!protoPal.empty()); + assume(!protoPal.empty()); error("Failed to fit tile colors [%s] in specified palettes", listColors(protoPal)); bad = true; } @@ -725,7 +724,7 @@ public: for (uint32_t x = 0; x < 8; ++x) { row <<= 1; uint8_t index = palette.indexOf(tile.pixel(x, y).cgbColor()); - assert(index < palette.size()); // The color should be in the palette + assume(index < palette.size()); // The color should be in the palette if (index & 1) { row |= 1; } @@ -803,7 +802,7 @@ public: } // If we have both (i.e. we have symmetry), default to vflip only - assert(hasVFlip || hasVHFlip); + assume(hasVFlip || hasVHFlip); return hasVFlip ? MatchType::VFLIP : MatchType::VHFLIP; } friend bool operator==(TileData const &lhs, TileData const &rhs) { @@ -854,7 +853,7 @@ static void outputTileData( break; } } - assert(remainingTiles == 0); + assume(remainingTiles == 0); } static void outputMaps( @@ -877,7 +876,7 @@ static void outputMaps( uint8_t bank = 0; for (auto attr : attrmap) { if (tileID == options.maxNbTiles[bank]) { - assert(bank == 0); + assume(bank == 0); bank = 1; tileID = 0; } @@ -976,7 +975,7 @@ static void outputTileData(UniqueTiles const &tiles) { uint16_t tileID = 0; for (auto iter = tiles.begin(), end = tiles.end() - options.trim; iter != end; ++iter) { TileData const *tile = *iter; - assert(tile->tileID == tileID); + assume(tile->tileID == tileID); ++tileID; output->sputn(reinterpret_cast(tile->data().data()), options.bitDepth * 8); } diff --git a/src/gfx/proto_palette.cpp b/src/gfx/proto_palette.cpp index e1b80f10..4b63408e 100644 --- a/src/gfx/proto_palette.cpp +++ b/src/gfx/proto_palette.cpp @@ -3,7 +3,6 @@ #include "gfx/proto_palette.hpp" #include -#include #include "helpers.hpp" @@ -41,8 +40,8 @@ bool ProtoPalette::add(uint16_t color) { ProtoPalette::ComparisonResult ProtoPalette::compare(ProtoPalette const &other) const { // This works because the sets are sorted numerically - assert(std::is_sorted(RANGE(_colorIndices))); - assert(std::is_sorted(RANGE(other._colorIndices))); + assume(std::is_sorted(RANGE(_colorIndices))); + assume(std::is_sorted(RANGE(other._colorIndices))); auto ours = _colorIndices.begin(), theirs = other._colorIndices.begin(); bool weBigger = true, theyBigger = true; diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index 0413f253..b998d5e2 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -14,6 +13,7 @@ #include "defaultinitalloc.hpp" #include "file.hpp" +#include "helpers.hpp" // assume #include "itertools.hpp" #include "gfx/main.hpp" @@ -42,7 +42,7 @@ static DefaultInitVec readInto(std::string const &path) { // Arbitrary, but if you got a better idea... size_t newSize = oldSize != data.capacity() ? data.capacity() : oldSize * 2; - assert(oldSize != newSize); + assume(oldSize != newSize); data.resize(newSize); } @@ -343,9 +343,9 @@ void reverse() { tileID = (*tilemap)[index] - options.baseTileIDs[bank] + bank * options.maxNbTiles[0]; } - assert(tileID < nbTileInstances); // Should have been checked earlier + assume(tileID < nbTileInstances); // Should have been checked earlier size_t palID = palmap ? (*palmap)[index] : attribute & 0b111; - assert(palID < palettes.size()); // Should be ensured on data read + assume(palID < palettes.size()); // Should be ensured on data read // We do not have data for tiles trimmed with `-x`, so assume they are "blank" static std::array const trimmedTile{ diff --git a/src/gfx/rgba.cpp b/src/gfx/rgba.cpp index dbbbcf33..e2ae941d 100644 --- a/src/gfx/rgba.cpp +++ b/src/gfx/rgba.cpp @@ -3,10 +3,11 @@ #include "gfx/rgba.hpp" #include -#include #include #include +#include "helpers.hpp" // assume + #include "gfx/main.hpp" // options /* @@ -37,7 +38,7 @@ uint16_t Rgba::cgbColor() const { if (isTransparent()) { return transparent; } - assert(isOpaque()); + assume(isOpaque()); uint8_t r = red, g = green, b = blue; if (options.useColorCurve) { @@ -56,7 +57,7 @@ uint16_t Rgba::cgbColor() const { } uint8_t Rgba::grayIndex() const { - assert(isGray()); + assume(isGray()); // Convert from [0; 256[ to [0; maxOpaqueColors[ return static_cast(255 - red) * options.maxOpaqueColors() / 256; } diff --git a/src/link/main.cpp b/src/link/main.cpp index d0cb440c..40ee65e1 100644 --- a/src/link/main.cpp +++ b/src/link/main.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include "error.hpp" #include "extern/getopt.hpp" +#include "helpers.hpp" // assume #include "itertools.hpp" #include "platform.hpp" #include "script.hpp" @@ -46,28 +46,28 @@ FILE *linkerScript; static uint32_t nbErrors = 0; std::vector &FileStackNode::iters() { - assert(std::holds_alternative>(data)); + assume(std::holds_alternative>(data)); return std::get>(data); } std::vector const &FileStackNode::iters() const { - assert(std::holds_alternative>(data)); + assume(std::holds_alternative>(data)); return std::get>(data); } std::string &FileStackNode::name() { - assert(std::holds_alternative(data)); + assume(std::holds_alternative(data)); return std::get(data); } std::string const &FileStackNode::name() const { - assert(std::holds_alternative(data)); + assume(std::holds_alternative(data)); return std::get(data); } std::string const &FileStackNode::dump(uint32_t curLineNo) const { if (std::holds_alternative>(data)) { - assert(parent); // REPT nodes use their parent's name + assume(parent); // REPT nodes use their parent's name std::string const &lastName = parent->dump(lineNo); fputs(" -> ", stderr); fputs(lastName.c_str(), stderr); @@ -223,7 +223,7 @@ static void parseScrambleSpec(char const *spec) { // indicating their scramble limit. while (spec) { // Invariant: we should not be pointing at whitespace at this point - assert(*spec != ' ' && *spec != '\t'); + assume(*spec != ' ' && *spec != '\t'); // Remember where the region's name begins and ends char const *regionName = spec; @@ -232,7 +232,7 @@ static void parseScrambleSpec(char const *spec) { int regionNamePrintLen = regionNameLen > INT_MAX ? INT_MAX : (int)regionNameLen; ScrambledRegion region = SCRAMBLE_UNK; - // If this trips, `spec` must be pointing at a ',' or '=' (or NUL) due to the assert + // If this trips, `spec` must be pointing at a ',' or '=' (or NUL) due to the assumption if (regionNameLen == 0) { argErr('S', "Missing region name"); @@ -322,7 +322,7 @@ static void parseScrambleSpec(char const *spec) { next: // Can't `continue` a `for` loop with this nontrivial iteration logic if (spec) { - assert(*spec == ',' || *spec == '\0'); + assume(*spec == ',' || *spec == '\0'); if (*spec == ',') spec += 1 + strspn(&spec[1], " \t"); if (*spec == '\0') diff --git a/src/link/output.cpp b/src/link/output.cpp index 47cc7f88..fe0ccd88 100644 --- a/src/link/output.cpp +++ b/src/link/output.cpp @@ -3,7 +3,6 @@ #include "link/output.hpp" #include -#include #include #include #include @@ -162,7 +161,7 @@ static void if (bankSections) { for (Section const *section : *bankSections) { - assert(section->offset == 0); + assume(section->offset == 0); // Output padding up to the next SECTION while (offset + baseOffset < section->org) { putc(overlayFile ? getc(overlayFile) : padValue, outputFile); @@ -407,7 +406,7 @@ static void writeMapBank(SortedSections const §List, SectionType type, uint3 Section const *sect = *pickedSection; used += sect->size; - assert(sect->offset == 0); + assume(sect->offset == 0); writeEmptySpace(prevEndAddr, sect->org); diff --git a/src/link/patch.cpp b/src/link/patch.cpp index db7921d1..ed5ab62b 100644 --- a/src/link/patch.cpp +++ b/src/link/patch.cpp @@ -2,13 +2,13 @@ #include "link/patch.hpp" -#include #include #include #include #include #include +#include "helpers.hpp" // assume #include "linkdefs.hpp" #include "opmath.hpp" @@ -54,7 +54,7 @@ static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch cons } static Symbol const *getSymbol(std::vector const &symbolList, uint32_t index) { - assert(index != (uint32_t)-1); // PC needs to be handled specially, not here + assume(index != (uint32_t)-1); // PC needs to be handled specially, not here Symbol const &symbol = symbolList[index]; // If the symbol is defined elsewhere... @@ -297,7 +297,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector const &fil isError = true; value = 1; } else { - assert(sect->offset == 0); + assume(sect->offset == 0); value = sect->org; } break; @@ -377,7 +377,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector const &fil } else if (auto *label = std::get_if