mirror of https://github.com/cc65/cc65.git
1127 lines
31 KiB
C
1127 lines
31 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* pragma.c */
|
|
/* */
|
|
/* Pragma handling for the cc65 C compiler */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 1998-2011, Ullrich von Bassewitz */
|
|
/* Roemerstrasse 52 */
|
|
/* D-70794 Filderstadt */
|
|
/* EMail: uz@cc65.org */
|
|
/* */
|
|
/* */
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
/* arising from the use of this software. */
|
|
/* */
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
/* freely, subject to the following restrictions: */
|
|
/* */
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
/* claim that you wrote the original software. If you use this software */
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
/* appreciated but is not required. */
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
/* be misrepresented as being the original software. */
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
/* distribution. */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* common */
|
|
#include "addrsize.h"
|
|
#include "chartype.h"
|
|
#include "segnames.h"
|
|
#include "tgttrans.h"
|
|
#include "xmalloc.h"
|
|
|
|
/* cc65 */
|
|
#include "codegen.h"
|
|
#include "error.h"
|
|
#include "global.h"
|
|
#include "litpool.h"
|
|
#include "scanner.h"
|
|
#include "scanstrbuf.h"
|
|
#include "symtab.h"
|
|
#include "pragma.h"
|
|
#include "wrappedcall.h"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* 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_CHECK_STACK,
|
|
PRAGMA_CHECKSTACK, /* obsolete */
|
|
PRAGMA_CODE_NAME,
|
|
PRAGMA_CODESEG, /* obsolete */
|
|
PRAGMA_CODESIZE,
|
|
PRAGMA_DATA_NAME,
|
|
PRAGMA_DATASEG, /* obsolete */
|
|
PRAGMA_INLINE_STDFUNCS,
|
|
PRAGMA_LOCAL_STRINGS,
|
|
PRAGMA_MESSAGE,
|
|
PRAGMA_OPTIMIZE,
|
|
PRAGMA_REGISTER_VARS,
|
|
PRAGMA_REGVARADDR,
|
|
PRAGMA_REGVARS, /* obsolete */
|
|
PRAGMA_RODATA_NAME,
|
|
PRAGMA_RODATASEG, /* obsolete */
|
|
PRAGMA_SIGNED_CHARS,
|
|
PRAGMA_SIGNEDCHARS, /* obsolete */
|
|
PRAGMA_STATIC_LOCALS,
|
|
PRAGMA_STATICLOCALS, /* obsolete */
|
|
PRAGMA_WARN,
|
|
PRAGMA_WRAPPED_CALL,
|
|
PRAGMA_WRITABLE_STRINGS,
|
|
PRAGMA_ZPSYM,
|
|
PRAGMA_COUNT
|
|
} pragma_t;
|
|
|
|
/* Pragma table */
|
|
static const struct Pragma {
|
|
const char* Key; /* Keyword */
|
|
pragma_t Tok; /* Token */
|
|
} Pragmas[PRAGMA_COUNT] = {
|
|
{ "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 },
|
|
{ "message", PRAGMA_MESSAGE },
|
|
{ "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 */
|
|
typedef enum {
|
|
PP_NONE,
|
|
PP_POP,
|
|
PP_PUSH,
|
|
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;
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Helper functions */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static void PragmaErrorSkip (void)
|
|
/* Called in case of an error, skips tokens until the closing paren or a
|
|
** semicolon is reached.
|
|
*/
|
|
{
|
|
static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
|
|
SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
|
|
}
|
|
|
|
|
|
|
|
static int CmpKey (const void* Key, const void* Elem)
|
|
/* Compare function for bsearch */
|
|
{
|
|
return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key);
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
*/
|
|
{
|
|
struct Pragma* P;
|
|
P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
|
|
return P? P->Tok : PRAGMA_ILLEGAL;
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
*/
|
|
{
|
|
SB_SkipWhite (B);
|
|
if (SB_Get (B) != ',') {
|
|
Error ("Comma expected");
|
|
return 0;
|
|
}
|
|
SB_SkipWhite (B);
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
*/
|
|
{
|
|
if (!SB_GetString (B, S)) {
|
|
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.
|
|
*/
|
|
{
|
|
if (!SB_GetNumber (B, Val)) {
|
|
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.
|
|
*/
|
|
{
|
|
IntStack* S = 0;
|
|
StrBuf W = AUTO_STRBUF_INITIALIZER;
|
|
|
|
/* The warning name is a symbol but the '-' char is allowed within */
|
|
if (SB_GetSym (B, &W, "-")) {
|
|
|
|
/* Map the warning name to an IntStack that contains its state */
|
|
S = FindWarning (SB_GetConstBuf (&W));
|
|
|
|
/* Handle errors */
|
|
if (S == 0) {
|
|
Error ("Pragma expects a warning name as first argument");
|
|
}
|
|
}
|
|
|
|
/* Deallocate the string */
|
|
SB_Done (&W);
|
|
|
|
/* Done */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static int HasStr (StrBuf* B, const char* E)
|
|
/* Checks if E follows in B. If so, skips it and returns true */
|
|
{
|
|
unsigned Len = strlen (E);
|
|
if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
|
|
if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
|
|
/* Found */
|
|
SB_SkipMultiple (B, Len);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
*/
|
|
{
|
|
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)) {
|
|
|
|
/* Check if we have a first argument named "pop" */
|
|
if (SB_CompareStr (&Ident, "pop") == 0) {
|
|
|
|
Res = PP_POP;
|
|
|
|
/* Check if we have a first argument named "push" */
|
|
} else if (SB_CompareStr (&Ident, "push") == 0) {
|
|
|
|
Res = PP_PUSH;
|
|
|
|
/* Skip the following comma */
|
|
if (!GetComma (B)) {
|
|
/* Error already flagged by GetComma */
|
|
Res = PP_ERROR;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Unknown keyword, roll back */
|
|
SB_SetIndex (B, Index);
|
|
}
|
|
}
|
|
|
|
/* Free the string buffer and return the result */
|
|
SB_Done (&Ident);
|
|
return Res;
|
|
}
|
|
|
|
|
|
|
|
static void PopInt (IntStack* S)
|
|
/* Pops an integer from an IntStack. Prints an error if the stack is empty */
|
|
{
|
|
if (IS_GetCount (S) < 2) {
|
|
Error ("Cannot pop, stack is empty");
|
|
} else {
|
|
IS_Drop (S);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void PushInt (IntStack* S, long Val)
|
|
/* Pushes an integer onto an IntStack. Prints an error if the stack is full */
|
|
{
|
|
if (IS_IsFull (S)) {
|
|
Error ("Cannot push: stack overflow");
|
|
} else {
|
|
IS_Push (S, Val);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
*/
|
|
{
|
|
if (SB_CompareStr (Ident, "true") == 0) {
|
|
return 1;
|
|
}
|
|
if (SB_CompareStr (Ident, "on") == 0) {
|
|
return 1;
|
|
}
|
|
if (SB_CompareStr (Ident, "false") == 0) {
|
|
return 0;
|
|
}
|
|
if (SB_CompareStr (Ident, "off") == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Error */
|
|
Error ("Pragma argument must be one of 'on', 'off', 'true' or 'false'");
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
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 (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 */
|
|
Func (SB_GetConstBuf (&S));
|
|
}
|
|
|
|
/* Call the string buf destructor */
|
|
SB_Done (&S);
|
|
}
|
|
|
|
|
|
|
|
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 PushPop = 0;
|
|
|
|
/* Unused at the moment */
|
|
(void)Scope;
|
|
|
|
/* Check for the "push" or "pop" keywords */
|
|
switch (ParsePushPop (B)) {
|
|
|
|
case PP_NONE:
|
|
break;
|
|
|
|
case PP_PUSH:
|
|
PushPop = 1;
|
|
break;
|
|
|
|
case PP_POP:
|
|
/* Pop the old value and output it */
|
|
ApplySegNamePragma (Token, -1, 0, 0);
|
|
|
|
/* Done */
|
|
goto ExitPoint;
|
|
|
|
case PP_ERROR:
|
|
/* Bail out */
|
|
goto ExitPoint;
|
|
|
|
default:
|
|
Internal ("Invalid result from ParsePushPop");
|
|
|
|
}
|
|
|
|
/* A string argument must follow */
|
|
if (!GetString (B, &S)) {
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Get the name string of the segment */
|
|
Name = SB_GetConstBuf (&S);
|
|
|
|
/* Check if the name is valid */
|
|
if (ValidSegName (Name)) {
|
|
|
|
/* Skip the following comma */
|
|
SB_SkipWhite (B);
|
|
if (SB_Peek (B) == ',') {
|
|
SB_Skip (B);
|
|
SB_SkipWhite (B);
|
|
|
|
/* A string argument must follow */
|
|
if (!GetString (B, &A)) {
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Get the address size for the segment */
|
|
AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A));
|
|
|
|
/* 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 */
|
|
ApplySegNamePragma (Token, PushPop, Name, AddrSize);
|
|
|
|
} else {
|
|
|
|
/* Segment name is invalid */
|
|
Error ("Illegal segment name: '%s'", Name);
|
|
|
|
}
|
|
|
|
ExitPoint:
|
|
|
|
/* Call the string buf destructor */
|
|
SB_Done (&S);
|
|
SB_Done (&A);
|
|
}
|
|
|
|
|
|
|
|
static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B)
|
|
/* Handle the wrapped-call pragma */
|
|
{
|
|
StrBuf S = AUTO_STRBUF_INITIALIZER;
|
|
const char *Name;
|
|
long Val;
|
|
SymEntry *Entry;
|
|
|
|
/* Only PES_IMM is supported */
|
|
CHECK (Scope == PES_IMM);
|
|
|
|
/* 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:
|
|
PopWrappedCall();
|
|
|
|
/* 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 or the word 'bank' required for wrapped-call identifier");
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Next must be either a numeric value, or "bank" */
|
|
if (HasStr (B, "bank")) {
|
|
Val = WRAPPED_CALL_USE_BANK;
|
|
} else if (!GetNumber (B, &Val)) {
|
|
Error ("Value required for wrapped-call identifier");
|
|
goto ExitPoint;
|
|
}
|
|
|
|
if (!(Val == WRAPPED_CALL_USE_BANK) && (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_TYPEMASK) == SC_FUNC) {
|
|
|
|
PushWrappedCall(Entry, (unsigned int) Val);
|
|
Entry->Flags |= SC_REF;
|
|
GetFuncDesc (Entry->Type)->Flags |= FD_CALL_WRAPPER;
|
|
|
|
} else {
|
|
|
|
/* Segment name is invalid */
|
|
Error ("Wrapped-call target does not exist or is not a function");
|
|
|
|
}
|
|
|
|
ExitPoint:
|
|
/* Call the string buf destructor */
|
|
SB_Done (&S);
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
if (Index < 0 || Index > 255) {
|
|
Error ("Character index out of range");
|
|
return;
|
|
}
|
|
|
|
/* Comma follows */
|
|
if (!GetComma (B)) {
|
|
return;
|
|
}
|
|
|
|
/* Read the character code */
|
|
if (!GetNumber (B, &C)) {
|
|
return;
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
|
|
/* Remap the character */
|
|
TgtTranslateSet ((unsigned) Index, (unsigned char) C);
|
|
}
|
|
|
|
|
|
|
|
static void WarnPragma (pragma_scope_t Scope, StrBuf* B)
|
|
/* Enable/disable warnings */
|
|
{
|
|
long Val;
|
|
int Push;
|
|
|
|
/* A warning name must follow */
|
|
IntStack* S = GetWarning (B);
|
|
|
|
/* Only PES_IMM is supported */
|
|
CHECK (Scope == PES_IMM);
|
|
|
|
if (S == 0) {
|
|
return;
|
|
}
|
|
|
|
/* Comma follows */
|
|
if (!GetComma (B)) {
|
|
return;
|
|
}
|
|
|
|
/* Check for the "push" or "pop" keywords */
|
|
switch (ParsePushPop (B)) {
|
|
|
|
case PP_NONE:
|
|
Push = 0;
|
|
break;
|
|
|
|
case PP_PUSH:
|
|
Push = 1;
|
|
break;
|
|
|
|
case PP_POP:
|
|
/* Pop the old value and bail out */
|
|
PopInt (S);
|
|
return;
|
|
|
|
case PP_ERROR:
|
|
/* Bail out */
|
|
return;
|
|
|
|
default:
|
|
Internal ("Invalid result from ParsePushPop");
|
|
}
|
|
|
|
/* Boolean argument follows */
|
|
if (HasStr (B, "true") || HasStr (B, "on")) {
|
|
Val = 1;
|
|
} else if (HasStr (B, "false") || HasStr (B, "off")) {
|
|
Val = 0;
|
|
} else if (!SB_GetNumber (B, &Val)) {
|
|
Error ("Invalid pragma argument");
|
|
return;
|
|
}
|
|
|
|
/* Set/push the new value */
|
|
if (Push) {
|
|
PushInt (S, Val);
|
|
} else {
|
|
IS_Set (S, Val);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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 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) {
|
|
/* 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) {
|
|
PushPop = 1;
|
|
if (!GetComma (B)) {
|
|
goto ExitPoint;
|
|
}
|
|
IsIdent = SB_GetSym (B, &Ident, 0);
|
|
}
|
|
|
|
/* Boolean argument follows */
|
|
if (IsIdent) {
|
|
Val = IsBoolKeyword (&Ident);
|
|
} else if (!GetNumber (B, &Val)) {
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Add this pragma and apply it whenever appropriately */
|
|
ApplyPragma (PushPop, Stack, Val);
|
|
|
|
ExitPoint:
|
|
/* Free the identifier */
|
|
SB_Done (&Ident);
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
case PP_NONE:
|
|
Push = 0;
|
|
break;
|
|
|
|
case PP_PUSH:
|
|
Push = 1;
|
|
break;
|
|
|
|
case PP_POP:
|
|
/* Pop the old value and bail out */
|
|
ApplyPragma (-1, Stack, 0);
|
|
return;
|
|
|
|
case PP_ERROR:
|
|
/* Bail out */
|
|
return;
|
|
|
|
default:
|
|
Internal ("Invalid result from ParsePushPop");
|
|
|
|
}
|
|
|
|
/* Integer argument follows */
|
|
if (!GetNumber (B, &Val)) {
|
|
return;
|
|
}
|
|
|
|
/* Check the argument */
|
|
if (Val < Low || Val > High) {
|
|
Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
|
|
return;
|
|
}
|
|
|
|
/* Add this pragma and apply it whenever appropriately */
|
|
ApplyPragma (Push, Stack, Val);
|
|
}
|
|
|
|
|
|
|
|
static void NoteMessagePragma (const char* Message)
|
|
/* Wrapper for printf-like Note() function protected from user-provided format
|
|
** specifiers.
|
|
*/
|
|
{
|
|
Note ("%s", Message);
|
|
}
|
|
|
|
|
|
|
|
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 */
|
|
NextToken ();
|
|
|
|
/* Get the pragma name from the string */
|
|
SB_SkipWhite (&B);
|
|
if (!SB_GetSym (&B, &Ident, "-")) {
|
|
Error ("Invalid pragma");
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Search for the name */
|
|
Pragma = FindPragma (&Ident);
|
|
|
|
/* 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).
|
|
*/
|
|
if (IS_Get (&WarnUnknownPragma)) {
|
|
Warning ("Unknown pragma '%s'", SB_GetConstBuf (&Ident));
|
|
}
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Check for an open paren */
|
|
SB_SkipWhite (&B);
|
|
if (SB_Get (&B) != '(') {
|
|
Error ("'(' expected");
|
|
goto ExitPoint;
|
|
}
|
|
|
|
/* Skip white space before the argument */
|
|
SB_SkipWhite (&B);
|
|
|
|
/* Switch for the different pragmas */
|
|
switch (Pragma) {
|
|
|
|
case PRAGMA_ALIGN:
|
|
/* TODO: PES_EXPR (PES_DECL) */
|
|
IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096);
|
|
break;
|
|
|
|
case PRAGMA_ALLOW_EAGER_INLINE:
|
|
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:
|
|
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
|
|
SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B);
|
|
break;
|
|
|
|
case PRAGMA_CHARMAP:
|
|
CharMapPragma (PES_IMM, &B);
|
|
break;
|
|
|
|
case PRAGMA_CHECKSTACK:
|
|
Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
|
|
/* FALLTHROUGH */
|
|
case PRAGMA_CHECK_STACK:
|
|
/* 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:
|
|
/* PES_FUNC is the only sensible option so far */
|
|
SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B);
|
|
break;
|
|
|
|
case PRAGMA_CODESIZE:
|
|
/* 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:
|
|
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
|
|
SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B);
|
|
break;
|
|
|
|
case PRAGMA_INLINE_STDFUNCS:
|
|
/* TODO: PES_EXPR maybe? */
|
|
FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs);
|
|
break;
|
|
|
|
case PRAGMA_LOCAL_STRINGS:
|
|
/* TODO: PES_STMT or even PES_EXPR */
|
|
FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings);
|
|
break;
|
|
|
|
case PRAGMA_MESSAGE:
|
|
/* PES_IMM is the only sensible option */
|
|
StringPragma (PES_IMM, &B, NoteMessagePragma);
|
|
break;
|
|
|
|
case PRAGMA_OPTIMIZE:
|
|
/* TODO: PES_STMT or even PES_EXPR maybe? */
|
|
FlagPragma (PES_STMT, Pragma, &B, &Optimize);
|
|
break;
|
|
|
|
case PRAGMA_REGVARADDR:
|
|
/* 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:
|
|
/* 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:
|
|
/* 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:
|
|
/* 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:
|
|
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
|
|
FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals);
|
|
break;
|
|
|
|
case PRAGMA_WRAPPED_CALL:
|
|
/* PES_IMM is the only sensible option */
|
|
WrappedCallPragma (PES_IMM, &B);
|
|
break;
|
|
|
|
case PRAGMA_WARN:
|
|
/* PES_IMM is the only sensible option */
|
|
WarnPragma (PES_IMM, &B);
|
|
break;
|
|
|
|
case PRAGMA_WRITABLE_STRINGS:
|
|
/* TODO: PES_STMT or even PES_EXPR maybe? */
|
|
FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings);
|
|
break;
|
|
|
|
case PRAGMA_ZPSYM:
|
|
/* PES_IMM is the only sensible option */
|
|
StringPragma (PES_IMM, &B, MakeZPSym);
|
|
break;
|
|
|
|
default:
|
|
Internal ("Invalid pragma");
|
|
}
|
|
|
|
/* Closing paren expected */
|
|
SB_SkipWhite (&B);
|
|
if (SB_Get (&B) != ')') {
|
|
Error ("')' expected");
|
|
goto ExitPoint;
|
|
}
|
|
SB_SkipWhite (&B);
|
|
|
|
/* Allow an optional semicolon to be compatible with the old syntax */
|
|
if (SB_Peek (&B) == ';') {
|
|
SB_Skip (&B);
|
|
SB_SkipWhite (&B);
|
|
}
|
|
|
|
/* Make sure nothing follows */
|
|
if (SB_Peek (&B) != '\0') {
|
|
Error ("Unexpected input following pragma directive");
|
|
}
|
|
|
|
ExitPoint:
|
|
/* Release the string buffers */
|
|
SB_Done (&B);
|
|
SB_Done (&Ident);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Code */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
void ConsumePragma (void)
|
|
/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma()
|
|
** operator.
|
|
*/
|
|
{
|
|
/* Skip the _Pragma token */
|
|
NextToken ();
|
|
|
|
/* Prevent from translating string literals in _Pragma */
|
|
++InPragmaParser;
|
|
|
|
/* We expect an opening paren */
|
|
if (!ConsumeLParen ()) {
|
|
--InPragmaParser;
|
|
return;
|
|
}
|
|
|
|
/* String literal */
|
|
if (CurTok.Tok != TOK_SCONST) {
|
|
/* Print a diagnostic */
|
|
Error ("String literal expected");
|
|
|
|
/* Try some smart error recovery: Skip tokens until we reach the
|
|
** enclosing paren, or a semicolon.
|
|
*/
|
|
PragmaErrorSkip ();
|
|
} else {
|
|
/* Parse the pragma */
|
|
ParsePragmaString ();
|
|
}
|
|
|
|
--InPragmaParser;
|
|
|
|
/* Closing paren needed */
|
|
ConsumeRParen ();
|
|
}
|