Allow binary AND to be sometimes constant (#976)

This commit is contained in:
Eldred Habert
2022-02-05 20:17:57 +01:00
committed by GitHub
parent 6b903059fe
commit 6842c831fd
7 changed files with 84 additions and 11 deletions

View File

@@ -28,7 +28,7 @@ struct Section {
uint32_t size; uint32_t size;
uint32_t org; uint32_t org;
uint32_t bank; uint32_t bank;
uint8_t align; uint8_t align; // Exactly as specified in `ALIGN[]`
uint16_t alignOfs; uint16_t alignOfs;
struct Section *next; struct Section *next;
struct Patch *patches; struct Patch *patches;

View File

@@ -317,7 +317,7 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
{ {
if (!rpn_isSymbol(expr)) if (!rpn_isSymbol(expr))
return NULL; return NULL;
return sym_FindScopedSymbol((char *)expr->rpn + 1); return sym_FindScopedSymbol((char const *)expr->rpn + 1);
} }
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym) bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
@@ -339,10 +339,51 @@ static bool isDiffConstant(struct Expression const *src1,
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2)); return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
} }
/**
* Attempts to compute a constant binary AND from 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.
*/
static int32_t tryConstMask(struct Expression const *lhs, struct Expression const *rhs)
{
struct Symbol const *sym = rpn_SymbolOf(lhs);
struct Expression const *expr = rhs;
if (!sym || !sym_GetSection(sym)) {
// If the lhs isn't a symbol, try again the other way around
sym = rpn_SymbolOf(rhs);
expr = lhs;
if (!sym || !sym_GetSection(sym))
return -1;
}
assert(sym_IsNumeric(sym));
if (!rpn_isKnown(expr))
return -1;
// We can now safely use `expr->val`
struct Section const *sect = sym_GetSection(sym);
int32_t unknownBits = (1 << 16) - (1 << sect->align); // The max alignment is 16
// The mask must ignore all unknown bits
if ((expr->val & unknownBits) != 0)
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)
assert(sect->org == (uint32_t)-1);
int32_t symbolOfs = sym_GetValue(sym) + 1;
return (symbolOfs + sect->alignOfs) & ~unknownBits;
}
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
const struct Expression *src1, const struct Expression *src2) const struct Expression *src1, const struct Expression *src2)
{ {
expr->isSymbol = false; expr->isSymbol = false;
int32_t constMaskVal;
/* First, check if the expression is known */ /* First, check if the expression is known */
expr->isKnown = src1->isKnown && src2->isKnown; expr->isKnown = src1->isKnown && src2->isKnown;
@@ -490,6 +531,9 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2); expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
expr->isKnown = true; expr->isKnown = true;
} else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) {
expr->val = constMaskVal;
expr->isKnown = true;
} else { } else {
/* If it's not known, start computing the RPN expression */ /* If it's not known, start computing the RPN expression */

17
test/asm/const-and.asm Normal file
View File

@@ -0,0 +1,17 @@
println @ & 0 ; This should produce an error, but not crash
SECTION "Test", ROM0,ALIGN[4,2]
ds 40
Aligned:
println Aligned & $0f
println ~$fffc & Aligned
println Aligned & $1f ; Not constant
println @ & $0f
SECTION "Unaligned", ROM0
println @ & 0
println @ & 1 ; Nope

7
test/asm/const-and.err Normal file
View File

@@ -0,0 +1,7 @@
error: const-and.asm(2):
PC has no value outside a section
error: const-and.asm(11):
Expected constant expression: 'Aligned' is not constant at assembly time
error: const-and.asm(17):
Expected constant expression: PC is not constant at assembly time
error: Assembly aborted (3 errors)!

7
test/asm/const-and.out Normal file
View File

@@ -0,0 +1,7 @@
$0
$A
$A
$0
$A
$0
$0

View File

@@ -1,11 +1,9 @@
SECTION "test", ROM0 SECTION "test", ROM0
ds 123
FloatingBase: FloatingBase:
assert WARN, FloatingBase & 0, "Worry about me, but not too much." assert WARN, FloatingBase, "Worry about me, but not too much."
assert FAIL, FloatingBase & 0, "Okay, this is getting serious!" assert FAIL, FloatingBase, "Okay, this is getting serious!"
assert FATAL, FloatingBase & 0, "It all ends now." assert FATAL, FloatingBase, "It all ends now."
assert FAIL, FloatingBase & 0, "Not even time to roll credits!" assert FAIL, FloatingBase, "Not even time to roll credits!"
assert WARN, 0, "Still can finish the film, though!" assert WARN, 0, "Still can finish the film, though!"

View File

@@ -1,4 +1,4 @@
warning: assert.asm(7): Worry about me, but not too much. warning: assert.asm(5): Worry about me, but not too much.
error: assert.asm(8): Okay, this is getting serious! error: assert.asm(6): Okay, this is getting serious!
FATAL: assert.asm(9): It all ends now. FATAL: assert.asm(7): It all ends now.
Linking aborted after 2 errors Linking aborted after 2 errors