mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Multiple fixes and enhancements to RPN behavior: (#1448)
- FIX: `Label & const` was not actually doing the `& const` masking (fixes #1446) - ADD: `LOW(Label)` can be constant if `Label` is aligned to 8 or more bits (resolves #1444) - ADD: `!expr` can be constant 0 if `expr` has any non-zero bits (resolves #1447) - `LOW()` and `HIGH()` have their own RPN operator values (resolves #1445) The change to RPN values means that the object file version was incremented. This also refactors unary operators and functions, combining their evaluation similarly to binary ones.
This commit is contained in:
174
src/asm/rpn.cpp
174
src/asm/rpn.cpp
@@ -207,12 +207,57 @@ static bool tryConstNonzero(Expression const &lhs, Expression const &rhs) {
|
||||
return expr.isKnown() && expr.value() != 0;
|
||||
}
|
||||
|
||||
static bool tryConstLogNot(Expression const &expr) {
|
||||
Symbol const *sym = expr.symbolOf();
|
||||
if (!sym || !sym->getSection())
|
||||
return -1;
|
||||
|
||||
assume(sym->isNumeric());
|
||||
|
||||
Section const § = *sym->getSection();
|
||||
int32_t unknownBits = (1 << 16) - (1 << sect.align);
|
||||
|
||||
// `sym->getValue()` attempts to add the section's address, but that's "-1"
|
||||
// because the section is floating (otherwise we wouldn't be here)
|
||||
assume(sect.org == (uint32_t)-1);
|
||||
int32_t symbolOfs = sym->getValue() + 1;
|
||||
|
||||
int32_t knownBits = (symbolOfs + sect.alignOfs) & ~unknownBits;
|
||||
return knownBits != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to compute a constant binary AND from non-constant operands
|
||||
* Attempts to compute a constant LOW() from non-constant argument
|
||||
* This is possible if the argument is a symbol belonging to an `ALIGN[8]` section.
|
||||
*
|
||||
* @return The constant `LOW(expr)` result if it can be computed, or -1 otherwise.
|
||||
*/
|
||||
static int32_t tryConstLow(Expression const &expr) {
|
||||
Symbol const *sym = expr.symbolOf();
|
||||
if (!sym || !sym->getSection())
|
||||
return -1;
|
||||
|
||||
assume(sym->isNumeric());
|
||||
|
||||
// The low byte must not cover any unknown bits
|
||||
Section const § = *sym->getSection();
|
||||
if (sect.align < 8)
|
||||
return -1;
|
||||
|
||||
// `sym->getValue()` attempts to add the section's address, but that's "-1"
|
||||
// because the section is floating (otherwise we wouldn't be here)
|
||||
assume(sect.org == (uint32_t)-1);
|
||||
int32_t symbolOfs = sym->getValue() + 1;
|
||||
|
||||
return (symbolOfs + sect.alignOfs) & 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to compute a constant binary AND with one non-constant operands
|
||||
* This is possible if one operand is a symbol belonging to an `ALIGN[N]` section, and the other is
|
||||
* a constant that only keeps (some of) the lower N bits.
|
||||
*
|
||||
* @return The constant result if it can be computed, or -1 otherwise.
|
||||
* @return The constant `lhs & rhs` result if it can be computed, or -1 otherwise.
|
||||
*/
|
||||
static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||
Symbol const *lhsSymbol = lhs.symbolOf();
|
||||
@@ -232,11 +277,11 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||
if (!expr.isKnown())
|
||||
return -1;
|
||||
// We can now safely use `expr.value()`
|
||||
Section const § = *sym.getSection();
|
||||
int32_t unknownBits = (1 << 16) - (1 << sect.align); // The max alignment is 16
|
||||
int32_t mask = expr.value();
|
||||
|
||||
// The mask must ignore all unknown bits
|
||||
if ((expr.value() & unknownBits) != 0)
|
||||
// The mask must not cover any unknown bits
|
||||
Section const § = *sym.getSection();
|
||||
if (int32_t unknownBits = (1 << 16) - (1 << sect.align); (unknownBits & mask) != 0)
|
||||
return -1;
|
||||
|
||||
// `sym.getValue()` attempts to add the section's address, but that's "-1"
|
||||
@@ -244,61 +289,82 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||
assume(sect.org == (uint32_t)-1);
|
||||
int32_t symbolOfs = sym.getValue() + 1;
|
||||
|
||||
return (symbolOfs + sect.alignOfs) & ~unknownBits;
|
||||
return (symbolOfs + sect.alignOfs) & mask;
|
||||
}
|
||||
|
||||
void Expression::makeHigh() {
|
||||
isSymbol = false;
|
||||
if (isKnown()) {
|
||||
data = (int32_t)((uint32_t)value() >> 8 & 0xFF);
|
||||
void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
||||
clear();
|
||||
// First, check if the expression is known
|
||||
if (src.isKnown()) {
|
||||
// If the expressions is known, just compute the value
|
||||
int32_t val = src.value();
|
||||
|
||||
switch (op) {
|
||||
case RPN_NEG:
|
||||
data = (int32_t) - (uint32_t)val;
|
||||
break;
|
||||
case RPN_NOT:
|
||||
data = ~val;
|
||||
break;
|
||||
case RPN_LOGNOT:
|
||||
data = !val;
|
||||
break;
|
||||
case RPN_HIGH:
|
||||
data = (int32_t)((uint32_t)val >> 8 & 0xFF);
|
||||
break;
|
||||
case RPN_LOW:
|
||||
data = val & 0xFF;
|
||||
break;
|
||||
|
||||
case RPN_LOGOR:
|
||||
case RPN_LOGAND:
|
||||
case RPN_LOGEQ:
|
||||
case RPN_LOGGT:
|
||||
case RPN_LOGLT:
|
||||
case RPN_LOGGE:
|
||||
case RPN_LOGLE:
|
||||
case RPN_LOGNE:
|
||||
case RPN_ADD:
|
||||
case RPN_SUB:
|
||||
case RPN_XOR:
|
||||
case RPN_OR:
|
||||
case RPN_AND:
|
||||
case RPN_SHL:
|
||||
case RPN_SHR:
|
||||
case RPN_USHR:
|
||||
case RPN_MUL:
|
||||
case RPN_DIV:
|
||||
case RPN_MOD:
|
||||
case RPN_EXP:
|
||||
case RPN_BANK_SYM:
|
||||
case RPN_BANK_SECT:
|
||||
case RPN_BANK_SELF:
|
||||
case RPN_SIZEOF_SECT:
|
||||
case RPN_STARTOF_SECT:
|
||||
case RPN_SIZEOF_SECTTYPE:
|
||||
case RPN_STARTOF_SECTTYPE:
|
||||
case RPN_HRAM:
|
||||
case RPN_RST:
|
||||
case RPN_CONST:
|
||||
case RPN_SYM:
|
||||
fatalerror("%d is not an unary operator\n", op);
|
||||
}
|
||||
} else if (op == RPN_LOGNOT && tryConstLogNot(src)) {
|
||||
data = 0;
|
||||
} else if (int32_t constVal; op == RPN_LOW && (constVal = tryConstLow(src)) != -1) {
|
||||
data = constVal;
|
||||
} else {
|
||||
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR, RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||
|
||||
memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeLow() {
|
||||
isSymbol = false;
|
||||
if (isKnown()) {
|
||||
data = value() & 0xFF;
|
||||
} else {
|
||||
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||
|
||||
memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeNeg() {
|
||||
isSymbol = false;
|
||||
if (isKnown()) {
|
||||
data = (int32_t) - (uint32_t)value();
|
||||
} else {
|
||||
*reserveSpace(1) = RPN_NEG;
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeNot() {
|
||||
isSymbol = false;
|
||||
if (isKnown()) {
|
||||
data = ~value();
|
||||
} else {
|
||||
*reserveSpace(1) = RPN_NOT;
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeLogicNot() {
|
||||
isSymbol = false;
|
||||
if (isKnown()) {
|
||||
data = !value();
|
||||
} else {
|
||||
*reserveSpace(1) = RPN_LOGNOT;
|
||||
// If it's not known, just reuse its RPN buffer and append the operator
|
||||
rpnPatchSize = src.rpnPatchSize;
|
||||
std::swap(rpn, src.rpn);
|
||||
data = std::move(src.data);
|
||||
*reserveSpace(1) = op;
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2) {
|
||||
clear();
|
||||
// First, check if the expression is known
|
||||
// First, check if the expressions are known
|
||||
if (src1.isKnown() && src2.isKnown()) {
|
||||
// If both expressions are known, just compute the value
|
||||
int32_t lval = src1.value(), rval = src2.value();
|
||||
@@ -426,6 +492,8 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
case RPN_STARTOF_SECTTYPE:
|
||||
case RPN_HRAM:
|
||||
case RPN_RST:
|
||||
case RPN_HIGH:
|
||||
case RPN_LOW:
|
||||
case RPN_CONST:
|
||||
case RPN_SYM:
|
||||
fatalerror("%d is not a binary operator\n", op);
|
||||
|
||||
Reference in New Issue
Block a user