// SPDX-License-Identifier: MIT #include "link/patch.hpp" #include #include #include #include #include #include #include "diagnostics.hpp" #include "helpers.hpp" // assume #include "linkdefs.hpp" #include "opmath.hpp" #include "verbosity.hpp" #include "link/section.hpp" #include "link/symbol.hpp" #include "link/warning.hpp" static std::deque assertions; struct RPNStackEntry { int32_t value; bool errorFlag; // Whether the value is a placeholder inserted for error recovery }; static std::deque rpnStack; static void pushRPN(int32_t value, bool comesFromError) { rpnStack.push_front({.value = value, .errorFlag = comesFromError}); } // This flag tracks whether the RPN op that is currently being evaluated // has popped any values with the error flag set. static bool isError = false; #define diagnosticAt(patch, id, ...) \ do { \ bool errorDiag = warnings.getWarningBehavior(id) == WarningBehavior::ERROR; \ if (!isError || !errorDiag) { \ warningAt(patch, id, __VA_ARGS__); \ } \ if (errorDiag) { \ isError = true; \ } \ } while (0) #define firstErrorAt(...) \ do { \ if (!isError) { \ errorAt(__VA_ARGS__); \ isError = true; \ } \ } while (0) static int32_t popRPN(Patch const &patch) { if (rpnStack.empty()) { fatalAt(patch, "Internal error, RPN stack empty"); } RPNStackEntry entry = rpnStack.front(); rpnStack.pop_front(); isError |= entry.errorFlag; return entry.value; } // RPN operators static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch const &patch) { if (!size--) { fatalAt(patch, "Internal error, RPN expression overread"); } return *expression++; } static Symbol const *getSymbol(std::vector const &symbolList, uint32_t index) { assume(index != UINT32_MAX); // PC needs to be handled specially, not here Symbol const &symbol = symbolList[index]; // If the symbol is defined elsewhere... if (symbol.type == SYMTYPE_IMPORT) { return sym_GetSymbol(symbol.name); } return &symbol; } // Compute a patch's value from its RPN string. static int32_t computeRPNExpr(Patch const &patch, std::vector const &fileSymbols) { uint8_t const *expression = patch.rpnExpression.data(); int32_t size = static_cast(patch.rpnExpression.size()); rpnStack.clear(); while (size > 0) { RPNCommand command = static_cast(getRPNByte(expression, size, patch)); isError = false; // Be VERY careful with two `popRPN` in the same expression. // C++ does not guarantee order of evaluation of operands! // So, if there are two `popRPN` in the same expression, make // sure the operation is commutative. int32_t value; switch (command) { case RPN_ADD: value = popRPN(patch) + popRPN(patch); break; case RPN_SUB: value = popRPN(patch); value = popRPN(patch) - value; break; case RPN_MUL: value = popRPN(patch) * popRPN(patch); break; case RPN_DIV: value = popRPN(patch); if (value == 0) { firstErrorAt(patch, "Division by 0"); popRPN(patch); value = 0; } else if (int32_t lval = popRPN(patch); lval == INT32_MIN && value == -1) { diagnosticAt( patch, WARNING_DIV, "Division of %" PRId32 " by -1 yields %" PRId32, INT32_MIN, INT32_MIN ); value = INT32_MIN; } else { value = op_divide(lval, value); } break; case RPN_MOD: value = popRPN(patch); if (value == 0) { firstErrorAt(patch, "Modulo by 0"); popRPN(patch); value = 0; } else { value = op_modulo(popRPN(patch), value); } break; case RPN_NEG: value = op_neg(popRPN(patch)); break; case RPN_EXP: value = popRPN(patch); if (value < 0) { firstErrorAt(patch, "Exponent by negative value %" PRId32, value); popRPN(patch); value = 0; } else { value = op_exponent(popRPN(patch), value); } break; case RPN_HIGH: value = op_high(popRPN(patch)); break; case RPN_LOW: value = op_low(popRPN(patch)); break; case RPN_BITWIDTH: value = op_bitwidth(popRPN(patch)); break; case RPN_TZCOUNT: value = op_tzcount(popRPN(patch)); break; case RPN_OR: value = popRPN(patch) | popRPN(patch); break; case RPN_AND: value = popRPN(patch) & popRPN(patch); break; case RPN_XOR: value = popRPN(patch) ^ popRPN(patch); break; case RPN_NOT: value = ~popRPN(patch); break; case RPN_LOGAND: value = popRPN(patch); value = popRPN(patch) && value; break; case RPN_LOGOR: value = popRPN(patch); value = popRPN(patch) || value; break; case RPN_LOGNOT: value = !popRPN(patch); break; case RPN_LOGEQ: value = popRPN(patch) == popRPN(patch); break; case RPN_LOGNE: value = popRPN(patch) != popRPN(patch); break; case RPN_LOGGT: value = popRPN(patch); value = popRPN(patch) > value; break; case RPN_LOGLT: value = popRPN(patch); value = popRPN(patch) < value; break; case RPN_LOGGE: value = popRPN(patch); value = popRPN(patch) >= value; break; case RPN_LOGLE: value = popRPN(patch); value = popRPN(patch) <= value; break; case RPN_SHL: value = popRPN(patch); if (value < 0) { diagnosticAt( patch, WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32, value ); } if (value >= 32) { diagnosticAt( patch, WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32, value ); } value = op_shift_left(popRPN(patch), value); break; case RPN_SHR: { value = popRPN(patch); int32_t lval = popRPN(patch); if (lval < 0) { diagnosticAt(patch, WARNING_SHIFT, "Shifting right negative value %" PRId32, lval); } if (value < 0) { diagnosticAt( patch, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value ); } if (value >= 32) { diagnosticAt( patch, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value ); } value = op_shift_right(lval, value); break; } case RPN_USHR: value = popRPN(patch); if (value < 0) { diagnosticAt( patch, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value ); } if (value >= 32) { diagnosticAt( patch, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value ); } value = op_shift_right_unsigned(popRPN(patch), value); break; case RPN_BANK_SYM: value = 0; for (uint8_t shift = 0; shift < 32; shift += 8) { value |= getRPNByte(expression, size, patch) << shift; } if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) { errorAt( patch, "Requested `BANK()` of undefined symbol `%s`", fileSymbols[value].name.c_str() ); isError = true; value = 1; } else if (std::holds_alternative