From 20c3e994c6126a6d81dbe5854e9444779afe4a6f Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 4 Oct 2023 21:22:04 +0800 Subject: [PATCH] Fixed compiling with pragmas in the middle of declarations or statements. --- src/cc65/compile.c | 6 - src/cc65/pragma.c | 290 +++++++++++++++++++++++++++++++-------------- src/cc65/pragma.h | 6 +- src/cc65/scanner.c | 48 +++++--- src/cc65/scanner.h | 4 +- src/cc65/stmt.c | 4 - test/val/bug2151.c | 29 +++++ 7 files changed, 271 insertions(+), 116 deletions(-) create mode 100644 test/val/bug2151.c diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 7bf3cd8ab..9d7fbe20a 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -111,12 +111,6 @@ static void Parse (void) continue; } - /* Check for a #pragma */ - if (CurTok.Tok == TOK_PRAGMA) { - DoPragma (); - continue; - } - /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { ParseStaticAssert (); diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 83ed362c8..21d426a26 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -41,12 +41,11 @@ #include "chartype.h" #include "segnames.h" #include "tgttrans.h" +#include "xmalloc.h" /* cc65 */ #include "codegen.h" #include "error.h" -#include "expr.h" -#include "funcdesc.h" #include "global.h" #include "litpool.h" #include "scanner.h" @@ -58,7 +57,7 @@ /*****************************************************************************/ -/* data */ +/* Data */ /*****************************************************************************/ @@ -142,6 +141,21 @@ typedef enum { PP_ERROR, } PushPopResult; +/* Effective scope of the pragma. +** This talks about how far the pragma has effects on whenever it shows up, +** even in the middle of an expression, statement or something. +*/ +typedef enum { + PES_NONE, + PES_IMM, /* No way back */ + PES_EXPR, /* Current expression/declarator */ + PES_STMT, /* Current statement/declaration */ + PES_SCOPE, /* Current scope */ + PES_FUNC, /* Current function */ + PES_FILE, /* Current file */ + PES_ALL, /* All */ +} pragma_scope_t; + /*****************************************************************************/ @@ -339,7 +353,7 @@ static void PushInt (IntStack* S, long Val) -static int BoolKeyword (StrBuf* Ident) +static int IsBoolKeyword (StrBuf* Ident) /* Check if the identifier in Ident is a keyword for a boolean value. Currently ** accepted are true/false/on/off. */ @@ -364,17 +378,92 @@ static int BoolKeyword (StrBuf* Ident) +static void ApplyPragma (int PushPop, IntStack* Stack, long Val) +/* Apply a pragma immediately */ +{ + if (PushPop > 0) { + /* Push the new value */ + PushInt (Stack, Val); + } else if (PushPop < 0) { + /* Pop the old value */ + PopInt (Stack); + } else { + /* Set the new value */ + IS_Set (Stack, Val); + } +} + + + +static void ApplySegNamePragma (pragma_t Token, int PushPop, const char* Name, unsigned char AddrSize) +/* Process a segname pragma */ +{ + segment_t Seg = SEG_CODE; + + switch (Token) { + case PRAGMA_CODE_NAME: + case PRAGMA_CODESEG: + Seg = SEG_CODE; + break; + + case PRAGMA_RODATA_NAME: + case PRAGMA_RODATASEG: + Seg = SEG_RODATA; + break; + + case PRAGMA_DATA_NAME: + case PRAGMA_DATASEG: + Seg = SEG_DATA; + break; + + case PRAGMA_BSS_NAME: + case PRAGMA_BSSSEG: + Seg = SEG_BSS; + break; + + default: + Internal ("Unknown segment name pragma: %02X", Token); + break; + } + + /* Set the new name */ + if (PushPop > 0) { + PushSegName (Seg, Name); + } else if (PushPop < 0) { + PopSegName (Seg); + } else { + SetSegName (Seg, Name); + } + + /* Set the optional address size for the segment if valid */ + if (PushPop >= 0 && AddrSize != ADDR_SIZE_INVALID) { + SetSegAddrSize (Name, AddrSize); + } + + /* BSS variables are output at the end of the compilation. Don't + ** bother to change their segment, now. + */ + if (Seg != SEG_BSS) { + g_segname (Seg); + } +} + + + /*****************************************************************************/ /* Pragma handling functions */ /*****************************************************************************/ -static void StringPragma (StrBuf* B, void (*Func) (const char*)) +static void StringPragma (pragma_scope_t Scope, StrBuf* B, void (*Func) (const char*)) /* Handle a pragma that expects a string parameter */ { StrBuf S = AUTO_STRBUF_INITIALIZER; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* We expect a string here */ if (GetString (B, &S)) { /* Call the given function with the string argument */ @@ -387,14 +476,17 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*)) -static void SegNamePragma (StrBuf* B, segment_t Seg) +static void SegNamePragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B) /* Handle a pragma that expects a segment name parameter */ { const char* Name; unsigned char AddrSize = ADDR_SIZE_INVALID; StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf A = AUTO_STRBUF_INITIALIZER; - int Push = 0; + int PushPop = 0; + + /* Unused at the moment */ + (void)Scope; /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -403,19 +495,12 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) break; case PP_PUSH: - Push = 1; + PushPop = 1; break; case PP_POP: /* Pop the old value and output it */ - PopSegName (Seg); - - /* BSS variables are output at the end of the compilation. Don't - ** bother to change their segment, now. - */ - if (Seg != SEG_BSS) { - g_segname (Seg); - } + ApplySegNamePragma (Token, -1, 0, 0); /* Done */ goto ExitPoint; @@ -454,27 +539,14 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) /* Get the address size for the segment */ AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A)); - /* Set the address size for the segment if valid */ - if (AddrSize != ADDR_SIZE_INVALID) { - SetSegAddrSize (Name, AddrSize); - } else { - Warning ("Invalid address size for segment!"); + /* Check the address size for the segment */ + if (AddrSize == ADDR_SIZE_INVALID) { + Warning ("Invalid address size for segment"); } } /* Set the new name and optionally address size */ - if (Push) { - PushSegName (Seg, Name); - } else { - SetSegName (Seg, Name); - } - - /* BSS variables are output at the end of the compilation. Don't - ** bother to change their segment, now. - */ - if (Seg != SEG_BSS) { - g_segname (Seg); - } + ApplySegNamePragma (Token, PushPop, Name, AddrSize); } else { @@ -484,13 +556,15 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) } ExitPoint: + /* Call the string buf destructor */ SB_Done (&S); SB_Done (&A); } -static void WrappedCallPragma (StrBuf* B) + +static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B) /* Handle the wrapped-call pragma */ { StrBuf S = AUTO_STRBUF_INITIALIZER; @@ -498,6 +572,9 @@ static void WrappedCallPragma (StrBuf* B) long Val; SymEntry *Entry; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -573,11 +650,14 @@ ExitPoint: -static void CharMapPragma (StrBuf* B) +static void CharMapPragma (pragma_scope_t Scope, StrBuf* B) /* Change the character map */ { long Index, C; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* Read the character index */ if (!GetNumber (B, &Index)) { return; @@ -619,7 +699,7 @@ static void CharMapPragma (StrBuf* B) -static void WarnPragma (StrBuf* B) +static void WarnPragma (pragma_scope_t Scope, StrBuf* B) /* Enable/disable warnings */ { long Val; @@ -627,6 +707,10 @@ static void WarnPragma (StrBuf* B) /* A warning name must follow */ IntStack* S = GetWarning (B); + + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + if (S == 0) { return; } @@ -680,48 +764,47 @@ static void WarnPragma (StrBuf* B) -static void FlagPragma (StrBuf* B, IntStack* Stack) +static void FlagPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack) /* Handle a pragma that expects a boolean parameter */ { StrBuf Ident = AUTO_STRBUF_INITIALIZER; long Val; - int Push; + int PushPop = 0; + /* Unused at the moment */ + (void)Scope; + (void)Token; /* Try to read an identifier */ int IsIdent = SB_GetSym (B, &Ident, 0); /* Check if we have a first argument named "pop" */ if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) { - PopInt (Stack); + /* Pop the old value and bail out */ + ApplyPragma (-1, Stack, 0); + /* No other arguments allowed */ return; } /* Check if we have a first argument named "push" */ if (IsIdent && SB_CompareStr (&Ident, "push") == 0) { - Push = 1; + PushPop = 1; if (!GetComma (B)) { goto ExitPoint; } IsIdent = SB_GetSym (B, &Ident, 0); - } else { - Push = 0; } /* Boolean argument follows */ if (IsIdent) { - Val = BoolKeyword (&Ident); + Val = IsBoolKeyword (&Ident); } else if (!GetNumber (B, &Val)) { goto ExitPoint; } - /* Set/push the new value */ - if (Push) { - PushInt (Stack, Val); - } else { - IS_Set (Stack, Val); - } + /* Add this pragma and apply it whenever appropriately */ + ApplyPragma (PushPop, Stack, Val); ExitPoint: /* Free the identifier */ @@ -730,12 +813,16 @@ ExitPoint: -static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) +static void IntPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack, long Low, long High) /* Handle a pragma that expects an int parameter */ { long Val; int Push; + /* Unused at the moment */ + (void)Scope; + (void)Token; + /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -749,7 +836,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) case PP_POP: /* Pop the old value and bail out */ - PopInt (Stack); + ApplyPragma (-1, Stack, 0); return; case PP_ERROR: @@ -772,31 +859,32 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) return; } - /* Set/push the new value */ - if (Push) { - PushInt (Stack, Val); - } else { - IS_Set (Stack, Val); - } + /* Add this pragma and apply it whenever appropriately */ + ApplyPragma (Push, Stack, Val); } -static void MakeMessage (const char* Message) +static void NoteMessagePragma (const char* Message) +/* Wrapper for printf-like Note() function protected from user-provided format +** specifiers. +*/ { Note ("%s", Message); } -static void ParsePragma (void) -/* Parse the contents of the _Pragma statement */ +static void ParsePragmaString (void) +/* Parse the contents of _Pragma */ { pragma_t Pragma; StrBuf Ident = AUTO_STRBUF_INITIALIZER; /* Create a string buffer from the string literal */ StrBuf B = AUTO_STRBUF_INITIALIZER; + + SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); /* Skip the string token */ @@ -837,111 +925,130 @@ static void ParsePragma (void) switch (Pragma) { case PRAGMA_ALIGN: - IntPragma (&B, &DataAlignment, 1, 4096); + /* TODO: PES_EXPR (PES_DECL) */ + IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096); break; case PRAGMA_ALLOW_EAGER_INLINE: - FlagPragma (&B, &EagerlyInlineFuncs); + FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs); break; case PRAGMA_BSSSEG: Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead"); /* FALLTHROUGH */ case PRAGMA_BSS_NAME: - SegNamePragma (&B, SEG_BSS); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B); break; case PRAGMA_CHARMAP: - CharMapPragma (&B); + CharMapPragma (PES_IMM, &B); break; case PRAGMA_CHECKSTACK: Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead"); /* FALLTHROUGH */ case PRAGMA_CHECK_STACK: - FlagPragma (&B, &CheckStack); + /* TODO: PES_SCOPE maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &CheckStack); break; case PRAGMA_CODESEG: Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead"); /* FALLTHROUGH */ case PRAGMA_CODE_NAME: - SegNamePragma (&B, SEG_CODE); + /* PES_FUNC is the only sensible option so far */ + SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B); break; case PRAGMA_CODESIZE: - IntPragma (&B, &CodeSizeFactor, 10, 1000); + /* PES_EXPR would be optimization nightmare */ + IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000); break; case PRAGMA_DATASEG: Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead"); /* FALLTHROUGH */ case PRAGMA_DATA_NAME: - SegNamePragma (&B, SEG_DATA); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B); break; case PRAGMA_INLINE_STDFUNCS: - FlagPragma (&B, &InlineStdFuncs); + /* TODO: PES_EXPR maybe? */ + FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs); break; case PRAGMA_LOCAL_STRINGS: - FlagPragma (&B, &LocalStrings); + /* TODO: PES_STMT or even PES_EXPR */ + FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings); break; case PRAGMA_MESSAGE: - StringPragma (&B, MakeMessage); + /* PES_IMM is the only sensible option */ + StringPragma (PES_IMM, &B, NoteMessagePragma); break; case PRAGMA_OPTIMIZE: - FlagPragma (&B, &Optimize); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_STMT, Pragma, &B, &Optimize); break; case PRAGMA_REGVARADDR: - FlagPragma (&B, &AllowRegVarAddr); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr); break; case PRAGMA_REGVARS: Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead"); /* FALLTHROUGH */ case PRAGMA_REGISTER_VARS: - FlagPragma (&B, &EnableRegVars); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars); break; case PRAGMA_RODATASEG: Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead"); /* FALLTHROUGH */ case PRAGMA_RODATA_NAME: - SegNamePragma (&B, SEG_RODATA); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B); break; case PRAGMA_SIGNEDCHARS: Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead"); /* FALLTHROUGH */ case PRAGMA_SIGNED_CHARS: - FlagPragma (&B, &SignedChars); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &SignedChars); break; case PRAGMA_STATICLOCALS: Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead"); /* FALLTHROUGH */ case PRAGMA_STATIC_LOCALS: - FlagPragma (&B, &StaticLocals); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals); break; case PRAGMA_WRAPPED_CALL: - WrappedCallPragma(&B); + /* PES_IMM is the only sensible option */ + WrappedCallPragma (PES_IMM, &B); break; case PRAGMA_WARN: - WarnPragma (&B); + /* PES_IMM is the only sensible option */ + WarnPragma (PES_IMM, &B); break; case PRAGMA_WRITABLE_STRINGS: - FlagPragma (&B, &WritableStrings); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings); break; case PRAGMA_ZPSYM: - StringPragma (&B, MakeZPSym); + /* PES_IMM is the only sensible option */ + StringPragma (PES_IMM, &B, MakeZPSym); break; default: @@ -975,10 +1082,18 @@ ExitPoint: -void DoPragma (void) -/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ConsumePragma (void) +/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma() +** operator. +*/ { - /* Skip the token itself */ + /* Skip the _Pragma token */ NextToken (); /* We expect an opening paren */ @@ -988,7 +1103,6 @@ void DoPragma (void) /* String literal */ if (CurTok.Tok != TOK_SCONST) { - /* Print a diagnostic */ Error ("String literal expected"); @@ -996,11 +1110,9 @@ void DoPragma (void) ** enclosing paren, or a semicolon. */ PragmaErrorSkip (); - } else { - - /* Parse the _Pragma statement */ - ParsePragma (); + /* Parse the pragma */ + ParsePragmaString (); } /* Closing paren needed */ diff --git a/src/cc65/pragma.h b/src/cc65/pragma.h index d1b94fa23..55e907453 100644 --- a/src/cc65/pragma.h +++ b/src/cc65/pragma.h @@ -44,8 +44,10 @@ -void DoPragma (void); -/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ +void ConsumePragma (void); +/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma() +** operator. +*/ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index bf3d9365d..4591f86a2 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -56,6 +56,7 @@ #include "ident.h" #include "input.h" #include "litpool.h" +#include "pragma.h" #include "preproc.h" #include "scanner.h" #include "standard.h" @@ -800,36 +801,30 @@ static void NumericConst (void) -void NextToken (void) +static void GetNextInputToken (void) /* Get next token from input stream */ { ident token; /* We have to skip white space here before shifting tokens, since the ** tokens and the current line info is invalid at startup and will get - ** initialized by reading the first time from the file. Remember if - ** we were at end of input and handle that later. + ** initialized by reading the first time from the file. Remember if we + ** were at end of input and handle that later. */ - int GotEOF = (SkipWhite() == 0); + int GotEOF = (SkipWhite () == 0); /* Current token is the lookahead token */ if (CurTok.LI) { ReleaseLineInfo (CurTok.LI); } - CurTok = NextTok; - /* When reading the first time from the file, the line info in NextTok, - ** which was copied to CurTok is invalid. Since the information from - ** the token is used for error messages, we must make it valid. - */ - if (CurTok.LI == 0) { - CurTok.LI = UseLineInfo (GetCurLineInfo ()); - } + /* Get the current token */ + CurTok = NextTok; /* Remember the starting position of the next token */ NextTok.LI = UseLineInfo (GetCurLineInfo ()); - /* Now handle end of input. */ + /* Now handle end of input */ if (GotEOF) { /* End of file reached */ NextTok.Tok = TOK_CEOF; @@ -859,7 +854,8 @@ void NextToken (void) if (!PPParserRunning) { /* Check for a keyword */ - if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) { + NextTok.Tok = FindKey (token); + if (NextTok.Tok != TOK_IDENT) { /* Reserved word found */ return; } @@ -1117,7 +1113,31 @@ void NextToken (void) UnknownChar (CurC); } +} + + +void NextToken (void) +/* Get next non-pragma token from input stream consuming any pragmas +** encountered. Adjacent string literal tokens will be concatenated. +*/ +{ + /* When reading the first time from the file, the line info in NextTok, + ** which will be copied to CurTok is invalid. Since the information from + ** the token is used for error messages, we must make it valid. + */ + if (NextTok.LI == 0) { + NextTok.LI = UseLineInfo (GetCurLineInfo ()); + } + + /* Read the next token from the file */ + GetNextInputToken (); + + /* Consume all pragmas at hand, including those nested in a _Pragma() */ + if (CurTok.Tok == TOK_PRAGMA) { + /* Repeated and/or nested _Pragma()'s will be handled recursively */ + ConsumePragma (); + } } diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 338ad6a65..cff2b0347 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -299,7 +299,9 @@ void CopyPPNumber (StrBuf* Target); /* Copy a pp-number from the input to Target */ void NextToken (void); -/* Get next token from input stream */ +/* Get next non-pragma token from input stream consuming any pragmas +** encountered. Adjacent string literal tokens will be concatenated. +*/ void SkipTokens (const token_t* TokenList, unsigned TokenCount); /* Skip tokens until we reach TOK_CEOF or a token in the given token list. diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 613129e1b..18df2b2b1 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -735,10 +735,6 @@ int AnyStatement (int* PendingToken) GotBreak = 1; break; - case TOK_PRAGMA: - DoPragma (); - break; - case TOK_SEMI: /* Empty statement. Ignore it */ CheckSemi (PendingToken); diff --git a/test/val/bug2151.c b/test/val/bug2151.c new file mode 100644 index 000000000..905eeac39 --- /dev/null +++ b/test/val/bug2151.c @@ -0,0 +1,29 @@ +/* Bug #2151 - #pragma causes errors when used within functions */ + +#pragma bss-name("BSS1") +int +#pragma code-name("CODE_WUT") +main _Pragma("message(\"_Pragma note\")") +( +void +_Pragma _Pragma ( +#pragma message("nested message 1") +"message(\"nested message 2\")" +) +( +"message(\"_Pragma in function parentheses\")") +#pragma code-name("CODE") +) +#pragma bss-name("BSS") +{ + extern int y; +#pragma bss-name("BSS2") + static +#pragma zpsym ("y") + int x; // TODO: currently in "BSS", but supposed to be in "BSS2"? + x = 0; + return x + y; +#pragma bss-name("BSS") +} + +int y;