/* Id: gcc_compat.c,v 1.10 2015/01/05 21:31:01 plunky Exp */
/* $NetBSD: gcc_compat.c,v 1.1.1.4 2016/02/09 20:29:05 plunky Exp $ */
/*
* Copyright (c) 2004 Anders Magnusson (ragge@ludd.luth.se).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Routines to support some of the gcc extensions to C.
*/
#ifdef GCC_COMPAT
#include "pass1.h"
#include "cgram.h"
#include <string.h>
static struct kw {
char *name, *ptr;
int rv;
} kw[] = {
/*
* Do NOT change the order of these entries unless you know
* what you're doing!
*/
/* 0 */ { "__asm", NULL, C_ASM },
/* 1 */ { "__signed", NULL, 0 },
/* 2 */ { "__inline", NULL, C_FUNSPEC },
/* 3 */ { "__const", NULL, 0 },
/* 4 */ { "__asm__", NULL, C_ASM },
/* 5 */ { "__inline__", NULL, C_FUNSPEC },
/* 6 */ { "__thread", NULL, 0 },
/* 7 */ { "__FUNCTION__", NULL, 0 },
/* 8 */ { "__volatile", NULL, 0 },
/* 9 */ { "__volatile__", NULL, 0 },
/* 10 */{ "__restrict", NULL, -1 },
/* 11 */{ "__typeof__", NULL, C_TYPEOF },
/* 12 */{ "typeof", NULL, C_TYPEOF },
/* 13 */{ "__extension__", NULL, -1 },
/* 14 */{ "__signed__", NULL, 0 },
/* 15 */{ "__attribute__", NULL, 0 },
/* 16 */{ "__attribute", NULL, 0 },
/* 17 */{ "__real__", NULL, 0 },
/* 18 */{ "__imag__", NULL, 0 },
/* 19 */{ "__builtin_offsetof", NULL, PCC_OFFSETOF },
/* 20 */{ "__PRETTY_FUNCTION__", NULL, 0 },
/* 21 */{ "__alignof__", NULL, C_ALIGNOF },
/* 22 */{ "__typeof", NULL, C_TYPEOF },
/* 23 */{ "__alignof", NULL, C_ALIGNOF },
/* 24 */{ "__restrict__", NULL, -1 },
{ NULL, NULL, 0 },
};
/* g77 stuff */
#if SZFLOAT == SZLONG
#define G77_INTEGER LONG
#define G77_UINTEGER ULONG
#elif SZFLOAT == SZINT
#define G77_INTEGER INT
#define G77_UINTEGER UNSIGNED
#else
#error fix g77 stuff
#endif
#if SZFLOAT*2 == SZLONG
#define G77_LONGINT LONG
#define G77_ULONGINT ULONG
#elif SZFLOAT*2 == SZLONGLONG
#define G77_LONGINT LONGLONG
#define G77_ULONGINT ULONGLONG
#else
#error fix g77 long stuff
#endif
static TWORD g77t[] = { G77_INTEGER, G77_UINTEGER, G77_LONGINT, G77_ULONGINT };
static char *g77n[] = { "__g77_integer", "__g77_uinteger",
"__g77_longint", "__g77_ulongint" };
void
gcc_init(void)
{
struct kw *kwp;
NODE *p;
TWORD t;
int i;
for (kwp = kw; kwp->name; kwp++)
kwp->ptr = addname(kwp->name);
for (i = 0; i < 4; i++) {
struct symtab *sp;
t = ctype(g77t[i]);
p = block(NAME, NIL, NIL, t, NULL, 0);
sp = lookup(addname(g77n[i]), 0);
p->n_sp = sp;
defid(p, TYPEDEF);
nfree(p);
}
}
#define TS "\n#pragma tls\n# %d\n"
#define TLLEN sizeof(TS)+10
/*
* See if a string matches a gcc keyword.
*/
int
gcc_keyword(char *str, NODE **n)
{
extern int inattr, parlvl, parbal;
YYSTYPE *yyl = (YYSTYPE *)n; /* XXX should pass yylval */
char tlbuf[TLLEN], *tw;
struct kw *kwp;
int i;
/* XXX hack, should pass everything in expressions */
if (str == kw[21].ptr)
return kw[21].rv;
if (inattr)
return 0;
for (i = 0, kwp = kw; kwp->name; kwp++, i++)
if (str == kwp->ptr)
break;
if (kwp->name == NULL)
return 0;
if (kwp->rv)
return kwp->rv;
switch (i) {
case 1: /* __signed */
case 14: /* __signed__ */
*n = mkty((TWORD)SIGNED, 0, 0);
return C_TYPE;
case 3: /* __const */
*n = block(QUALIFIER, NIL, NIL, CON, 0, 0);
(*n)->n_qual = CON;
return C_QUALIFIER;
case 6: /* __thread */
snprintf(tlbuf, TLLEN, TS, lineno);
tw = &tlbuf[strlen(tlbuf)];
while (tw > tlbuf)
cunput(*--tw);
return -1;
case 7: /* __FUNCTION__ */
case 20: /* __PRETTY_FUNCTION__ */
if (cftnsp == NULL) {
uerror("%s outside function", kwp->name);
yylval.strp = "";
} else
yylval.strp = cftnsp->sname; /* XXX - not C99 */
return C_STRING;
case 8: /* __volatile */
case 9: /* __volatile__ */
*n = block(QUALIFIER, NIL, NIL, VOL, 0, 0);
(*n)->n_qual = VOL;
return C_QUALIFIER;
case 15: /* __attribute__ */
case 16: /* __attribute */
inattr = 1;
parlvl = parbal;
return C_ATTRIBUTE;
case 17: /* __real__ */
yyl->intval = XREAL;
return C_UNOP;
case 18: /* __imag__ */
yyl->intval = XIMAG;
return C_UNOP;
}
cerror("gcc_keyword");
return 0;
}
#ifndef TARGET_ATTR
#define TARGET_ATTR(p, sue) 0
#endif
#ifndef ALMAX
#define ALMAX (ALLDOUBLE > ALLONGLONG ? ALLDOUBLE : ALLONGLONG)
#endif
/* allowed number of args */
#define A_0ARG 0x01
#define A_1ARG 0x02
#define A_2ARG 0x04
#define A_3ARG 0x08
/* arg # is a name */
#define A1_NAME 0x10
#define A2_NAME 0x20
#define A3_NAME 0x40
#define A_MANY 0x80
/* arg # is "string" */
#define A1_STR 0x100
#define A2_STR 0x200
#define A3_STR 0x400
#ifdef __MSC__
#define CS(x)
#else
#define CS(x) [x] =
#endif
struct atax {
int typ;
char *name;
} atax[GCC_ATYP_MAX] = {
CS(ATTR_NONE) { 0, NULL },
CS(ATTR_COMPLEX) { 0, NULL },
CS(xxxATTR_BASETYP) { 0, NULL },
CS(ATTR_QUALTYP) { 0, NULL },
CS(ATTR_STRUCT) { 0, NULL },
CS(ATTR_ALIGNED) { A_0ARG|A_1ARG, "aligned" },
CS(GCC_ATYP_PACKED) { A_0ARG|A_1ARG, "packed" },
CS(GCC_ATYP_SECTION) { A_1ARG|A1_STR, "section" },
CS(GCC_ATYP_TRANSP_UNION) { A_0ARG, "transparent_union" },
CS(GCC_ATYP_UNUSED) { A_0ARG, "unused" },
CS(GCC_ATYP_DEPRECATED) { A_0ARG, "deprecated" },
CS(GCC_ATYP_MAYALIAS) { A_0ARG, "may_alias" },
CS(GCC_ATYP_MODE) { A_1ARG|A1_NAME, "mode" },
CS(GCC_ATYP_NORETURN) { A_0ARG, "noreturn" },
CS(GCC_ATYP_FORMAT) { A_3ARG|A1_NAME, "format" },
CS(GCC_ATYP_NONNULL) { A_MANY, "nonnull" },
CS(GCC_ATYP_SENTINEL) { A_0ARG|A_1ARG, "sentinel" },
CS(GCC_ATYP_WEAK) { A_0ARG, "weak" },
CS(GCC_ATYP_FORMATARG) { A_1ARG, "format_arg" },
CS(GCC_ATYP_GNU_INLINE) { A_0ARG, "gnu_inline" },
CS(GCC_ATYP_MALLOC) { A_0ARG, "malloc" },
CS(GCC_ATYP_NOTHROW) { A_0ARG, "nothrow" },
CS(GCC_ATYP_CONST) { A_0ARG, "const" },
CS(GCC_ATYP_PURE) { A_0ARG, "pure" },
CS(GCC_ATYP_CONSTRUCTOR) { A_0ARG, "constructor" },
CS(GCC_ATYP_DESTRUCTOR) { A_0ARG, "destructor" },
CS(GCC_ATYP_VISIBILITY) { A_1ARG|A1_STR, "visibility" },
CS(GCC_ATYP_STDCALL) { A_0ARG, "stdcall" },
CS(GCC_ATYP_CDECL) { A_0ARG, "cdecl" },
CS(GCC_ATYP_WARN_UNUSED_RESULT) { A_0ARG, "warn_unused_result" },
CS(GCC_ATYP_USED) { A_0ARG, "used" },
CS(GCC_ATYP_NO_INSTR_FUN) { A_0ARG, "no_instrument_function" },
CS(GCC_ATYP_NOINLINE) { A_0ARG, "noinline" },
CS(GCC_ATYP_ALIAS) { A_1ARG|A1_STR, "alias" },
CS(GCC_ATYP_WEAKREF) { A_0ARG|A_1ARG|A1_STR, "weakref" },
CS(GCC_ATYP_ALLOCSZ) { A_1ARG|A_2ARG, "alloc_size" },
CS(GCC_ATYP_ALW_INL) { A_0ARG, "always_inline" },
CS(GCC_ATYP_TLSMODEL) { A_1ARG|A1_STR, "tls_model" },
CS(GCC_ATYP_ALIASWEAK) { A_1ARG|A1_STR, "aliasweak" },
CS(GCC_ATYP_REGPARM) { A_1ARG, "regparm" },
CS(GCC_ATYP_FASTCALL) { A_0ARG, "fastcall" },
CS(GCC_ATYP_BOUNDED) { A_3ARG|A_MANY|A1_NAME, "bounded" },
};
#if SZPOINT(CHAR) == SZLONGLONG
#define GPT LONGLONG
#else
#define GPT INT
#endif
struct atax mods[] = {
{ 0, NULL },
{ INT, "SI" },
{ INT, "word" },
{ GPT, "pointer" },
{ CHAR, "byte" },
{ CHAR, "QI" },
{ SHORT, "HI" },
{ LONGLONG, "DI" },
{ FLOAT, "SF" },
{ DOUBLE, "DF" },
{ LDOUBLE, "XF" },
{ FCOMPLEX, "SC" },
{ COMPLEX, "DC" },
{ LCOMPLEX, "XC" },
#ifdef TARGET_MODS
TARGET_MODS
#endif
};
#define ATSZ (sizeof(mods)/sizeof(mods[0]))
static int
amatch(char *s, struct atax *at, int mx)
{
int i, len;
if (s[0] == '_' && s[1] == '_')
s += 2;
len = strlen(s);
if (len > 2 && s[len-1] == '_' && s[len-2] == '_')
len -= 2;
for (i = 0; i < mx; i++) {
char *t = at[i].name;
if (t != NULL && strncmp(s, t, len) == 0 && t[len] == 0)
return i;
}
return 0;
}
static void
setaarg(int str, union aarg *aa, NODE *p)
{
if (str) {
if (((str & (A1_STR|A2_STR|A3_STR)) && p->n_op != STRING) ||
((str & (A1_NAME|A2_NAME|A3_NAME)) && p->n_op != NAME))
uerror("bad arg to attribute");
if (p->n_op == STRING) {
aa->sarg = newstring(p->n_name, strlen(p->n_name));
} else
aa->sarg = (char *)p->n_sp;
nfree(p);
} else
aa->iarg = (int)icons(eve(p));
}
/*
* Parse attributes from an argument list.
*/
static struct attr *
gcc_attribs(NODE *p)
{
NODE *q, *r;
struct attr *ap;
char *name = NULL, *c;
int cw, attr, narg, i;
if (p->n_op == NAME) {
name = (char *)p->n_sp;
} else if (p->n_op == CALL || p->n_op == UCALL) {
name = (char *)p->n_left->n_sp;
} else if (p->n_op == ICON && p->n_type == STRTY) {
return NULL;
} else
cerror("bad variable attribute");
if ((attr = amatch(name, atax, GCC_ATYP_MAX)) == 0) {
warner(Wattributes, name);
ap = NULL;
goto out;
}
narg = 0;
if (p->n_op == CALL)
for (narg = 1, q = p->n_right; q->n_op == CM; q = q->n_left)
narg++;
cw = atax[attr].typ;
if (!(cw & A_MANY) && ((narg > 3) || ((cw & (1 << narg)) == 0))) {
uerror("wrong attribute arg count");
return NULL;
}
ap = attr_new(attr, 3); /* XXX should be narg */
q = p->n_right;
switch (narg) {
default:
/* XXX */
while (narg-- > 3) {
r = q;
q = q->n_left;
tfree(r->n_right);
nfree(r);
}
/* FALLTHROUGH */
case 3:
setaarg(cw & (A3_NAME|A3_STR), &ap->aa[2], q->n_right);
r = q;
q = q->n_left;
nfree(r);
/* FALLTHROUGH */
case 2:
setaarg(cw & (A2_NAME|A2_STR), &ap->aa[1], q->n_right);
r = q;
q = q->n_left;
nfree(r);
/* FALLTHROUGH */
case 1:
setaarg(cw & (A1_NAME|A1_STR), &ap->aa[0], q);
p->n_op = UCALL;
/* FALLTHROUGH */
case 0:
break;
}
/* some attributes must be massaged special */
switch (attr) {
case ATTR_ALIGNED:
if (narg == 0)
ap->aa[0].iarg = ALMAX;
else
ap->aa[0].iarg *= SZCHAR;
break;
case GCC_ATYP_PACKED:
if (narg == 0)
ap->aa[0].iarg = 1; /* bitwise align */
else
ap->aa[0].iarg *= SZCHAR;
break;
case GCC_ATYP_MODE:
if ((i = amatch(ap->aa[0].sarg, mods, ATSZ)) == 0)
werror("unknown mode arg %s", ap->aa[0].sarg);
ap->aa[0].iarg = ctype(mods[i].typ);
break;
case GCC_ATYP_VISIBILITY:
c = ap->aa[0].sarg;
if (strcmp(c, "default") && strcmp(c, "hidden") &&
strcmp(c, "internal") && strcmp(c, "protected"))
werror("unknown visibility %s", c);
break;
case GCC_ATYP_TLSMODEL:
c = ap->aa[0].sarg;
if (strcmp(c, "global-dynamic") && strcmp(c, "local-dynamic") &&
strcmp(c, "initial-exec") && strcmp(c, "local-exec"))
werror("unknown tls model %s", c);
break;
default:
break;
}
out:
return ap;
}
/*
* Extract attributes from a node tree and return attribute entries
* based on its contents.
*/
struct attr *
gcc_attr_parse(NODE *p)
{
struct attr *b, *c;
if (p == NIL)
return NULL;
if (p->n_op != CM) {
b = gcc_attribs(p);
tfree(p);
} else {
b = gcc_attr_parse(p->n_left);
c = gcc_attr_parse(p->n_right);
nfree(p);
b = b ? attr_add(b, c) : c;
}
return b;
}
/*
* Fixup struct/unions depending on attributes.
*/
void
gcc_tcattrfix(NODE *p)
{
struct symtab *sp;
struct attr *ap;
int sz, coff, csz, al, oal, mxal;
if ((ap = attr_find(p->n_ap, GCC_ATYP_PACKED)) == NULL)
return; /* nothing to fix */
al = ap->iarg(0);
mxal = 0;
/* Must repack struct */
coff = csz = 0;
for (sp = strmemb(ap); sp; sp = sp->snext) {
oal = talign(sp->stype, sp->sap);
if (oal > al)
oal = al;
if (mxal < oal)
mxal = oal;
if (sp->sclass & FIELD)
sz = sp->sclass&FLDSIZ;
else
sz = (int)tsize(sp->stype, sp->sdf, sp->sap);
sp->soffset = upoff(sz, oal, &coff);
if (coff > csz)
csz = coff;
if (p->n_type == UNIONTY)
coff = 0;
}
if (mxal < ALCHAR)
mxal = ALCHAR; /* for bitfields */
SETOFF(csz, mxal); /* Roundup to whatever */
ap = attr_find(p->n_ap, ATTR_STRUCT);
ap->amsize = csz;
ap = attr_find(p->n_ap, ATTR_ALIGNED);
ap->iarg(0) = mxal;
}
/*
* gcc-specific pragmas.
*/
int
pragmas_gcc(char *t)
{
char u;
extern char *pragstore;
if (strcmp((t = pragtok(NULL)), "diagnostic") == 0) {
int warn, err;
if (strcmp((t = pragtok(NULL)), "ignored") == 0)
warn = 0, err = 0;
else if (strcmp(t, "warning") == 0)
warn = 1, err = 0;
else if (strcmp(t, "error") == 0)
warn = 1, err = 1;
else
return 1;
if (eat('\"') || eat('-'))
return 1;
for (t = pragstore; *t && *t != '\"'; t++)
;
u = *t;
*t = 0;
Wset(pragstore + 1, warn, err);
*t = u;
} else if (strcmp(t, "poison") == 0) {
/* currently ignore */;
} else if (strcmp(t, "visibility") == 0) {
/* currently ignore */;
} else if (strcmp(t, "system_header") == 0) {
/* currently ignore */;
} else
werror("gcc pragma unsupported");
return 0;
}
#ifdef PCC_DEBUG
void
dump_attr(struct attr *ap)
{
printf("attributes; ");
for (; ap; ap = ap->next) {
if (ap->atype >= GCC_ATYP_MAX) {
printf("bad type %d, ", ap->atype);
} else if (atax[ap->atype].name == 0) {
char *c = ap->atype == ATTR_COMPLEX ? "complex" :
ap->atype == ATTR_STRUCT ? "struct" : "badtype";
printf("%s, ", c);
} else {
printf("%s: ", atax[ap->atype].name);
printf("%d %d %d, ", ap->iarg(0),
ap->iarg(1), ap->iarg(2));
}
}
printf("\n");
}
#endif
#endif