GCC-3.4.6源代码学习笔记(19续)

cpp_reader中的域op_stack用于多次包含优化(multiple-include optimization,即将#include指示加入#if !defined#endif对中),它将缓存#if#elseif表达式的符号。

 

cpp_create_reader (continue)

 

199    /* The expression parser stack.  */

200    _cpp_expand_op_stack (pfile);

200 

201    /* Initialize the buffer obstack.  */

202    _obstack_begin (&pfile->buffer_ob, 0, 0,

203                 (void *(*) (long)) xmalloc,

204                 (void (*) (void *)) free);

205 

206    _cpp_init_files (pfile);

207 

208    _cpp_init_hashtable (pfile, table);

209 

210    return pfile;

211   }

 

这个栈由_cpp_expand_op_stack初始化及扩展。

 

970  struct op *

971  _cpp_expand_op_stack (cpp_reader *pfile)                                            in cppexp.c

972  {

973    size_t old_size = (size_t) (pfile->op_limit - pfile->op_stack);

974    size_t new_size = old_size * 2 + 20;

975 

976    pfile->op_stack = xrealloc (pfile->op_stack, new_size * sizeof (struct op));

977    pfile->op_limit = pfile->op_stack + new_size;

978 

979    return pfile->op_stack + old_size;

980  }

 

这个栈的初始大小是20。而op的定义如下。

 

31    struct op                                                                                            in cppexp.c

32    {

33      const cpp_token *token;    /* The token forming op (for diagnostics).  */

34      cpp_num value;         /* The value logically "right" of op.  */

35      enum cpp_ttype op;

36    };

 

Op中的域token保存用于诊断的符号信息。域value记录了符号的值(如果有的话),它是语义处理的关键。域op保存符号的类型,它也是语义分析的关键。

进一步深入op定义。

 

165  #define PREV_WHITE (1 << 0) /* If whitespace before this token.  */        in cpplib.h

166  #define DIGRAPH        (1 << 1) /* If it was a digraph.  */

167  #define STRINGIFY_ARG   (1 << 2) /* If macro argument to be stringified.  */

168  #define PASTE_LEFT   (1 << 3) /* If on LHS of a ## operator.  */

169  #define NAMED_OP    (1 << 4) /* C++ named operators.  */

170  #define NO_EXPAND  (1 << 5) /* Do not macro-expand this token.  */

171  #define BOL         (1 << 6) /* Token at beginning of line.  */

 

上面的宏用于cpp_tokenflags域,这个域表示了符号的特性。而cpp_token的定义如下所示。

 

175  struct cpp_token                                                                                         in cpplib.h

176  {

177    fileline line;              /* Logical line of first char of token.  */

178    unsigned short col;            /* Column of first char of token.  */

179    ENUM_BITFIELD(cpp_ttype) type : CHAR_BIT;  /* token type */

180    unsigned char flags;          /* flags - see above */

181 

182    union

183    {

184      cpp_hashnode *node;            /* An identifier.  */

185      const cpp_token *source;       /* Inherit padding from this token.  */

186      struct cpp_string str;      /* A string, or number.  */

187      unsigned int arg_no;      /* Argument no. for a CPP_MACRO_ARG.  */

188    } val;

189  };

 

op的域value则定义在下面。注意到结构中的cpp_num_part是机器上最宽的整型。

 

4929 struct cpp_num                                                                                         in cpplib.h

4930 {

4931   cpp_num_part high;

4932   cpp_num_part low;

4933   bool unsignedp;  /* True if value should be treated as unsigned.  */

4934   bool overflow;   /* True if the most recent calculation overflowed.  */

4935 };

 

而上面的179行,cpp_ttype是这样定义的。同时注意伴随的OPTK的定义。

 

143  #define OP(e, s) e,                                                                                    in cpplib.h

144  #define TK(e, s) e,

145  enum cpp_ttype

146  {

147    TTYPE_TABLE

148    N_TTYPES

149  };

150  #undef OP

151  #undef TK

 

147行的TTYPE_TABLE的内容见如下,它包含了所有可能的符号类型。

 

60    #define TTYPE_TABLE                          /                                                in cpplib.h

61      OP(CPP_EQ = 0,      "=")               /

62      OP (CPP_NOT,         "!")                /

63      OP (CPP_GREATER,       ">") /* compare */  /

64      OP (CPP_LESS,              "<")               /

65      OP (CPP_PLUS,              "+") /* math */       /

66      OP (CPP_MINUS,           "-")                /

67      OP (CPP_MULT,             "*")               /

68      OP (CPP_DIV,          "/")                /

69     OP (CPP_MOD,              "%")                     /

70      OP (CPP_AND,        "&")       /* bit ops */    /

71     OP (CPP_OR,           "|")                /

72      OP (CPP_XOR,        "^")               /

73      OP (CPP_RSHIFT,    ">>")                    /

74      OP (CPP_LSHIFT,    "<<")                    /

75      OP (CPP_MIN,        "<?")      /* extension */       /

76      OP (CPP_MAX,              ">?")                    /

77    /

78      OP (CPP_COMPL,           "~")               /

79      OP (CPP_AND_AND,      "&&")    /* logical */    /

80      OP (CPP_OR_OR,           "||")               /

81      OP (CPP_QUERY,           "?")               /

82      OP (CPP_COLON,           ":")                /

83      OP (CPP_COMMA,         ",")  /* grouping */ /

84      OP (CPP_OPEN_PAREN,       "(")                /

85      OP (CPP_CLOSE_PAREN,     ")")                /

86      TK(CPP_EOF,          SPELL_NONE)            /

87      OP (CPP_EQ_EQ,           "==")      /* compare */  /

88      OP (CPP_NOT_EQ,  "!=")                     /

89      OP (CPP_GREATER_EQ,       ">=")                    /

90      OP (CPP_LESS_EQ, "<=")                    /

91    /

92      /* These two are unary + / - in preprocessor expressions.  */ /

93      OP (CPP_PLUS_EQ,       "+=")      /* math */       /

94      OP (CPP_MINUS_EQ,     "-=")                     /

95    /

96      OP (CPP_MULT_EQ,       "*=")                    /

97      OP (CPP_DIV_EQ,   "/=")                     /

98      OP (CPP_MOD_EQ, "%=")                   /

99      OP (CPP_AND_EQ, "&=")     /* bit ops */    /

100    OP (CPP_OR_EQ,           "|=")                     /

101    OP (CPP_XOR_EQ,  "^=")                    /

102    OP (CPP_RSHIFT_EQ,    ">>=")                  /

103    OP (CPP_LSHIFT_EQ,    "<<=")                  /

104    OP (CPP_MIN_EQ,  "<?=")    /* extension */       /

105    OP (CPP_MAX_EQ, ">?=")                  /

106    /* Digraphs together, beginning with CPP_FIRST_DIGRAPH.  */      /

107    OP (CPP_HASH,             "#") /* digraphs */  /

108    OP (CPP_PASTE,            "##")                    /

109    OP (CPP_OPEN_SQUARE,    "[")                /

110     OP (CPP_CLOSE_SQUARE,   "]")                /

111     OP (CPP_OPEN_BRACE,       "{")               /

112     OP (CPP_CLOSE_BRACE,     "}")               /

113     /* The remainder of the punctuation. Order is not significant.  */ /

114     OP (CPP_SEMICOLON,  ";")  /* structure */ /

115     OP (CPP_ELLIPSIS, "...")                     /

116     OP (CPP_PLUS_PLUS,    "++")      /* increment */       /

117     OP (CPP_MINUS_MINUS,     "--")                     /

118     OP (CPP_DEREF,           "->")       /* accessors */ /

119     OP (CPP_DOT,         ".")                /

120    OP (CPP_SCOPE,           "::")               /

121    OP (CPP_DEREF_STAR, "->*")                   /

122    OP (CPP_DOT_STAR,     ".*")                     /

123    OP (CPP_ATSIGN,   "@")  /* used in Objective-C */ /

124  /

125    TK (CPP_NAME,            SPELL_IDENT)    /* word */                    /

126    TK (CPP_AT_NAME,   SPELL_IDENT)    /* @word - Objective-C */       /

127    TK (CPP_NUMBER, SPELL_LITERAL) /* 34_be+ta  */                   /

128  /

129    TK (CPP_CHAR,             SPELL_LITERAL) /* 'char' */                     /

130    TK (CPP_WCHAR,          SPELL_LITERAL) /* L'char' */                   /

131    TK (CPP_OTHER,           SPELL_LITERAL) /* stray punctuation */          /

132  /

133    TK (CPP_STRING,   SPELL_LITERAL) /* "string" */                 /

134    TK (CPP_WSTRING,       SPELL_LITERAL) /* L"string" */               /

135    TK (CPP_OBJC_STRING,   SPELL_LITERAL)  /* @"string" - Objective-C */      /

136    TK (CPP_HEADER_NAME,   SPELL_LITERAL) /* <stdio.h> in #include */    /

137  /

138    TK (CPP_COMMENT,     SPELL_LITERAL) /* Only if output comments.  */ /

139                                       /* SPELL_LITERAL happens to DTRT.  */ /

140    TK (CPP_MACRO_ARG, SPELL_NONE)     /* Macro argument.  */        /

141    TK (CPP_PADDING,       SPELL_NONE)     /* Whitespace for cpp0.  */

 

其中OP应该是operator的缩写,TK应该是token的缩写。

cpp_reader使用哈希表来管理编译的文件及头文件的查找目录。接下来,就是初始化这些哈希表。

 

930  void

931  _cpp_init_files (cpp_reader *pfile)                                                              in cppfiles.c

932  {

933    pfile->file_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,

934                                  NULL, xcalloc, free);

935    pfile->dir_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,

936                                 NULL, xcalloc, free);

937    allocate_file_hash_entries (pfile);

938  }

 

cpp_create_reader的最后一步是调用下面的_cpp_init_hashtable

这里,我们看到在调用时,ident_hash被传入做第二个参数。它保存了所有的标识符(identifier):由#define定义的宏(符号类型NT_MACRO),由#assert声明的断言(符号类型NT_ASSERTION),及其他(符号类型NT_VOID)。内建的宏(builtin macro),例如__LINE__,由NODE_BUILTIN标识。中毒(poisoned)的标识符由NODE_POISONED标记。NODE_OPERATOR(仅对于C++)表示标识符的代表操作符,比如“xor”。 NODE_DIAGNOSTIC用于加速词法分析:它表示该符号可能需要诊断(diagnostic)。目前,这个标识仅用于__VA_ARGS__及中毒的标识符。

 

47    void

48    _cpp_init_hashtable (cpp_reader *pfile, hash_table *table)                      in cpphash.c

49    {

50      struct spec_nodes *s;

51   

52      if (table == NULL)

53      {

54        pfile->our_hashtable = 1;

55        table = ht_create (13);   /* 8K (=2^13) entries.  */

56        table->alloc_node = (hashnode (*) (hash_table *)) alloc_node;

57   

58        _obstack_begin (&pfile->hash_ob, 0, 0,

59                        (void *(*) (long)) xmalloc,

60                    (void (*) (void *)) free);

61      }

62   

63      table->pfile = pfile;

64      pfile->hash_table = table;

65   

66      /* Now we can initialize things that use the hash table.  */

67      _cpp_init_directives (pfile);

68      _cpp_init_internal_pragmas (pfile);

69   

70      s = &pfile->spec_nodes;

71      s->n_defined      = cpp_lookup (pfile, DSC("defined"));

72      s->n_true           = cpp_lookup (pfile, DSC ("true"));

73      s->n_false          = cpp_lookup (pfile, DSC ("false"));

74      s->n__VA_ARGS__ = cpp_lookup (pfile, DSC ("__VA_ARGS__"));

75      s->n__VA_ARGS__->flags |= NODE_DIAGNOSTIC;

76    }

 

C/C++语言里定义了一系列的指示directive_cpp_init_directives确保指示的hashnode都已出现在cpp_reader中的hash_table哈希表中。

 

1983 void

1984 _cpp_init_directives (cpp_reader *pfile)                                                      in cpplib.c

1985 {

1986   unsigned int i;

1987   cpp_hashnode *node;

1988

1989   for (i = 0; i < (unsigned int) N_DIRECTIVES; i++)

1990   {

1991     node = cpp_lookup (pfile, dtable[i].name, dtable[i].length);

1992     node->is_directive = 1;

1993     node->directive_index = i;

1994   }

1995 }

 

上面的1991行,dtable依照DIRECTIVE_TABLE的内容,以以下的方式初始化。

 

179    #define D(name, t, origin, flags) /                                                             in cpplib.c

180    { do_##name, (const uchar *) #name, /

181      sizeof #name - 1, origin, flags },

182    static const directive dtable[] =

183    {

184    DIRECTIVE_TABLE

185    };

186    #undef D

187    #undef DIRECTIVE_TABLE

 

记录指示细节的节点具有如下定义。其中的handler是指向处理函数的函数指针。

 

84      struct directive                                                                                         in cpplib.c

85      {

86        directive_handler handler;      /* Function to handle directive.  */

87        const uchar *name;         /* Name of directive.  */

88        unsigned short length;     /* Length of name.  */

89        unsigned char origin;      /* Origin of directive.  */

90        unsigned char flags;     /* Flags describing this directive.  */

91      };

 

DIRECTIVE_TABLE由宏D展开。D的定义在上面的179行,以第一行为例,展开后为:do_define, (const unchar*) “define”, sizeof “define” -1, KANDR, IN_I

 

143    #define DIRECTIVE_TABLE                         /                                         in cpplib.c

144    D(define,     T_DEFINE = 0,     KANDR,     IN_I)        /* 270554 */ /

145    D(include,    T_INCLUDE, KANDR,     INCL | EXPAND)  /* 52262 */ /

146    D(endif,       T_ENDIF,      KANDR,     COND)     /* 45855 */ /

147    D(ifdef,              T_IFDEF,       KANDR,     COND | IF_COND) /* 22000 */ /

148    D(if,            T_IF,             KANDR, COND | IF_COND | EXPAND) /* 18162 */ /

149    D(else,         T_ELSE,        KANDR,     COND)     /* 9863 */ /

150    D(ifndef,     T_IFNDEF,    KANDR,     COND | IF_COND) /* 9675 */ /

151    D(undef,      T_UNDEF,     KANDR,     IN_I)        /* 4837 */ /

152    D(line,         T_LINE,        KANDR,     EXPAND)        /* 2465 */ /

153    D(elif,         T_ELIF,         STDC89,    COND | EXPAND)  /* 610 */ /

154    D(error,              T_ERROR,     STDC89,    0)              /* 475 */ /

155    D(pragma,   T_PRAGMA,  STDC89,    IN_I)       /* 195 */ /

156    D(warning,  T_WARNING, EXTENSION, 0)          /* 22 */ /

157    D(include_next,   T_INCLUDE_NEXT,    EXTENSION, INCL | EXPAND)  /* 19 */ /

158    D(ident,       T_IDENT,      EXTENSION, IN_I)        /* 11 */ /

159    D(import,    T_IMPORT,    EXTENSION, INCL | EXPAND)  /* 0 ObjC */ /

160    D(assert,      T_ASSERT,    EXTENSION, 0)             /* 0 SVR4 */    /

161    D(unassert,   T_UNASSERT,      EXTENSION, 0)             /* 0 SVR4 */    /

162    D(sccs,        T_SCCS,        EXTENSION, 0)             /* 0 SVR4? */

 

上面代码中的第二列在cpplib.c的其他地方,以类似的方式展开为一个枚举类型。在第三及第四列,是已定义的宏。第三列的宏表示指示的来源,目前我们已有以下宏定义。

KANDR:指示来自传统(K&RC

STDC89:指示来自1989 C标准

EXTENSION:指示是扩展的(extension

在第四列的宏表示了指示的特性,我们已有如下宏定义。

COND:表示一个条件指示

IF_COND:表示一个引导的(opening)条件指示

INCL:表示分别将"..."<...> 作为q-charh-char序列来处理

IN_I:表示该指示即便使用了-fpreprocessed(只做预处理即退出),也要被处理(这些都是设置了回调钩子的指示)。

EXPAND:在需要宏展开的指示中设置

接着,_cpp_init_directives会注册预处理器所要处理的#pragma[4]给出了详细的解释。

#pragma GCC dependency

#pragma GCC dependency允许你检查当前文件和其他文件的相对日期。如果其他文件比当前文件更新,给出一个警告。如果当前文件从其他文件导出,需要重新生成,这#pragma是有用的。其他文件使用普通的包含文件查找路径来查找。可选的,尾随字符串可用于在警告消息中给出更多信息。

       #pragma GCC dependency “parse.y”

       #pragma GCC dependency “/usr/include/time.h” rerun fixincludes

#pragma GCC poison

有时,你希望从你的程序中完全移除一个标识符(identifier),以确保它不会悄悄地回来。为了达到这个目的,你可以使用这个pragma毒化这个标识符。#pragma GCC poison 后面跟着要毒化的标识符列表。如果其中的任一标识符出现在以后的代码中,它是个错误。例如,

       #pragma GCC poison printf sprint fprintf

       sprint (some_string, “hello”);

将产生一个错误。

如果被毒化的标识符出现在展开的宏里,并且这个宏定义在毒化之前,那么它不会导致错误。这使得你可以毒化一个标识符,而不需要担心使用它,定义在系统头文件中的宏。例如,

       #define strrchr rindex

       #pragma GCC poison rindex

       strrchr (some_string, ‘h’);

将不会产生错误。

#pragma GCC system_header

这个pragma不带参数。它使得当前文件余下的代码,按系统头文件来处理(当GCC处理一个系统头文件时,所有的警告,除了#warning产生的,都被压制。在系统头文件中定义的宏,在展开时也对一些警告免疫。当我们发现,因为系统头文件定义的宏,一个警告产生大量的误报(false positives)时,这个免疫被特别地授予)。

#pragma once不是标准支持的但它被广泛支持。它的效果与利用#ifdef宏来防止重复包含相同。http://en.wikipedia.org/wiki/Pragma_once上有更详细的介绍。

 

1048 void

1049 _cpp_init_internal_pragmas (cpp_reader *pfile)                                            in cpplib.c

1050 {

1051   /* Pragmas in the global namespace.  */

1052   cpp_register_pragma (pfile, 0, "once", do_pragma_once);

1053

1054   /* New GCC-specific pragmas should be put in the GCC namespace.  */

1055   cpp_register_pragma (pfile, "GCC", "poison", do_pragma_poison);

1056   cpp_register_pragma (pfile, "GCC", "system_header", do_pragma_system_header);

1057   cpp_register_pragma (pfile, "GCC", "dependency", do_pragma_dependency);

1058 }

 

GCC#pragma定义了pragma_entry

 

49    typedef void (*pragma_cb) (cpp_reader *);                                                   in cpplib.c

50    struct pragma_entry

51    {

52      struct pragma_entry *next;

53      const cpp_hashnode *pragma;   /* Name and length.  */

54      int is_nspace;

55      union {

56        pragma_cb handler;

57        struct pragma_entry *space;

58      } u;

59    };

 

对于后跟参数的#pragma,使用在56行的handler,它是一个函数指针,提供这个#pragma的功能。而对于构成一个空间的#pragma,使用57行的space把出现于它的空间中的其他#pragma链起来,同时设置54行的is_nspace

编译器使用结构pragma_entry来记录#pragma,所有的#pragma都被保存在parse_inpragmas域,这样可以尽早开始处理#pragma

 

1005 void

1006 cpp_register_pragma (cpp_reader *pfile, const char *space,                          in cpplib.c

1007                   const char *name, pragma_cb handler)

1008 {

1009   struct pragma_entry **chain = &pfile->pragmas;

1010   struct pragma_entry *entry;

1011   const cpp_hashnode *node;

1012

1013   if (!handler)

1014     abort ();

1015

1016   if (space)

1017   {

1018     node = cpp_lookup (pfile, U space, strlen (space));

1019     entry = lookup_pragma_entry (*chain, node);

1020     if (!entry)

1021       entry = insert_pragma_entry (pfile, chain, node, NULL);

1022     else if (!entry->is_nspace)

1023       goto clash;

1024     chain = &entry->u.space;

1025   }

1026

1027   /* Check for duplicates.  */

1028   node = cpp_lookup (pfile, U name, strlen (name));

1029   entry = lookup_pragma_entry (*chain, node);

1030   if (entry)

1031   {

1032     if (entry->is_nspace)

1033 clash:

1034       cpp_error (pfile, CPP_DL_ICE,

1035                "registering /"%s/" as both a pragma and a pragma namespace",

1036                NODE_NAME (node));

1037     else if (space)

1038       cpp_error (pfile, CPP_DL_ICE, "#pragma %s %s is already registered",

1039                space, name);

1040     else

1041       cpp_error (pfile, CPP_DL_ICE, "#pragma %s is already registered", name);

1042   }

1043   else

1044     insert_pragma_entry (pfile, chain, node, handler);

1045 }

 

因为#pragma的种类有限,以nul结尾的简单链表已经足够。注意到对于类似#pragma GCC dependency等指示,GCC构成了一个空间,dependencyposionsystem_header都在这个空间中。因此对应于GCC的节点是一个分枝,其中的节点对应所包含的内容。

 

965  static struct pragma_entry *

966  lookup_pragma_entry (struct pragma_entry *chain, const cpp_hashnode *pragma) in cpplib.c

967  {

968    while (chain && chain->pragma != pragma)

969      chain = chain->next;

970 

971    return chain;

972  }

 

For insert_pragma_entry, notice that argument pragma is of type cpp_hashnode which is the identifier for the directive in ident_hash table.

 

977  static struct pragma_entry *

978  insert_pragma_entry (cpp_reader *pfile, struct pragma_entry **chain,             in cpplib.c

979                     const cpp_hashnode *pragma, pragma_cb handler)

980  {

981    struct pragma_entry *new;

982 

983    new = (struct pragma_entry *)

984      _cpp_aligned_alloc (pfile, sizeof (struct pragma_entry));

985    new->pragma = pragma;

986    if (handler)

987    {

988      new->is_nspace = 0;

989      new->u.handler = handler;

990    }

991    else

992    {

993      new->is_nspace = 1;

994      new->u.space = NULL;

995    }

996 

997    new->next = *chain;

998    *chain = new;

999    return new;

1000 }

 

回到_cpp_init_hashtablecpp_readerspec_nodes记录了语言中特殊的标识符。这里是defined”,“true”,“false__VAR_ARGS。它们必须是全局唯一的,因此我们在以下spec_nodes的定义中使用指针引用它们。

 

247  struct spec_nodes                                                                                       in cpphash.h

248  {

249    cpp_hashnode *n_defined;        /* defined operator */

250    cpp_hashnode *n_true;                    /* C++ keyword true */

251    cpp_hashnode *n_false;                   /* C++ keyword false */

252    cpp_hashnode *n__VA_ARGS__;    /* C99 vararg macros */

253  };

 

587  #define DSC(str) (const uchar *)str, sizeof str – 1                                          in cpphash.h

 

连同上面DSC的定义,在函数最后“defined”“true”“false”“__VAR_ARGS”的唯一节点被创建。

你可能感兴趣的:(struct,gcc,table,token,extension,whitespace)