]> git.draconx.ca Git - cdecl99.git/commitdiff
libcdecl: Combine identifier and struct allocation.
authorNick Bowler <nbowler@draconx.ca>
Sun, 23 Jul 2023 19:06:29 +0000 (15:06 -0400)
committerNick Bowler <nbowler@draconx.ca>
Sun, 23 Jul 2023 19:13:11 +0000 (15:13 -0400)
Whenever the parser uses an identifier token, it is always to stuff
it into a newly-allocated declarator or declspec structure.

So we can reduce the amount of work that needs to be done in the
parser actions by by pre-allocating a declarator structure in the
scanner.  By using a union, we can also use this same allocation
for the declspec case.

And by happy accident, some of the structure members are identical
between both cases, and GCC at least recognizes that it doesn't need
to do anything to copy e.g., declarator.u.ident to declspec.ident
within the union.

src/cdecl-internal.h
src/parse-decl.c
src/parse.y
src/scan.l

index a5f3272298a20aeb3422ce685849cf223f0ee730..cae248bc899c38330510d9433a8bd2ceb79eb15b 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <stddef.h>
 #include <gettext.h>
+#include "cdecl.h"
 
 #define _(s) dgettext(PACKAGE, s)
 #define N_(s) s
@@ -100,4 +101,16 @@ const char *cdecl__emit_specs(struct output_state *dst,
  */
 unsigned cdecl__to_keyword(const char *s, int len, int english_mode);
 
+/* Container for an allocated parser token's value */
+struct parse_item {
+       union {
+               struct cdecl_declarator declarator;
+               struct cdecl_declspec   declspec;
+       } u;
+
+       char s[FLEXIBLE_ARRAY_MEMBER];
+};
+
+struct parse_item *cdecl__alloc_item(size_t s_sz);
+
 #endif
index a6204a18a00c32b524d9bc5dc89cff67cc5b7905..8516a3acab732e26388cb6e40c1ff8fadab4dc69 100644 (file)
 #include "scan.h"
 #include "errmsg.h"
 
+/*
+ * Allocate a "parse item", which is a union of several parse tree
+ * structure types, together with a string buffer.  The s_sz argument
+ * specifies the size of the string (including its terminator), which
+ * may be zero.
+ *
+ * The union's declarator member is pre-initialized to a valid "identifier"
+ * declarator, which shares several interesting offsets with the "declspec"
+ * structure for an "identifier" type specifier.
+ */
+struct parse_item *cdecl__alloc_item(size_t s_sz)
+{
+       struct parse_item *ret;
+
+       ret = malloc(offsetof(struct parse_item, s) + s_sz);
+       if (!ret) {
+               cdecl__errmsg(CDECL__ENOMEM);
+               return NULL;
+       }
+
+       ret->u.declarator.child   = NULL;
+       ret->u.declarator.type    = CDECL_DECL_IDENT;
+       ret->u.declarator.u.ident = ret->s;
+
+       return ret;
+}
+
 /*
  * We can represent type specifiers as a bitmap, which gives us a finite
  * list of acceptable bitmap values according to the C standard.  However,
@@ -173,22 +200,30 @@ static bool valid_declspecs(struct cdecl *decl, bool top)
 
 static struct cdecl_declarator *reduce_function(struct cdecl *param)
 {
-       struct cdecl_declspec   *spec = param->specifiers;
-       struct cdecl_declarator *decl = param->declarators;
-       struct cdecl_declarator *last;
+       struct cdecl_declarator *d, **p = &param->declarators;
+       struct parse_item *spec = (void *)param->specifiers;
 
-       for (last = decl; last && last->type != CDECL_DECL_NULL;)
-               last = last->child;
+       while ((d = *p)->child)
+               p = &d->child;
 
-       if (!last)
+       if (d->type != CDECL_DECL_NULL)
                return NULL;
 
-       last->type = CDECL_DECL_IDENT;
-       last->u.ident = spec->ident;
-       free(param);
-       free(spec);
+       /*
+        * The child and u.ident members of cdecl_declarator are expected
+        * to be located at identical offsets as, respectively, the next
+        * and ident members within cdecl_declspec, so the expectation is
+        * that the compiler can elide both assignments.
+        */
+       spec->u.declarator.child = (void *)spec->u.declspec.next;
+       spec->u.declarator.u.ident = spec->u.declspec.ident;
+       spec->u.declarator.type = CDECL_DECL_IDENT;
+       *p = &spec->u.declarator;
 
-       return decl;
+       free(d);
+       d = param->declarators;
+       free(param);
+       return d;
 }
 
 static bool function_is_reducible(struct cdecl_declarator *d)
index ec7ea1b74b5e78f1738a240c84b459f39e051575..8631f3b4613fd028b720865f4b85ed03237c3384 100644 (file)
@@ -76,10 +76,10 @@ const char *cdecl__token_name(unsigned token);
        uintmax_t uintval;
        unsigned spectype;
        _Bool boolval;
-       char *strval;
        struct cdecl_declspec *declspec;
        struct cdecl_declarator *declarator;
        struct cdecl *decl;
+       struct parse_item *item;
 }
 
 %{
@@ -91,7 +91,6 @@ static void free_declspec(struct cdecl_declspec *x)
        struct cdecl_declspec *p;
        while (x) {
                p = x->next;
-               free(x->ident);
                free(x);
                x = p;
        }
@@ -106,16 +105,12 @@ static void free_declarator(struct cdecl_declarator *x)
 
                switch (x->type) {
                case CDECL_DECL_NULL:
-                       break;
                case CDECL_DECL_IDENT:
-                       free(x->u.ident);
+               case CDECL_DECL_ARRAY:
                        break;
                case CDECL_DECL_POINTER:
                        free_declspec(x->u.pointer.qualifiers);
                        break;
-               case CDECL_DECL_ARRAY:
-                       free(x->u.array.vla);
-                       break;
                case CDECL_DECL_FUNCTION:
                        free_decl(x->u.function.parameters);
                        break;
@@ -167,21 +162,20 @@ static void join_specs(struct cdecl_declspec *a, struct cdecl_declspec *b)
  * Alter an abstract declarator (type name) to declare an identifier instead,
  * used by the English parser rules to reduce "identifier as type" sequences.
  */
-static struct cdecl *insert_identifier(struct cdecl *decl, char *ident)
+static struct cdecl *insert_identifier(struct cdecl *decl, struct parse_item *ident)
 {
-       struct cdecl_declarator *d = decl->declarators;
+       struct cdecl_declarator *d, **p = &decl->declarators;
 
-       while (d->child)
-               d = d->child;
-
-       d->type = CDECL_DECL_IDENT;
-       d->u.ident = ident;
+       while ((d = *p)->child)
+               p = &d->child;
+       free(d);
 
+       *p = d = &ident->u.declarator;
        return decl;
 }
 %}
 
-%destructor { free($$); }            <strval>
+%destructor { free($$); }            <item>
 %destructor { free_declspec($$); }   <declspec>
 %destructor { free_declarator($$); } <declarator>
 %destructor { free_decl($$); }       <decl>
@@ -189,8 +183,8 @@ static struct cdecl *insert_identifier(struct cdecl *decl, char *ident)
 /* Magic tokens */
 %token T_LEX_ERROR
 
-%token <strval> T_IDENT "identifier"
-%token <uintval> T_UINT "integer constant"
+%token <item>    T_IDENT "identifier"
+%token <uintval> T_UINT  "integer constant"
 
 %token T_SEMICOLON ";"
 %token T_ASTERISK  "*"
@@ -244,7 +238,7 @@ static struct cdecl *insert_identifier(struct cdecl *decl, char *ident)
 %token T_AS        "as"
 %token T_VLA       "variable-length"
 
-%type <strval>     vla_ident
+%type <item>       vla_ident
 %type <uintval>    array_length
 %type <boolval>    varargs
 %type <spectype>   declspec_simple qualifier_simple
@@ -257,7 +251,7 @@ static struct cdecl *insert_identifier(struct cdecl *decl, char *ident)
 %type <decl>       declaration declarators declarator_wrap
 %type <decl>       parameter
 
-%type <strval>     english_vla
+%type <item>       english_vla
 %type <declspec>   storage_func_specs post_specs
 %type <declspec>   type_qual_spec type_qual_specs typedef_name_qual
 %type <declarator> english_declarator english_array english_function
@@ -366,16 +360,19 @@ qualifier: qualifier_simple {
 }
 
 typespec: typespec_noid | typespec_tagged T_IDENT {
-       ALLOC_STRUCT($$, struct cdecl_declspec,
-               .type  = $1,
-               .ident = $2);
+       /* Compiler should be able to elide this assignment. */
+       $2->u.declspec.ident = $2->u.declarator.u.ident;
+
+       $$ = &$2->u.declspec;
+       $$->type = $1;
 }
 
 declspec_noid: declspec_notype | typespec_noid
 
 vla_ident: T_IDENT | T_ASTERISK {
-       ALLOC($$, sizeof "");
-       *$$ = 0;
+       if (!($$ = cdecl__alloc_item(1)))
+               YYERROR;
+       *$$->s = 0;
 }
 
 array: T_LBRACKET array_length T_RBRACKET {
@@ -383,9 +380,10 @@ array: T_LBRACKET array_length T_RBRACKET {
                .type = CDECL_DECL_ARRAY,
                .u.array.length = $2);
 } | T_LBRACKET vla_ident T_RBRACKET {
-       ALLOC_STRUCT($$, struct cdecl_declarator,
-               .type = CDECL_DECL_ARRAY,
-               .u.array.vla = $2);
+       $$ = &$2->u.declarator;
+       $$->type = CDECL_DECL_ARRAY;
+       $$->u.array.vla = $$->u.ident;
+       $$->u.array.length = 0;
 }
 
 parameter: declspecs declarator {
@@ -444,9 +442,7 @@ direct_declarator: {
        ALLOC_STRUCT($$, struct cdecl_declarator,
                .type = CDECL_DECL_NULL);
 } | T_IDENT {
-       ALLOC_STRUCT($$, struct cdecl_declarator,
-               .type = CDECL_DECL_IDENT,
-               .u.ident = $1);
+       $$ = &$1->u.declarator;
 } | direct_declarator postfix {
        $$ = $2;
        $$->child = $1;
@@ -530,10 +526,12 @@ english_parameter_list: english_parameter varargs {
 }
 
 typedef_name_qual: T_IDENT qualifiers {
-       ALLOC_STRUCT($$, struct cdecl_declspec,
-               .type = CDECL_TYPE_IDENT,
-               .ident = $1,
-               .next = $2);
+       /* Compiler should be able to elide this assignment. */
+       $1->u.declspec.ident = $1->u.declarator.u.ident;
+
+       $$ = &$1->u.declspec;
+       $$->type = CDECL_TYPE_IDENT;
+       $$->next = $2;
 }
 
 null_decl: {
@@ -564,9 +562,10 @@ english_parameter: english_declaration | typedef_name_qual null_decl {
 }
 
 english_array: T_VLA T_ARRAY english_vla T_OF {
-       ALLOC_STRUCT($$, struct cdecl_declarator,
-               .type = CDECL_DECL_ARRAY,
-               .u.array.vla = $3);
+       $$ = &$3->u.declarator;
+       $$->type = CDECL_DECL_ARRAY;
+       $$->u.array.vla = $$->u.ident;
+       $$->u.array.length = 0;
 } | T_ARRAY array_length T_OF {
        ALLOC_STRUCT($$, struct cdecl_declarator,
                .type = CDECL_DECL_ARRAY,
@@ -580,8 +579,9 @@ array_length: T_UINT {
 }
 
 english_vla: T_IDENT | {
-       ALLOC($$, sizeof "");
-       *$$ = 0;
+       if (!($$ = cdecl__alloc_item(1)))
+               YYERROR;
+       *$$->s = 0;
 }
 
 %%
index 29dd33da4374060ea6a9428e7e297a34e0ee6190..7549e6f7baee99cafb40a46d72f2211f10d511ce 100644 (file)
 #define STRTOUMAX strtoul
 #endif
 
-#define dup_token() do { \
-       yylval->strval = malloc(yyleng+1); \
-       if (!yylval->strval) { \
-               cdecl__errmsg(CDECL__ENOMEM); \
-               return T_LEX_ERROR; \
-       } \
-       memcpy(yylval->strval, yytext, yyleng); \
-       yylval->strval[yyleng] = 0; \
-} while(0)
-
 static char *to_octal(char *dst, unsigned val)
 {
        unsigned i;
@@ -188,7 +178,9 @@ INTEGER 0x[[:xdigit:]]+|0[0-7]+|[[:digit:]]+
 #else
                yyless(strcspn(yytext, "-"));
 #endif
-               dup_token();
+               if (!(yylval->item = cdecl__alloc_item(yyleng+1)))
+                       return T_LEX_ERROR; \
+               memcpy(yylval->item->s, yytext, yyleng+1);
        }
        return UNPACK_TOKEN(tok);
 }