Merge pull request #2808 from kugelfuhr/kugelfuhr/flowanalysis

Simple flow analysis to find unreachable code
This commit is contained in:
Bob Andrews
2025-07-26 00:10:21 +02:00
committed by GitHub
28 changed files with 1051 additions and 152 deletions

View File

@@ -29,7 +29,7 @@ int optopt; /* character checked for validity */
char *optarg; /* argument associated with option */ char *optarg; /* argument associated with option */
#define tell(s) fputs(*argv,stderr);fputs(s,stderr); \ #define tell(s) fputs(*argv,stderr);fputs(s,stderr); \
fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); fputc(optopt,stderr);fputc('\n',stderr);return(BADCH)
int __fastcall__ getopt (int argc, char* const* argv, const char* optstring) int __fastcall__ getopt (int argc, char* const* argv, const char* optstring)
/* Get option letter from argument vector */ /* Get option letter from argument vector */

View File

@@ -95,6 +95,7 @@ struct WarnMapEntry {
static WarnMapEntry WarnMap[] = { static WarnMapEntry WarnMap[] = {
/* Keep names sorted, even if it isn't used for now */ /* Keep names sorted, even if it isn't used for now */
{ &WarnConstComparison, "const-comparison" }, { &WarnConstComparison, "const-comparison" },
{ &WarnConstOverflow, "const-overflow" },
{ &WarningsAreErrors, "error" }, { &WarningsAreErrors, "error" },
{ &WarnNoEffect, "no-effect" }, { &WarnNoEffect, "no-effect" },
{ &WarnPointerSign, "pointer-sign" }, { &WarnPointerSign, "pointer-sign" },
@@ -108,7 +109,6 @@ static WarnMapEntry WarnMap[] = {
{ &WarnUnusedLabel, "unused-label" }, { &WarnUnusedLabel, "unused-label" },
{ &WarnUnusedParam, "unused-param" }, { &WarnUnusedParam, "unused-param" },
{ &WarnUnusedVar, "unused-var" }, { &WarnUnusedVar, "unused-var" },
{ &WarnConstOverflow, "const-overflow" },
}; };
Collection DiagnosticStrBufs; Collection DiagnosticStrBufs;
@@ -142,7 +142,7 @@ void PrintFileInclusionInfo (const LineInfo* LI)
static LineInfo* GetDiagnosticLI (void) LineInfo* GetDiagnosticLI (void)
/* Get the line info where the diagnostic info refers to */ /* Get the line info where the diagnostic info refers to */
{ {
if (CurTok.LI) { if (CurTok.LI) {
@@ -184,7 +184,7 @@ static unsigned GetDiagnosticLineNum (void)
void Fatal_ (const char *file, int line, const char* Format, ...) void Fatal_ (const char* file, int line, const char* Format, ...)
/* Print a message about a fatal error and die */ /* Print a message about a fatal error and die */
{ {
va_list ap; va_list ap;
@@ -208,7 +208,7 @@ void Fatal_ (const char *file, int line, const char* Format, ...)
void Internal_ (const char *file, int line, const char* Format, ...) void Internal_ (const char* file, int line, const char* Format, ...)
/* Print a message about an internal compiler error and die */ /* Print a message about an internal compiler error and die */
{ {
va_list ap; va_list ap;
@@ -279,7 +279,7 @@ static void IntError (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap)
void LIError_ (const char *file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...) void LIError_ (const char* file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...)
/* Print an error message with the line info given explicitly */ /* Print an error message with the line info given explicitly */
{ {
va_list ap; va_list ap;
@@ -295,7 +295,7 @@ void LIError_ (const char *file, int line, errcat_t EC, LineInfo* LI, const char
void Error_ (const char *file, int line, const char* Format, ...) void Error_ (const char* file, int line, const char* Format, ...)
/* Print an error message */ /* Print an error message */
{ {
va_list ap; va_list ap;
@@ -311,7 +311,7 @@ void Error_ (const char *file, int line, const char* Format, ...)
void PPError_ (const char *file, int line, const char* Format, ...) void PPError_ (const char* file, int line, const char* Format, ...)
/* Print an error message. For use within the preprocessor */ /* Print an error message. For use within the preprocessor */
{ {
va_list ap; va_list ap;
@@ -370,7 +370,7 @@ static void IntWarning (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap)
void LIWarning_ (const char *file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...) void LIWarning_ (const char* file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...)
/* Print a warning message with the line info given explicitly */ /* Print a warning message with the line info given explicitly */
{ {
va_list ap; va_list ap;
@@ -386,7 +386,7 @@ void LIWarning_ (const char *file, int line, errcat_t EC, LineInfo* LI, const ch
void Warning_ (const char *file, int line, const char* Format, ...) void Warning_ (const char* file, int line, const char* Format, ...)
/* Print a warning message */ /* Print a warning message */
{ {
va_list ap; va_list ap;
@@ -402,7 +402,7 @@ void Warning_ (const char *file, int line, const char* Format, ...)
void PPWarning_ (const char *file, int line, const char* Format, ...) void PPWarning_ (const char* file, int line, const char* Format, ...)
/* Print a warning message. For use within the preprocessor */ /* Print a warning message. For use within the preprocessor */
{ {
va_list ap; va_list ap;
@@ -424,7 +424,45 @@ void UnreachableCodeWarning (void)
*/ */
{ {
if (IS_Get (&WarnUnreachableCode)) { if (IS_Get (&WarnUnreachableCode)) {
Warning ("Unreachable code");
LineInfo* LI;
/* Add special handling for compound statements if the current token
** is from the source. Doing this here is a bit hacky but unfortunately
** there's no better place.
*/
if (CurTok.LI && NextTok.LI) {
if (CurTok.Tok == TOK_LCURLY) {
/* Do not point to the compound statement but to the first
** statement within it. If the compound statement is empty
** do not even output a warning. This fails of course for
** nested compounds but will do the right thing in most cases.
*/
if (NextTok.Tok == TOK_RCURLY) {
return;
}
LI = NextTok.LI;
} else {
LI = CurTok.LI;
}
} else {
LI = GetCurLineInfo ();
}
/* Now output the warning */
LIWarning (EC_PARSER, LI, "Unreachable code");
}
}
void LIUnreachableCodeWarning (LineInfo* LI)
/* Print a warning about unreachable code at the given location if these
** warnings are enabled.
*/
{
if (IS_Get (&WarnUnreachableCode)) {
LIWarning (EC_PARSER, LI, "Unreachable code");
} }
} }

View File

@@ -103,6 +103,9 @@ struct StrBuf;
void PrintFileInclusionInfo (const LineInfo* LI); void PrintFileInclusionInfo (const LineInfo* LI);
/* Print hierarchy of file inclusion */ /* Print hierarchy of file inclusion */
LineInfo* GetDiagnosticLI (void);
/* Get the line info where the diagnostic info refers to */
void Fatal_ (const char *file, int line, const char* Format, ...) attribute ((noreturn, format (printf, 3, 4))); void Fatal_ (const char *file, int line, const char* Format, ...) attribute ((noreturn, format (printf, 3, 4)));
#define Fatal(...) Fatal_(__FILE__, __LINE__, __VA_ARGS__) #define Fatal(...) Fatal_(__FILE__, __LINE__, __VA_ARGS__)
/* Print a message about a fatal error and die */ /* Print a message about a fatal error and die */
@@ -140,6 +143,11 @@ void UnreachableCodeWarning (void);
** warnings are enabled. ** warnings are enabled.
*/ */
void LIUnreachableCodeWarning (LineInfo* LI);
/* Print a warning about unreachable code at the given location if these
** warnings are enabled.
*/
IntStack* FindWarning (const char* Name); IntStack* FindWarning (const char* Name);
/* Search for a warning in the WarnMap table and return a pointer to the /* Search for a warning in the WarnMap table and return a pointer to the
** intstack that holds its state. Return NULL if there is no such warning. ** intstack that holds its state. Return NULL if there is no such warning.

View File

@@ -453,6 +453,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
SymEntry* Param; SymEntry* Param;
const Type* RType; /* Real type used for struct parameters */ const Type* RType; /* Real type used for struct parameters */
const Type* ReturnType; /* Return type */ const Type* ReturnType; /* Return type */
int StmtFlags; /* Flow control flags for the function compound */
/* Remember this function descriptor used for definition */ /* Remember this function descriptor used for definition */
GetFuncDesc (Func->Type)->FuncDef = D; GetFuncDesc (Func->Type)->FuncDef = D;
@@ -626,13 +627,13 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
*/ */
CurrentFunc->TopLevelSP = StackPtr; CurrentFunc->TopLevelSP = StackPtr;
/* Now process statements in this block */ /* Now process statements in this block checking for unreachable code */
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { StmtFlags = StatementBlock (0);
AnyStatement (0);
}
/* Check if this function is missing a return value */ /* Check if this function is missing a return value */
if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc)) { if (!SF_Unreach (StmtFlags) &&
!F_HasVoidReturn (CurrentFunc) &&
!F_HasReturn (CurrentFunc)) {
/* If this is the main function in a C99 environment returning an int, /* If this is the main function in a C99 environment returning an int,
** let it always return zero. Otherwise output a warning. ** let it always return zero. Otherwise output a warning.
*/ */

View File

@@ -41,10 +41,12 @@
#include "error.h" #include "error.h"
#include "exprdesc.h" #include "exprdesc.h"
#include "expr.h" #include "expr.h"
#include "function.h"
#include "loadexpr.h" #include "loadexpr.h"
#include "scanner.h" #include "scanner.h"
#include "seqpoint.h" #include "seqpoint.h"
#include "standard.h" #include "standard.h"
#include "stmt.h"
#include "symtab.h" #include "symtab.h"
#include "goto.h" #include "goto.h"
@@ -56,8 +58,8 @@
void GotoStatement (void) int GotoStatement (void)
/* Process a goto statement. */ /* Process a goto statement and return one of the SF_xxx flags from stmt.h. */
{ {
/* Eat the "goto" */ /* Eat the "goto" */
NextToken (); NextToken ();
@@ -165,6 +167,8 @@ void GotoStatement (void)
} else { } else {
Error ("Label name expected"); Error ("Label name expected");
} }
return SF_GOTO | SF_ANY_GOTO;
} }

View File

@@ -44,8 +44,8 @@
void GotoStatement (void); int GotoStatement (void);
/* Process a goto statement. */ /* Process a goto statement and return one of the SF_xxx flags from stmt.h. */
void DoLabel (void); void DoLabel (void);
/* Define a goto label. */ /* Define a goto label. */

View File

@@ -104,7 +104,7 @@ LineInfo* GetCurLineInfo (void);
void UpdateCurrentLineInfo (const StrBuf* Line); void UpdateCurrentLineInfo (const StrBuf* Line);
/* Update the current line info - called if a new line is read */ /* Update the current line info - called if a new line is read */
void RememberCheckedLI (struct LineInfo* LI); void RememberCheckedLI (LineInfo* LI);
/* Remember the latest checked line info struct */ /* Remember the latest checked line info struct */
LineInfo* GetPrevCheckedLI (void); LineInfo* GetPrevCheckedLI (void);

View File

@@ -154,7 +154,7 @@ static int IfStatement (void)
{ {
unsigned Label1; unsigned Label1;
unsigned TestResult; unsigned TestResult;
int GotBreak; int StmtFlags;
/* Skip the if */ /* Skip the if */
NextToken (); NextToken ();
@@ -163,21 +163,33 @@ static int IfStatement (void)
Label1 = GetLocalLabel (); Label1 = GetLocalLabel ();
TestResult = TestInParens (Label1, 0); TestResult = TestInParens (Label1, 0);
/* Output a warning if the condition is always false */
if (TestResult == TESTEXPR_FALSE) {
UnreachableCodeWarning ();
}
/* Parse the if body */ /* Parse the if body */
GotBreak = AnyStatement (0); StmtFlags = AnyStatement (0, 0);
/* Else clause present? */ /* Else clause present? */
if (CurTok.Tok != TOK_ELSE) { if (CurTok.Tok != TOK_ELSE) {
g_defcodelabel (Label1); g_defcodelabel (Label1);
/* Since there's no else clause, we're not sure, if the a break /* If the test result is always true, any special statement (return,
** statement is really executed. ** break etc.) is always executed. If not, we cannot be sure.
*/ */
return 0; if (TestResult != TESTEXPR_TRUE) {
/* There's no else so we're not sure, if any special statements
** are really executed.
*/
StmtFlags &= ~SF_MASK_UNREACH;
}
} else { } else {
int StmtFlags2;
/* Generate a jump around the else branch */ /* Generate a jump around the else branch */
unsigned Label2 = GetLocalLabel (); unsigned Label2 = GetLocalLabel ();
g_jump (Label2); g_jump (Label2);
@@ -195,22 +207,40 @@ static int IfStatement (void)
/* Define the target for the first test */ /* Define the target for the first test */
g_defcodelabel (Label1); g_defcodelabel (Label1);
/* Total break only if both branches had a break. */ /* Parse the else body and evaluate special statements */
GotBreak &= AnyStatement (0); StmtFlags2 = AnyStatement (0, 0);
if (TestResult == TESTEXPR_FALSE) {
/* The "else" part is always executed */
StmtFlags = StmtFlags2;
} else if (TestResult != TESTEXPR_TRUE) {
/* If both branches have a special statement the following code is
** unreachable and we combine the flags. Otherwise the code
** following the "if" is always reachable.
*/
StmtFlags |= SF_Any (StmtFlags2);
if (SF_Unreach (StmtFlags) && SF_Unreach (StmtFlags2)) {
StmtFlags |= SF_Unreach (StmtFlags2);
} else {
StmtFlags &= ~SF_MASK_UNREACH;
}
}
/* Generate the label for the else clause */ /* Generate the label for the else clause */
g_defcodelabel (Label2); g_defcodelabel (Label2);
/* Done */
return GotBreak;
} }
/* Done */
return StmtFlags;
} }
static void DoStatement (void) static int DoStatement (void)
/* Handle the 'do' statement */ /* Handle the 'do' statement */
{ {
int StmtFlags;
unsigned TestResult;
/* Get the loop control labels */ /* Get the loop control labels */
unsigned LoopLabel = GetLocalLabel (); unsigned LoopLabel = GetLocalLabel ();
unsigned BreakLabel = GetLocalLabel (); unsigned BreakLabel = GetLocalLabel ();
@@ -226,14 +256,14 @@ static void DoStatement (void)
g_defcodelabel (LoopLabel); g_defcodelabel (LoopLabel);
/* Parse the loop body */ /* Parse the loop body */
AnyStatement (0); StmtFlags = AnyStatement (0, 0);
/* Output the label for a continue */ /* Output the label for a continue */
g_defcodelabel (ContinueLabel); g_defcodelabel (ContinueLabel);
/* Parse the end condition */ /* Parse the end condition */
Consume (TOK_WHILE, "'while' expected"); Consume (TOK_WHILE, "'while' expected");
TestInParens (LoopLabel, 1); TestResult = TestInParens (LoopLabel, 1);
ConsumeSemi (); ConsumeSemi ();
/* Define the break label */ /* Define the break label */
@@ -241,17 +271,52 @@ static void DoStatement (void)
/* Remove the loop from the loop stack */ /* Remove the loop from the loop stack */
DelLoop (); DelLoop ();
/* Fix the flags for the loop. */
if (TestResult == TESTEXPR_TRUE) {
/* If the loop condition is always true, and we do not have a
** "break" statement, the loop won't terminate. So the only valid
** "unreach" flag is that for the endless loop. Otherwise - if there
** is a "break" statement, the code after the loop is reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
if (!SF_Any_Break (StmtFlags)) {
StmtFlags |= (SF_OTHER | SF_ANY_OTHER);
}
} else if (SF_Any_Break (StmtFlags)) {
/* If the loop condition is not always true, but we have a "break"
** anywhere, the following code is reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
} else {
/* Otherwise the last statement in the loop determines the status for
** the following code, so we have to change "continue" into "other"
** but apart from that flags stay as they are.
*/
if (SF_Continue (StmtFlags)) {
StmtFlags &= ~SF_CONTINUE;
StmtFlags |= (SF_OTHER | SF_ANY_OTHER);
}
}
/* "break" and "continue" are not relevant for the following code */
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
/* Done */
return StmtFlags;
} }
static void WhileStatement (void) static int WhileStatement (void)
/* Handle the 'while' statement */ /* Handle the 'while' statement */
{ {
int PendingToken; int PendingToken;
CodeMark CondCodeStart; /* Start of condition evaluation code */ CodeMark CondCodeStart; /* Start of condition evaluation code */
CodeMark CondCodeEnd; /* End of condition evaluation code */ CodeMark CondCodeEnd; /* End of condition evaluation code */
CodeMark Here; /* "Here" location of code */ CodeMark Here; /* "Here" location of code */
unsigned TestResult; /* Result of the while loop test expression */
int StmtFlags;
/* Get the loop control labels */ /* Get the loop control labels */
unsigned LoopLabel = GetLocalLabel (); unsigned LoopLabel = GetLocalLabel ();
@@ -274,8 +339,15 @@ static void WhileStatement (void)
/* Remember the current position */ /* Remember the current position */
GetCodePos (&CondCodeStart); GetCodePos (&CondCodeStart);
/* Test the loop condition */ /* Test the loop condition. While loops are somewhat different from other
TestInParens (LoopLabel, 1); ** loops: While an "always true" condition is used often for endless loops
** (or loops left by "break"), an "always false" condition doesn't make
** sense, so we check that here and warn about it.
*/
TestResult = TestInParens (LoopLabel, 1);
if (TestResult == TESTEXPR_FALSE) {
UnreachableCodeWarning ();
}
/* Remember the end of the condition evaluation code */ /* Remember the end of the condition evaluation code */
GetCodePos (&CondCodeEnd); GetCodePos (&CondCodeEnd);
@@ -284,7 +356,7 @@ static void WhileStatement (void)
g_defcodelabel (LoopLabel); g_defcodelabel (LoopLabel);
/* Loop body */ /* Loop body */
AnyStatement (&PendingToken); StmtFlags = AnyStatement (&PendingToken, 0);
/* Emit the while condition label */ /* Emit the while condition label */
g_defcodelabel (CondLabel); g_defcodelabel (CondLabel);
@@ -303,11 +375,34 @@ static void WhileStatement (void)
/* Remove the loop from the loop stack */ /* Remove the loop from the loop stack */
DelLoop (); DelLoop ();
/* Fix the flags for the loop. */
if (TestResult == TESTEXPR_TRUE) {
/* If the loop condition is always true, and we do not have a
** "break" statement, the loop won't terminate. So the only valid
** "unreach" flag is that for the endless loop. Otherwise - if there
** is a "break" statement, the code after the loop is reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
if (!SF_Any_Break (StmtFlags)) {
StmtFlags |= (SF_OTHER | SF_ANY_OTHER);
}
} else {
/* If the loop condition is not always true, the code after the loop
** is always reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
}
/* "break" and "continue" are not relevant for the following code */
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
/* Done */
return StmtFlags;
} }
static void ReturnStatement (void) static int ReturnStatement (void)
/* Handle the 'return' statement */ /* Handle the 'return' statement */
{ {
ExprDesc Expr; ExprDesc Expr;
@@ -357,11 +452,14 @@ static void ReturnStatement (void)
/* Output a jump to the function exit code */ /* Output a jump to the function exit code */
g_jump (F_GetRetLab (CurrentFunc)); g_jump (F_GetRetLab (CurrentFunc));
/* Done */
return SF_RETURN | SF_ANY_RETURN;
} }
static void BreakStatement (void) static int BreakStatement (void)
/* Handle the 'break' statement */ /* Handle the 'break' statement */
{ {
LoopDesc* L; LoopDesc* L;
@@ -376,7 +474,7 @@ static void BreakStatement (void)
if (L == 0) { if (L == 0) {
/* Error: No current loop */ /* Error: No current loop */
Error ("'break' statement not within loop or switch"); Error ("'break' statement not within loop or switch");
return; return SF_NONE;
} }
/* Correct the stack pointer if needed */ /* Correct the stack pointer if needed */
@@ -384,11 +482,14 @@ static void BreakStatement (void)
/* Jump to the exit label of the loop */ /* Jump to the exit label of the loop */
g_jump (L->BreakLabel); g_jump (L->BreakLabel);
/* Done */
return SF_BREAK | SF_ANY_BREAK;
} }
static void ContinueStatement (void) static int ContinueStatement (void)
/* Handle the 'continue' statement */ /* Handle the 'continue' statement */
{ {
LoopDesc* L; LoopDesc* L;
@@ -411,7 +512,7 @@ static void ContinueStatement (void)
/* Did we find it? */ /* Did we find it? */
if (L == 0) { if (L == 0) {
Error ("'continue' statement not within a loop"); Error ("'continue' statement not within a loop");
return; return SF_NONE;
} }
/* Correct the stackpointer if needed */ /* Correct the stackpointer if needed */
@@ -419,17 +520,22 @@ static void ContinueStatement (void)
/* Jump to next loop iteration */ /* Jump to next loop iteration */
g_jump (L->ContinueLabel); g_jump (L->ContinueLabel);
/* Done */
return SF_CONTINUE | SF_ANY_CONTINUE;
} }
static void ForStatement (void) static int ForStatement (void)
/* Handle a 'for' statement */ /* Handle a 'for' statement */
{ {
int HaveIncExpr; int HaveIncExpr;
CodeMark IncExprStart; CodeMark IncExprStart;
CodeMark IncExprEnd; CodeMark IncExprEnd;
int PendingToken; int PendingToken;
unsigned TestResult;
int StmtFlags;
/* Get several local labels needed later */ /* Get several local labels needed later */
unsigned TestLabel = GetLocalLabel (); unsigned TestLabel = GetLocalLabel ();
@@ -463,9 +569,10 @@ static void ForStatement (void)
/* Parse the test expression */ /* Parse the test expression */
if (CurTok.Tok != TOK_SEMI) { if (CurTok.Tok != TOK_SEMI) {
Test (BodyLabel, 1); TestResult = Test (BodyLabel, 1);
g_jump (BreakLabel); g_jump (BreakLabel);
} else { } else {
TestResult = TESTEXPR_TRUE;
g_jump (BodyLabel); g_jump (BodyLabel);
} }
ConsumeSemi (); ConsumeSemi ();
@@ -497,7 +604,7 @@ static void ForStatement (void)
/* Loop body */ /* Loop body */
g_defcodelabel (BodyLabel); g_defcodelabel (BodyLabel);
AnyStatement (&PendingToken); StmtFlags = AnyStatement (&PendingToken, 0);
/* If we had an increment expression, move the code to the bottom of /* If we had an increment expression, move the code to the bottom of
** the loop. In this case we don't need to jump there at the end of ** the loop. In this case we don't need to jump there at the end of
@@ -520,16 +627,21 @@ static void ForStatement (void)
/* Remove the loop from the loop stack */ /* Remove the loop from the loop stack */
DelLoop (); DelLoop ();
/* If the condition is always true, any special statements are always
** executed. Otherwise we don't know.
*/
return (TestResult == TESTEXPR_TRUE)? StmtFlags : SF_NONE;
} }
static int CompoundStatement (int* PendingToken) static int CompoundStatement (int* PendingToken, struct SwitchCtrl* Switch)
/* Compound statement. Allow any number of statements inside braces. The /* Compound statement. Allow any number of statements inside braces. The
** function returns true if the last statement was a break or return. ** function returns true if the last statement was a break or return.
*/ */
{ {
int GotBreak = 0; int StmtFlags;
/* Remember the stack at block entry */ /* Remember the stack at block entry */
int OldStack = StackPtr; int OldStack = StackPtr;
@@ -544,17 +656,11 @@ static int CompoundStatement (int* PendingToken)
/* Parse local variable declarations if any */ /* Parse local variable declarations if any */
DeclareLocals (); DeclareLocals ();
/* Now process statements in this block */ /* Now process statements in this block checking for unreachable code */
while (CurTok.Tok != TOK_RCURLY) { StmtFlags = StatementBlock (Switch);
if (CurTok.Tok != TOK_CEOF) {
GotBreak = AnyStatement (0);
} else {
break;
}
}
/* Clean up the stack if the codeflow may reach the end */ /* Clean up the stack if the codeflow may reach the end */
if (!GotBreak) { if ((StmtFlags & SF_MASK_UNREACH) == SF_NONE) {
g_space (StackPtr - OldStack); g_space (StackPtr - OldStack);
} }
@@ -564,7 +670,6 @@ static int CompoundStatement (int* PendingToken)
if (OldBlockStackSize != CollCount (&CurrentFunc->LocalsBlockStack)) { if (OldBlockStackSize != CollCount (&CurrentFunc->LocalsBlockStack)) {
CollPop (&CurrentFunc->LocalsBlockStack); CollPop (&CurrentFunc->LocalsBlockStack);
} }
StackPtr = OldStack; StackPtr = OldStack;
/* Emit references to imports/exports for this block */ /* Emit references to imports/exports for this block */
@@ -576,12 +681,13 @@ static int CompoundStatement (int* PendingToken)
/* Skip '}' */ /* Skip '}' */
CheckTok (TOK_RCURLY, "'}' expected", PendingToken); CheckTok (TOK_RCURLY, "'}' expected", PendingToken);
return GotBreak; /* Done */
return StmtFlags;
} }
static void Statement (int* PendingToken) static int Statement (int* PendingToken)
/* Single-line statement */ /* Single-line statement */
{ {
ExprDesc Expr; ExprDesc Expr;
@@ -615,54 +721,73 @@ static void Statement (int* PendingToken)
} }
CheckSemi (PendingToken); CheckSemi (PendingToken);
/* Done. All special statements are handled in other subroutines. */
return SF_NONE;
} }
static int ParseAnyLabels (void) int StatementBlock (struct SwitchCtrl* Switch)
/* Return -1 if there are any labels with a statement */ /* Parse multiple statements within curly braces checking for unreachable
{ ** code. Returns the SF_xxx flags for the last statement.
unsigned PrevErrorCount = ErrorCount;
int HasLabels = 0;
for (;;) {
if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
/* C 'goto' label */
DoLabel ();
} else if (CurTok.Tok == TOK_CASE) {
/* C 'case' label */
CaseLabel ();
} else if (CurTok.Tok == TOK_DEFAULT) {
/* C 'default' label */
DefaultLabel ();
} else {
/* No labels */
break;
}
HasLabels = 1;
}
if (HasLabels) {
if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) {
return -1;
}
}
return 0;
}
int AnyStatement (int* PendingToken)
/* Statement parser. Returns 1 if the statement does a return/break, returns
** 0 otherwise. If the PendingToken pointer is not NULL, the function will
** not skip the terminating token of the statement (closing brace or
** semicolon), but store true if there is a pending token, and false if there
** is none. The token is always checked, so there is no need for the caller to
** check this token, it must be skipped, however. If the argument pointer is
** NULL, the function will skip the token.
*/ */
{ {
int GotBreak = 0; /* We want to emit an "unreachable code" warning for statements following
** a "goto", "return" etc. But only - and this is what complicates it -
** if the following statement is not preceeded by a label. Since the latter
** means that a jump may go there so the statement is actually reachable.
*/
if (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
LineInfo* LI1 = UseLineInfo (GetDiagnosticLI ());
int StmtFlags1 = AnyStatement (0, Switch);
int UnreachableWarning = 0;
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
LineInfo* LI2 = UseLineInfo (GetDiagnosticLI ());
int StmtFlags2 = AnyStatement (0, Switch);
if (!UnreachableWarning) {
/* Check if the code is unreachable because of a preceeding
** jump and if the code doesn't have a jump label.
*/
if ((StmtFlags1 & SF_MASK_UNREACH) != SF_NONE &&
(StmtFlags2 & SF_MASK_LABEL) == SF_NONE) {
LIUnreachableCodeWarning (LI2);
UnreachableWarning = 1;
}
}
if (LI1) {
ReleaseLineInfo (LI1);
}
LI1 = LI2;
StmtFlags1 = (StmtFlags1 & SF_MASK_ANY) | StmtFlags2;
}
if (LI1) {
ReleaseLineInfo (LI1);
}
return StmtFlags1;
} else {
return SF_NONE;
}
}
int AnyStatement (int* PendingToken, struct SwitchCtrl* Switch)
/* Statement parser. Returns one of the SF_xxx flags describing if the
** statement does a return/break. If the PendingToken pointer is not NULL,
** the function will not skip the terminating token of the statement (closing
** brace or semicolon), but store true if there is a pending token, and false
** if there is none. The token is always checked, so there is no need for the
** caller to check this token, it must be skipped, however. If the argument
** pointer is NULL, the function will skip the token. When called to parse a
** switch body, the switch control structure must be passed via the Switch
** argument. Otherwise it must be NULL.
*/
{
int LabelFlags = SF_NONE;
int StmtFlags;
unsigned PrevErrorCount;
LineInfo* LI;
/* Assume no pending token */ /* Assume no pending token */
if (PendingToken) { if (PendingToken) {
@@ -670,75 +795,113 @@ int AnyStatement (int* PendingToken)
} }
/* Handle any labels. A label is always part of a statement, it does not /* Handle any labels. A label is always part of a statement, it does not
** replace one. ** replace one. If we have errors parsing labels, return without reading
** the following statement.
*/ */
if (ParseAnyLabels ()) { PrevErrorCount = ErrorCount;
return 0; while (1) {
if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
/* C 'goto' label */
DoLabel ();
LabelFlags |= SF_LABEL_GOTO;
} else if (CurTok.Tok == TOK_CASE) {
/* C 'case' label */
CaseLabel ();
LabelFlags |= SF_LABEL_CASE;
} else if (CurTok.Tok == TOK_DEFAULT) {
/* C 'default' label */
DefaultLabel ();
LabelFlags |= SF_LABEL_DEFAULT;
} else {
/* No labels */
break;
}
}
if (LabelFlags != SF_NONE) {
/* We had labels, check for errors */
if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) {
return SF_NONE;
}
} }
/* Remember the line info for the now following statement */
LI = UseLineInfo (GetDiagnosticLI ());
/* Now look at the actual statement. */
switch (CurTok.Tok) { switch (CurTok.Tok) {
case TOK_IF: case TOK_IF:
GotBreak = IfStatement (); StmtFlags = IfStatement ();
break; break;
case TOK_SWITCH: case TOK_SWITCH:
SwitchStatement (); StmtFlags = SwitchStatement ();
break; break;
case TOK_WHILE: case TOK_WHILE:
WhileStatement (); StmtFlags = WhileStatement ();
break; break;
case TOK_DO: case TOK_DO:
DoStatement (); StmtFlags = DoStatement ();
break; break;
case TOK_FOR: case TOK_FOR:
ForStatement (); StmtFlags = ForStatement ();
break; break;
case TOK_GOTO: case TOK_GOTO:
GotoStatement (); StmtFlags = GotoStatement ();
CheckSemi (PendingToken); CheckSemi (PendingToken);
GotBreak = 1;
break; break;
case TOK_RETURN: case TOK_RETURN:
ReturnStatement (); StmtFlags = ReturnStatement ();
CheckSemi (PendingToken); CheckSemi (PendingToken);
GotBreak = 1;
break; break;
case TOK_BREAK: case TOK_BREAK:
BreakStatement (); StmtFlags = BreakStatement ();
CheckSemi (PendingToken); CheckSemi (PendingToken);
GotBreak = 1;
break; break;
case TOK_CONTINUE: case TOK_CONTINUE:
ContinueStatement (); StmtFlags = ContinueStatement ();
CheckSemi (PendingToken); CheckSemi (PendingToken);
GotBreak = 1;
break; break;
case TOK_SEMI: case TOK_SEMI:
/* Empty statement. Ignore it */ /* Empty statement. Ignore it */
CheckSemi (PendingToken); CheckSemi (PendingToken);
StmtFlags = SF_NONE;
break; break;
case TOK_LCURLY: case TOK_LCURLY:
GotBreak = CompoundStatement (PendingToken); StmtFlags = CompoundStatement (PendingToken, Switch);
break; break;
default: default:
/* Simple statement */ /* Simple statement */
Statement (PendingToken); StmtFlags = Statement (PendingToken);
break; break;
} }
/* The flags for labels returned by subroutines are invalid in most cases,
** so we remove them and use the ones determined before.
*/
StmtFlags = (StmtFlags & ~SF_MASK_LABEL) | LabelFlags;
/* Reset SQP flags */ /* Reset SQP flags */
SetSQPFlags (SQP_KEEP_NONE); SetSQPFlags (SQP_KEEP_NONE);
return GotBreak; /* If we're inside a switch, do tracking of special statements */
if (Switch) {
SwitchBodyStatement (Switch, LI, StmtFlags);
}
/* Release the line info we remembered above */
ReleaseLineInfo (LI);
/* Done */
return StmtFlags;
} }

View File

@@ -38,20 +38,128 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Enumeration for the AnyStatement() return flags. It is used for simple flow
** analysis and tracks occurance of "return", "break", "goto" and "continue"
** (anything that jumps). The flags for the distinct statements are only set
** if they are always executed. So for example in case of an if/else statement,
** SF_RETURN is set only if both branches contain a "return". The SF_ANY_xxx
** flags are set if any of the statements occurred. So for an if/else
** statement, if one branch contains a "return" and the other a "continue"
** statement, neither SF_RETURN, nor SF_CONTINUE is set, but SF_ANY_RETURN and
** SF_ANY_CONTINUE.
** There are some additional flags that tell if the statement parsed was
** preceeded by at least one label.
*/
enum {
SF_NONE = 0x0000,
/* Flags for special statements that cause the next statement to be
** unreachable. They are only return as long as the condition is true.
** Please note that a statement function can return more than one of
** these flags set. For example for an "if" that has "return" in one
** branch and "continue" in another.
*/
SF_RETURN = 0x00001, /* Code definitely returns */
SF_BREAK = 0x00002, /* Code definitely breaks */
SF_GOTO = 0x00004, /* Code definitely jumps */
SF_CONTINUE = 0x00008, /* Code definitely continues */
SF_OTHER = 0x00010, /* Endless loop, terminating func */
SF_MASK_UNREACH = 0x000FF, /* Following code is unreachable */
/* Flags for the occurance of any of the conditions from above. Statements
** will clear whatever is no longer valid when exiting. A while loop, for
** example will never return SF_ANY_BREAK or SF_ANY_CONTINUE since that was
** handled inside the loop.
*/
SF_ANY_RETURN = 0x00100, /* Code contains a return statement */
SF_ANY_BREAK = 0x00200, /* Code contains a break statement */
SF_ANY_GOTO = 0x00400, /* Code contains a goto statement */
SF_ANY_CONTINUE = 0x00800, /* Code contains a continue statement */
SF_ANY_OTHER = 0x01000, /* Code contains endless loop etc. */
SF_MASK_ANY = 0x0FF00, /* Code contains any of the above */
/* Flags for labels */
SF_LABEL_GOTO = 0x10000, /* Statement preceeded by goto label */
SF_LABEL_CASE = 0x20000, /* Statement preceeded by case label */
SF_LABEL_DEFAULT = 0x40000, /* Statement preceeded by default label */
SF_MASK_LABEL = 0x70000, /* Mask for any label */
};
/* Forward */
struct SwitchCtrl;
/*****************************************************************************/
/* Functions to handle the flow control flags */
/*****************************************************************************/
static inline int SF_Break (int F)
/* Return just the "break" flag in F */
{
return (F & SF_BREAK);
}
static inline int SF_Continue (int F)
/* Return just the "continue" flag in F */
{
return (F & SF_CONTINUE);
}
static inline int SF_Unreach (int F)
/* Return just the "unreachable" part of the given flags */
{
return (F & SF_MASK_UNREACH);
}
static inline int SF_Any_Break (int F)
/* Check if there was any "break" statement */
{
return (F & SF_ANY_BREAK);
}
static inline int SF_Any (int F)
/* Return just the "any" part of the given flags */
{
return (F & SF_MASK_ANY);
}
static inline int SF_Label (int F)
/* Return just the "label" part of the given flags */
{
return (F & SF_MASK_LABEL);
}
/*****************************************************************************/ /*****************************************************************************/
/* Code */ /* Code */
/*****************************************************************************/ /*****************************************************************************/
int AnyStatement (int* PendingToken); int StatementBlock (struct SwitchCtrl* Switch);
/* Statement parser. Returns 1 if the statement does a return/break, returns /* Parse multiple statements within curly braces checking for unreachable
** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** code. Returns the SF_xxx flags for the last statement.
** not skip the terminating token of the statement (closing brace or */
** semicolon), but store true if there is a pending token, and false if there
** is none. The token is always checked, so there is no need for the caller to int AnyStatement (int* PendingToken, struct SwitchCtrl* Switch);
** check this token, it must be skipped, however. If the argument pointer is /* Statement parser. Returns one of the SF_xxx flags describing if the
** NULL, the function will skip the token. ** statement does a return/break. If the PendingToken pointer is not NULL,
** the function will not skip the terminating token of the statement (closing
** brace or semicolon), but store true if there is a pending token, and false
** if there is none. The token is always checked, so there is no need for the
** caller to check this token, it must be skipped, however. If the argument
** pointer is NULL, the function will skip the token. When called to parse a
** switch body, the switch control structure must be passed via the Switch
** argument. Otherwise it must be NULL.
*/ */

View File

@@ -61,15 +61,23 @@
/* Some bitmapped flags for use in SwitchCtrl */
#define SC_NONE 0x0000
#define SC_CASE 0x0001 /* We had a case label */
#define SC_DEFAULT 0x0002 /* We had a default label */
#define SC_MASK_LABEL 0x0003 /* Mask for the labels */
#define SC_WEIRD 0x0004 /* Flag for a weird switch that contains code
** outside of any label.
*/
typedef struct SwitchCtrl SwitchCtrl; typedef struct SwitchCtrl SwitchCtrl;
struct SwitchCtrl { struct SwitchCtrl {
Collection* Nodes; /* CaseNode tree */ Collection* Nodes; /* CaseNode tree */
const Type* ExprType; /* Switch controlling expression type */ const Type* ExprType; /* Switch controlling expression type */
unsigned Depth; /* Number of bytes the selector type has */ unsigned Depth; /* Number of bytes the selector type has */
unsigned DefaultLabel; /* Label for the default branch */ unsigned DefaultLabel; /* Label for the default branch */
unsigned CtrlFlags; /* Bitmapped flags as defined above */
int StmtFlags; /* Collected statement flags */
}; };
/* Pointer to current switch control struct */ /* Pointer to current switch control struct */
@@ -77,14 +85,60 @@ static SwitchCtrl* Switch = 0;
/*****************************************************************************/
/* Functions that work with struct SwitchExpr */
/*****************************************************************************/
static int SC_Label (const SwitchCtrl* S)
/* Check if we had a switch label */
{
return (S->CtrlFlags & SC_MASK_LABEL) != SC_NONE;
}
static int SC_IsWeird (const SwitchCtrl* S)
/* Check if this switch is weird */
{
return (S->CtrlFlags & SC_WEIRD) != SC_NONE;
}
static void SC_SetCase (SwitchCtrl* S)
/* Set the current label type to "case" */
{
S->CtrlFlags |= SC_CASE;
}
static void SC_SetDefault (SwitchCtrl* S)
/* Set the current label type to "default" */
{
S->CtrlFlags |= SC_DEFAULT;
}
static void SC_MakeWeird (SwitchCtrl* S)
/* Mark the switch as weird */
{
S->CtrlFlags |= SC_WEIRD;
}
/*****************************************************************************/ /*****************************************************************************/
/* Code */ /* Code */
/*****************************************************************************/ /*****************************************************************************/
void SwitchStatement (void) int SwitchStatement (void)
/* Handle a switch statement for chars with a cmp cascade for the selector */ /* Handle a 'switch' statement and return the corresponding SF_xxx flags */
{ {
ExprDesc SwitchExpr; /* Switch statement expression */ ExprDesc SwitchExpr; /* Switch statement expression */
CodeMark CaseCodeStart; /* Start of code marker */ CodeMark CaseCodeStart; /* Start of code marker */
@@ -92,7 +146,7 @@ void SwitchStatement (void)
CodeMark SwitchCodeEnd; /* End of switch code */ CodeMark SwitchCodeEnd; /* End of switch code */
unsigned ExitLabel; /* Exit label */ unsigned ExitLabel; /* Exit label */
unsigned SwitchCodeLabel;/* Label for the switch code */ unsigned SwitchCodeLabel;/* Label for the switch code */
int HaveBreak = 0; /* True if the last statement had a break */ int StmtFlags; /* True if the last statement had a break */
int RCurlyBrace; /* True if last token is right curly brace */ int RCurlyBrace; /* True if last token is right curly brace */
SwitchCtrl* OldSwitch; /* Pointer to old switch control data */ SwitchCtrl* OldSwitch; /* Pointer to old switch control data */
SwitchCtrl SwitchData; /* New switch data */ SwitchCtrl SwitchData; /* New switch data */
@@ -136,6 +190,8 @@ void SwitchStatement (void)
SwitchData.ExprType = SwitchExpr.Type; SwitchData.ExprType = SwitchExpr.Type;
SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.Depth = SizeOf (SwitchExpr.Type);
SwitchData.DefaultLabel = 0; SwitchData.DefaultLabel = 0;
SwitchData.CtrlFlags = SC_NONE;
SwitchData.StmtFlags = SF_NONE;
OldSwitch = Switch; OldSwitch = Switch;
Switch = &SwitchData; Switch = &SwitchData;
@@ -148,7 +204,7 @@ void SwitchStatement (void)
/* Parse the following statement, which may actually be a compound /* Parse the following statement, which may actually be a compound
** statement if there is a curly brace at the current input position ** statement if there is a curly brace at the current input position
*/ */
HaveBreak = AnyStatement (&RCurlyBrace); StmtFlags = AnyStatement (&RCurlyBrace, Switch);
/* Check if we had any labels */ /* Check if we had any labels */
if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) {
@@ -162,7 +218,7 @@ void SwitchStatement (void)
** carry the label), add a jump to the exit. If it is useless, the ** carry the label), add a jump to the exit. If it is useless, the
** optimizer will remove it later. ** optimizer will remove it later.
*/ */
if (!HaveBreak) { if (SF_Unreach (StmtFlags) == SF_NONE) {
g_jump (ExitLabel); g_jump (ExitLabel);
} }
@@ -201,6 +257,11 @@ void SwitchStatement (void)
if (RCurlyBrace) { if (RCurlyBrace) {
NextToken (); NextToken ();
} }
/* We only return the combined "any" flags from all the statements within
** the switch. Minus "break" which is handled inside the switch.
*/
return SwitchData.StmtFlags & ~SF_ANY_BREAK;
} }
@@ -268,6 +329,9 @@ void CaseLabel (void)
Warning (DiagMsg, CaseExpr.IVal); Warning (DiagMsg, CaseExpr.IVal);
} }
/* Remember that we're in a case label section now */
SC_SetCase (Switch);
} else { } else {
/* case keyword outside a switch statement */ /* case keyword outside a switch statement */
@@ -297,6 +361,9 @@ void DefaultLabel (void)
Switch->DefaultLabel = GetLocalLabel (); Switch->DefaultLabel = GetLocalLabel ();
g_defcodelabel (Switch->DefaultLabel); g_defcodelabel (Switch->DefaultLabel);
/* Remember that we're in the default label section now */
SC_SetDefault (Switch);
} else { } else {
/* We had the default label already */ /* We had the default label already */
Error ("Multiple default labels in one switch"); Error ("Multiple default labels in one switch");
@@ -312,3 +379,31 @@ void DefaultLabel (void)
/* Skip the colon */ /* Skip the colon */
ConsumeColon (); ConsumeColon ();
} }
void SwitchBodyStatement (struct SwitchCtrl* S, LineInfo* LI, int StmtFlags)
/* Helper function for flow analysis. Must be called for all statements within
** a switch passing the flags for special statements.
*/
{
/* The control structure passed must be the current one */
PRECONDITION (S == Switch);
/* Handle code without a label in the switch */
if (SC_Label (S) == SC_NONE) {
/* This is a statement that preceedes any switch labels. If the
** switch is not already marked as weird, output and the current
** statement has no label, output a warning about unreachable code.
*/
if (!SC_IsWeird (S)) {
if (!SF_Label (StmtFlags)) {
LIUnreachableCodeWarning (LI);
}
SC_MakeWeird (S);
}
}
/* Collect the statement flags. */
S->StmtFlags = SF_Any (S->StmtFlags | StmtFlags);
}

View File

@@ -38,14 +38,25 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Forwards */
struct SwitchCtrl;
/*****************************************************************************/ /*****************************************************************************/
/* Code */ /* Code */
/*****************************************************************************/ /*****************************************************************************/
void SwitchStatement (void); int SwitchStatement (void);
/* Handle a 'switch' statement */ /* Handle a 'switch' statement and return the corresponding SF_xxx flags */
void CaseLabel (void); void CaseLabel (void);
/* Handle a case label */ /* Handle a case label */
@@ -53,6 +64,11 @@ void CaseLabel (void);
void DefaultLabel (void); void DefaultLabel (void);
/* Handle a default label */ /* Handle a default label */
void SwitchBodyStatement (struct SwitchCtrl* Switch, LineInfo* LI, int RetFlags);
/* Helper function for flow analysis. Must be called for all statements within
** a switch passing the flags for special statements.
*/
/* End of swstmt.h */ /* End of swstmt.h */

View File

@@ -53,7 +53,7 @@
unsigned Test (unsigned Label, int Invert) unsigned Test (unsigned Label, int Invert)
/* Evaluate a boolean test expression and jump depending on the result of /* Evaluate a boolean test expression and jump depending on the result of
** the test and on Invert. The function returns one of the TESTEXPR_xx codes ** the test and on Invert. The function returns one of the TESTEXPR_xx codes
** defined above. If the jump is always true, a warning is output. ** defined above.
*/ */
{ {
ExprDesc Expr; ExprDesc Expr;
@@ -74,10 +74,7 @@ unsigned Test (unsigned Label, int Invert)
Result = (Expr.IVal != 0) ? TESTEXPR_TRUE : TESTEXPR_FALSE; Result = (Expr.IVal != 0) ? TESTEXPR_TRUE : TESTEXPR_FALSE;
/* Constant rvalue */ /* Constant rvalue */
if (!Invert && Expr.IVal == 0) { if ((!Invert && Expr.IVal == 0) || (Invert && Expr.IVal != 0)) {
g_jump (Label);
UnreachableCodeWarning ();
} else if (Invert && Expr.IVal != 0) {
g_jump (Label); g_jump (Label);
} }
@@ -125,8 +122,7 @@ unsigned Test (unsigned Label, int Invert)
unsigned TestInParens (unsigned Label, int Invert) unsigned TestInParens (unsigned Label, int Invert)
/* Evaluate a boolean test expression in parenthesis and jump depending on /* Evaluate a boolean test expression in parenthesis and jump depending on
** the result of the test * and on Invert. The function returns one of the ** the result of the test * and on Invert. The function returns one of the
** TESTEXPR_xx codes defined above. If the jump is always true, a warning is ** TESTEXPR_xx codes defined above.
** output.
*/ */
{ {
unsigned Result; unsigned Result;

View File

@@ -59,14 +59,13 @@
unsigned Test (unsigned Label, int Invert); unsigned Test (unsigned Label, int Invert);
/* Evaluate a boolean test expression and jump depending on the result of /* Evaluate a boolean test expression and jump depending on the result of
** the test and on Invert. The function returns one of the TESTEXPR_xx codes ** the test and on Invert. The function returns one of the TESTEXPR_xx codes
** defined above. If the jump is always true, a warning is output. ** defined above.
*/ */
unsigned TestInParens (unsigned Label, int Invert); unsigned TestInParens (unsigned Label, int Invert);
/* Evaluate a boolean test expression in parenthesis and jump depending on /* Evaluate a boolean test expression in parenthesis and jump depending on
** the result of the test * and on Invert. The function returns one of the ** the result of the test * and on Invert. The function returns one of the
** TESTEXPR_xx codes defined above. If the jump is always true, a warning is ** TESTEXPR_xx codes defined above.
** output.
*/ */

View File

@@ -152,6 +152,36 @@ $(WORKDIR)/bug2655.$1.$2.prg: bug2655.c $(ISEQUAL) | $(WORKDIR)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/bug2655.$1.$2.out $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/bug2655.$1.$2.out
$(ISEQUAL) $(WORKDIR)/bug2655.$1.$2.out bug2655.ref $(ISEQUAL) $(WORKDIR)/bug2655.$1.$2.out bug2655.ref
$(WORKDIR)/flow-do-01.$1.$2.prg: flow-do-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-do-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-do-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-do-01.$1.$2.out flow-do-01.ref
$(WORKDIR)/flow-if-01.$1.$2.prg: flow-if-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-if-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-if-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-if-01.$1.$2.out flow-if-01.ref
$(WORKDIR)/flow-if-02.$1.$2.prg: flow-if-02.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-if-02.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-if-02.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-if-02.$1.$2.out flow-if-02.ref
$(WORKDIR)/flow-switch-01.$1.$2.prg: flow-switch-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-switch-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-switch-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-switch-01.$1.$2.out flow-switch-01.ref
$(WORKDIR)/flow-while-01.$1.$2.prg: flow-while-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-while-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-while-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-while-01.$1.$2.out flow-while-01.ref
$(WORKDIR)/flow-while-02.$1.$2.prg: flow-while-02.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-while-02.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-while-02.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-while-02.$1.$2.out flow-while-02.ref
$(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) $(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/limits.$1.$2.prg) $(if $(QUIET),echo misc/limits.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLOUT) $(CATERR) $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLOUT) $(CATERR)

View File

@@ -1,2 +1,2 @@
bug2655.c:5: Warning: Unreachable code bug2655.c:6: Warning: Unreachable code
bug2655.c:12: Warning: Unreachable code bug2655.c:13: Warning: Unreachable code

87
test/misc/flow-do-01.c Normal file
View File

@@ -0,0 +1,87 @@
int a;
static int f1(void)
{
do {
return a;
} while (a == 2);
/* Unreachable */
a = 2;
return a;
}
static int f2(void)
{
do {
return a;
} while (1);
/* Unreachable */
a = 2;
return a;
}
static int f3(void)
{
do {
return a;
} while (0);
/* Unreachable */
a = 2;
return a;
}
static int f4(void)
{
do {
continue; /* Turns do/while into an endless loop */
} while (0);
/* Unreachable */
a = 2;
return a;
}
static int f5(void)
{
do {
if (a == 2) {
break;
}
return a;
} while (0);
/* Reachable */
a = 2;
return a;
}
static int f6(void)
{
do {
if (a == 2) {
break;
}
continue;
} while (0);
/* Reachable */
a = 2;
return a;
}
static int f7(void)
{
do {
if (a == 2) {
return a;
} else {
continue;
}
} while (1);
/* Unreachable */
a = 2;
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4() + f5() + f6() + f7();
}

5
test/misc/flow-do-01.ref Normal file
View File

@@ -0,0 +1,5 @@
flow-do-01.c:9: Warning: Unreachable code
flow-do-01.c:19: Warning: Unreachable code
flow-do-01.c:29: Warning: Unreachable code
flow-do-01.c:39: Warning: Unreachable code
flow-do-01.c:79: Warning: Unreachable code

62
test/misc/flow-if-01.c Normal file
View File

@@ -0,0 +1,62 @@
int a, b;
static int f1(void)
{
if (a == 1) {
return 1;
}
/* Reachable */
a = 2;
return a;
}
static int f2(void)
{
if (a == 1) {
a = 2;
} else {
return 1;
}
/* Reachable */
return a;
}
static int f3(void)
{
if (a == 1) {
return 1;
} else {
a = 2;
}
/* Reachable */
return a;
}
static int f4(void)
{
if (a == 1) {
return 1;
} else {
return 0;
}
/* Unreachable */
a = 2;
}
static int f5(void)
{
if (1) {
return 1;
} else {
/* Unreachable */
return 0;
}
/* Unreachable */
a = 2;
}
int main(void)
{
return f1() + f2() + f3() + f4() + f5();
}

3
test/misc/flow-if-01.ref Normal file
View File

@@ -0,0 +1,3 @@
flow-if-01.c:43: Warning: Unreachable code
flow-if-01.c:52: Warning: Unreachable code
flow-if-01.c:55: Warning: Unreachable code

49
test/misc/flow-if-02.c Normal file
View File

@@ -0,0 +1,49 @@
int a, b;
static int f1(void)
{
if (0) {
/* Unreachable but no warning */
} else {
a = 2;
}
return a;
}
static int f2(void)
{
if (0) {
/* Unreachable */
a = 1;
} else {
a = 2;
}
return a;
}
static int f3(void)
{
if (1) {
a = 2;
} else {
/* Unreachable but no warning */
}
return a;
}
static int f4(void)
{
if (1) {
a = 2;
} else {
/* Unreachable */
a = 1;
}
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4();
}

2
test/misc/flow-if-02.ref Normal file
View File

@@ -0,0 +1,2 @@
flow-if-02.c:17: Warning: Unreachable code
flow-if-02.c:40: Warning: Unreachable code

View File

@@ -0,0 +1,68 @@
int a;
static int f1(void)
{
switch (a) {
/* Unreachable */
a = 3;
case 1:
a = 2;
break;
case 2:
a = 1;
break;
default:
a = 0;
break;
}
/* Reachable */
return a;
}
static int f2(void)
{
switch (a) {
/* Reachable */
L: a = 3;
case 1:
goto L;
case 2:
a = 1;
break;
default:
a = 0;
break;
}
/* Reachable */
return a;
}
static int f3(void)
{
switch (a) {
case 1: return a;
case 2: return a+1;
default: return a+2;
}
/* Unreachable but no warning */
return a;
}
static int f4(void)
{
switch (a) {
/* No warning */
do {
case 1: ++a; continue;
case 2: return a+1;
default: return a+2;
} while (1);
}
/* Unreachable but no warning */
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4();
}

View File

@@ -0,0 +1 @@
flow-switch-01.c:7: Warning: Unreachable code

70
test/misc/flow-while-01.c Normal file
View File

@@ -0,0 +1,70 @@
int a;
static int f1(void)
{
while (1) {
++a;
}
/* Unreachable */
a = 2;
return a;
}
static int f2(void)
{
while (1) {
++a;
if (a == 5) break;
}
/* Reachable */
a = 2;
return a;
}
static int f3(void)
{
while (1) {
++a;
return a;
}
/* Unreachable */
a = 2;
}
static int f4(void)
{
while (0) {
/* Unreachable */
++a;
return a;
}
/* Reachable */
a = 2;
return 0;
}
static int f5(void)
{
while (1) {
++a;
if (a == 4) goto L;
return a;
}
/* Reachable via L */
L: a = 2;
}
static int f6(void)
{
while (0) {
/* Unreachable but no warning */
}
a = 2;
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4() + f5() + f6();
}

View File

@@ -0,0 +1,3 @@
flow-while-01.c:9: Warning: Unreachable code
flow-while-01.c:31: Warning: Unreachable code
flow-while-01.c:38: Warning: Unreachable code

74
test/misc/flow-while-02.c Normal file
View File

@@ -0,0 +1,74 @@
int a;
static int f1(void)
{
while (1) {
while (0) {
/* Unreachable */
++a;
}
}
/* Unreachable */
a = 2;
return a;
}
static int f2(void)
{
while (1) {
do {
return a;
} while (0);
/* Unreachable */
break;
}
/* Unreachable but no warning */
a = 2;
return a;
}
static int f3(void)
{
do {
while (1) {
break;
}
} while (1);
/* Unreachable */
a = 2;
return a;
}
static int f4(void)
{
do {
while (1) {
return a;
}
} while (0);
/* Unreachable */
a = 2;
return a;
}
static int f5(void)
{
do {
do {
if (a == 2) {
return a;
} else {
continue;
}
} while (0);
} while (0);
/* Unreachable */
a = 2;
return a;
}
int main(void)
{
return f1()/ + f2() + f3() + f4() + f5();
}

View File

@@ -0,0 +1,6 @@
flow-while-02.c:8: Warning: Unreachable code
flow-while-02.c:12: Warning: Unreachable code
flow-while-02.c:23: Warning: Unreachable code
flow-while-02.c:38: Warning: Unreachable code
flow-while-02.c:50: Warning: Unreachable code
flow-while-02.c:66: Warning: Unreachable code

View File

@@ -1,4 +1,9 @@
goto.c:3: Warning: Unreachable code
goto.c:8: Warning: Goto at line 8 to label start jumps into a block with initialization of an object that has automatic storage duration goto.c:8: Warning: Goto at line 8 to label start jumps into a block with initialization of an object that has automatic storage duration
goto.c:17: Warning: Unreachable code
goto.c:38: Warning: Unreachable code
goto.c:59: Warning: Unreachable code
goto.c:80: Warning: Unreachable code
goto.c:97: Warning: Variable 'a' is defined but never used goto.c:97: Warning: Variable 'a' is defined but never used
goto.c:117: Warning: Variable 'a' is defined but never used goto.c:117: Warning: Variable 'a' is defined but never used
goto.c:137: Warning: Variable 'a' is defined but never used goto.c:137: Warning: Variable 'a' is defined but never used
@@ -9,6 +14,7 @@ goto.c:159: Warning: Goto at line 86 to label l8 jumps into a block with initial
goto.c:159: Warning: Goto at line 106 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c:159: Warning: Goto at line 106 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:159: Warning: Goto at line 126 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c:159: Warning: Goto at line 126 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:159: Warning: Goto at line 146 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c:159: Warning: Goto at line 146 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:161: Warning: Unreachable code
goto.c:180: Warning: Goto at line 24 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:180: Warning: Goto at line 24 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 45 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:180: Warning: Goto at line 45 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 66 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:180: Warning: Goto at line 66 to label l9 jumps into a block with initialization of an object that has automatic storage duration
@@ -17,6 +23,7 @@ goto.c:180: Warning: Goto at line 107 to label l9 jumps into a block with initia
goto.c:180: Warning: Goto at line 127 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:180: Warning: Goto at line 127 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 147 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:180: Warning: Goto at line 147 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 168 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:180: Warning: Goto at line 168 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:182: Warning: Unreachable code
goto.c:201: Warning: Goto at line 25 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:201: Warning: Goto at line 25 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 46 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:201: Warning: Goto at line 46 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 67 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:201: Warning: Goto at line 67 to label la jumps into a block with initialization of an object that has automatic storage duration
@@ -26,6 +33,7 @@ goto.c:201: Warning: Goto at line 128 to label la jumps into a block with initia
goto.c:201: Warning: Goto at line 148 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:201: Warning: Goto at line 148 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 169 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:201: Warning: Goto at line 169 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 190 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:201: Warning: Goto at line 190 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:203: Warning: Unreachable code
goto.c:221: Warning: Goto at line 26 to label lb jumps into a block with initialization of an object that has automatic storage duration goto.c:221: Warning: Goto at line 26 to label lb jumps into a block with initialization of an object that has automatic storage duration
goto.c:221: Warning: Goto at line 47 to label lb jumps into a block with initialization of an object that has automatic storage duration goto.c:221: Warning: Goto at line 47 to label lb jumps into a block with initialization of an object that has automatic storage duration
goto.c:221: Warning: Goto at line 68 to label lb jumps into a block with initialization of an object that has automatic storage duration goto.c:221: Warning: Goto at line 68 to label lb jumps into a block with initialization of an object that has automatic storage duration
@@ -57,6 +65,7 @@ goto.c:263: Warning: Goto at line 193 to label ld jumps into a block with initia
goto.c:263: Warning: Goto at line 214 to label ld jumps into a block with initialization of an object that has automatic storage duration goto.c:263: Warning: Goto at line 214 to label ld jumps into a block with initialization of an object that has automatic storage duration
goto.c:263: Warning: Goto at line 234 to label ld jumps into a block with initialization of an object that has automatic storage duration goto.c:263: Warning: Goto at line 234 to label ld jumps into a block with initialization of an object that has automatic storage duration
goto.c:263: Warning: Goto at line 254 to label ld jumps into a block with initialization of an object that has automatic storage duration goto.c:263: Warning: Goto at line 254 to label ld jumps into a block with initialization of an object that has automatic storage duration
goto.c:265: Warning: Unreachable code
goto.c:271: Warning: Goto at line 271 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c:271: Warning: Goto at line 271 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:272: Warning: Goto at line 272 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:272: Warning: Goto at line 272 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:273: Warning: Goto at line 273 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:273: Warning: Goto at line 273 to label la jumps into a block with initialization of an object that has automatic storage duration
@@ -75,6 +84,7 @@ goto.c:284: Warning: Goto at line 215 to label le jumps into a block with initia
goto.c:284: Warning: Goto at line 235 to label le jumps into a block with initialization of an object that has automatic storage duration goto.c:284: Warning: Goto at line 235 to label le jumps into a block with initialization of an object that has automatic storage duration
goto.c:284: Warning: Goto at line 255 to label le jumps into a block with initialization of an object that has automatic storage duration goto.c:284: Warning: Goto at line 255 to label le jumps into a block with initialization of an object that has automatic storage duration
goto.c:284: Warning: Goto at line 277 to label le jumps into a block with initialization of an object that has automatic storage duration goto.c:284: Warning: Goto at line 277 to label le jumps into a block with initialization of an object that has automatic storage duration
goto.c:286: Warning: Unreachable code
goto.c:292: Warning: Goto at line 292 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c:292: Warning: Goto at line 292 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:293: Warning: Goto at line 293 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:293: Warning: Goto at line 293 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:294: Warning: Goto at line 294 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:294: Warning: Goto at line 294 to label la jumps into a block with initialization of an object that has automatic storage duration
@@ -94,6 +104,7 @@ goto.c:305: Warning: Goto at line 236 to label lf jumps into a block with initia
goto.c:305: Warning: Goto at line 256 to label lf jumps into a block with initialization of an object that has automatic storage duration goto.c:305: Warning: Goto at line 256 to label lf jumps into a block with initialization of an object that has automatic storage duration
goto.c:305: Warning: Goto at line 278 to label lf jumps into a block with initialization of an object that has automatic storage duration goto.c:305: Warning: Goto at line 278 to label lf jumps into a block with initialization of an object that has automatic storage duration
goto.c:305: Warning: Goto at line 299 to label lf jumps into a block with initialization of an object that has automatic storage duration goto.c:305: Warning: Goto at line 299 to label lf jumps into a block with initialization of an object that has automatic storage duration
goto.c:307: Warning: Unreachable code
goto.c:313: Warning: Goto at line 313 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c:313: Warning: Goto at line 313 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:314: Warning: Goto at line 314 to label l9 jumps into a block with initialization of an object that has automatic storage duration goto.c:314: Warning: Goto at line 314 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:315: Warning: Goto at line 315 to label la jumps into a block with initialization of an object that has automatic storage duration goto.c:315: Warning: Goto at line 315 to label la jumps into a block with initialization of an object that has automatic storage duration