From 6842c831fdedec5deff3f1dc7fcca1465a4a9bd7 Mon Sep 17 00:00:00 2001 From: Eldred Habert Date: Sat, 5 Feb 2022 20:17:57 +0100 Subject: [PATCH] Allow binary AND to be sometimes constant (#976) --- include/asm/section.h | 2 +- src/asm/rpn.c | 46 +++++++++++++++++++++++++++++++++++++++++- test/asm/const-and.asm | 17 ++++++++++++++++ test/asm/const-and.err | 7 +++++++ test/asm/const-and.out | 7 +++++++ test/link/assert.asm | 10 ++++----- test/link/assert.out | 6 +++--- 7 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 test/asm/const-and.asm create mode 100644 test/asm/const-and.err create mode 100644 test/asm/const-and.out diff --git a/include/asm/section.h b/include/asm/section.h index e227b984..8d5020cd 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -28,7 +28,7 @@ struct Section { uint32_t size; uint32_t org; uint32_t bank; - uint8_t align; + uint8_t align; // Exactly as specified in `ALIGN[]` uint16_t alignOfs; struct Section *next; struct Patch *patches; diff --git a/src/asm/rpn.c b/src/asm/rpn.c index 0db22073..cabc26bc 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -317,7 +317,7 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr) { if (!rpn_isSymbol(expr)) 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) @@ -339,10 +339,51 @@ static bool isDiffConstant(struct Expression const *src1, 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, const struct Expression *src1, const struct Expression *src2) { expr->isSymbol = false; + int32_t constMaskVal; /* First, check if the expression is known */ 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->isKnown = true; + } else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) { + expr->val = constMaskVal; + expr->isKnown = true; } else { /* If it's not known, start computing the RPN expression */ diff --git a/test/asm/const-and.asm b/test/asm/const-and.asm new file mode 100644 index 00000000..a792e5a0 --- /dev/null +++ b/test/asm/const-and.asm @@ -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 diff --git a/test/asm/const-and.err b/test/asm/const-and.err new file mode 100644 index 00000000..68e3894d --- /dev/null +++ b/test/asm/const-and.err @@ -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)! diff --git a/test/asm/const-and.out b/test/asm/const-and.out new file mode 100644 index 00000000..8359c714 --- /dev/null +++ b/test/asm/const-and.out @@ -0,0 +1,7 @@ +$0 +$A +$A +$0 +$A +$0 +$0 diff --git a/test/link/assert.asm b/test/link/assert.asm index e10d485c..fb0d0a45 100644 --- a/test/link/assert.asm +++ b/test/link/assert.asm @@ -1,11 +1,9 @@ SECTION "test", ROM0 - ds 123 - FloatingBase: - assert WARN, FloatingBase & 0, "Worry about me, but not too much." - assert FAIL, FloatingBase & 0, "Okay, this is getting serious!" - assert FATAL, FloatingBase & 0, "It all ends now." - assert FAIL, FloatingBase & 0, "Not even time to roll credits!" + assert WARN, FloatingBase, "Worry about me, but not too much." + assert FAIL, FloatingBase, "Okay, this is getting serious!" + assert FATAL, FloatingBase, "It all ends now." + assert FAIL, FloatingBase, "Not even time to roll credits!" assert WARN, 0, "Still can finish the film, though!" diff --git a/test/link/assert.out b/test/link/assert.out index 5cb54bad..9530c917 100644 --- a/test/link/assert.out +++ b/test/link/assert.out @@ -1,4 +1,4 @@ -warning: assert.asm(7): Worry about me, but not too much. -error: assert.asm(8): Okay, this is getting serious! -FATAL: assert.asm(9): It all ends now. +warning: assert.asm(5): Worry about me, but not too much. +error: assert.asm(6): Okay, this is getting serious! +FATAL: assert.asm(7): It all ends now. Linking aborted after 2 errors