Use std::variant for RPN expression value (#1389)

This commit is contained in:
Sylvie
2024-04-01 10:47:15 -04:00
committed by GitHub
parent 9ab3446d1a
commit 1d39e5ed56
5 changed files with 154 additions and 175 deletions

View File

@@ -5,6 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <variant>
#include <vector> #include <vector>
#include "linkdefs.hpp" #include "linkdefs.hpp"
@@ -12,9 +13,10 @@
struct Symbol; struct Symbol;
struct Expression { struct Expression {
int32_t val = 0; // If the expression's value is known, it's here std::variant<
std::string reason{}; // Why the expression is not known, if it isn't int32_t, // If the expression's value is known, it's here
bool isKnown = true; // Whether the expression's value is known at assembly time std::string // Why the expression is not known, if it isn't
> data = 0;
bool isSymbol = false; // Whether the expression represents a symbol suitable for const diffing bool isSymbol = false; // Whether the expression represents a symbol suitable for const diffing
std::vector<uint8_t> rpn{}; // Bytes serializing the RPN expression std::vector<uint8_t> rpn{}; // Bytes serializing the RPN expression
uint32_t rpnPatchSize = 0; // Size the expression will take in the object file uint32_t rpnPatchSize = 0; // Size the expression will take in the object file
@@ -28,6 +30,9 @@ struct Expression {
Expression &operator=(Expression &&) = default; Expression &operator=(Expression &&) = default;
bool isKnown() const { return std::holds_alternative<int32_t>(data); }
int32_t value() const;
int32_t getConstVal() const; int32_t getConstVal() const;
Symbol const *symbolOf() const; Symbol const *symbolOf() const;
bool isDiffConstant(Symbol const *symName) const; bool isDiffConstant(Symbol const *symName) const;
@@ -56,14 +61,6 @@ private:
void clear(); void clear();
uint8_t *reserveSpace(uint32_t size); uint8_t *reserveSpace(uint32_t size);
uint8_t *reserveSpace(uint32_t size, uint32_t patchSize); uint8_t *reserveSpace(uint32_t size, uint32_t patchSize);
// Makes an expression "not known", also setting its error message
template<typename... Ts>
void makeUnknown(Ts... parts) {
isKnown = false;
reason.clear();
(reason.append(parts), ...);
}
}; };
#endif // RGBDS_ASM_RPN_H #endif // RGBDS_ASM_RPN_H

View File

@@ -244,14 +244,15 @@ static void initpatch(Patch &patch, uint32_t type, Expression const &expr, uint3
patch.pcSection = sect_GetSymbolSection(); patch.pcSection = sect_GetSymbolSection();
patch.pcOffset = sect_GetSymbolOffset(); patch.pcOffset = sect_GetSymbolOffset();
if (expr.isKnown) { if (expr.isKnown()) {
// If the RPN expr's value is known, output a constant directly // If the RPN expr's value is known, output a constant directly
uint32_t val = expr.value();
patch.rpn.resize(5); patch.rpn.resize(5);
patch.rpn[0] = RPN_CONST; patch.rpn[0] = RPN_CONST;
patch.rpn[1] = (uint32_t)expr.val & 0xFF; patch.rpn[1] = val & 0xFF;
patch.rpn[2] = (uint32_t)expr.val >> 8; patch.rpn[2] = val >> 8;
patch.rpn[3] = (uint32_t)expr.val >> 16; patch.rpn[3] = val >> 16;
patch.rpn[4] = (uint32_t)expr.val >> 24; patch.rpn[4] = val >> 24;
} else { } else {
patch.rpn.resize(expr.rpnPatchSize); patch.rpn.resize(expr.rpnPatchSize);
writerpn(patch.rpn, expr.rpn); writerpn(patch.rpn, expr.rpn);

View File

@@ -711,16 +711,16 @@ assert_type:
assert: assert:
POP_ASSERT assert_type relocexpr { POP_ASSERT assert_type relocexpr {
if (!$3.isKnown) { if (!$3.isKnown()) {
out_CreateAssert($2, $3, "", sect_GetOutputOffset()); out_CreateAssert($2, $3, "", sect_GetOutputOffset());
} else if ($3.val == 0) { } else if ($3.value() == 0) {
failAssert($2); failAssert($2);
} }
} }
| POP_ASSERT assert_type relocexpr COMMA string { | POP_ASSERT assert_type relocexpr COMMA string {
if (!$3.isKnown) { if (!$3.isKnown()) {
out_CreateAssert($2, $3, $5, sect_GetOutputOffset()); out_CreateAssert($2, $3, $5, sect_GetOutputOffset());
} else if ($3.val == 0) { } else if ($3.value() == 0) {
failAssertMsg($2, $5); failAssertMsg($2, $5);
} }
} }
@@ -1304,7 +1304,7 @@ relocexpr_no_str:
$$.makeLow(); $$.makeLow();
} }
| OP_ISCONST LPAREN relocexpr RPAREN { | OP_ISCONST LPAREN relocexpr RPAREN {
$$.makeNumber($3.isKnown); $$.makeNumber($3.isKnown());
} }
| OP_BANK LPAREN scoped_anon_id RPAREN { | OP_BANK LPAREN scoped_anon_id RPAREN {
// '@' is also an ID; it is handled here // '@' is also an ID; it is handled here
@@ -1846,7 +1846,7 @@ z80_ldio:
c_ind: c_ind:
LBRACK MODE_C RBRACK LBRACK MODE_C RBRACK
| LBRACK relocexpr OP_ADD MODE_C RBRACK { | LBRACK relocexpr OP_ADD MODE_C RBRACK {
if (!$2.isKnown || $2.val != 0xFF00) if (!$2.isKnown() || $2.value() != 0xFF00)
::error("Expected constant expression equal to $FF00 for \"$ff00+c\"\n"); ::error("Expected constant expression equal to $FF00 for \"$ff00+c\"\n");
} }
; ;
@@ -2060,10 +2060,10 @@ z80_rrca:
z80_rst: z80_rst:
Z80_RST reloc_8bit { Z80_RST reloc_8bit {
$2.makeCheckRST(); $2.makeCheckRST();
if (!$2.isKnown) if (!$2.isKnown())
sect_RelByte($2, 0); sect_RelByte($2, 0);
else else
sect_AbsByte(0xC7 | $2.val); sect_AbsByte(0xC7 | $2.value());
} }
; ;

View File

@@ -17,10 +17,15 @@
#include "asm/symbol.hpp" #include "asm/symbol.hpp"
#include "asm/warning.hpp" #include "asm/warning.hpp"
using namespace std::literals;
int32_t Expression::value() const {
assert(std::holds_alternative<int32_t>(data));
return std::get<int32_t>(data);
}
void Expression::clear() { void Expression::clear() {
val = 0; data = 0;
reason.clear();
isKnown = true;
isSymbol = false; isSymbol = false;
rpn.clear(); rpn.clear();
rpnPatchSize = 0; rpnPatchSize = 0;
@@ -38,11 +43,11 @@ uint8_t *Expression::reserveSpace(uint32_t size, uint32_t patchSize) {
} }
int32_t Expression::getConstVal() const { int32_t Expression::getConstVal() const {
if (!isKnown) { if (!isKnown()) {
error("Expected constant expression: %s\n", reason.c_str()); error("Expected constant expression: %s\n", std::get<std::string>(data).c_str());
return 0; return 0;
} }
return val; return value();
} }
Symbol const *Expression::symbolOf() const { Symbol const *Expression::symbolOf() const {
@@ -65,21 +70,19 @@ bool Expression::isDiffConstant(Symbol const *sym) const {
void Expression::makeNumber(uint32_t value) { void Expression::makeNumber(uint32_t value) {
clear(); clear();
val = value; data = (int32_t)value;
} }
void Expression::makeSymbol(std::string const &symName) { void Expression::makeSymbol(std::string const &symName) {
clear(); clear();
if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) { if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) {
error("PC has no value outside a section\n"); error("PC has no value outside a section\n");
val = 0; data = 0;
} else if (!sym || !sym->isConstant()) { } else if (!sym || !sym->isConstant()) {
isSymbol = true; isSymbol = true;
if (sym_IsPC(sym)) data = sym_IsPC(sym) ? "PC is not constant at assembly time"
makeUnknown("PC is not constant at assembly time"); : "'"s + symName + "' is not constant at assembly time";
else
makeUnknown("'", symName, "' is not constant at assembly time");
sym = sym_Ref(symName); sym = sym_Ref(symName);
size_t nameLen = sym->name.length() + 1; // Don't forget NUL! size_t nameLen = sym->name.length() + 1; // Don't forget NUL!
@@ -89,7 +92,7 @@ void Expression::makeSymbol(std::string const &symName) {
*ptr++ = RPN_SYM; *ptr++ = RPN_SYM;
memcpy(ptr, sym->name.c_str(), nameLen); memcpy(ptr, sym->name.c_str(), nameLen);
} else { } else {
val = sym_GetConstantValue(symName); data = (int32_t)sym_GetConstantValue(symName);
} }
} }
@@ -99,27 +102,27 @@ void Expression::makeBankSymbol(std::string const &symName) {
// The @ symbol is treated differently. // The @ symbol is treated differently.
if (!currentSection) { if (!currentSection) {
error("PC has no bank outside a section\n"); error("PC has no bank outside a section\n");
val = 1; data = 1;
} else if (currentSection->bank == (uint32_t)-1) { } else if (currentSection->bank == (uint32_t)-1) {
makeUnknown("Current section's bank is not known"); data = "Current section's bank is not known";
*reserveSpace(1) = RPN_BANK_SELF; *reserveSpace(1) = RPN_BANK_SELF;
} else { } else {
val = currentSection->bank; data = (int32_t)currentSection->bank;
} }
return; return;
} else if (sym && !sym->isLabel()) { } else if (sym && !sym->isLabel()) {
error("BANK argument must be a label\n"); error("BANK argument must be a label\n");
val = 1; data = 1;
} else { } else {
sym = sym_Ref(symName); sym = sym_Ref(symName);
assert(sym); // If the symbol didn't exist, it should have been created assert(sym); // If the symbol didn't exist, it should have been created
if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) { if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) {
// Symbol's section is known and bank is fixed // Symbol's section is known and bank is fixed
val = sym->getSection()->bank; data = (int32_t)sym->getSection()->bank;
} else { } else {
makeUnknown("\"", symName, "\"'s bank is not known"); data = "\""s + symName + "\"'s bank is not known";
size_t nameLen = sym->name.length() + 1; // Room for NUL! size_t nameLen = sym->name.length() + 1; // Room for NUL!
@@ -134,9 +137,9 @@ void Expression::makeBankSymbol(std::string const &symName) {
void Expression::makeBankSection(std::string const &sectName) { void Expression::makeBankSection(std::string const &sectName) {
clear(); clear();
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != (uint32_t)-1) { if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != (uint32_t)-1) {
val = sect->bank; data = (int32_t)sect->bank;
} else { } else {
makeUnknown("Section \"", sectName, "\"'s bank is not known"); data = "Section \""s + sectName + "\"'s bank is not known";
size_t nameLen = sectName.length() + 1; // Room for NUL! size_t nameLen = sectName.length() + 1; // Room for NUL!
@@ -149,9 +152,9 @@ void Expression::makeBankSection(std::string const &sectName) {
void Expression::makeSizeOfSection(std::string const &sectName) { void Expression::makeSizeOfSection(std::string const &sectName) {
clear(); clear();
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->isSizeKnown()) { if (Section *sect = sect_FindSectionByName(sectName); sect && sect->isSizeKnown()) {
val = sect->size; data = (int32_t)sect->size;
} else { } else {
makeUnknown("Section \"", sectName, "\"'s size is not known"); data = "Section \""s + sectName + "\"'s size is not known";
size_t nameLen = sectName.length() + 1; // Room for NUL! size_t nameLen = sectName.length() + 1; // Room for NUL!
@@ -164,9 +167,9 @@ void Expression::makeSizeOfSection(std::string const &sectName) {
void Expression::makeStartOfSection(std::string const &sectName) { void Expression::makeStartOfSection(std::string const &sectName) {
clear(); clear();
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != (uint32_t)-1) { if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != (uint32_t)-1) {
val = sect->org; data = (int32_t)sect->org;
} else { } else {
makeUnknown("Section \"", sectName, "\"'s start is not known"); data = "Section \""s + sectName + "\"'s start is not known";
size_t nameLen = sectName.length() + 1; // Room for NUL! size_t nameLen = sectName.length() + 1; // Room for NUL!
@@ -178,7 +181,7 @@ void Expression::makeStartOfSection(std::string const &sectName) {
void Expression::makeSizeOfSectionType(SectionType type) { void Expression::makeSizeOfSectionType(SectionType type) {
clear(); clear();
makeUnknown("Section type's size is not known"); data = "Section type's size is not known";
uint8_t *ptr = reserveSpace(2); uint8_t *ptr = reserveSpace(2);
*ptr++ = RPN_SIZEOF_SECTTYPE; *ptr++ = RPN_SIZEOF_SECTTYPE;
@@ -187,7 +190,7 @@ void Expression::makeSizeOfSectionType(SectionType type) {
void Expression::makeStartOfSectionType(SectionType type) { void Expression::makeStartOfSectionType(SectionType type) {
clear(); clear();
makeUnknown("Section type's start is not known"); data = "Section type's start is not known";
uint8_t *ptr = reserveSpace(2); uint8_t *ptr = reserveSpace(2);
*ptr++ = RPN_STARTOF_SECTTYPE; *ptr++ = RPN_STARTOF_SECTTYPE;
@@ -216,14 +219,14 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
assert(sym.isNumeric()); assert(sym.isNumeric());
if (!expr.isKnown) if (!expr.isKnown())
return -1; return -1;
// We can now safely use `expr.val` // We can now safely use `expr.value()`
Section const &sect = *sym.getSection(); Section const &sect = *sym.getSection();
int32_t unknownBits = (1 << 16) - (1 << sect.align); // The max alignment is 16 int32_t unknownBits = (1 << 16) - (1 << sect.align); // The max alignment is 16
// The mask must ignore all unknown bits // The mask must ignore all unknown bits
if ((expr.val & unknownBits) != 0) if ((expr.value() & unknownBits) != 0)
return -1; return -1;
// `sym.getValue()` attempts to add the section's address, but that's "-1" // `sym.getValue()` attempts to add the section's address, but that's "-1"
@@ -236,8 +239,8 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
void Expression::makeHigh() { void Expression::makeHigh() {
isSymbol = false; isSymbol = false;
if (isKnown) { if (isKnown()) {
val = (uint32_t)val >> 8 & 0xFF; data = (int32_t)((uint32_t)value() >> 8 & 0xFF);
} else { } else {
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR, RPN_CONST, 0xFF, 0, 0, 0, RPN_AND}; uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR, RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
@@ -247,8 +250,8 @@ void Expression::makeHigh() {
void Expression::makeLow() { void Expression::makeLow() {
isSymbol = false; isSymbol = false;
if (isKnown) { if (isKnown()) {
val = val & 0xFF; data = value() & 0xFF;
} else { } else {
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND}; uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
@@ -258,8 +261,8 @@ void Expression::makeLow() {
void Expression::makeNeg() { void Expression::makeNeg() {
isSymbol = false; isSymbol = false;
if (isKnown) { if (isKnown()) {
val = -(uint32_t)val; data = (int32_t) - (uint32_t)value();
} else { } else {
*reserveSpace(1) = RPN_NEG; *reserveSpace(1) = RPN_NEG;
} }
@@ -267,8 +270,8 @@ void Expression::makeNeg() {
void Expression::makeNot() { void Expression::makeNot() {
isSymbol = false; isSymbol = false;
if (isKnown) { if (isKnown()) {
val = ~val; data = ~value();
} else { } else {
*reserveSpace(1) = RPN_NOT; *reserveSpace(1) = RPN_NOT;
} }
@@ -276,8 +279,8 @@ void Expression::makeNot() {
void Expression::makeLogicNot() { void Expression::makeLogicNot() {
isSymbol = false; isSymbol = false;
if (isKnown) { if (isKnown()) {
val = !val; data = !value();
} else { } else {
*reserveSpace(1) = RPN_LOGNOT; *reserveSpace(1) = RPN_LOGNOT;
} }
@@ -286,130 +289,119 @@ void Expression::makeLogicNot() {
void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2) { void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2) {
clear(); clear();
// First, check if the expression is known // First, check if the expression is known
isKnown = src1.isKnown && src2.isKnown; if (src1.isKnown() && src2.isKnown()) {
if (isKnown) {
// If both expressions are known, just compute the value // If both expressions are known, just compute the value
uint32_t uleft = src1.val, uright = src2.val; int32_t lval = src1.value(), rval = src2.value();
switch (op) { switch (op) {
case RPN_LOGOR: case RPN_LOGOR:
val = src1.val || src2.val; data = lval || rval;
break; break;
case RPN_LOGAND: case RPN_LOGAND:
val = src1.val && src2.val; data = lval && rval;
break; break;
case RPN_LOGEQ: case RPN_LOGEQ:
val = src1.val == src2.val; data = lval == rval;
break; break;
case RPN_LOGGT: case RPN_LOGGT:
val = src1.val > src2.val; data = lval > rval;
break; break;
case RPN_LOGLT: case RPN_LOGLT:
val = src1.val < src2.val; data = lval < rval;
break; break;
case RPN_LOGGE: case RPN_LOGGE:
val = src1.val >= src2.val; data = lval >= rval;
break; break;
case RPN_LOGLE: case RPN_LOGLE:
val = src1.val <= src2.val; data = lval <= rval;
break; break;
case RPN_LOGNE: case RPN_LOGNE:
val = src1.val != src2.val; data = lval != rval;
break; break;
case RPN_ADD: case RPN_ADD:
val = uleft + uright; data = (int32_t)((uint32_t)lval + (uint32_t)rval);
break; break;
case RPN_SUB: case RPN_SUB:
val = uleft - uright; data = (int32_t)((uint32_t)lval - (uint32_t)rval);
break; break;
case RPN_XOR: case RPN_XOR:
val = src1.val ^ src2.val; data = lval ^ rval;
break; break;
case RPN_OR: case RPN_OR:
val = src1.val | src2.val; data = lval | rval;
break; break;
case RPN_AND: case RPN_AND:
val = src1.val & src2.val; data = lval & rval;
break; break;
case RPN_SHL: case RPN_SHL:
if (src2.val < 0) if (rval < 0)
warning( warning(
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", src2.val WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", rval
); );
if (src2.val >= 32) if (rval >= 32)
warning( warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", rval);
WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", src2.val
);
val = op_shift_left(src1.val, src2.val); data = op_shift_left(lval, rval);
break; break;
case RPN_SHR: case RPN_SHR:
if (src1.val < 0) if (lval < 0)
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", src1.val); warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", lval);
if (src2.val < 0) if (rval < 0)
warning( warning(
WARNING_SHIFT_AMOUNT, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
"Shifting right by negative amount %" PRId32 "\n",
src2.val
); );
if (src2.val >= 32) if (rval >= 32)
warning( warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", src2.val
);
val = op_shift_right(src1.val, src2.val); data = op_shift_right(lval, rval);
break; break;
case RPN_USHR: case RPN_USHR:
if (src2.val < 0) if (rval < 0)
warning( warning(
WARNING_SHIFT_AMOUNT, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
"Shifting right by negative amount %" PRId32 "\n",
src2.val
); );
if (src2.val >= 32) if (rval >= 32)
warning( warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", src2.val
);
val = op_shift_right_unsigned(src1.val, src2.val); data = op_shift_right_unsigned(lval, rval);
break; break;
case RPN_MUL: case RPN_MUL:
val = uleft * uright; data = (int32_t)((uint32_t)lval * (uint32_t)rval);
break; break;
case RPN_DIV: case RPN_DIV:
if (src2.val == 0) if (rval == 0)
fatalerror("Division by zero\n"); fatalerror("Division by zero\n");
if (src1.val == INT32_MIN && src2.val == -1) { if (lval == INT32_MIN && rval == -1) {
warning( warning(
WARNING_DIV, WARNING_DIV,
"Division of %" PRId32 " by -1 yields %" PRId32 "\n", "Division of %" PRId32 " by -1 yields %" PRId32 "\n",
INT32_MIN, INT32_MIN,
INT32_MIN INT32_MIN
); );
val = INT32_MIN; data = INT32_MIN;
} else { } else {
val = op_divide(src1.val, src2.val); data = op_divide(lval, rval);
} }
break; break;
case RPN_MOD: case RPN_MOD:
if (src2.val == 0) if (rval == 0)
fatalerror("Modulo by zero\n"); fatalerror("Modulo by zero\n");
if (src1.val == INT32_MIN && src2.val == -1) if (lval == INT32_MIN && rval == -1)
val = 0; data = 0;
else else
val = op_modulo(src1.val, src2.val); data = op_modulo(lval, rval);
break; break;
case RPN_EXP: case RPN_EXP:
if (src2.val < 0) if (rval < 0)
fatalerror("Exponentiation by negative power\n"); fatalerror("Exponentiation by negative power\n");
val = op_exponent(src1.val, src2.val); data = op_exponent(lval, rval);
break; break;
case RPN_NEG: case RPN_NEG:
@@ -429,20 +421,15 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
fatalerror("%d is not a binary operator\n", op); fatalerror("%d is not a binary operator\n", op);
} }
} else if (op == RPN_SUB && src1.isDiffConstant(src2.symbolOf())) { } else if (op == RPN_SUB && src1.isDiffConstant(src2.symbolOf())) {
Symbol const &symbol1 = *src1.symbolOf(); data = src1.symbolOf()->getValue() - src2.symbolOf()->getValue();
Symbol const &symbol2 = *src2.symbolOf();
val = symbol1.getValue() - symbol2.getValue();
isKnown = true;
} else if (int32_t constVal; op == RPN_AND && (constVal = tryConstMask(src1, src2)) != -1) { } else if (int32_t constVal; op == RPN_AND && (constVal = tryConstMask(src1, src2)) != -1) {
val = constVal; data = constVal;
isKnown = true;
} else { } else {
// If it's not known, start computing the RPN expression // If it's not known, start computing the RPN expression
// Convert the left-hand expression if it's constant // Convert the left-hand expression if it's constant
if (src1.isKnown) { if (src1.isKnown()) {
uint32_t lval = src1.val; uint32_t lval = src1.value();
uint8_t bytes[] = { uint8_t bytes[] = {
RPN_CONST, RPN_CONST,
(uint8_t)lval, (uint8_t)lval,
@@ -455,67 +442,61 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes)); memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes));
// Use the other expression's un-const reason // Use the other expression's un-const reason
reason = src2.reason; data = std::move(src2.data);
} else { } else {
// Otherwise just reuse its RPN buffer // Otherwise just reuse its RPN buffer
rpnPatchSize = src1.rpnPatchSize; rpnPatchSize = src1.rpnPatchSize;
std::swap(rpn, src1.rpn); std::swap(rpn, src1.rpn);
reason = src1.reason; data = std::move(src1.data);
} }
// Now, merge the right expression into the left one // Now, merge the right expression into the left one
uint8_t const *srcBytes = nullptr; if (src2.isKnown()) {
uint32_t srcLen = 0; // If the right expression is constant, append a shim instead
uint32_t srcPatchSize = 0; uint32_t rval = src2.value();
uint8_t bytes[] = {
// If the right expression is constant, merge a shim instead RPN_CONST,
uint32_t rval = src2.val; (uint8_t)rval,
uint8_t bytes[] = { (uint8_t)(rval >> 8),
RPN_CONST, (uint8_t)(rval >> 16),
(uint8_t)rval, (uint8_t)(rval >> 24),
(uint8_t)(rval >> 8), };
(uint8_t)(rval >> 16), uint8_t *ptr = reserveSpace(sizeof(bytes) + 1, sizeof(bytes) + 1);
(uint8_t)(rval >> 24), memcpy(ptr, bytes, sizeof(bytes));
}; ptr[sizeof(bytes)] = op;
if (src2.isKnown) {
srcBytes = bytes;
srcLen = sizeof(bytes);
srcPatchSize = sizeof(bytes);
} else { } else {
srcBytes = src2.rpn.data(); // Pointer to the right RPN // Copy the right RPN and append the operator
srcLen = src2.rpn.size(); // Size of the right RPN uint32_t rightRpnSize = src2.rpn.size();
srcPatchSize = src2.rpnPatchSize; uint8_t *ptr = reserveSpace(rightRpnSize + 1, src2.rpnPatchSize + 1);
if (rightRpnSize > 0)
// If `rightRpnSize == 0`, then `memcpy(ptr, nullptr, rightRpnSize)` would be UB
memcpy(ptr, src2.rpn.data(), rightRpnSize);
ptr[rightRpnSize] = op;
} }
// Copy the right RPN and append the operator
uint8_t *ptr = reserveSpace(srcLen + 1, srcPatchSize + 1);
if (srcBytes)
// If there were no `srcBytes`, then `memcpy(ptr, nullptr, 0)` would be UB
memcpy(ptr, srcBytes, srcLen);
ptr[srcLen] = op;
} }
} }
void Expression::makeCheckHRAM() { void Expression::makeCheckHRAM() {
isSymbol = false; isSymbol = false;
if (!isKnown) { if (!isKnown()) {
*reserveSpace(1) = RPN_HRAM; *reserveSpace(1) = RPN_HRAM;
} else if (val >= 0xFF00 && val <= 0xFFFF) { } else if (int32_t val = value(); val >= 0xFF00 && val <= 0xFFFF) {
// That range is valid, but only keep the lower byte // That range is valid, but only keep the lower byte
val &= 0xFF; data = val & 0xFF;
} else if (val < 0 || val > 0xFF) { } else if (val < 0 || val > 0xFF) {
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val); error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val);
} }
} }
void Expression::makeCheckRST() { void Expression::makeCheckRST() {
if (isKnown) { if (!isKnown()) {
// A valid RST address must be masked with 0x38
if (val & ~0x38)
error("Invalid address $%" PRIx32 " for RST\n", val);
// The target is in the "0x38" bits, all other bits are set
val |= 0xC7;
} else {
*reserveSpace(1) = RPN_RST; *reserveSpace(1) = RPN_RST;
} else if (int32_t val = value(); val & ~0x38) {
// A valid RST address must be masked with 0x38
error("Invalid address $%" PRIx32 " for RST\n", val);
} else {
// The target is in the "0x38" bits, all other bits are set
data = val | 0xC7;
} }
} }
@@ -524,8 +505,8 @@ void Expression::checkNBit(uint8_t n) const {
assert(n != 0); // That doesn't make sense assert(n != 0); // That doesn't make sense
assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
if (isKnown) { if (isKnown()) {
if (val < -(1 << n) || val >= 1 << n) if (int32_t val = value(); val < -(1 << n) || val >= 1 << n)
warning(WARNING_TRUNCATION_1, "Expression must be %u-bit\n", n); warning(WARNING_TRUNCATION_1, "Expression must be %u-bit\n", n);
else if (val < -(1 << (n - 1))) else if (val < -(1 << (n - 1)))
warning(WARNING_TRUNCATION_2, "Expression must be %u-bit\n", n); warning(WARNING_TRUNCATION_2, "Expression must be %u-bit\n", n);

View File

@@ -734,11 +734,11 @@ void sect_RelByte(Expression &expr, uint32_t pcShift) {
if (!reserveSpace(1)) if (!reserveSpace(1))
return; return;
if (!expr.isKnown) { if (!expr.isKnown()) {
createPatch(PATCHTYPE_BYTE, expr, pcShift); createPatch(PATCHTYPE_BYTE, expr, pcShift);
writebyte(0); writebyte(0);
} else { } else {
writebyte(expr.val); writebyte(expr.value());
} }
} }
@@ -753,11 +753,11 @@ void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
for (uint32_t i = 0; i < n; i++) { for (uint32_t i = 0; i < n; i++) {
Expression &expr = exprs[i % exprs.size()]; Expression &expr = exprs[i % exprs.size()];
if (!expr.isKnown) { if (!expr.isKnown()) {
createPatch(PATCHTYPE_BYTE, expr, i); createPatch(PATCHTYPE_BYTE, expr, i);
writebyte(0); writebyte(0);
} else { } else {
writebyte(expr.val); writebyte(expr.value());
} }
} }
} }
@@ -770,11 +770,11 @@ void sect_RelWord(Expression &expr, uint32_t pcShift) {
if (!reserveSpace(2)) if (!reserveSpace(2))
return; return;
if (!expr.isKnown) { if (!expr.isKnown()) {
createPatch(PATCHTYPE_WORD, expr, pcShift); createPatch(PATCHTYPE_WORD, expr, pcShift);
writeword(0); writeword(0);
} else { } else {
writeword(expr.val); writeword(expr.value());
} }
} }
@@ -786,11 +786,11 @@ void sect_RelLong(Expression &expr, uint32_t pcShift) {
if (!reserveSpace(2)) if (!reserveSpace(2))
return; return;
if (!expr.isKnown) { if (!expr.isKnown()) {
createPatch(PATCHTYPE_LONG, expr, pcShift); createPatch(PATCHTYPE_LONG, expr, pcShift);
writelong(0); writelong(0);
} else { } else {
writelong(expr.val); writelong(expr.value());
} }
} }