link: Suppress cascading errors.

This commit is contained in:
Jakub Kądziołka
2020-08-09 15:40:21 +02:00
parent 3036b58598
commit b421c983d6
5 changed files with 107 additions and 27 deletions

View File

@@ -62,9 +62,17 @@ static int32_t asl(int32_t value, int32_t shiftamt)
return (uint32_t)value << shiftamt; return (uint32_t)value << shiftamt;
} }
/* This is an "empty"-type stack */ /*
* This is an "empty"-type stack. Apart from the actual values, we also remember
* whether the value is a placeholder inserted for error recovery. This allows
* us to avoid cascading errors.
*
* The best way to think about this is a stack of (value, errorFlag) pairs.
* They are only separated for reasons of memory efficiency.
*/
struct RPNStack { struct RPNStack {
int32_t *buf; int32_t *values;
bool *errorFlags;
size_t size; size_t size;
size_t capacity; size_t capacity;
} stack; } stack;
@@ -72,8 +80,9 @@ struct RPNStack {
static inline void initRPNStack(void) static inline void initRPNStack(void)
{ {
stack.capacity = 64; stack.capacity = 64;
stack.buf = malloc(sizeof(*stack.buf) * stack.capacity); stack.values = malloc(sizeof(*stack.values) * stack.capacity);
if (!stack.buf) stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
if (!stack.values || !stack.errorFlags)
err(1, "Failed to init RPN stack"); err(1, "Failed to init RPN stack");
} }
@@ -82,7 +91,7 @@ static inline void clearRPNStack(void)
stack.size = 0; stack.size = 0;
} }
static void pushRPN(int32_t value) static void pushRPN(int32_t value, bool comesFromError)
{ {
if (stack.size >= stack.capacity) { if (stack.size >= stack.capacity) {
static const size_t increase_factor = 2; static const size_t increase_factor = 2;
@@ -91,33 +100,42 @@ static void pushRPN(int32_t value)
errx(1, "Overflow in RPN stack resize"); errx(1, "Overflow in RPN stack resize");
stack.capacity *= increase_factor; stack.capacity *= increase_factor;
stack.buf = stack.values =
realloc(stack.buf, sizeof(*stack.buf) * stack.capacity); realloc(stack.values, sizeof(*stack.values) * stack.capacity);
stack.errorFlags =
realloc(stack.errorFlags, sizeof(*stack.errorFlags) * stack.capacity);
/* /*
* Static analysis tools complain that the capacity might become * Static analysis tools complain that the capacity might become
* zero due to overflow, but fail to realize that it's caught by * zero due to overflow, but fail to realize that it's caught by
* the overflow check above. Hence the stringent check below. * the overflow check above. Hence the stringent check below.
*/ */
if (!stack.buf || !stack.capacity) if (!stack.values || !stack.errorFlags || !stack.capacity)
err(1, "Failed to resize RPN stack"); err(1, "Failed to resize RPN stack");
} }
stack.buf[stack.size] = value; stack.values[stack.size] = value;
stack.errorFlags[stack.size] = comesFromError;
stack.size++; stack.size++;
} }
// This flag tracks whether the RPN op that is currently being evaluated
// has popped any values with the error flag set.
static bool isError = false;
static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo) static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
{ {
if (stack.size == 0) if (stack.size == 0)
fatal(node, lineNo, "Internal error, RPN stack empty"); fatal(node, lineNo, "Internal error, RPN stack empty");
stack.size--; stack.size--;
return stack.buf[stack.size]; isError |= stack.errorFlags[stack.size];
return stack.values[stack.size];
} }
static inline void freeRPNStack(void) static inline void freeRPNStack(void)
{ {
free(stack.buf); free(stack.values);
free(stack.errorFlags);
} }
/* RPN operators */ /* RPN operators */
@@ -149,6 +167,8 @@ static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
* @param patch The patch to compute the value of * @param patch The patch to compute the value of
* @param section The section the patch is contained in * @param section The section the patch is contained in
* @return The patch's value * @return The patch's value
* @return isError Set if an error occurred during evaluation, and further
* errors caused by the value should be suppressed.
*/ */
static int32_t computeRPNExpr(struct Patch const *patch, static int32_t computeRPNExpr(struct Patch const *patch,
struct Symbol const * const *fileSymbols) struct Symbol const * const *fileSymbols)
@@ -166,6 +186,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
patch->src, patch->lineNo); patch->src, patch->lineNo);
int32_t value; int32_t value;
isError = false;
/* /*
* Friendly reminder: * Friendly reminder:
* Be VERY careful with two `popRPN` in the same expression. * Be VERY careful with two `popRPN` in the same expression.
@@ -191,7 +213,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_DIV: case RPN_DIV:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
if (!isError)
error(patch->src, patch->lineNo, "Division by 0"); error(patch->src, patch->lineNo, "Division by 0");
isError = true;
popRPN(); popRPN();
value = INT32_MAX; value = INT32_MAX;
} else { } else {
@@ -201,7 +225,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_MOD: case RPN_MOD:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
if (!isError)
error(patch->src, patch->lineNo, "Modulo by 0"); error(patch->src, patch->lineNo, "Modulo by 0");
isError = true;
popRPN(); popRPN();
value = 0; value = 0;
} else { } else {
@@ -280,11 +306,13 @@ static int32_t computeRPNExpr(struct Patch const *patch,
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Requested BANK() of symbol \"%s\", which was not found", "Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value]->name); fileSymbols[value]->name);
isError = true;
value = 1; value = 1;
} else if (!symbol->section) { } else if (!symbol->section) {
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Requested BANK() of non-label symbol \"%s\"", "Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value]->name); fileSymbols[value]->name);
isError = true;
value = 1; value = 1;
} else { } else {
value = symbol->section->bank; value = symbol->section->bank;
@@ -302,6 +330,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Requested BANK() of section \"%s\", which was not found", "Requested BANK() of section \"%s\", which was not found",
name); name);
isError = true;
value = 1; value = 1;
} else { } else {
value = sect->bank; value = sect->bank;
@@ -312,6 +341,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
if (!patch->pcSection) { if (!patch->pcSection) {
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"PC has no bank outside a section"); "PC has no bank outside a section");
isError = true;
value = 1; value = 1;
} else { } else {
value = patch->pcSection->bank; value = patch->pcSection->bank;
@@ -320,11 +350,13 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_HRAM: case RPN_HRAM:
value = popRPN(); value = popRPN();
if (value < 0 if (!isError && (value < 0
|| (value > 0xFF && value < 0xFF00) || (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF) || value > 0xFFFF)) {
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Value %" PRId32 " is not in HRAM range", value); "Value %" PRId32 " is not in HRAM range", value);
isError = true;
}
value &= 0xFF; value &= 0xFF;
break; break;
@@ -333,9 +365,12 @@ static int32_t computeRPNExpr(struct Patch const *patch,
/* Acceptable values are 0x00, 0x08, 0x10, ..., 0x38 /* Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
* They can be easily checked with a bitmask * They can be easily checked with a bitmask
*/ */
if (value & ~0x38) if (value & ~0x38) {
if (!isError)
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Value %" PRId32 " is not a RST vector", value); "Value %" PRId32 " is not a RST vector", value);
isError = true;
}
value |= 0xC7; value |= 0xC7;
break; break;
@@ -357,6 +392,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"PC has no value outside a section"); "PC has no value outside a section");
value = 0; value = 0;
isError = true;
} else { } else {
value = patch->pcOffset + patch->pcSection->org; value = patch->pcOffset + patch->pcSection->org;
} }
@@ -366,6 +402,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
if (!symbol) { if (!symbol) {
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Unknown symbol \"%s\"", fileSymbols[value]->name); "Unknown symbol \"%s\"", fileSymbols[value]->name);
isError = true;
} else { } else {
value = symbol->value; value = symbol->value;
/* Symbols attached to sections have offsets */ /* Symbols attached to sections have offsets */
@@ -376,13 +413,14 @@ static int32_t computeRPNExpr(struct Patch const *patch,
break; break;
} }
pushRPN(value); pushRPN(value, isError);
} }
if (stack.size > 1) if (stack.size > 1)
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"RPN stack has %zu entries on exit, not 1", stack.size); "RPN stack has %zu entries on exit, not 1", stack.size);
isError = false;
return popRPN(); return popRPN();
#undef popRPN #undef popRPN
@@ -394,10 +432,12 @@ void patch_CheckAssertions(struct Assertion *assert)
initRPNStack(); initRPNStack();
while (assert) { while (assert) {
if (!computeRPNExpr(&assert->patch, int32_t value = computeRPNExpr(&assert->patch,
(struct Symbol const * const *) (struct Symbol const * const *)assert->fileSymbols);
assert->fileSymbols)) { enum AssertionType type = assert->patch.type;
switch ((enum AssertionType)assert->patch.type) {
if (!isError && !value) {
switch (type) {
case ASSERT_FATAL: case ASSERT_FATAL:
fatal(assert->patch.src, assert->patch.lineNo, "%s", fatal(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
@@ -415,6 +455,11 @@ void patch_CheckAssertions(struct Assertion *assert)
: "assert failure"); : "assert failure");
break; break;
} }
} else if (isError && type == ASSERT_FATAL) {
fatal(assert->patch.src, assert->patch.lineNo,
"couldn't evaluate assertion%s%s",
assert->message[0] ? ": " : "",
assert->message);
} }
struct Assertion *next = assert->next; struct Assertion *next = assert->next;
@@ -450,7 +495,7 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
+ patch->pcOffset + 1; + patch->pcOffset + 1;
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (jumpOffset < -128 || jumpOffset > 127) if (!isError && (jumpOffset < -128 || jumpOffset > 127))
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"jr target out of reach (expected -129 < %" PRId16 " < 128)", "jr target out of reach (expected -129 < %" PRId16 " < 128)",
jumpOffset); jumpOffset);
@@ -467,8 +512,8 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
[PATCHTYPE_LONG] = {4, INT32_MIN, INT32_MAX} [PATCHTYPE_LONG] = {4, INT32_MIN, INT32_MAX}
}; };
if (value < types[patch->type].min if (!isError && (value < types[patch->type].min
|| value > types[patch->type].max) || value > types[patch->type].max))
error(patch->src, patch->lineNo, error(patch->src, patch->lineNo,
"Value %#" PRIx32 "%s is not %u-bit", "Value %#" PRIx32 "%s is not %u-bit",
value, value < 0 ? " (maybe negative?)" : "", value, value < 0 ? " (maybe negative?)" : "",

View File

@@ -0,0 +1,2 @@
assert FATAL, UnknownSymbol == 42
assert WeDontReachHere

View File

@@ -0,0 +1,3 @@
error: cascading-errors-fatal-assert.asm(1): Unknown symbol "UnknownSymbol"
fatal: cascading-errors-fatal-assert.asm(1): couldn't evaluate assertion
Linking aborted after 2 errors

View File

@@ -0,0 +1,19 @@
SECTION "zero", ROM0[$0]
Zero:
; Pin the section such that a jr to 0 is out of range
SECTION "test", ROM0[$1000]
;; XXX: the fallback value used is the index of the symbol (in the object file?)
;; Is this intended?
dw Bar
dw Foo / Bar
dw Foo / Zero
rst Foo
jr NonExist
ldh a, [hNonExist + $200]
assert Foo == 42
assert WARN, Bar == 42

View File

@@ -0,0 +1,11 @@
error: cascading-errors.asm(18): Unknown symbol "Foo"
error: cascading-errors.asm(19): Unknown symbol "Bar"
error: cascading-errors.asm(16): Unknown symbol "hNonExist"
error: cascading-errors.asm(14): Unknown symbol "NonExist"
error: cascading-errors.asm(12): Unknown symbol "Foo"
error: cascading-errors.asm(10): Unknown symbol "Foo"
error: cascading-errors.asm(10): Division by 0
error: cascading-errors.asm(9): Unknown symbol "Foo"
error: cascading-errors.asm(9): Unknown symbol "Bar"
error: cascading-errors.asm(8): Unknown symbol "Bar"
Linking failed with 10 errors