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);
|
g_defcodelabel (Label2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Done */
|
/* Done. We will never return "empty" for an if statement because of side
|
||||||
return StmtFlags;
|
** 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 */
|
/* "break" and "continue" are not relevant for the following code. "empty"
|
||||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
|
** is removed because of side effects when evaluating the condition.
|
||||||
|
*/
|
||||||
|
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY);
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
return StmtFlags;
|
return StmtFlags;
|
||||||
@@ -393,8 +397,10 @@ static int WhileStatement (void)
|
|||||||
*/
|
*/
|
||||||
StmtFlags &= ~SF_MASK_UNREACH;
|
StmtFlags &= ~SF_MASK_UNREACH;
|
||||||
}
|
}
|
||||||
/* "break" and "continue" are not relevant for the following code */
|
/* "break" and "continue" are not relevant for the following code. "empty"
|
||||||
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
|
** is removed because of side effects when evaluating the condition.
|
||||||
|
*/
|
||||||
|
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY);
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
return StmtFlags;
|
return StmtFlags;
|
||||||
@@ -602,6 +608,11 @@ static int ForStatement (void)
|
|||||||
/* Skip the closing paren */
|
/* Skip the closing paren */
|
||||||
ConsumeRParen ();
|
ConsumeRParen ();
|
||||||
|
|
||||||
|
/* Output a warning if the loop body is never executed */
|
||||||
|
if (TestResult == TESTEXPR_FALSE) {
|
||||||
|
UnreachableCodeWarning ();
|
||||||
|
}
|
||||||
|
|
||||||
/* Loop body */
|
/* Loop body */
|
||||||
g_defcodelabel (BodyLabel);
|
g_defcodelabel (BodyLabel);
|
||||||
StmtFlags = AnyStatement (&PendingToken, 0);
|
StmtFlags = AnyStatement (&PendingToken, 0);
|
||||||
@@ -628,10 +639,30 @@ static int 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
|
/* Fix the flags for the loop. */
|
||||||
** executed. Otherwise we don't know.
|
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.
|
** function returns true if the last statement was a break or return.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
|
int OldStack;
|
||||||
|
unsigned OldBlockStackSize;
|
||||||
int StmtFlags;
|
int StmtFlags;
|
||||||
|
|
||||||
/* Remember the stack at block entry */
|
|
||||||
int OldStack = StackPtr;
|
|
||||||
unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack);
|
|
||||||
|
|
||||||
/* Skip '{' */
|
/* Skip '{' */
|
||||||
NextToken ();
|
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 */
|
/* Enter a new lexical level */
|
||||||
EnterBlockLevel ();
|
EnterBlockLevel ();
|
||||||
|
|
||||||
@@ -742,36 +781,41 @@ int StatementBlock (struct SwitchCtrl* Switch)
|
|||||||
LineInfo* LI1 = UseLineInfo (GetDiagnosticLI ());
|
LineInfo* LI1 = UseLineInfo (GetDiagnosticLI ());
|
||||||
int StmtFlags1 = AnyStatement (0, Switch);
|
int StmtFlags1 = AnyStatement (0, Switch);
|
||||||
int Unreachable = 0; /* True if code is unreachable */
|
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) {
|
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
|
||||||
LineInfo* LI2 = UseLineInfo (GetDiagnosticLI ());
|
LineInfo* LI2 = UseLineInfo (GetDiagnosticLI ());
|
||||||
int StmtFlags2 = AnyStatement (0, Switch);
|
int StmtFlags2 = AnyStatement (0, Switch);
|
||||||
if (!UnreachableWarning) {
|
|
||||||
/* If this statement is not already unreachable, check if the
|
/* If this statement is not already unreachable, check if the
|
||||||
** previous statement made it unreachable.
|
** previous statement made it unreachable.
|
||||||
*/
|
*/
|
||||||
if (!Unreachable) {
|
if (!Unreachable) {
|
||||||
Unreachable = SF_Unreach (StmtFlags1);
|
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 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) {
|
if (LI1) {
|
||||||
ReleaseLineInfo (LI1);
|
ReleaseLineInfo (LI1);
|
||||||
}
|
}
|
||||||
LI1 = LI2;
|
LI1 = LI2;
|
||||||
StmtFlags1 = (StmtFlags1 & SF_MASK_ANY) | StmtFlags2;
|
|
||||||
}
|
}
|
||||||
if (LI1) {
|
if (LI1) {
|
||||||
ReleaseLineInfo (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
|
$(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
|
$(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)
|
$(WORKDIR)/flow-if-01.$1.$2.prg: flow-if-01.c $(ISEQUAL) | $(WORKDIR)
|
||||||
$(if $(QUIET),echo misc/flow-if-01.$1.$2.prg)
|
$(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
|
$(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;
|
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 */
|
/* Unreachable */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Unreachable but no warning */
|
/* Unreachable */
|
||||||
a = 2;
|
a = 2;
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
flow-while-02.c:8: Warning: Unreachable code
|
flow-while-02.c:8: Warning: Unreachable code
|
||||||
flow-while-02.c:12: Warning: Unreachable code
|
flow-while-02.c:12: Warning: Unreachable code
|
||||||
flow-while-02.c:23: 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:38: Warning: Unreachable code
|
||||||
flow-while-02.c:50: Warning: Unreachable code
|
flow-while-02.c:50: Warning: Unreachable code
|
||||||
flow-while-02.c:66: Warning: Unreachable code
|
flow-while-02.c:66: Warning: Unreachable code
|
||||||
|
|||||||
Reference in New Issue
Block a user