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 */ /* common */
#include "check.h" #include "check.h"
#include "coll.h" #include "coll.h"
#include "debugflag.h"
#include "filestat.h" #include "filestat.h"
#include "fname.h" #include "fname.h"
#include "print.h" #include "print.h"
@@ -77,17 +78,6 @@ char NextC = '\0';
/* Maximum count of nested includes */ /* Maximum count of nested includes */
#define MAX_INC_NESTING 16 #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 */ /* Struct that describes an active input file */
typedef struct AFile AFile; typedef struct AFile AFile;
struct AFile { struct AFile {
@@ -132,11 +122,13 @@ static IFile* NewIFile (const char* Name, InputType Type)
IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len); IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
/* Initialize the fields */ /* Initialize the fields */
IF->Index = CollCount (&IFiles) + 1; IF->Index = CollCount (&IFiles) + 1;
IF->Usage = 0; IF->Usage = 0;
IF->Size = 0; IF->Size = 0;
IF->MTime = 0; IF->MTime = 0;
IF->Type = Type; IF->Type = Type;
IF->GFlags = IG_NONE;
SB_Init (&IF->GuardMacro);
memcpy (IF->Name, Name, Len+1); memcpy (IF->Name, Name, Len+1);
/* Insert the new structure into the IFile collection */ /* Insert the new structure into the IFile collection */
@@ -280,7 +272,7 @@ void OpenMainFile (const char* Name)
SetPPIfStack (&MainFile->IfStack); SetPPIfStack (&MainFile->IfStack);
/* Begin PP for this file */ /* Begin PP for this file */
PreprocessBegin (); PreprocessBegin (IF);
/* Allocate the input line buffer */ /* Allocate the input line buffer */
Line = NewStrBuf (); 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 /* 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 = FindFile (N);
if (IF == 0) { if (IF == 0) {
IF = NewIFile (N, IT); 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 */ /* 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); SetPPIfStack (&AF->IfStack);
/* Begin PP for this file */ /* Begin PP for this file */
PreprocessBegin (); PreprocessBegin (IF);
} }
@@ -356,27 +358,24 @@ void CloseIncludeFile (void)
** NULL if this was the main file. ** 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 */ /* Determine the file that is active after closing this one. We never
unsigned AFileCount = CollCount (&AFiles); ** 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 */ /* End preprocessing for the current input file */
PRECONDITION (AFileCount > 0); PreprocessEnd (NextInput->Input);
/* End preprocessor in this file */
PreprocessEnd ();
/* Get the current active input file */
Input = CollLast (&AFiles);
/* Close the current input file (we're just reading so no error check) */ /* Close the current input file (we're just reading so no error check) */
fclose (Input->F); 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 we had added an extra search path for this AFile, remove it */
if (Input->SearchPath) { if (Input->SearchPath) {
PopSearchPath (UsrIncSearchPath); PopSearchPath (UsrIncSearchPath);
@@ -385,10 +384,9 @@ void CloseIncludeFile (void)
/* Delete the active file structure */ /* Delete the active file structure */
FreeAFile (Input); FreeAFile (Input);
/* Use previous file with PP if it is not the main file */ /* If we've switched files, use the if stack from the previous file */
if (AFileCount > 0) { if (Input != NextInput) {
Input = CollLast (&AFiles); SetPPIfStack (&NextInput->IfStack);
SetPPIfStack (&Input->IfStack);
} }
} }

View File

@@ -65,6 +65,28 @@ typedef enum {
IT_USRINC = 0x04, /* User include file (using "") */ IT_USRINC = 0x04, /* User include file (using "") */
} InputType; } 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 */ /* The current input line */
extern StrBuf* Line; extern StrBuf* Line;

View File

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

View File

@@ -54,6 +54,9 @@ struct PPIfStack {
int Index; 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); void Preprocess (void);
/* Preprocess a line */ /* 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); void InitPreprocess (void);
/* Init preprocessor */ /* Init preprocessor */
void DonePreprocess (void); void DonePreprocess (void);
/* Done with preprocessor */ /* Done with preprocessor */
void HandleSpecialMacro (Macro* M, const char* Name); void SetPPIfStack (PPIfStack* Stack);
/* Handle special "magic" macros that may change */ /* 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.
*/