cc65/src/cc65/initdata.c

818 lines
26 KiB
C

/*****************************************************************************/
/* */
/* initdata.c */
/* */
/* Parse and generate initializer data */
/* */
/* */
/* */
/* (C) 1998-2015, 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 <limits.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* common */
#include "addrsize.h"
#include "mmodel.h"
#include "shift.h"
#include "xmalloc.h"
/* cc65 */
#include "anonname.h"
#include "codegen.h"
#include "datatype.h"
#include "declattr.h"
#include "error.h"
#include "expr.h"
#include "exprdesc.h"
#include "funcdesc.h"
#include "function.h"
#include "global.h"
#include "litpool.h"
#include "pragma.h"
#include "scanner.h"
#include "seqpoint.h"
#include "shift.h"
#include "standard.h"
#include "symtab.h"
#include "wrappedcall.h"
#include "typeconv.h"
#include "initdata.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
typedef struct StructInitData StructInitData;
struct StructInitData {
unsigned Size; /* Size of struct */
unsigned Offs; /* Current offset in struct */
unsigned BitVal; /* Summed up bit-field value */
unsigned ValBits; /* Valid bits in Val */
};
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers);
/* Parse initialization of variables. Return the number of data bytes. */
/*****************************************************************************/
/* code */
/*****************************************************************************/
static void SkipInitializer (int BracesExpected)
/* Skip the remainder of an initializer in case of errors. Try to be somewhat
** smart so we don't have too many following errors.
*/
{
while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) {
switch (CurTok.Tok) {
case TOK_RCURLY: --BracesExpected; break;
case TOK_LCURLY: ++BracesExpected; break;
default: break;
}
if (BracesExpected >= 0) {
NextToken ();
}
}
}
static unsigned OpeningCurlyBraces (unsigned BracesNeeded)
/* Accept any number of opening curly braces around an initialization, skip
** them and return the number. If the number of curly braces is less than
** BracesNeeded, issue a warning.
*/
{
unsigned BraceCount = 0;
while (CurTok.Tok == TOK_LCURLY) {
++BraceCount;
NextToken ();
}
if (BraceCount < BracesNeeded) {
Error ("'{' expected");
}
return BraceCount;
}
static void ClosingCurlyBraces (unsigned BracesExpected)
/* Accept and skip the given number of closing curly braces together with
** an optional comma. Output an error messages, if the input does not contain
** the expected number of braces.
*/
{
while (BracesExpected) {
/* TODO: Skip all excess initializers until next closing curly brace */
if (CurTok.Tok == TOK_RCURLY) {
NextToken ();
} else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) {
NextToken ();
NextToken ();
} else {
Error ("'}' expected");
return;
}
--BracesExpected;
}
}
static void DefineData (ExprDesc* Expr)
/* Output a data definition for the given expression */
{
switch (ED_GetLoc (Expr)) {
case E_LOC_NONE:
/* Immediate numeric value with no storage */
g_defdata (CF_IMM | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
break;
case E_LOC_ABS:
/* Absolute numeric address */
g_defdata (CF_ABSOLUTE | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
break;
case E_LOC_GLOBAL:
/* Global variable */
g_defdata (CF_EXTERNAL, Expr->Name, Expr->IVal);
break;
case E_LOC_STATIC:
/* Static variable */
g_defdata (CF_STATIC, Expr->Name, Expr->IVal);
break;
case E_LOC_LITERAL:
/* Literal in the literal pool */
g_defdata (CF_LITERAL, Expr->Name, Expr->IVal);
break;
case E_LOC_REGISTER:
/* Register variable. Taking the address is usually not
** allowed.
*/
if (IS_Get (&AllowRegVarAddr) == 0) {
Error ("Cannot take the address of a register variable");
}
g_defdata (CF_REGVAR, Expr->Name, Expr->IVal);
break;
case E_LOC_CODE:
/* Code label location */
g_defdata (CF_CODE, Expr->Name, Expr->IVal);
break;
case E_LOC_STACK:
case E_LOC_PRIMARY:
case E_LOC_EXPR:
Error ("Non constant initializer");
break;
default:
Internal ("Unknown constant type: 0x%04X", ED_GetLoc (Expr));
}
}
static void DefineBitFieldData (StructInitData* SI)
/* Output bit field data */
{
/* Ignore if we have no data */
if (SI->ValBits > 0) {
/* Output the data */
g_defdata (CF_CHAR | CF_UNSIGNED | CF_CONST, SI->BitVal, 0);
/* Update the data from SI and account for the size */
if (SI->ValBits >= CHAR_BITS) {
SI->BitVal >>= CHAR_BITS;
SI->ValBits -= CHAR_BITS;
} else {
SI->BitVal = 0;
SI->ValBits = 0;
}
SI->Offs += SIZEOF_CHAR;
}
}
static void DefineStrData (Literal* Lit, unsigned Count)
{
/* Output the data */
g_defbytes (GetLiteralStr (Lit), Count);
}
static ExprDesc ParseScalarInitInternal (const Type* T)
/* Parse initializaton for scalar data types. This function will not output the
** data but return it in ED.
*/
{
/* Optional opening brace */
unsigned BraceCount = OpeningCurlyBraces (0);
/* We warn if an initializer for a scalar contains braces, because this is
** quite unusual and often a sign for some problem in the input.
*/
if (BraceCount > 0) {
Warning ("Braces around scalar initializer");
}
/* Get the expression and convert it to the target type */
ExprDesc ED = NoCodeConstExpr (hie1);
TypeConversion (&ED, T);
/* Close eventually opening braces */
ClosingCurlyBraces (BraceCount);
return ED;
}
static unsigned ParseScalarInit (const Type* T)
/* Parse initializaton for scalar data types. Return the number of data bytes. */
{
/* Parse initialization */
ExprDesc ED = ParseScalarInitInternal (T);
/* Output the data */
DefineData (&ED);
/* Do this anyways for safety */
DoDeferred (SQP_KEEP_NONE, &ED);
/* Done */
return SizeOf (T);
}
static unsigned ParsePointerInit (const Type* T)
/* Parse initializaton for pointer data types. Return the number of data bytes. */
{
/* Optional opening brace */
unsigned BraceCount = OpeningCurlyBraces (0);
/* Expression */
ExprDesc ED = NoCodeConstExpr (hie1);
TypeConversion (&ED, T);
/* Output the data */
DefineData (&ED);
/* Do this anyways for safety */
DoDeferred (SQP_KEEP_NONE, &ED);
/* Close eventually opening braces */
ClosingCurlyBraces (BraceCount);
/* Done */
return SIZEOF_PTR;
}
static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initializaton for arrays. Return the number of data bytes. */
{
int Count;
int HasCurly = 0;
/* Get the array data */
Type* ElementType = GetElementTypeModifiable (T);
unsigned ElementSize = SizeOf (ElementType);
long ElementCount = GetElementCount (T);
/* Special handling for a character array initialized by a literal */
if (IsDeclRankChar (ElementType) &&
(CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST ||
(CurTok.Tok == TOK_LCURLY &&
(NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) {
/* Char array initialized by string constant */
int NeedParen;
/* If the initializer is enclosed in curly braces, remember this fact
** and skip the opening one.
*/
NeedParen = (CurTok.Tok == TOK_LCURLY);
if (NeedParen) {
NextToken ();
}
/* If the array is one too small for the string literal, omit the
** trailing zero.
*/
Count = GetLiteralSize (CurTok.SVal);
if (ElementCount != UNSPECIFIED &&
ElementCount != FLEXIBLE &&
Count == ElementCount + 1) {
/* Omit the trailing zero */
--Count;
}
/* Output the data */
DefineStrData (CurTok.SVal, Count);
/* Skip the string */
NextToken ();
/* If the initializer was enclosed in curly braces, we need a closing
** one.
*/
if (NeedParen) {
ConsumeRCurly ();
}
} else {
/* An array can be initialized without a pair of enclosing curly braces
** if it is itself a member of a struct/union or an element of an array.
*/
if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
/* Consume the opening curly brace */
HasCurly = ConsumeLCurly ();
*Braces += HasCurly;
}
/* Initialize the array members */
Count = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Flexible array members cannot be initialized within an array.
** (Otherwise the size of each element may differ.)
*/
ParseInitInternal (ElementType, Braces, 0);
++Count;
if (CurTok.Tok != TOK_COMMA) {
break;
}
if (!HasCurly && ElementCount > 0 && Count >= ElementCount) {
/* If the array is initialized without enclosing curly braces,
** it only accepts how many elements initializers up to its
** count of elements, leaving any following initializers out.
*/
break;
}
NextToken ();
}
if (HasCurly) {
/* Closing curly braces */
ConsumeRCurly ();
}
}
/* Size of 'void' elements are determined after initialization */
if (ElementSize == 0) {
ElementSize = SizeOf (ElementType);
}
if (ElementCount == UNSPECIFIED) {
/* Number of elements determined by initializer */
SetElementCount (T, Count);
ElementCount = Count;
} else if (ElementCount == FLEXIBLE) {
if (AllowFlexibleMembers) {
/* In non ANSI mode, allow initialization of flexible array
** members.
*/
ElementCount = Count;
} else {
/* Forbid */
Error ("Initializing flexible array member is forbidden");
ElementCount = Count;
}
} else if (Count < ElementCount) {
g_zerobytes ((ElementCount - Count) * ElementSize);
} else if (Count > ElementCount && HasCurly) {
Error ("Excess elements in array initializer");
}
return ElementCount * ElementSize;
}
static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initialization of a struct or union. Return the number of data bytes. */
{
SymEntry* TagSym;
SymTable* Tab;
StructInitData SI;
int HasCurly = 0;
int SkipComma = 0;
/* Fields can be initialized without a pair of curly braces */
if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
/* Consume the opening curly brace */
HasCurly = ConsumeLCurly ();
*Braces += HasCurly;
}
/* Get a pointer to the struct entry from the type */
TagSym = GetESUTagSym (T);
/* Get the size of the struct from the symbol table entry */
SI.Size = TagSym->V.S.Size;
/* Check if this struct definition has a field table. If it doesn't, it
** is an incomplete definition.
*/
Tab = TagSym->V.S.SymTab;
if (Tab == 0) {
Error ("Cannot initialize variables with incomplete type");
/* Try error recovery */
SkipInitializer (HasCurly);
/* Nothing initialized */
return 0;
}
/* Get a pointer to the list of symbols */
TagSym = Tab->SymHead;
/* Initialize fields */
SI.Offs = 0;
SI.BitVal = 0;
SI.ValBits = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Check for excess elements */
if (TagSym == 0) {
/* Is there just one trailing comma before a closing curly? */
if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
/* Skip comma and exit scope */
NextToken ();
break;
}
if (HasCurly) {
Error ("Excess elements in %s initializer", GetBasicTypeName (T));
SkipInitializer (0);
}
break;
}
/* Check for special members that don't consume the initializer */
if ((TagSym->Flags & SC_ALIAS) == SC_ALIAS) {
/* Just skip */
goto NextMember;
}
/* This may be an anonymous bit-field, in which case it doesn't
** have an initializer.
*/
if (SymIsBitField (TagSym) && IsAnonName (TagSym->Name)) {
if (!IsTypeUnion (T)) {
/* Account for the data and output it if we have at least a full
** byte. We may have more if there was storage unit overlap, for
** example two consecutive 7 bit fields. Those would be packed
** into 2 bytes.
*/
SI.ValBits += TagSym->Type->A.B.Width;
CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= LONG_BITS);
while (SI.ValBits >= CHAR_BITS) {
DefineBitFieldData (&SI);
}
}
/* Avoid consuming the comma if any */
goto NextMember;
}
/* Skip comma this round */
if (SkipComma) {
NextToken ();
SkipComma = 0;
}
if (SymIsBitField (TagSym)) {
/* Parse initialization of one field. Bit-fields need a special
** handling.
*/
ExprDesc Field;
ED_Init (&Field);
unsigned long Val;
unsigned Shift;
/* Calculate the bitmask from the bit-field data */
unsigned long Mask = shl_l (1UL, TagSym->Type->A.B.Width) - 1UL;
/* Safety ... */
CHECK (TagSym->V.Offs * CHAR_BITS + TagSym->Type->A.B.Offs ==
SI.Offs * CHAR_BITS + SI.ValBits);
/* Read the data, check for a constant integer, do a range check */
Field = ParseScalarInitInternal (IntPromotion (TagSym->Type));
if (!ED_IsConstAbsInt (&Field)) {
Error ("Constant initializer expected");
ED_MakeConstAbsInt (&Field, 1);
}
/* Truncate the initializer value to the width of the bit-field and check if we lost
** any useful bits.
*/
Val = (unsigned long) Field.IVal & Mask;
if (IsSignUnsigned (TagSym->Type)) {
if (Field.IVal < 0 || (unsigned long) Field.IVal != Val) {
Warning (IsSignUnsigned (Field.Type) ?
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %lu to %lu" :
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %lu",
GetFullTypeName (Field.Type), GetFullTypeName (TagSym->Type),
TagSym->Type->A.B.Width, Field.IVal, Val);
}
} else {
/* Sign extend back to full width of host long. */
unsigned ShiftBits = sizeof (long) * CHAR_BIT - TagSym->Type->A.B.Width;
long RestoredVal = asr_l (asl_l (Val, ShiftBits), ShiftBits);
if (Field.IVal != RestoredVal) {
Warning (IsSignUnsigned (Field.Type) ?
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %lu to %ld" :
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %ld",
GetFullTypeName (Field.Type), GetFullTypeName (TagSym->Type),
TagSym->Type->A.B.Width, Field.IVal, RestoredVal);
}
}
/* Add the value to the currently stored bit-field value */
Shift = (TagSym->V.Offs - SI.Offs) * CHAR_BITS + TagSym->Type->A.B.Offs;
SI.BitVal |= (Val << Shift);
/* Account for the data and output any full bytes we have. */
SI.ValBits += TagSym->Type->A.B.Width;
/* Make sure unsigned is big enough to hold the value, 32 bits.
** This cannot be more than 32 bits because a 16-bit or 32-bit
** bit-field will always be byte-aligned with padding before it
** if there are bits from prior fields that haven't been output
** yet.
*/
CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= LONG_BITS);
while (SI.ValBits >= CHAR_BITS) {
DefineBitFieldData (&SI);
}
} else {
/* Standard member. We should never have stuff from a
** bit-field left because an anonymous member was added
** for padding by ParseStructSpec.
*/
CHECK (SI.ValBits == 0);
/* Flexible array members may only be initialized if they are
** the last field (or part of the last struct field).
*/
SI.Offs += ParseInitInternal (TagSym->Type, Braces, AllowFlexibleMembers && TagSym->NextSym == 0);
}
/* More initializers? */
if (CurTok.Tok != TOK_COMMA) {
break;
}
/* Skip the comma next round */
SkipComma = 1;
/* For unions, only the first named member can be initialized */
if (IsTypeUnion (T)) {
TagSym = 0;
continue;
}
NextMember:
/* Next member */
TagSym = TagSym->NextSym;
}
if (HasCurly) {
/* Consume the closing curly brace */
ConsumeRCurly ();
}
/* If we have data from a bit-field left, output it now */
CHECK (SI.ValBits < CHAR_BITS);
DefineBitFieldData (&SI);
/* If there are struct fields left, reserve additional storage */
if (SI.Offs < SI.Size) {
g_zerobytes (SI.Size - SI.Offs);
SI.Offs = SI.Size;
}
/* Return the actual number of bytes initialized. This number may be
** larger than sizeof (Struct) if flexible array members are present and
** were initialized (possible in non ANSI mode).
*/
return SI.Offs;
}
static unsigned ParseVoidInit (Type* T)
/* Parse an initialization of a void variable (special cc65 extension).
** Return the number of bytes initialized.
*/
{
unsigned Size;
/* Opening brace */
ConsumeLCurly ();
/* Allow an arbitrary list of values */
Size = 0;
do {
ExprDesc Expr = NoCodeConstExpr (hie1);
switch (GetUnderlyingTypeCode (&Expr.Type[0])) {
case T_SCHAR:
case T_UCHAR:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it byte sized */
Expr.IVal &= 0xFF;
}
DefineData (&Expr);
Size += SIZEOF_CHAR;
break;
case T_SHORT:
case T_USHORT:
case T_INT:
case T_UINT:
case T_PTR:
case T_ARRAY:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it word sized */
Expr.IVal &= 0xFFFF;
}
DefineData (&Expr);
Size += SIZEOF_INT;
break;
case T_LONG:
case T_ULONG:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it dword sized */
Expr.IVal &= 0xFFFFFFFF;
}
DefineData (&Expr);
Size += SIZEOF_LONG;
break;
default:
Error ("Illegal type in initialization");
break;
}
if (CurTok.Tok != TOK_COMMA) {
break;
}
NextToken ();
} while (CurTok.Tok != TOK_RCURLY);
/* Closing brace */
ConsumeRCurly ();
/* Number of bytes determined by initializer */
if (T->A.U != 0 && T->A.U != Size) {
Error ("'void' array initialized with elements of variant sizes");
} else {
T->A.U = Size;
}
/* Return the number of bytes initialized */
return Size;
}
static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers)
/* Parse initialization of variables. Return the number of data bytes. */
{
switch (GetUnderlyingTypeCode (T)) {
case T_SCHAR:
case T_UCHAR:
case T_SHORT:
case T_USHORT:
case T_INT:
case T_UINT:
case T_LONG:
case T_ULONG:
case T_FLOAT:
case T_DOUBLE:
return ParseScalarInit (T);
case T_PTR:
return ParsePointerInit (T);
case T_ARRAY:
return ParseArrayInit (T, Braces, AllowFlexibleMembers);
case T_STRUCT:
case T_UNION:
return ParseStructInit (T, Braces, AllowFlexibleMembers);
case T_ENUM:
/* Incomplete enum type must have already raised errors.
** Just proceed to consume the value.
*/
return ParseScalarInit (T);
case T_VOID:
if (IS_Get (&Standard) == STD_CC65) {
/* Special cc65 extension in non-ANSI mode */
return ParseVoidInit (T);
}
/* FALLTHROUGH */
default:
Error ("Illegal type");
return SIZEOF_CHAR;
}
}
unsigned ParseInit (Type* T)
/* Parse initialization of variables. Return the number of data bytes. */
{
/* Current curly braces layers */
int Braces = 0;
/* Parse the initialization. Flexible array members can only be initialized
** in cc65 mode.
*/
unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65);
/* The initialization may not generate code on global level, because code
** outside function scope will never get executed.
*/
if (HaveGlobalCode ()) {
Error ("Non constant initializers");
RemoveGlobalCode ();
}
/* Return the size needed for the initialization */
return Size;
}