/*
* Copyright (c) 2003 - 2007, Nils R. Weller
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*
* This file contains the functions and data needed to parse GNU-C-style
* __attribute__ arguments
*/
#include "attribute.h"
#include <string.h>
#include <stdlib.h>
#include "token.h"
#include "misc.h"
#include "scope.h"
#include "symlist.h"
#include "backend.h"
#include "expr.h"
#include "type.h"
#include "decl.h"
#include "typemap.h"
#include "error.h"
#include "debug.h"
#include "n_libc.h"
#define A_OK 1
#define A_UNIMPL 2
#define A_IGNORED 3
struct name_val_pair {
char *name;
int value;
};
struct attrarg {
int type;
int extended_type;
struct name_val_pair *identifiers;
};
struct name_val_pair mode_strings[] = {
{ "QI", ATTR_MODE_DI },
{ "DI", ATTR_MODE_DI },
{ "SI", ATTR_MODE_SI },
{ "HI", ATTR_MODE_SI },
{ "byte", ATTR_MODE_SI },
{ "word", ATTR_MODE_SI },
{ NULL, 0 }
};
struct attrarg mode_args[] = {
{ TOK_IDENTIFIER, 0, mode_strings }
};
struct attrarg aligned_args[] = {
{ TY_INT, 0, NULL }
};
static struct attrent {
char *name;
int value;
int type;
int nargs; /* 0 = no args, -1 = variable number */
int ignorable;
unsigned fastattrflag;
struct attrarg *argdata;
int warned;
} attrtab[] = {
{ "alias", ATTRF_ALIAS, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "aligned", ATTRS_ALIGNED, A_OK, 1, 0, CATTR_ALIGNED, aligned_args, 0 },
{ "always_inline", ATTRF_ALWAYS_INLINE, A_UNIMPL, 0, 1, 0, NULL, 0 },
{ "bounded", ATTRV_BOUNDED, A_IGNORED, -1, 1, 0, NULL, 0 },
{ "cdecl", ATTRF_CDECL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "cleanup", ATTRV_CLEANUP, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "common", ATTRV_COMMON, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "const", ATTRF_CONST, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "constructor", ATTRF_CONSTRUCTOR, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "deprecated", ATTRS_DEPRECATED, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "destructor", ATTRF_DESTRUCTOR, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "dllimport", ATTRV_DLLIMPORT, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "dllexport", ATTRV_DLLEXPORT, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "eightbit_data", ATTRF_EIGHTBIT_DATA, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "exception_handler", ATTRF_EXCEPTION_HANDLER, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "externally_visible", ATTRF_EXTERNALLY_VISIBLE, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "far", ATTRF_FAR, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "fastcall", ATTRF_FASTCALL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "flatten", ATTRF_FLATTEN, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "force_align_arg_pointer", ATTRF_FORCE_ALIGN_ARG_POINTER, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "format", ATTRF_FORMAT, A_IGNORED, 3, 0, CATTR_FORMAT, NULL, 0 },
{ "format_arg", ATTRF_FORMAT_ARG, A_IGNORED, 1, 1, 0,NULL, 0 },
{ "function_vector", ATTRF_FUNCTION_VECTOR, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "interrupt", ATTRF_INTERRUPT, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "interrupt_handler", ATTRF_INTERRUPT_HANDLER, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "longcall", ATTRF_LONGCALL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "long_call", ATTRF_LONG_CALL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "gnu_inline", ATTRF_GNU_INLINE, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "kspisusp", ATTRF_KSPISUSP, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "malloc", ATTRF_MALLOC, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "may_alias", ATTRS_MAY_ALIAS, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "mode", ATTRV_MODE, A_OK, 1, 0, CATTR_MODE, mode_args, 0 },
{ "model", ATTRV_MODEL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "naked", ATTRF_NAKED, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "near", ATTRF_NEAR, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "nesting", ATTRF_NESTING, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "nmi_handler", ATTRF_NMI_HANDLER, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "no_instrument_function",
ATTRF_NO_INSTRUMENT_FUNCTION, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "nocommon", ATTRV_NOCOMMON, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "noinline", ATTRF_NOINLINE, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "nonnull", ATTRF_NONNULL, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "noreturn", ATTRF_NORETURN, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "nothrow", ATTRF_NOTHROW, A_IGNORED, 0, 0, 0, NULL, 0 },
{ "packed", ATTRS_PACKED, A_UNIMPL, 0, 0, CATTR_PACKED, NULL, 0 },
{ "pure", ATTRF_PURE, A_IGNORED, 0, 1, CATTR_PURE, NULL, 0 },
{ "regparm", ATTRF_REGPARM, A_UNIMPL, 1, 0, 0, NULL, 0 },
{ "returns_twice", ATTRF_RETURNS_TWICE, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "saveall", ATTRF_SECTION, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "section", ATTRF_SECTION, A_UNIMPL, 0, 0, CATTR_SECTION, NULL, 0 },
{ "sentinel", ATTRF_SENTINEL, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "shared", ATTRV_SHARED, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "shortcall", ATTRF_SHORTCALL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "short_call", ATTRF_SHORT_CALL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "signal", ATTRF_SIGNAL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "sp_switch", ATTRF_SP_SWITCH, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "sseregparam", ATTRF_SSEREGPARAM, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "stdcall", ATTRF_STDCALL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "tiny_data", ATTRF_TINY_DATA, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "tls_model", ATTRV_TLS_MODEL, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "transparent_union", ATTRS_TRANSPARENT_UNION, A_OK, 0, 0, CATTR_TRANSPARENT_UNION, NULL, 0 },
{ "trap_exit", ATTRF_TRAP_EXIT, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "unused", ATTRF_UNUSED, A_IGNORED, 0, 1, CATTR_UNUSED, NULL, 0 },
{ "used", ATTRF_USED, A_IGNORED, 0, 1, CATTR_USED, NULL, 0 },
{ "vector_size", ATTRV_VECTOR_SIZE, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "visibility", ATTRF_VISIBILITY, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ "warn_unused_result", ATTRF_WEAK, A_IGNORED, 0, 1, 0, NULL, 0 },
{ "weak", ATTRF_WEAK, A_UNIMPL, 0, 0, CATTR_WEAK, NULL, 0 },
{ "weakref", ATTRF_WEAK, A_UNIMPL, 0, 0, 0, NULL, 0 },
{ 0, 0, 0, 0, 0, 0, NULL, 0 }
};
static struct attrent *
find_attrent(char *name) {
int i;
size_t len = 0;
char *p = NULL;
/*
* All attribute specifiers might either have the form ``attr'' or
* ``__attr__'', or ``__attr'', or ...????? so we have to determine
* which is the case here
*/
if (*name == '_') {
if (name[1] != '_') {
return NULL;
}
p = name + 2;
if ((len = strlen(p)) < 3) {
/* Bogus attribute, becase it can't match __*X*__ */
return NULL;
}
if (p[len - 1] != '_' || p[len - 2] != '_') {
/* Doesn't end with __ */
;
} else {
/* Ends with __ */
len -= 2;
}
}
for (i = 0; attrtab[i].name != NULL; ++i) {
if (p == NULL) {
/* No trailing __ to take into account */
if (strcmp(attrtab[i].name, name) == 0) {
/* Found */
return &attrtab[i];
}
} else if (strncmp(attrtab[i].name, p, len) == 0) {
/* Found */
return &attrtab[i];
}
}
/* Not found */
return NULL;
}
static void
free_attrib(struct attrib *attr) {
struct attrib *at;
while (attr) {
at = attr;
attr = attr->next;
free(at);
}
}
static struct attrib *
alloc_attrib(void) {
struct attrib *ret = n_xmalloc(sizeof *ret);
static struct attrib nullattrib;
*ret = nullattrib;
return ret;
}
void
append_attribute(struct attrib **head, struct attrib *attr) {
if (*head == NULL) {
*head = attr;
} else {
/* Check whether this is a duplicated attribute */
struct attrib *tmp;
struct attrib *lastnode = NULL;
for (tmp = *head; tmp != NULL; tmp = tmp->next) {
if (tmp->next == NULL) {
lastnode = tmp;
}
if (tmp->code == attr->code) {
errorfl(attr->tok,
"Duplicated attribute");
return;
}
}
/* Append */
lastnode->next = attr;
}
attr->next = NULL;
}
struct attrib *
get_single_attribute(struct token **tok, int *fatalerror) {
struct token *t;
struct token *start;
struct attrent *at;
struct attrib *ret;
char *attrname;
int parens = 0;
struct token *attrargs[128];
int intargs[128];
struct attrarg *arginfo;
int argidx = 0;
int error = 0;
int i;
*fatalerror = 0;
#define N_ATTRARGS (sizeof attrargs / sizeof attrargs[0])
t = start = *tok;
attrname = t->data;
if ((at = find_attrent(attrname)) == NULL) {
warningfl(t, "Unknown attribute `%s'", attrname);
*fatalerror = 1;
return NULL;
}
if (next_token(&t) != 0) {
*fatalerror = 1;
return NULL;
}
if ((t->type == TOK_OPERATOR
&& *(int *)t->data == TOK_OP_COMMA)
|| t->type == TOK_PAREN_CLOSE) {
/*
* This is a comma or parentheses, so the current
* attribute already ends here. Is that OK?
*/
if (at->nargs != 0 && at->nargs != -1) {
/* Error! (but only warn) */
warningfl(start, "Not enough arguments for "
"attribute `%s' - ignoring", attrname);
return NULL;
} else {
t = t->prev;
goto skipargs;
}
}
/* Read arguments to attribute */
if (t->type != TOK_PAREN_OPEN) {
warningfl(t, "Syntax error at `%s', expected "
"opening parentheses",
t->ascii);
*fatalerror = 1;
return NULL;
}
if (next_token(&t) != 0) {
return NULL;
}
++parens;
for (; t != NULL; t = t->next) {
int is_paren = 1;
if (t->type == TOK_PAREN_OPEN) {
++parens;
} else if (t->type == TOK_PAREN_CLOSE) {
--parens;
} else {
is_paren = 0;
}
if (parens == 0) {
/* Done! */
break;
} else {
if (argidx > (int)N_ATTRARGS) {
if (!error) {
warningfl(start, "Too many arguments "
"for attribute `%s' - ignoring ",
attrname);
error = 1;
}
} else if (!error) {
if (!is_paren) {
attrargs[argidx++] = t;
}
}
if (t->next != NULL
&& t->next->type == TOK_OPERATOR
&& *(int *)t->next->data == TOK_OP_COMMA) {
t = t->next;
}
}
}
if (error) {
return NULL;
}
skipargs:
/*
* Now we have an attribute declaration complete with arguments. Let's
* check if it looks valid
*/
if (at->nargs != -1 && at->nargs != argidx) {
if (!(at->warned & 1) && at->type != A_IGNORED) {
warningfl(t, "Attribute `%s' used with wrong number of "
"arguments (%d instead of %d)",
attrname, argidx, at->nargs);
at->warned |= 1;
}
*tok = t;
return NULL;
} else if (at->type == A_UNIMPL) {
if (!(at->warned & 2)) {
warningfl(t, "Attribute `%s' is not implemented - this "
"code may not work correctly without it",
attrname);
at->warned |= 2;
}
*tok = t;
return NULL;
} else if (at->type == A_IGNORED) {
/* Safe to ignore */
*tok = t;
return NULL;
}
/*
* Check if the arguments have the correct type
*/
arginfo = at->argdata;
for (i = 0; i < argidx; ++i) {
int is_num = 0;
if (attrargs[i]->type != arginfo[i].type) {
if (IS_CONSTANT(attrargs[i]->type)
&& attrargs[i]->type !=
TOK_STRING_LITERAL) {
/*
* This may be some sort of wicked non-int
* type (e.g. unsigned long) while the
* expected type is ``int''
*/
if (!IS_FLOATING(attrargs[i]->type)
&& arginfo[i].type == TY_INT) {
is_num = 1;
} else {
error = 1;
}
} else {
error = 1;
}
if (error) {
warningfl(start, "Argument %d for "
"attribute %s has wrong type",
i+1, attrname);
return NULL;
}
} else if (attrargs[i]->type == TY_INT) {
is_num = 1;
}
if (is_num) {
static struct tyval tv;
size_t val;
tv.type = make_basic_type(attrargs[i]->type);
tv.value = attrargs[i]->data;
val = cross_to_host_size_t(&tv);
intargs[i] = val;
} else if (attrargs[i]->type == TOK_IDENTIFIER) {
if (arginfo->identifiers != NULL) {
/*
* This identifier must correspond to some
* expected string
*/
struct name_val_pair *tmpnv;
char *p = attrargs[i]->data;
int len;
if (p[0] == '_' && p[1] == '_') {
p = (char *)attrargs[i]->data + 2;
}
len = strlen(p);
if (p[len - 1] == '_' && len > 1
&& p[len - 2] == '_') {
len -= 2;
}
for (tmpnv = arginfo->identifiers;
tmpnv->name != NULL;
++tmpnv) {
if (memcmp(p, tmpnv->name, len) == 0) {
intargs[i]= tmpnv->value;
break;
}
}
if (tmpnv->name == NULL) {
warningfl(start, "Argument %d for "
"attribute %s not recognized",
i+1, attrname);
return NULL;
}
}
}
}
ret = alloc_attrib();
ret->tok = start;
ret->fastattrflag = at->fastattrflag;
switch (ret->code = at->value) {
case ATTRV_MODE:
ret->iarg = intargs[0];
break;
case ATTRS_TRANSPARENT_UNION:
break;
case ATTRS_ALIGNED:
ret->iarg = intargs[0];
break;
}
*tok = t;
return ret;
}
void
merge_attr_with_type(struct type *ty) {
struct attrib *last_node = NULL;
struct attrib *attr;
for (attr = ty->attributes; attr != NULL;) {
int del = 0;
switch (attr->code) {
case ATTRS_TRANSPARENT_UNION:
if (ty->code != TY_UNION
|| ty->tlist != NULL) {
errorfl(attr->tok, "Attribute "
"`transparent_union' applied "
"to non-union type");
del = 1;
} else {
/*
* Check whether all members of the
* union have the same representation
*/
struct sym_entry *se;
struct decl *first;
se = ty->tstruc->scope->slist;
first = se->dec;
for (; se != NULL; se = se->next) {
if (!backend->
same_representation(first->dtype,
se->dec->dtype)) {
errorfl(attr->tok,
"Not all members in "
"`transparent_union'-"
"attributed union have the "
"same representation");
del = 1;
}
}
}
break;
case ATTRV_MODE:
if (attr->iarg == ATTR_MODE_DI) {
if (ty->code == TY_FLOAT) {
ty->code = TY_DOUBLE;
} else if (is_integral_type(ty)) {
ty->code = TY_LLONG;
}
}
break;
case ATTRS_ALIGNED:
if ((attr->iarg & (attr->iarg - 1)) != 0) {
warningfl(attr->tok, "Alignment is not "
"power of two - ignoring attribute");
del = 1;
}
break;
default:
del = 1;
}
if (del) {
/*
* Attribute makes no sense, delete it
*/
struct attrib *tmp = attr->next;
if (last_node == NULL) {
/* Must be head */
ty->attributes = ty->attributes->next;
} else {
last_node->next = attr->next;
}
free(attr);
attr = tmp;
} else {
ty->fastattr |= attr->fastattrflag;
last_node = attr;
attr = attr->next;
}
}
}
struct attrib *
lookup_attr(struct attrib *attr, int type) {
for (; attr != NULL; attr = attr->next) {
if (attr->code == type) {
return attr;
}
}
return NULL;
}
struct attrib *
dup_attr_list(struct attrib *attr) {
struct attrib *ret = NULL;
struct attrib *ret_tail = NULL;
for (; attr != NULL; attr = attr->next) {
struct attrib *tmp = n_xmemdup(attr, sizeof *attr);
if (ret == NULL) {
ret = ret_tail = tmp;
} else {
ret_tail->next = tmp;
ret_tail = tmp;
}
}
return ret;
}
struct attrib *
get_attribute(struct type *ty, struct token **tok) {
struct token *t = *tok;
struct attrib *ret = NULL;
int err;
#if 0
/* XXX temporary */
*tok = ignore_attr(*tok);
return NULL;
#endif
/*
* Function might be called at ``__attribute__'' so as to avoid
* cluttering the caller with next_token() calls
*/
if (t->type == TOK_KEY_ATTRIBUTE) {
if (next_token(&t) != 0) {
return NULL;
}
}
/* Every attribute must look like ``__attribute__((attr-list))'' */
if (t->type != TOK_PAREN_OPEN) {
errorfl(t, "Syntax error at %s", t->ascii);
*tok = t;
return NULL;
}
if (next_token(&t) != 0) {
*tok = t;
return NULL;
}
if (t->type != TOK_PAREN_OPEN) {
errorfl(t, "Syntax error at %s", t->ascii);
*tok = t;
return NULL;
}
#if 0
ret = n_xmalloc(sizeof *ret);
*ret = nullattr;
#endif
if (next_token(&t) != 0) {
*tok = t;
return NULL;
}
err = 0;
for (; t != NULL; t = t->next) {
if (t->type == TOK_PAREN_CLOSE) {
/* Ends here */
if (next_token(&t) != 0) {
*tok = t;
free_attrib(ret);
return NULL;
}
/* ... )) */
if (t->type != TOK_PAREN_CLOSE) {
errorfl(t, "Syntax error at %s", t->ascii);
*tok = t;
free_attrib(ret);
return NULL;
}
if (err) {
return NULL;
} else {
*tok = t->next;
return ret;
}
} else {
/* This must be another attribute */
struct attrib *tmp;
int fatal;
tmp = get_single_attribute(&t, &fatal);
if (tmp == NULL && fatal) {
*tok = ignore_attr(*tok);
return NULL;
} else if (tmp != NULL) {
if (ty->attributes == NULL) {
ty->attributes = ty->attributes_tail =
tmp;
} else {
ty->attributes_tail->next = tmp;
ty->attributes_tail = tmp;
}
if (ret == NULL) {
ret = tmp;
}
}
if (t->next
&& t->next->type == TOK_OPERATOR
&& *(int *)t->next->data == TOK_OP_COMMA) {
t = t->next;
}
}
}
errorfl(*tok, "Unexpected end of file");
*tok = t;
return NULL;
}
struct token *
ignore_attr(struct token *tok) {
int parens = 0;
if (tok == NULL) {
return NULL;
}
if (tok->type != TOK_KEY_ATTRIBUTE) {
return tok;
}
for (tok = tok->next; tok != NULL; tok = tok->next) {
if (tok->type == TOK_PAREN_OPEN) {
++parens;
} else if (tok->type == TOK_PAREN_CLOSE) {
if (--parens == 0) {
return tok->next;
}
}
}
return NULL;
}
void
stupid_append_attr(struct attrib **dest, struct attrib *src) {
struct attrib *d = *dest;
while (d && d->next) {
d = d->next;
}
if (d) {
d->next = src;
} else {
*dest = src;
}
}
syntax highlighted by Code2HTML, v. 0.9.1