mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Use std::variant for RPN expression value (#1389)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
253
src/asm/rpn.cpp
253
src/asm/rpn.cpp
@@ -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 §Name) {
|
void Expression::makeBankSection(std::string const §Name) {
|
||||||
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 §Name) {
|
|||||||
void Expression::makeSizeOfSection(std::string const §Name) {
|
void Expression::makeSizeOfSection(std::string const §Name) {
|
||||||
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 §Name) {
|
|||||||
void Expression::makeStartOfSection(std::string const §Name) {
|
void Expression::makeStartOfSection(std::string const §Name) {
|
||||||
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 §Name) {
|
|||||||
|
|
||||||
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 § = *sym.getSection();
|
Section const § = *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,21 +442,18 @@ 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();
|
||||||
|
|
||||||
// If the right expression is constant, merge a shim instead
|
|
||||||
uint32_t rval = src2.val;
|
|
||||||
uint8_t bytes[] = {
|
uint8_t bytes[] = {
|
||||||
RPN_CONST,
|
RPN_CONST,
|
||||||
(uint8_t)rval,
|
(uint8_t)rval,
|
||||||
@@ -477,45 +461,42 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
(uint8_t)(rval >> 16),
|
(uint8_t)(rval >> 16),
|
||||||
(uint8_t)(rval >> 24),
|
(uint8_t)(rval >> 24),
|
||||||
};
|
};
|
||||||
if (src2.isKnown) {
|
uint8_t *ptr = reserveSpace(sizeof(bytes) + 1, sizeof(bytes) + 1);
|
||||||
srcBytes = bytes;
|
memcpy(ptr, bytes, sizeof(bytes));
|
||||||
srcLen = sizeof(bytes);
|
ptr[sizeof(bytes)] = op;
|
||||||
srcPatchSize = sizeof(bytes);
|
|
||||||
} else {
|
} else {
|
||||||
srcBytes = src2.rpn.data(); // Pointer to the right RPN
|
|
||||||
srcLen = src2.rpn.size(); // Size of the right RPN
|
|
||||||
srcPatchSize = src2.rpnPatchSize;
|
|
||||||
}
|
|
||||||
// Copy the right RPN and append the operator
|
// Copy the right RPN and append the operator
|
||||||
uint8_t *ptr = reserveSpace(srcLen + 1, srcPatchSize + 1);
|
uint32_t rightRpnSize = src2.rpn.size();
|
||||||
if (srcBytes)
|
uint8_t *ptr = reserveSpace(rightRpnSize + 1, src2.rpnPatchSize + 1);
|
||||||
// If there were no `srcBytes`, then `memcpy(ptr, nullptr, 0)` would be UB
|
if (rightRpnSize > 0)
|
||||||
memcpy(ptr, srcBytes, srcLen);
|
// If `rightRpnSize == 0`, then `memcpy(ptr, nullptr, rightRpnSize)` would be UB
|
||||||
ptr[srcLen] = op;
|
memcpy(ptr, src2.rpn.data(), rightRpnSize);
|
||||||
|
ptr[rightRpnSize] = 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);
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user