Improved flow analysis in general and especially for "for" loops. Added more
tests.
This commit is contained in:
114
src/cc65/stmt.c
114
src/cc65/stmt.c
@@ -229,8 +229,10 @@ static int IfStatement (void)
|
||||
g_defcodelabel (Label2);
|
||||
}
|
||||
|
||||
/* Done */
|
||||
return StmtFlags;
|
||||
/* Done. We will never return "empty" for an if statement because of side
|
||||
** effects when evaluating the condition.
|
||||
*/
|
||||
return StmtFlags & ~SF_EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@@ -299,8 +301,10 @@ static int DoStatement (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* "break" and "continue" are not relevant for the following code */
|
||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
|
||||
/* "break" and "continue" are not relevant for the following code. "empty"
|
||||
** is removed because of side effects when evaluating the condition.
|
||||
*/
|
||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY);
|
||||
|
||||
/* Done */
|
||||
return StmtFlags;
|
||||
@@ -393,8 +397,10 @@ static int WhileStatement (void)
|
||||
*/
|
||||
StmtFlags &= ~SF_MASK_UNREACH;
|
||||
}
|
||||
/* "break" and "continue" are not relevant for the following code */
|
||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
|
||||
/* "break" and "continue" are not relevant for the following code. "empty"
|
||||
** is removed because of side effects when evaluating the condition.
|
||||
*/
|
||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY);
|
||||
|
||||
/* Done */
|
||||
return StmtFlags;
|
||||
@@ -602,6 +608,11 @@ static int ForStatement (void)
|
||||
/* Skip the closing paren */
|
||||
ConsumeRParen ();
|
||||
|
||||
/* Output a warning if the loop body is never executed */
|
||||
if (TestResult == TESTEXPR_FALSE) {
|
||||
UnreachableCodeWarning ();
|
||||
}
|
||||
|
||||
/* Loop body */
|
||||
g_defcodelabel (BodyLabel);
|
||||
StmtFlags = AnyStatement (&PendingToken, 0);
|
||||
@@ -628,10 +639,30 @@ static int ForStatement (void)
|
||||
/* Remove the loop from the loop stack */
|
||||
DelLoop ();
|
||||
|
||||
/* If the condition is always true, any special statements are always
|
||||
** executed. Otherwise we don't know.
|
||||
/* 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. "empty"
|
||||
** is removed because of side effects when evaluating the condition.
|
||||
*/
|
||||
return (TestResult == TESTEXPR_TRUE)? StmtFlags : SF_NONE;
|
||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY);
|
||||
|
||||
/* Done */
|
||||
return StmtFlags;
|
||||
}
|
||||
|
||||
|
||||
@@ -641,15 +672,23 @@ static int CompoundStatement (int* PendingToken, struct SwitchCtrl* Switch)
|
||||
** function returns true if the last statement was a break or return.
|
||||
*/
|
||||
{
|
||||
int OldStack;
|
||||
unsigned OldBlockStackSize;
|
||||
int StmtFlags;
|
||||
|
||||
/* Remember the stack at block entry */
|
||||
int OldStack = StackPtr;
|
||||
unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack);
|
||||
|
||||
/* Skip '{' */
|
||||
NextToken ();
|
||||
|
||||
/* If the closing curly bracket follows we have an empty statement */
|
||||
if (CurTok.Tok == TOK_RCURLY) {
|
||||
NextToken ();
|
||||
return SF_EMPTY;
|
||||
}
|
||||
|
||||
/* Remember the stack at block entry */
|
||||
OldStack = StackPtr;
|
||||
OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack);
|
||||
|
||||
/* Enter a new lexical level */
|
||||
EnterBlockLevel ();
|
||||
|
||||
@@ -742,36 +781,41 @@ int StatementBlock (struct SwitchCtrl* Switch)
|
||||
LineInfo* LI1 = UseLineInfo (GetDiagnosticLI ());
|
||||
int StmtFlags1 = AnyStatement (0, Switch);
|
||||
int Unreachable = 0; /* True if code is unreachable */
|
||||
int UnreachableWarning = 0; /* True if warning was output */
|
||||
int Warning = 0; /* True if warning was output */
|
||||
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
|
||||
LineInfo* LI2 = UseLineInfo (GetDiagnosticLI ());
|
||||
int StmtFlags2 = AnyStatement (0, Switch);
|
||||
if (!UnreachableWarning) {
|
||||
/* If this statement is not already unreachable, check if the
|
||||
** previous statement made it unreachable.
|
||||
*/
|
||||
if (!Unreachable) {
|
||||
Unreachable = SF_Unreach (StmtFlags1);
|
||||
}
|
||||
/* If the previous statement made this one unreachable, but
|
||||
** this one has a label, it is not unreachable.
|
||||
*/
|
||||
if (Unreachable && SF_Label (StmtFlags2)) {
|
||||
Unreachable = 0;
|
||||
}
|
||||
/* If this statement is unreachable but not the empty
|
||||
** statement, output a warning.
|
||||
*/
|
||||
if (Unreachable && !SF_Empty (StmtFlags2)) {
|
||||
LIUnreachableCodeWarning (LI2);
|
||||
UnreachableWarning = 1;
|
||||
}
|
||||
|
||||
/* If this statement is not already unreachable, check if the
|
||||
** previous statement made it unreachable.
|
||||
*/
|
||||
if (!Unreachable) {
|
||||
Unreachable = SF_Unreach (StmtFlags1);
|
||||
}
|
||||
/* If the previous statement made this one unreachable, but this
|
||||
** one has a label, it is not unreachable.
|
||||
*/
|
||||
if (Unreachable && SF_Label (StmtFlags2)) {
|
||||
Unreachable = 0;
|
||||
}
|
||||
/* If this statement is unreachable but not the empty statement,
|
||||
** and we didn't give a warning before, to that now
|
||||
*/
|
||||
if (Unreachable && !SF_Empty (StmtFlags2) && !Warning) {
|
||||
LIUnreachableCodeWarning (LI2);
|
||||
Warning = 1;
|
||||
}
|
||||
|
||||
/* If the current statement wasn't unreachable update the flags */
|
||||
if (!Unreachable) {
|
||||
StmtFlags1 = SF_Any (StmtFlags1) | StmtFlags2;
|
||||
}
|
||||
|
||||
/* Prepare for the next round */
|
||||
if (LI1) {
|
||||
ReleaseLineInfo (LI1);
|
||||
}
|
||||
LI1 = LI2;
|
||||
StmtFlags1 = (StmtFlags1 & SF_MASK_ANY) | StmtFlags2;
|
||||
}
|
||||
if (LI1) {
|
||||
ReleaseLineInfo (LI1);
|
||||
|
||||
@@ -157,6 +157,11 @@ $(WORKDIR)/flow-do-01.$1.$2.prg: flow-do-01.c $(ISEQUAL) | $(WORKDIR)
|
||||
$(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-for-01.$1.$2.prg: flow-for-01.c $(ISEQUAL) | $(WORKDIR)
|
||||
$(if $(QUIET),echo misc/flow-for-01.$1.$2.prg)
|
||||
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-for-01.$1.$2.out
|
||||
$(ISEQUAL) $(WORKDIR)/flow-for-01.$1.$2.out flow-for-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
|
||||
|
||||
90
test/misc/flow-for-01.c
Normal file
90
test/misc/flow-for-01.c
Normal file
@@ -0,0 +1,90 @@
|
||||
int a;
|
||||
|
||||
static int f1(void)
|
||||
{
|
||||
for (;;) {
|
||||
++a;
|
||||
}
|
||||
/* Unreachable */
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
static int f2(void)
|
||||
{
|
||||
for (;1;) {
|
||||
++a;
|
||||
}
|
||||
/* Unreachable */
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
static int f3(void)
|
||||
{
|
||||
for (;;) {
|
||||
++a;
|
||||
if (a == 5) break;
|
||||
}
|
||||
/* Reachable */
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
static int f4(void)
|
||||
{
|
||||
for (;;) {
|
||||
++a;
|
||||
return a;
|
||||
}
|
||||
/* Unreachable */
|
||||
a = 2;
|
||||
}
|
||||
|
||||
static int f5(void)
|
||||
{
|
||||
for (;0;) {
|
||||
/* Unreachable */
|
||||
++a;
|
||||
return a;
|
||||
}
|
||||
/* Reachable */
|
||||
a = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f6(void)
|
||||
{
|
||||
for (;;) {
|
||||
++a;
|
||||
if (a == 4) goto L;
|
||||
return a;
|
||||
}
|
||||
/* Reachable via L */
|
||||
L: a = 2;
|
||||
}
|
||||
|
||||
static int f7(void)
|
||||
{
|
||||
for (;0;) {
|
||||
/* Unreachable but no warning */
|
||||
}
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
static int f8(void)
|
||||
{
|
||||
for (;a;) {
|
||||
return a;
|
||||
}
|
||||
/* Reachable */
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return f1() + f2() + f3() + f4() + f5() + f6() + f7() + f8();
|
||||
}
|
||||
|
||||
4
test/misc/flow-for-01.ref
Normal file
4
test/misc/flow-for-01.ref
Normal file
@@ -0,0 +1,4 @@
|
||||
flow-for-01.c:9: Warning: Unreachable code
|
||||
flow-for-01.c:19: Warning: Unreachable code
|
||||
flow-for-01.c:41: Warning: Unreachable code
|
||||
flow-for-01.c:48: Warning: Unreachable code
|
||||
@@ -63,8 +63,18 @@ static int f6(void)
|
||||
return a;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
static int f7(void)
|
||||
{
|
||||
return f1() + f2() + f3() + f4() + f5() + f6();
|
||||
while (a) {
|
||||
return a;
|
||||
}
|
||||
/* Reachable */
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return f1() + f2() + f3() + f4() + f5() + f6() + f7();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ static int f2(void)
|
||||
/* Unreachable */
|
||||
break;
|
||||
}
|
||||
/* Unreachable but no warning */
|
||||
/* Unreachable */
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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:26: 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
|
||||
|
||||
Reference in New Issue
Block a user