1
0
mirror of https://github.com/cc65/cc65.git synced 2026-01-22 17:16:21 +00:00

Introduce an optimization for (header) files containing include guards: If

such an include guard exists, the file is not read and parsed multiple times
(as before) but duplicate inclusion is detected before opening the file and
the additional overhead is avoided.
This commit is contained in:
Kugel Fuhr
2025-06-26 10:10:11 +02:00
parent 3a6766d0a0
commit d5e7c94eb2
4 changed files with 278 additions and 176 deletions

View File

@@ -40,6 +40,7 @@
/* common */
#include "check.h"
#include "coll.h"
#include "debugflag.h"
#include "filestat.h"
#include "fname.h"
#include "print.h"
@@ -77,17 +78,6 @@ char NextC = '\0';
/* Maximum count of nested includes */
#define MAX_INC_NESTING 16
/* Struct that describes an input file */
typedef struct IFile IFile;
struct IFile {
unsigned Index; /* File index */
unsigned Usage; /* Usage counter */
unsigned long Size; /* File size */
unsigned long MTime; /* Time of last modification */
InputType Type; /* Type of input file */
char Name[1]; /* Name of file (dynamically allocated) */
};
/* Struct that describes an active input file */
typedef struct AFile AFile;
struct AFile {
@@ -132,11 +122,13 @@ static IFile* NewIFile (const char* Name, InputType Type)
IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
/* Initialize the fields */
IF->Index = CollCount (&IFiles) + 1;
IF->Usage = 0;
IF->Size = 0;
IF->MTime = 0;
IF->Type = Type;
IF->Index = CollCount (&IFiles) + 1;
IF->Usage = 0;
IF->Size = 0;
IF->MTime = 0;
IF->Type = Type;
IF->GFlags = IG_NONE;
SB_Init (&IF->GuardMacro);
memcpy (IF->Name, Name, Len+1);
/* Insert the new structure into the IFile collection */
@@ -280,7 +272,7 @@ void OpenMainFile (const char* Name)
SetPPIfStack (&MainFile->IfStack);
/* Begin PP for this file */
PreprocessBegin ();
PreprocessBegin (IF);
/* Allocate the input line buffer */
Line = NewStrBuf ();
@@ -318,11 +310,21 @@ void OpenIncludeFile (const char* Name, InputType IT)
}
/* Search the list of all input files for this file. If we don't find
** it, create a new IFile object.
** it, create a new IFile object. If we do already know the file and it
** has an include guard, check for the include guard before opening the
** file.
*/
IF = FindFile (N);
if (IF == 0) {
IF = NewIFile (N, IT);
} else if ((IF->GFlags & IG_ISGUARDED) != 0 &&
IsMacro (SB_GetConstBuf (&IF->GuardMacro))) {
if (Debug) {
printf ("Include guard %s found for \"%s\" - won't include it again\n",
SB_GetConstBuf (&IF->GuardMacro),
Name);
}
return;
}
/* We don't need N any longer, since we may now use IF->Name */
@@ -346,7 +348,7 @@ void OpenIncludeFile (const char* Name, InputType IT)
SetPPIfStack (&AF->IfStack);
/* Begin PP for this file */
PreprocessBegin ();
PreprocessBegin (IF);
}
@@ -356,27 +358,24 @@ void CloseIncludeFile (void)
** NULL if this was the main file.
*/
{
AFile* Input;
/* Get the currently active input file and remove it from set of active
** files. CollPop will FAIL if the collection is empty so no need to
** check this here.
*/
AFile* Input = CollPop (&AFiles);
/* Get the number of active input files */
unsigned AFileCount = CollCount (&AFiles);
/* Determine the file that is active after closing this one. We never
** actually close the main file, since it is needed for errors found after
** compilation is completed.
*/
AFile* NextInput = (CollCount (&AFiles) > 0)? CollLast (&AFiles) : Input;
/* Must have an input file when called */
PRECONDITION (AFileCount > 0);
/* End preprocessor in this file */
PreprocessEnd ();
/* Get the current active input file */
Input = CollLast (&AFiles);
/* End preprocessing for the current input file */
PreprocessEnd (NextInput->Input);
/* Close the current input file (we're just reading so no error check) */
fclose (Input->F);
/* Delete the last active file from the active file collection */
--AFileCount;
CollDelete (&AFiles, AFileCount);
/* If we had added an extra search path for this AFile, remove it */
if (Input->SearchPath) {
PopSearchPath (UsrIncSearchPath);
@@ -385,10 +384,9 @@ void CloseIncludeFile (void)
/* Delete the active file structure */
FreeAFile (Input);
/* Use previous file with PP if it is not the main file */
if (AFileCount > 0) {
Input = CollLast (&AFiles);
SetPPIfStack (&Input->IfStack);
/* If we've switched files, use the if stack from the previous file */
if (Input != NextInput) {
SetPPIfStack (&NextInput->IfStack);
}
}

View File

@@ -65,6 +65,28 @@ typedef enum {
IT_USRINC = 0x04, /* User include file (using "") */
} InputType;
/* A bitmapped set of flags for include guard processing in the preprocessor */
typedef enum {
IG_NONE = 0x00,
IG_NEWFILE = 0x01, /* File processing started */
IG_ISGUARDED = 0x02, /* File contains an include guard */
IG_GUARDCLOSED = 0x04, /* Include guard was closed */
IG_COMPLETE = IG_ISGUARDED | IG_GUARDCLOSED,
} GuardFlags;
/* Struct that describes an input file */
typedef struct IFile IFile;
struct IFile {
unsigned Index; /* File index */
unsigned Usage; /* Usage counter */
unsigned long Size; /* File size */
unsigned long MTime; /* Time of last modification */
InputType Type; /* Type of input file */
GuardFlags GFlags; /* Flags for include guard processing */
StrBuf GuardMacro; /* Include guard macro name */
char Name[1]; /* Name of file (dynamically allocated) */
};
/* The current input line */
extern StrBuf* Line;

View File

@@ -93,6 +93,10 @@
#define IFCOND_SKIP 0x01U
#define IFCOND_ELSE 0x02U
#define IFCOND_NEEDTERM 0x04U
#define IFCOND_ISGUARD 0x08U
/* Current input file */
static IFile* CurInput = 0;
/* Current PP if stack */
static PPIfStack* PPStack;
@@ -2701,7 +2705,7 @@ Error_Handler:
static int PushIf (int Skip, int Invert, int Cond)
static int PushIf (int Skip, int Invert, int Cond, unsigned Flags)
/* Push a new if level onto the if stack */
{
/* Check for an overflow of the if stack */
@@ -2713,10 +2717,10 @@ static int PushIf (int Skip, int Invert, int Cond)
/* Push the #if condition */
++PPStack->Index;
if (Skip) {
PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM;
PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM | Flags;
return 1;
} else {
PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM;
PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM | Flags;
return (Invert ^ Cond);
}
}
@@ -2794,29 +2798,40 @@ static int DoIf (int Skip)
}
/* Set the #if condition according to the expression result */
return PushIf (Skip, 1, Expr.IVal != 0);
return PushIf (Skip, 1, Expr.IVal != 0, IFCOND_NONE);
}
static int DoIfDef (int skip, int flag)
/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
static int DoIfDef (int Skip, int Flag)
/* Process #ifdef if Flag == 1, or #ifndef if Flag == 0. */
{
int Value = 0;
int IsDef = 0;
unsigned GuardFlag = IFCOND_NONE;
if (!skip) {
if (!Skip) {
ident Ident;
SkipWhitespace (0);
if (MacName (Ident)) {
CheckForBadIdent (Ident, IS_Get (&Standard), 0);
Value = IsMacro (Ident);
IsDef = IsMacro (Ident);
/* Check for extra tokens */
CheckExtraTokens (flag ? "ifdef" : "ifndef");
CheckExtraTokens (Flag ? "ifdef" : "ifndef");
/* Check if this is an include guard. This is the case if we have
** a #ifndef directive at the start of a new file and the macro
** is not defined. If so, remember it.
*/
if (Flag == 0 && (CurInput->GFlags & IG_NEWFILE) != 0 && !IsDef) {
CurInput->GFlags |= IG_ISGUARDED;
SB_CopyStr (&CurInput->GuardMacro, Ident);
SB_Terminate (&CurInput->GuardMacro);
GuardFlag = IFCOND_ISGUARD;
}
}
}
return PushIf (skip, flag, Value);
return PushIf (Skip, Flag, IsDef, GuardFlag);
}
@@ -3070,18 +3085,31 @@ static int ParseDirectives (unsigned ModeFlags)
int PPSkip = 0;
ident Directive;
/* Skip white space at the beginning of the first line */
/* Skip white space at the beginning of the line */
int Whitespace = SkipWhitespace (0);
/* Check for stuff to skip */
while (CurC == '\0' || CurC == '#' || PPSkip) {
/* If a #ifndef that was assumed to be an include guard was closed
** recently, we may not have anything else following in the file
** besides whitespace otherwise the assumption was false and we don't
** actually have an include guard.
*/
if (CurC != '\0' && (CurInput->GFlags & IG_COMPLETE) == IG_COMPLETE) {
CurInput->GFlags &= ~IG_ISGUARDED;
}
/* Check for preprocessor lines lines */
if (CurC == '#') {
unsigned IfCond;
/* Skip the hash and following white space */
NextChar ();
SkipWhitespace (0);
if (CurC == '\0') {
/* Ignore the empty preprocessor directive */
CurInput->GFlags &= ~IG_NEWFILE;
continue;
}
if (!IsSym (Directive)) {
@@ -3089,145 +3117,175 @@ static int ParseDirectives (unsigned ModeFlags)
PPError ("Preprocessor directive expected");
}
ClearLine ();
} else {
switch (FindPPDirectiveType (Directive)) {
CurInput->GFlags &= ~IG_NEWFILE;
continue;
}
switch (FindPPDirectiveType (Directive)) {
case PPD_DEFINE:
if (!PPSkip) {
DoDefine ();
}
break;
case PPD_DEFINE:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
DoDefine ();
}
break;
case PPD_ELIF:
if (PPStack->Index >= 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) {
/* Handle as #else/#if combination */
if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) {
PPSkip = !PPSkip;
}
PPStack->Stack[PPStack->Index] |= IFCOND_ELSE;
PPSkip = DoIf (PPSkip);
case PPD_ELIF:
CurInput->GFlags &= ~IG_NEWFILE;
if (PPStack->Index >= 0) {
unsigned char PPCond = PPStack->Stack[PPStack->Index];
if ((PPCond & IFCOND_ELSE) == 0) {
/* Handle as #else/#if combination */
if ((PPCond & IFCOND_SKIP) == 0) {
PPSkip = !PPSkip;
}
PPStack->Stack[PPStack->Index] |= IFCOND_ELSE;
PPSkip = DoIf (PPSkip);
/* #elif doesn't need a terminator */
PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM;
} else {
PPError ("Duplicate #else/#elif");
/* #elif doesn't need a terminator */
PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM;
/* An include guard cannot have a #elif */
if (PPCond & IFCOND_ISGUARD) {
CurInput->GFlags &= ~IG_ISGUARDED;
}
} else {
PPError ("Unexpected #elif");
PPError ("Duplicate #else/#elif");
}
break;
} else {
PPError ("Unexpected #elif");
}
break;
case PPD_ELSE:
if (PPStack->Index >= 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) {
PPSkip = !PPSkip;
}
PPStack->Stack[PPStack->Index] |= IFCOND_ELSE;
/* Check for extra tokens */
CheckExtraTokens ("else");
} else {
PPError ("Duplicate #else");
case PPD_ELSE:
CurInput->GFlags &= ~IG_NEWFILE;
if (PPStack->Index >= 0) {
unsigned char PPCond = PPStack->Stack[PPStack->Index];
if ((PPCond & IFCOND_ELSE) == 0) {
if ((PPCond & IFCOND_SKIP) == 0) {
PPSkip = !PPSkip;
}
} else {
PPError ("Unexpected '#else'");
}
break;
PPStack->Stack[PPStack->Index] |= IFCOND_ELSE;
case PPD_ENDIF:
if (PPStack->Index >= 0) {
/* Remove any clauses on top of stack that do not
** need a terminating #endif.
*/
while (PPStack->Index >= 0 &&
(PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) == 0) {
--PPStack->Index;
/* An include guard cannot have a #else */
if (PPCond & IFCOND_ISGUARD) {
CurInput->GFlags &= ~IG_ISGUARDED;
}
/* Stack may not be empty here or something is wrong */
CHECK (PPStack->Index >= 0);
/* Remove the clause that needs a terminator */
PPSkip = (PPStack->Stack[PPStack->Index--] & IFCOND_SKIP) != 0;
/* Check for extra tokens */
CheckExtraTokens ("endif");
CheckExtraTokens ("else");
} else {
PPError ("Unexpected '#endif'");
PPError ("Duplicate #else");
}
break;
} else {
PPError ("Unexpected '#else'");
}
break;
case PPD_ERROR:
if (!PPSkip) {
DoError ();
case PPD_ENDIF:
CurInput->GFlags &= ~IG_NEWFILE;
if (PPStack->Index >= 0) {
/* Remove any clauses on top of stack that do not
** need a terminating #endif.
*/
while (PPStack->Index >= 0 &&
(PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) == 0) {
--PPStack->Index;
}
break;
case PPD_IF:
PPSkip = DoIf (PPSkip);
break;
/* Stack may not be empty here or something is wrong */
CHECK (PPStack->Index >= 0);
case PPD_IFDEF:
PPSkip = DoIfDef (PPSkip, 1);
break;
case PPD_IFNDEF:
PPSkip = DoIfDef (PPSkip, 0);
break;
case PPD_INCLUDE:
if (!PPSkip) {
DoInclude ();
/* Remove the clause that needs a terminator */
IfCond = PPStack->Stack[PPStack->Index--];
PPSkip = (IfCond & IFCOND_SKIP) != 0;
if (IfCond & IFCOND_ISGUARD) {
CurInput->GFlags |= IG_GUARDCLOSED;
}
break;
case PPD_LINE:
if (!PPSkip) {
DoLine ();
}
break;
/* Check for extra tokens */
CheckExtraTokens ("endif");
} else {
PPError ("Unexpected '#endif'");
}
break;
case PPD_PRAGMA:
if (!PPSkip) {
if ((ModeFlags & MSM_IN_ARG_LIST) == 0) {
DoPragma ();
return Whitespace;
} else {
PPError ("Embedded #pragma directive within macro arguments is unsupported");
}
}
break;
case PPD_ERROR:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
DoError ();
}
break;
case PPD_UNDEF:
if (!PPSkip) {
DoUndef ();
}
break;
case PPD_IF:
CurInput->GFlags &= ~IG_NEWFILE;
PPSkip = DoIf (PPSkip);
break;
case PPD_WARNING:
/* #warning is a non standard extension */
if (IS_Get (&Standard) > STD_C99) {
if (!PPSkip) {
DoWarning ();
}
case PPD_IFDEF:
CurInput->GFlags &= ~IG_NEWFILE;
PPSkip = DoIfDef (PPSkip, 1);
break;
case PPD_IFNDEF:
PPSkip = DoIfDef (PPSkip, 0);
CurInput->GFlags &= ~IG_NEWFILE;
break;
case PPD_INCLUDE:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
DoInclude ();
}
break;
case PPD_LINE:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
DoLine ();
}
break;
case PPD_PRAGMA:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
if ((ModeFlags & MSM_IN_ARG_LIST) == 0) {
DoPragma ();
return Whitespace;
} else {
if (!PPSkip) {
PPError ("Preprocessor directive expected");
}
ClearLine ();
PPError ("Embedded #pragma directive within macro arguments is unsupported");
}
break;
}
break;
default:
case PPD_UNDEF:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
DoUndef ();
}
break;
case PPD_WARNING:
CurInput->GFlags &= ~IG_NEWFILE;
/* #warning is a non standard extension */
if (IS_Get (&Standard) > STD_C99) {
if (!PPSkip) {
DoWarning ();
}
} else {
if (!PPSkip) {
PPError ("Preprocessor directive expected");
}
ClearLine ();
}
}
}
break;
default:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) {
PPError ("Preprocessor directive expected");
}
ClearLine ();
}
}
if (NextLine () == 0) {
break;
@@ -3236,6 +3294,15 @@ static int ParseDirectives (unsigned ModeFlags)
Whitespace = SkipWhitespace (0) || Whitespace;
}
/* If a #ifndef that was assumed to be an include guard was closed
** recently, we may not have anything else following in the file
** besides whitespace otherwise the assumption was false and we don't
** actually have an include guard.
*/
if (CurC != '\0' && (CurInput->GFlags & IG_COMPLETE) == IG_COMPLETE) {
CurInput->GFlags &= ~IG_ISGUARDED;
}
return Whitespace;
}
@@ -3346,6 +3413,7 @@ void Preprocess (void)
OLine = PLine;
ParseDirectives (MSM_MULTILINE);
OLine = 0;
CurInput->GFlags &= ~IG_NEWFILE;
/* Add the source info to preprocessor output if needed */
AddPreLine (PLine);
@@ -3414,9 +3482,13 @@ void ContinueLine (void)
void PreprocessBegin (void)
/* Initialize preprocessor with current file */
void PreprocessBegin (IFile* Input)
/* Initialize the preprocessor for a new input file */
{
/* Remember the new input file and flag it as new */
CurInput = Input;
CurInput->GFlags |= IG_NEWFILE;
/* Reset #if depth */
PPStack->Index = -1;
@@ -3429,9 +3501,14 @@ void PreprocessBegin (void)
void PreprocessEnd (void)
/* Preprocessor done with current file */
void PreprocessEnd (IFile* Input)
/* Preprocessor done with current file. The parameter is the file we're
** switching back to.
*/
{
/* Switch back to the old input file */
CurInput = Input;
/* Check for missing #endif */
while (PPStack->Index >= 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) != 0) {

View File

@@ -54,6 +54,9 @@ struct PPIfStack {
int Index;
};
/* Forward */
struct IFile;
/*****************************************************************************/
@@ -62,29 +65,31 @@ struct PPIfStack {
void HandleSpecialMacro (Macro* M, const char* Name);
/* Handle special "magic" macros that may change */
void Preprocess (void);
/* Preprocess a line */
void SetPPIfStack (PPIfStack* Stack);
/* Specify which PP #if stack to use */
void ContinueLine (void);
/* Continue the current line ended with a '\\' */
void PreprocessBegin (void);
/* Initialize preprocessor with current file */
void PreprocessEnd (void);
/* Preprocessor done with current file */
void InitPreprocess (void);
/* Init preprocessor */
void DonePreprocess (void);
/* Done with preprocessor */
void HandleSpecialMacro (Macro* M, const char* Name);
/* Handle special "magic" macros that may change */
void SetPPIfStack (PPIfStack* Stack);
/* Specify which PP #if stack to use */
void ContinueLine (void);
/* Continue the current line ended with a '\\' */
void PreprocessBegin (struct IFile* Input);
/* Initialize the preprocessor for a new input file */
void PreprocessEnd (struct IFile* Input);
/* Preprocessor done with current file. The parameter is the file we're
** switching back to.
*/