/*****************************************************************************/
/* */
-/* pragma.c */
+/* pragma.c */
/* */
-/* Pragma handling for the cc65 C compiler */
+/* Pragma handling for the cc65 C compiler */
/* */
/* */
/* */
-/* (C) 1998-2009, Ullrich von Bassewitz */
+/* (C) 1998-2011, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
#include "scanstrbuf.h"
#include "symtab.h"
#include "pragma.h"
+#include "trampoline.h"
/*****************************************************************************/
-/* data */
+/* data */
/*****************************************************************************/
/* Tokens for the #pragmas */
typedef enum {
PRAGMA_ILLEGAL = -1,
+ PRAGMA_ALIGN,
+ PRAGMA_ALLOW_EAGER_INLINE,
PRAGMA_BSS_NAME,
PRAGMA_BSSSEG, /* obsolete */
PRAGMA_CHARMAP,
PRAGMA_CODESIZE,
PRAGMA_DATA_NAME,
PRAGMA_DATASEG, /* obsolete */
+ PRAGMA_INLINE_STDFUNCS,
PRAGMA_LOCAL_STRINGS,
PRAGMA_OPTIMIZE,
PRAGMA_REGVARADDR,
PRAGMA_STATIC_LOCALS,
PRAGMA_STATICLOCALS, /* obsolete */
PRAGMA_WARN,
+ PRAGMA_WRAPPED_CALL,
PRAGMA_WRITABLE_STRINGS,
PRAGMA_ZPSYM,
PRAGMA_COUNT
/* Pragma table */
static const struct Pragma {
- const char* Key; /* Keyword */
- pragma_t Tok; /* Token */
+ const char* Key; /* Keyword */
+ pragma_t Tok; /* Token */
} Pragmas[PRAGMA_COUNT] = {
- { "bss-name", PRAGMA_BSS_NAME },
- { "bssseg", PRAGMA_BSSSEG }, /* obsolete */
- { "charmap", PRAGMA_CHARMAP },
- { "check-stack", PRAGMA_CHECK_STACK },
- { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */
- { "code-name", PRAGMA_CODE_NAME },
- { "codeseg", PRAGMA_CODESEG }, /* obsolete */
- { "codesize", PRAGMA_CODESIZE },
- { "data-name", PRAGMA_DATA_NAME },
- { "dataseg", PRAGMA_DATASEG }, /* obsolete */
- { "local-strings", PRAGMA_LOCAL_STRINGS },
- { "optimize", PRAGMA_OPTIMIZE },
- { "register-vars", PRAGMA_REGISTER_VARS },
- { "regvaraddr", PRAGMA_REGVARADDR },
- { "regvars", PRAGMA_REGVARS }, /* obsolete */
- { "rodata-name", PRAGMA_RODATA_NAME },
- { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */
- { "signed-chars", PRAGMA_SIGNED_CHARS },
- { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */
- { "static-locals", PRAGMA_STATIC_LOCALS },
- { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */
- { "warn", PRAGMA_WARN },
- { "writable-strings", PRAGMA_WRITABLE_STRINGS },
- { "zpsym", PRAGMA_ZPSYM },
+ { "align", PRAGMA_ALIGN },
+ { "allow-eager-inline", PRAGMA_ALLOW_EAGER_INLINE },
+ { "bss-name", PRAGMA_BSS_NAME },
+ { "bssseg", PRAGMA_BSSSEG }, /* obsolete */
+ { "charmap", PRAGMA_CHARMAP },
+ { "check-stack", PRAGMA_CHECK_STACK },
+ { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */
+ { "code-name", PRAGMA_CODE_NAME },
+ { "codeseg", PRAGMA_CODESEG }, /* obsolete */
+ { "codesize", PRAGMA_CODESIZE },
+ { "data-name", PRAGMA_DATA_NAME },
+ { "dataseg", PRAGMA_DATASEG }, /* obsolete */
+ { "inline-stdfuncs", PRAGMA_INLINE_STDFUNCS },
+ { "local-strings", PRAGMA_LOCAL_STRINGS },
+ { "optimize", PRAGMA_OPTIMIZE },
+ { "register-vars", PRAGMA_REGISTER_VARS },
+ { "regvaraddr", PRAGMA_REGVARADDR },
+ { "regvars", PRAGMA_REGVARS }, /* obsolete */
+ { "rodata-name", PRAGMA_RODATA_NAME },
+ { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */
+ { "signed-chars", PRAGMA_SIGNED_CHARS },
+ { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */
+ { "static-locals", PRAGMA_STATIC_LOCALS },
+ { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */
+ { "warn", PRAGMA_WARN },
+ { "wrapped-call", PRAGMA_WRAPPED_CALL },
+ { "writable-strings", PRAGMA_WRITABLE_STRINGS },
+ { "zpsym", PRAGMA_ZPSYM },
};
/* Result of ParsePushPop */
static void PragmaErrorSkip (void)
/* Called in case of an error, skips tokens until the closing paren or a
- * semicolon is reached.
- */
+** semicolon is reached.
+*/
{
static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
static pragma_t FindPragma (const StrBuf* Key)
/* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is
- * not a valid pragma.
- */
+** not a valid pragma.
+*/
{
struct Pragma* P;
P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
static int GetComma (StrBuf* B)
/* Expects and skips a comma in B. Prints an error and returns zero if no
- * comma is found. Return a value <> 0 otherwise.
- */
+** comma is found. Return a value <> 0 otherwise.
+*/
{
SB_SkipWhite (B);
if (SB_Get (B) != ',') {
static int GetString (StrBuf* B, StrBuf* S)
/* Expects and skips a string in B. Prints an error and returns zero if no
- * string is found. Returns a value <> 0 otherwise.
- */
+** string is found. Returns a value <> 0 otherwise.
+*/
{
if (!SB_GetString (B, S)) {
- Error ("String literal expected");
+ Error ("String literal expected");
return 0;
}
return 1;
static int GetNumber (StrBuf* B, long* Val)
/* Expects and skips a number in B. Prints an eror and returns zero if no
- * number is found. Returns a value <> 0 otherwise.
- */
+** number is found. Returns a value <> 0 otherwise.
+*/
{
if (!SB_GetNumber (B, Val)) {
- Error ("Constant integer expected");
+ Error ("Constant integer expected");
return 0;
}
return 1;
static IntStack* GetWarning (StrBuf* B)
/* Get a warning name from the string buffer. Returns a pointer to the intstack
- * that holds the state of the warning, and NULL in case of errors. The
- * function will output error messages in case of problems.
- */
+** that holds the state of the warning, and NULL in case of errors. The
+** function will output error messages in case of problems.
+*/
{
IntStack* S = 0;
StrBuf W = AUTO_STRBUF_INITIALIZER;
/* Done */
return S;
-}
+}
static PushPopResult ParsePushPop (StrBuf* B)
/* Check for and parse the "push" and "pop" keywords. In case of "push", a
- * following comma is expected and skipped.
- */
+** following comma is expected and skipped.
+*/
{
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
PushPopResult Res = PP_NONE;
+ /* Remember the current string index, so we can go back in case of errors */
+ unsigned Index = SB_GetIndex (B);
/* Try to read an identifier */
if (SB_GetSym (B, &Ident, 0)) {
/* Skip the following comma */
if (!GetComma (B)) {
+ /* Error already flagged by GetComma */
Res = PP_ERROR;
}
} else {
- /* Unknown keyword */
- Error ("Invalid pragma arguments");
- Res = PP_ERROR;
+ /* Unknown keyword, roll back */
+ SB_SetIndex (B, Index);
}
}
static int BoolKeyword (StrBuf* Ident)
/* Check if the identifier in Ident is a keyword for a boolean value. Currently
- * accepted are true/false/on/off.
- */
+** accepted are true/false/on/off.
+*/
{
if (SB_CompareStr (Ident, "true") == 0) {
return 1;
/* We expect a string here */
if (GetString (B, &S)) {
- /* Call the given function with the string argument */
- Func (SB_GetConstBuf (&S));
+ /* Call the given function with the string argument */
+ Func (SB_GetConstBuf (&S));
}
/* Call the string buf destructor */
}
+static void WrappedCallPragma (StrBuf* B)
+/* Handle the wrapped-call pragma */
+{
+ StrBuf S = AUTO_STRBUF_INITIALIZER;
+ const char *Name;
+ long Val;
+ SymEntry *Entry;
+
+ /* Check for the "push" or "pop" keywords */
+ switch (ParsePushPop (B)) {
+
+ case PP_NONE:
+ Error ("Push or pop required");
+ break;
+
+ case PP_PUSH:
+ break;
+
+ case PP_POP:
+ PopTrampoline();
+
+ /* Done */
+ goto ExitPoint;
+
+ case PP_ERROR:
+ /* Bail out */
+ goto ExitPoint;
+
+ default:
+ Internal ("Invalid result from ParsePushPop");
+
+ }
+
+ /* A symbol argument must follow */
+ if (!SB_GetSym (B, &S, NULL)) {
+ goto ExitPoint;
+ }
+
+ /* Skip the following comma */
+ if (!GetComma (B)) {
+ /* Error already flagged by GetComma */
+ Error ("Value required for wrapped-call identifier");
+ goto ExitPoint;
+ }
+
+ if (!GetNumber (B, &Val)) {
+ Error ("Value required for wrapped-call identifier");
+ goto ExitPoint;
+ }
+
+ if (Val < 0 || Val > 255) {
+ Error ("Identifier must be between 0-255");
+ goto ExitPoint;
+ }
+
+ /* Get the string */
+ Name = SB_GetConstBuf (&S);
+ Entry = FindSym(Name);
+
+ /* Check if the name is valid */
+ if (Entry && Entry->Flags & (SC_FUNC | SC_STORAGE)) {
+
+ PushTrampoline(Entry, Val);
+ Entry->Flags |= SC_REF;
+
+ } else {
+
+ /* Segment name is invalid */
+ Error ("Wrapped-call target does not exist or is not a function or array");
+
+ }
+
+ExitPoint:
+ /* Call the string buf destructor */
+ SB_Done (&S);
+}
+
+
static void CharMapPragma (StrBuf* B)
/* Change the character map */
if (!GetNumber (B, &Index)) {
return;
}
- if (Index < 1 || Index > 255) {
- if (Index == 0) {
- /* For groepaz */
- Error ("Remapping 0 is not allowed");
- } else {
- Error ("Character index out of range");
- }
- return;
+ if (Index < 0 || Index > 255) {
+ Error ("Character index out of range");
+ return;
}
/* Comma follows */
if (!GetNumber (B, &C)) {
return;
}
- if (C < 1 || C > 255) {
- if (C == 0) {
- /* For groepaz */
- Error ("Remapping 0 is not allowed");
- } else {
- Error ("Character code out of range");
+ if (C < 0 || C > 255) {
+ Error ("Character code out of range");
+ return;
+ }
+
+ /* Warn about remapping character code 0x00
+ ** (except when remapping it back to itself).
+ */
+ if (Index + C != 0 && IS_Get (&WarnRemapZero)) {
+ if (Index == 0) {
+ Warning ("Remapping from 0 is dangerous with string functions");
+ }
+ else if (C == 0) {
+ Warning ("Remapping to 0 can make string functions stop unexpectedly");
}
- return;
}
/* Remap the character */
/* Do we know this pragma? */
if (Pragma == PRAGMA_ILLEGAL) {
- /* According to the ANSI standard, we're not allowed to generate errors
- * for unknown pragmas, but warn about them if enabled (the default).
- */
+ /* According to the ANSI standard, we're not allowed to generate errors
+ ** for unknown pragmas, but warn about them if enabled (the default).
+ */
if (IS_Get (&WarnUnknownPragma)) {
- Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
+ Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
}
- goto ExitPoint;
+ goto ExitPoint;
}
/* Check for an open paren */
/* Switch for the different pragmas */
switch (Pragma) {
- case PRAGMA_BSSSEG:
+ case PRAGMA_ALIGN:
+ IntPragma (&B, &DataAlignment, 1, 4096);
+ break;
+
+ case PRAGMA_ALLOW_EAGER_INLINE:
+ FlagPragma (&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);
- break;
+ SegNamePragma (&B, SEG_BSS);
+ break;
- case PRAGMA_CHARMAP:
- CharMapPragma (&B);
- break;
+ case PRAGMA_CHARMAP:
+ CharMapPragma (&B);
+ break;
- case PRAGMA_CHECKSTACK:
+ case PRAGMA_CHECKSTACK:
Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
/* FALLTHROUGH */
case PRAGMA_CHECK_STACK:
- FlagPragma (&B, &CheckStack);
- break;
+ FlagPragma (&B, &CheckStack);
+ break;
- case PRAGMA_CODESEG:
+ case PRAGMA_CODESEG:
Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
/* FALLTHROUGH */
case PRAGMA_CODE_NAME:
- SegNamePragma (&B, SEG_CODE);
- break;
+ SegNamePragma (&B, SEG_CODE);
+ break;
- case PRAGMA_CODESIZE:
- IntPragma (&B, &CodeSizeFactor, 10, 1000);
- break;
+ case PRAGMA_CODESIZE:
+ IntPragma (&B, &CodeSizeFactor, 10, 1000);
+ break;
- case PRAGMA_DATASEG:
+ case PRAGMA_DATASEG:
Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
/* FALLTHROUGH */
case PRAGMA_DATA_NAME:
- SegNamePragma (&B, SEG_DATA);
- break;
+ SegNamePragma (&B, SEG_DATA);
+ break;
+
+ case PRAGMA_INLINE_STDFUNCS:
+ FlagPragma (&B, &InlineStdFuncs);
+ break;
case PRAGMA_LOCAL_STRINGS:
FlagPragma (&B, &LocalStrings);
FlagPragma (&B, &Optimize);
break;
- case PRAGMA_REGVARADDR:
- FlagPragma (&B, &AllowRegVarAddr);
- break;
+ case PRAGMA_REGVARADDR:
+ FlagPragma (&B, &AllowRegVarAddr);
+ break;
- case PRAGMA_REGVARS:
+ case PRAGMA_REGVARS:
Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
/* FALLTHROUGH */
case PRAGMA_REGISTER_VARS:
- FlagPragma (&B, &EnableRegVars);
- break;
+ FlagPragma (&B, &EnableRegVars);
+ break;
- case PRAGMA_RODATASEG:
+ case PRAGMA_RODATASEG:
Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
/* FALLTHROUGH */
case PRAGMA_RODATA_NAME:
- SegNamePragma (&B, SEG_RODATA);
- break;
+ SegNamePragma (&B, SEG_RODATA);
+ break;
- case PRAGMA_SIGNEDCHARS:
+ case PRAGMA_SIGNEDCHARS:
Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
/* FALLTHROUGH */
case PRAGMA_SIGNED_CHARS:
- FlagPragma (&B, &SignedChars);
- break;
+ FlagPragma (&B, &SignedChars);
+ break;
- case PRAGMA_STATICLOCALS:
+ case PRAGMA_STATICLOCALS:
Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
/* FALLTHROUGH */
case PRAGMA_STATIC_LOCALS:
- FlagPragma (&B, &StaticLocals);
- break;
+ FlagPragma (&B, &StaticLocals);
+ break;
+
+ case PRAGMA_WRAPPED_CALL:
+ WrappedCallPragma(&B);
+ break;
case PRAGMA_WARN:
WarnPragma (&B);
FlagPragma (&B, &WritableStrings);
break;
- case PRAGMA_ZPSYM:
- StringPragma (&B, MakeZPSym);
- break;
+ case PRAGMA_ZPSYM:
+ StringPragma (&B, MakeZPSym);
+ break;
- default:
- Internal ("Invalid pragma");
+ default:
+ Internal ("Invalid pragma");
}
/* Closing paren expected */
/* We expect an opening paren */
if (!ConsumeLParen ()) {
- return;
+ return;
}
/* String literal */
if (CurTok.Tok != TOK_SCONST) {
- /* Print a diagnostic */
- Error ("String literal expected");
+ /* Print a diagnostic */
+ Error ("String literal expected");
- /* Try some smart error recovery: Skip tokens until we reach the
- * enclosing paren, or a semicolon.
- */
- PragmaErrorSkip ();
+ /* Try some smart error recovery: Skip tokens until we reach the
+ ** enclosing paren, or a semicolon.
+ */
+ PragmaErrorSkip ();
} else {
- /* Parse the _Pragma statement */
- ParsePragma ();
+ /* Parse the _Pragma statement */
+ ParsePragma ();
}
/* Closing paren needed */
ConsumeRParen ();
}
-
-
-