Processus de compilation GCC : du code source au programme exécutable——Analyse du principe de compilation

À suivre》》》
Veuillez patienter~~~

Lorsque vous étiez à l'école, avez-vous écouté un cours intitulé "Principes de compilation", et l'enseignant en classe vous a-t-il demandé "Savez-vous comment le code que vous avez écrit se transforme en un fichier exécutable à exécuter ???" A beaucoup de "compilation", "prétraitement", "expansion de définition de macro", "arbre de syntaxe abstraite", "assemblage", "désassemblage", etc. Beaucoup de contenu capiteux dissuade la motivation de l'apprendre .
Revenons sur le processus de compilation d'un programme. La compilation, nous disons souvent généralement, fait référence au processus de génération de fichiers exécutables à partir d'un ou plusieurs fichiers de code source via un compilateur. Le processus de compilation du programme peut être divisé en trois étapes :

  • (1) Étape de compilation
  • (2) Étape de liaison
  • (3) Phase de relocalisation
    processus de compilation

Adresse de téléchargement de carte haute définition PNG: https://download.csdn.net/download/qq_36393978/85329800 Adresse de téléchargement de fichier
ultra haute définition : https://download.csdn.net/download/qq_36393978/85329905PDF

1. Phase de compilation

La chaîne d'outils de compilation couramment utilisée gccdoit appeler le "préprocesseur", le "compilateur" et l'"assembleur" dans la chaîne d'outils pendant la phase de compilation. Le processus de traitement comprend le flux suivant.
[注]:GIMPLE代码也可称为中间代码,寄存器传输语言(Register Transfer Language,RTL)
insérez la description de l'image ici

1.1. Prétraitement du programme

C'est-à-dire le traitement de chaque commande de prétraitement dans le fichier source (inclusion de fichiers d'en-tête, développement de macros personnalisées et de macros intégrées, compilation conditionnelle, instructions de contrôle pour la compilation du programme), suppression de commentaires, ajout de numéros de ligne et nom de fichier identifiants, quelles macros intégrées sont spécifiquement incluses Et quelles commandes de prétraitement doivent être traitées, nous pouvons le voir à partir du code source ;
gcc-12.1.0les macros intégrées fournies sont les suivantes :

//# gcc-12.1.0/libcpp/init.cc	##------## struct builtin_macro builtin_array[]
struct builtin_macro
{
    
    
  const uchar *const name;
  const unsigned short len;
  const unsigned short value;
  const bool always_warn_if_redefined;
};

#define B(n, t, f)    {
      
       DSC(n), t, f }
static const struct builtin_macro builtin_array[] =
{
    
    
  B("__TIMESTAMP__",	 BT_TIMESTAMP,     false),
  B("__TIME__",		 BT_TIME,          false),
  B("__DATE__",		 BT_DATE,          false),
  B("__FILE__",		 BT_FILE,          false),
  B("__FILE_NAME__",	 BT_FILE_NAME,     false),
  B("__BASE_FILE__",	 BT_BASE_FILE,     false),
  B("__LINE__",		 BT_SPECLINE,      true),
  B("__INCLUDE_LEVEL__", BT_INCLUDE_LEVEL, true),
  B("__COUNTER__",	 BT_COUNTER,       true),
  /* Make sure to update the list of built-in
     function-like macros in traditional.cc:
     fun_like_macro() when adding more following */
  B("__has_attribute",	 BT_HAS_ATTRIBUTE, true),
  B("__has_c_attribute", BT_HAS_STD_ATTRIBUTE, true),
  B("__has_cpp_attribute", BT_HAS_ATTRIBUTE, true),
  B("__has_builtin",	 BT_HAS_BUILTIN,   true),
  B("__has_include",	 BT_HAS_INCLUDE,   true),
  B("__has_include_next",BT_HAS_INCLUDE_NEXT,   true),
  /* Keep builtins not used for -traditional-cpp at the end, and
     update init_builtins() if any more are added.  */
  B("_Pragma",		 BT_PRAGMA,        true),
  B("__STDC__",		 BT_STDC,          true),
};
#undef B

gcc-12.1.0Les commandes de prétraitement qui doivent être traitées sont les suivantes, et les fonctions de traitement correspondantes sont placées dans btablece tableau de structure :

//# gcc12.1.0/libcpp/directives.cc

typedef struct directive directive;
struct directive
{
    
    
  directive_handler handler;	/* Function to handle directive.  */
  const uchar *name;		/* Name of directive.  */
  unsigned short length;	/* Length of name.  */
  unsigned char origin;		/* Origin of directive.  */
  unsigned char flags;	        /* Flags describing this directive.  */
};

/* This is the table of directive handlers.  All extensions other than
   #warning, #include_next, and #import are deprecated.  The name is
   where the extension appears to have come from.  */

#define DIRECTIVE_TABLE							\
  D(define,	T_DEFINE = 0,	KANDR,     IN_I)			\
  D(include,	T_INCLUDE,	KANDR,     INCL | EXPAND)		\
  D(endif,	T_ENDIF,	KANDR,     COND)			\
  D(ifdef,	T_IFDEF,	KANDR,     COND | IF_COND)		\
  D(if,		T_IF,		KANDR, 	   COND | IF_COND | EXPAND) 	\
  D(else,	T_ELSE,		KANDR,     COND)	   		\
  D(ifndef,	T_IFNDEF,	KANDR,     COND | IF_COND)		\
  D(undef,	T_UNDEF,	KANDR,     IN_I)			\
  D(line,	T_LINE,		KANDR,     EXPAND)			\
  D(elif,	T_ELIF,		STDC89,    COND | EXPAND)		\
  D(elifdef,	T_ELIFDEF,	STDC2X,    COND | ELIFDEF)		\
  D(elifndef,	T_ELIFNDEF,	STDC2X,    COND | ELIFDEF)		\
  D(error,	T_ERROR,	STDC89,    0)				\
  D(pragma,	T_PRAGMA,	STDC89,    IN_I)			\
  D(warning,	T_WARNING,	EXTENSION, 0)				\
  D(include_next, T_INCLUDE_NEXT, EXTENSION, INCL | EXPAND)		\
  D(ident,	T_IDENT,	EXTENSION, IN_I)			\
  D(import,	T_IMPORT,	EXTENSION, INCL | EXPAND)  /* ObjC */	\
  D(assert,	T_ASSERT,	EXTENSION, DEPRECATED)	   /* SVR4 */	\
  D(unassert,	T_UNASSERT,	EXTENSION, DEPRECATED)	   /* SVR4 */	\
  D(sccs,	T_SCCS,		EXTENSION, IN_I)   	   /*  SVR4? */

#define D(name, t, origin, flags) \
{
      
       do_##name, (const uchar *) #name, \
  sizeof #name - 1, origin, flags },
static const directive dtable[] =
{
    
    
DIRECTIVE_TABLE
};
#undef D

1.1.1 Petites connaissances (macro polymorphisme)

Les petits amis talentueux ici peuvent définir les macros ici telles que B, Dcelles-ci, pourquoi #defineaprès avoir été définies, elles sont alors #undefindéfinies ? ?
Il s'agit d'obtenir le polymorphisme des macros, c'est-à-dire que lorsqu'elles sont référencées à différents endroits et à différents moments, elles peuvent avoir des propriétés différentes et produire des résultats différents. Si vous ne comprenez toujours pas, vous pouvez regarder l'exemple simple ci-dessous et ce sera clair en un coup d'œil.

#include <stdio.h>

#define NUM_COUNT    \
    NUM(1)  \
    NUM(2)  \
    NUM(3)  \
    NUM(4)


void print_num(int num)
{
    
    
    printf("%d\n", num);

    return ;
}

void num_increase(int num)
{
    
    
    printf("%d\n", num + 1);

    return ;
}


int main(int argc, char **argv)
{
    
    

#define NUM(num)    print_num(num);
    NUM_COUNT
#undef NUM

        printf("==============\n");

#define NUM(num)    num_increase(num);
    NUM_COUNT
#undef NUM

    return 0;
}

Ses résultats d'exécution sont les suivants :

imaginemiracle@ubuntu:define$ ./a.out 
1
2
3
4
==============
2
3
4
5

1.2. Analyse lexicale

1.2.1 Unité lexicale (token)

L'analyseur lexical lit le code source, identifie chaque symbole lexical dans le code source et construit une unité lexicale tokenpour enregistrer le symbole. Ce processus est principalement _cpp_lex_token()complété par la fonction (la fonction est située gcc12.1.0/libcpp/lex.ccdans ), puis chacun tokensera vérifié et sortie tokenune séquence de . Dans le code source de l'analyseur lexical est tokenen fait représenté par une structure. À l' gcc-12.1.0heure actuelle définition dec l'unité lexicale de la langue est que la structure est utilisée pour stocker une unité lexicale de base. Il existe une définition de la structure , qui consiste à ajouter une représentation , qui est elle-même une macro définition Il est principalement utilisé dans le mécanisme ultérieur de gestion des ordures et ne sera pas abordé ici ;tokenstruct GTY(()) c_tokenGTY(())GTY

//# gcc-12.1.0/gcc/c/c-parser.h	##------## struct c_token
/* A single C token after string literal concatenation and conversion
   of preprocessing tokens to tokens.  */
struct GTY (()) c_token {
    
    
  /* The kind of token.  */
  ENUM_BITFIELD (cpp_ttype) type : 8;
  /* If this token is a CPP_NAME, this value indicates whether also
     declared as some kind of type.  Otherwise, it is C_ID_NONE.  */
  ENUM_BITFIELD (c_id_kind) id_kind : 8;
  /* If this token is a keyword, this value indicates which keyword.
     Otherwise, this value is RID_MAX.  */
  ENUM_BITFIELD (rid) keyword : 8;
  /* If this token is a CPP_PRAGMA, this indicates the pragma that
     was seen.  Otherwise it is PRAGMA_NONE.  */
  ENUM_BITFIELD (pragma_kind) pragma_kind : 8;
  /* The location at which this token was found.  */
  location_t location;
  /* The value associated with this token, if any.  */
  tree value;
  /* Token flags.  */
  unsigned char flags;

  source_range get_range () const
  {
    
    
    return get_range_from_loc (line_table, location);
  }

  location_t get_finish () const
  {
    
    
    return get_range ().m_finish;
  }
};

c++Et tokenla définition de l'unité lexicale du langage est struct GTY(()) cpp_token. Cet article utilise principalement cle langage comme exemple pour analyser l'ensemble du processus de compilation.

//# gcc-12.1.0/libcpp/include/cpplib.h	##------## struct cpp_token

/* A preprocessing token.  This has been carefully packed and should
   occupy 16 bytes on 32-bit hosts and 24 bytes on 64-bit hosts.  */
struct GTY(()) cpp_token {
    
    

  /* Location of first char of token, together with range of full token.  */
  location_t src_loc;

  ENUM_BITFIELD(cpp_ttype) type : CHAR_BIT;  /* token type */
  unsigned short flags;		/* flags - see above */

  union cpp_token_u
  {
    
    
    /* An identifier.  */
    struct cpp_identifier GTY ((tag ("CPP_TOKEN_FLD_NODE"))) node;
	 
    /* Inherit padding from this token.  */
    cpp_token * GTY ((tag ("CPP_TOKEN_FLD_SOURCE"))) source;

    /* A string, or number.  */
    struct cpp_string GTY ((tag ("CPP_TOKEN_FLD_STR"))) str;

    /* Argument no. (and original spelling) for a CPP_MACRO_ARG.  */
    struct cpp_macro_arg GTY ((tag ("CPP_TOKEN_FLD_ARG_NO"))) macro_arg;

    /* Original token no. for a CPP_PASTE (from a sequence of
       consecutive paste tokens in a macro expansion).  */
    unsigned int GTY ((tag ("CPP_TOKEN_FLD_TOKEN_NO"))) token_no;

    /* Caller-supplied identifier for a CPP_PRAGMA.  */
    unsigned int GTY ((tag ("CPP_TOKEN_FLD_PRAGMA"))) pragma;
  } GTY ((desc ("cpp_token_val_index (&%1)"))) val;
};

struct c_tokenLa structure comprend principalement le type de symbole ( type), le type d'identificateur ( id_kind), le mot-clé ( keyword), PRAGMAle type ( pragma_kind), la valeur du symbole ( value) et ( ) utilisés pour décrire la position du symbole dans le fichier source location.

1.2.2. Type de jeton (c_token::type)

gccUne série de symboles d'opération sont définis dans , tels que les opérateurs réguliers =, , +, -, >et , <etc . , les séparateurs :, ,, ;, { , }etc. Les définitions et les symboles de ces symboles d'opération sont spécifiés valuepar OPdes macros , et certains autres types de symboles sont Spécifiés par TKla macro , il comprend principalement EOF, NAME, CHAR, STRING, PRAGMAetc. Ces symboles sont tous définis dans les variables d'énumération fichier ./libcpp/iclude/cpplib.h .enum cpp_ttype

1.2.3. Type d'identifiant (c_token::id_kind)

D'après le commentaire à la définition de struct c_tokentla structure , nous pouvons comprendre que lorsque typela valeur de n'est pas CPP_NAME, la valeur de id_kindest C_IN_NONE, indiquant que le type n'est pas un identifiant ; au contraire, lorsque typela valeur de CPP_NAMEest , id_kindil sera être valide (c'est-à-dire qu'une valeur de signification lui sera attribuée), ce qui signifie que le type est un type d'identificateur. CPP_NAMELes types de comprennent les identificateurs ordinaires ( C_ID_ID), les noms de type ( C_ID_TYPENAME), les noms d'objet en Objective-C ( C_ID_CLASSNAME) et les types d'espace d'adressage ( C_ID_ADDRSPACE).

//# gcc-12.1.0/gcc/c/c-parser.h	##------## enum c_id_kind
/* More information about the type of a CPP_NAME token.  */
enum c_id_kind {
    
    
  /* An ordinary identifier.  */
  /* 普通标识符 */
  C_ID_ID,
  /* An identifier declared as a typedef name.  */
  /* 描述类型的标识符,即用 typedef 定义的类型 */
  C_ID_TYPENAME,
  /* An identifier declared as an Objective-C class name.  */
  /* Objective-C 中的对象名称标识符 */
  C_ID_CLASSNAME,
  /* An address space identifier.  */
  /* 地址空间标识符 */
  C_ID_ADDRSPACE,
  /* Not an identifier.  */
  /* 不是标识符 */
  C_ID_NONE
};

1.2.4. Identification du mot clé (c_token::keyword)

Lorsqu'il est nécessaire d'utiliser un identifiant pour représenter un mot clé ( ) dans le code Key Workds, les valeurs spécifiques de ces mots clés doivent être déterminées, de manière à déterminer la signification spécifique représentée par le mot clé. cTous les mots clés et valeurs correspondantes utilisés par le langage sont définis dans les variables d'énumération du fichier ./gcc/c-family/c-common.h .enum rid

//# gcc-12.1.0/gcc/c-family/c-common.h	##------## enum rid

/* Reserved identifiers.  This is the union of all the keywords for C,
   C++, and Objective-C.  All the type modifiers have to be in one
   block at the beginning, because they are used as mask bits.  There
   are 28 type modifiers; if we add many more we will have to redesign
   the mask mechanism.  */

enum rid
{
    
    
  /* Modifiers: */
  /* C, in empirical order of frequency.  */
  RID_STATIC = 0,
  RID_UNSIGNED, RID_LONG,    RID_CONST, RID_EXTERN,
  RID_REGISTER, RID_TYPEDEF, RID_SHORT, RID_INLINE,
  RID_VOLATILE, RID_SIGNED,  RID_AUTO,  RID_RESTRICT,
  RID_NORETURN, RID_ATOMIC,

  /* C extensions */
  RID_COMPLEX, RID_THREAD, RID_SAT,

  /* C++ */
  RID_FRIEND, RID_VIRTUAL, RID_EXPLICIT, RID_EXPORT, RID_MUTABLE,

  /* ObjC ("PQ" reserved words - they do not appear after a '@' and
     are keywords only in specific contexts)  */
  RID_IN, RID_OUT, RID_INOUT, RID_BYCOPY, RID_BYREF, RID_ONEWAY,

  /* ObjC ("PATTR" reserved words - they do not appear after a '@' 
     and are keywords only as property attributes)  */
  RID_GETTER, RID_SETTER,
  RID_READONLY, RID_READWRITE,
  RID_ASSIGN, RID_RETAIN, RID_COPY,
  RID_PROPATOMIC, RID_NONATOMIC,

  /* ObjC nullability support keywords that also can appear in the
     property attribute context.  These values should remain contiguous
     with the other property attributes.  */
  RID_NULL_UNSPECIFIED, RID_NULLABLE, RID_NONNULL, RID_NULL_RESETTABLE,

  /* C (reserved and imaginary types not implemented, so any use is a
     syntax error) */
  RID_IMAGINARY,

  /* C */
  RID_INT,     RID_CHAR,   RID_FLOAT,    RID_DOUBLE, RID_VOID,
  RID_ENUM,    RID_STRUCT, RID_UNION,    RID_IF,     RID_ELSE,
  RID_WHILE,   RID_DO,     RID_FOR,      RID_SWITCH, RID_CASE,
  RID_DEFAULT, RID_BREAK,  RID_CONTINUE, RID_RETURN, RID_GOTO,
  RID_SIZEOF,

  /* C extensions */
  RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
  RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
  RID_BUILTIN_SHUFFLEVECTOR,   RID_BUILTIN_CONVERTVECTOR,   RID_BUILTIN_TGMATH,
  RID_BUILTIN_HAS_ATTRIBUTE,   RID_BUILTIN_ASSOC_BARRIER,
  RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,

  /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
  RID_FLOAT16,
  RID_FLOATN_NX_FIRST = RID_FLOAT16,
  RID_FLOAT32,
  RID_FLOAT64,
  RID_FLOAT128,
  RID_FLOAT32X,
  RID_FLOAT64X,
  RID_FLOAT128X,
#define CASE_RID_FLOATN_NX						\
  case RID_FLOAT16: case RID_FLOAT32: case RID_FLOAT64: case RID_FLOAT128: \
  case RID_FLOAT32X: case RID_FLOAT64X: case RID_FLOAT128X

  RID_FRACT, RID_ACCUM, RID_AUTO_TYPE, RID_BUILTIN_CALL_WITH_STATIC_CHAIN,

  /* "__GIMPLE", for the GIMPLE-parsing extension to the C frontend. */
  RID_GIMPLE,

  /* "__PHI", for parsing PHI function in GIMPLE FE.  */
  RID_PHI,

  /* "__RTL", for the RTL-parsing extension to the C frontend.  */
  RID_RTL,

  /* C11 */
  RID_ALIGNAS, RID_GENERIC,

  /* This means to warn that this is a C++ keyword, and then treat it
     as a normal identifier.  */
  RID_CXX_COMPAT_WARN,

  /* GNU transactional memory extension */
  RID_TRANSACTION_ATOMIC, RID_TRANSACTION_RELAXED, RID_TRANSACTION_CANCEL,

  /* Too many ways of getting the name of a function as a string */
  RID_FUNCTION_NAME, RID_PRETTY_FUNCTION_NAME, RID_C99_FUNCTION_NAME,

  /* C++ (some of these are keywords in Objective-C as well, but only
     if they appear after a '@') */
  RID_BOOL,     RID_WCHAR,    RID_CLASS,
  RID_PUBLIC,   RID_PRIVATE,  RID_PROTECTED,
  RID_TEMPLATE, RID_NULL,     RID_CATCH,
  RID_DELETE,   RID_FALSE,    RID_NAMESPACE,
  RID_NEW,      RID_OFFSETOF, RID_OPERATOR,
  RID_THIS,     RID_THROW,    RID_TRUE,
  RID_TRY,      RID_TYPENAME, RID_TYPEID,
  RID_USING,    RID_CHAR16,   RID_CHAR32,

  /* casts */
  RID_CONSTCAST, RID_DYNCAST, RID_REINTCAST, RID_STATCAST,

  /* C++ extensions */
  RID_ADDRESSOF,               RID_BASES,
  RID_BUILTIN_LAUNDER,         RID_DIRECT_BASES,
  RID_HAS_NOTHROW_ASSIGN,      RID_HAS_NOTHROW_CONSTRUCTOR,
  RID_HAS_NOTHROW_COPY,        RID_HAS_TRIVIAL_ASSIGN,
  RID_HAS_TRIVIAL_CONSTRUCTOR, RID_HAS_TRIVIAL_COPY,
  RID_HAS_TRIVIAL_DESTRUCTOR,  RID_HAS_UNIQUE_OBJ_REPRESENTATIONS,
  RID_HAS_VIRTUAL_DESTRUCTOR,  RID_BUILTIN_BIT_CAST,
  RID_IS_ABSTRACT,             RID_IS_AGGREGATE,
  RID_IS_BASE_OF,              RID_IS_CLASS,
  RID_IS_EMPTY,                RID_IS_ENUM,
  RID_IS_FINAL,                RID_IS_LAYOUT_COMPATIBLE,
  RID_IS_LITERAL_TYPE,
  RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
  RID_IS_POD,                  RID_IS_POLYMORPHIC,
  RID_IS_SAME_AS,
  RID_IS_STD_LAYOUT,           RID_IS_TRIVIAL,
  RID_IS_TRIVIALLY_ASSIGNABLE, RID_IS_TRIVIALLY_CONSTRUCTIBLE,
  RID_IS_TRIVIALLY_COPYABLE,
  RID_IS_UNION,                RID_UNDERLYING_TYPE,
  RID_IS_ASSIGNABLE,           RID_IS_CONSTRUCTIBLE,
  RID_IS_NOTHROW_ASSIGNABLE,   RID_IS_NOTHROW_CONSTRUCTIBLE,

  /* C++11 */
  RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,

  /* C++20 */
  RID_CONSTINIT, RID_CONSTEVAL,

  /* char8_t */
  RID_CHAR8,

  /* C++ concepts */
  RID_CONCEPT, RID_REQUIRES,

  /* C++ modules.  */
  RID__MODULE, RID__IMPORT, RID__EXPORT, /* Internal tokens.  */

  /* C++ coroutines */
  RID_CO_AWAIT, RID_CO_YIELD, RID_CO_RETURN,

  /* C++ transactional memory.  */
  RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED,

  /* Objective-C ("AT" reserved words - they are only keywords when
     they follow '@')  */
  RID_AT_ENCODE,   RID_AT_END,
  RID_AT_CLASS,    RID_AT_ALIAS,     RID_AT_DEFS,
  RID_AT_PRIVATE,  RID_AT_PROTECTED, RID_AT_PUBLIC,  RID_AT_PACKAGE,
  RID_AT_PROTOCOL, RID_AT_SELECTOR,
  RID_AT_THROW,	   RID_AT_TRY,       RID_AT_CATCH,
  RID_AT_FINALLY,  RID_AT_SYNCHRONIZED, 
  RID_AT_OPTIONAL, RID_AT_REQUIRED, RID_AT_PROPERTY,
  RID_AT_SYNTHESIZE, RID_AT_DYNAMIC,
  RID_AT_INTERFACE,
  RID_AT_IMPLEMENTATION,

  /* Named address support, mapping the keyword to a particular named address
     number.  Named address space 0 is reserved for the generic address.  If
     there are more than 254 named addresses, the addr_space_t type will need
     to be grown from an unsigned char to unsigned short.  */
  RID_ADDR_SPACE_0,		/* generic address */
  RID_ADDR_SPACE_1,
  RID_ADDR_SPACE_2,
  RID_ADDR_SPACE_3,
  RID_ADDR_SPACE_4,
  RID_ADDR_SPACE_5,
  RID_ADDR_SPACE_6,
  RID_ADDR_SPACE_7,
  RID_ADDR_SPACE_8,
  RID_ADDR_SPACE_9,
  RID_ADDR_SPACE_10,
  RID_ADDR_SPACE_11,
  RID_ADDR_SPACE_12,
  RID_ADDR_SPACE_13,
  RID_ADDR_SPACE_14,
  RID_ADDR_SPACE_15,

  RID_FIRST_ADDR_SPACE = RID_ADDR_SPACE_0,
  RID_LAST_ADDR_SPACE = RID_ADDR_SPACE_15,

  /* __intN keywords.  The _N_M here doesn't correspond to the intN
     in the keyword; use the bitsize in int_n_t_data_t[M] for that.
     For example, if int_n_t_data_t[0].bitsize is 13, then RID_INT_N_0
     is for __int13.  */

  /* Note that the range to use is RID_FIRST_INT_N through
     RID_FIRST_INT_N + NUM_INT_N_ENTS - 1 and c-parser.cc has a list of
     all RID_INT_N_* in a case statement.  */

  RID_INT_N_0,
  RID_INT_N_1,
  RID_INT_N_2,
  RID_INT_N_3,

  RID_FIRST_INT_N = RID_INT_N_0,
  RID_LAST_INT_N = RID_INT_N_3,

  RID_MAX,

  RID_FIRST_MODIFIER = RID_STATIC,
  RID_LAST_MODIFIER = RID_ONEWAY,

  RID_FIRST_CXX11 = RID_CONSTEXPR,
  RID_LAST_CXX11 = RID_STATIC_ASSERT,
  RID_FIRST_CXX20 = RID_CONSTINIT,
  RID_LAST_CXX20 = RID_CONSTINIT,
  RID_FIRST_AT = RID_AT_ENCODE,
  RID_LAST_AT = RID_AT_IMPLEMENTATION,
  RID_FIRST_PQ = RID_IN,
  RID_LAST_PQ = RID_ONEWAY,
  RID_FIRST_PATTR = RID_GETTER,
  RID_LAST_PATTR = RID_NULL_RESETTABLE
};

Comme on peut le voir dans le code ci-dessus, une énumération est écrite GCCpour tous les mots-clés pris en charge pour représenter la valeur spécifique du mot-clé correspondant, et la déclaration du mot-clé est placée dans ./gcc/c-family/c -Dans le tableau c_common_reswordsde structure fichier common.cc .

//# gcc-12.1.0/gcc/c-family/c-common.cc	##------## const struct c_common_resword c_common_reswords[]

/* Reserved words.  The third field is a mask: keywords are disabled
   if they match the mask.

   Masks for languages:
   C --std=c89: D_C99 | D_CXXONLY | D_OBJC | D_CXX_OBJC
   C --std=c99: D_CXXONLY | D_OBJC
   ObjC is like C except that D_OBJC and D_CXX_OBJC are not set
   C++ --std=c++98: D_CONLY | D_CXX11 | D_CXX20 | D_OBJC
   C++ --std=c++11: D_CONLY | D_CXX20 | D_OBJC
   C++ --std=c++20: D_CONLY | D_OBJC
   ObjC++ is like C++ except that D_OBJC is not set

   If -fno-asm is used, D_ASM is added to the mask.  If
   -fno-gnu-keywords is used, D_EXT is added.  If -fno-asm and C in
   C89 mode, D_EXT89 is added for both -fno-asm and -fno-gnu-keywords.
   In C with -Wc++-compat, we warn if D_CXXWARN is set.

   Note the complication of the D_CXX_OBJC keywords.  These are
   reserved words such as 'class'.  In C++, 'class' is a reserved
   word.  In Objective-C++ it is too.  In Objective-C, it is a
   reserved word too, but only if it follows an '@' sign.
*/
const struct c_common_resword c_common_reswords[] =
{
    
    
  {
    
     "_Alignas",		RID_ALIGNAS,   D_CONLY },
  {
    
     "_Alignof",		RID_ALIGNOF,   D_CONLY },
  {
    
     "_Atomic",		RID_ATOMIC,    D_CONLY },
  {
    
     "_Bool",		RID_BOOL,      D_CONLY },
  {
    
     "_Complex",		RID_COMPLEX,	0 },
  {
    
     "_Imaginary",	RID_IMAGINARY, D_CONLY },
  {
    
     "_Float16",         RID_FLOAT16,   D_CONLY },
  {
    
     "_Float32",         RID_FLOAT32,   D_CONLY },
  {
    
     "_Float64",         RID_FLOAT64,   D_CONLY },
  {
    
     "_Float128",        RID_FLOAT128,  D_CONLY },
  {
    
     "_Float32x",        RID_FLOAT32X,  D_CONLY },
  {
    
     "_Float64x",        RID_FLOAT64X,  D_CONLY },
  {
    
     "_Float128x",       RID_FLOAT128X, D_CONLY },
  {
    
     "_Decimal32",       RID_DFLOAT32,  D_CONLY },
  {
    
     "_Decimal64",       RID_DFLOAT64,  D_CONLY },
  {
    
     "_Decimal128",      RID_DFLOAT128, D_CONLY },
  {
    
     "_Fract",           RID_FRACT,     D_CONLY | D_EXT },
  {
    
     "_Accum",           RID_ACCUM,     D_CONLY | D_EXT },
  {
    
     "_Sat",             RID_SAT,       D_CONLY | D_EXT },
  {
    
     "_Static_assert",   RID_STATIC_ASSERT, D_CONLY },
  {
    
     "_Noreturn",        RID_NORETURN,  D_CONLY },
  {
    
     "_Generic",         RID_GENERIC,   D_CONLY },
  {
    
     "_Thread_local",    RID_THREAD,    D_CONLY },
  {
    
     "__FUNCTION__",	RID_FUNCTION_NAME, 0 },
  {
    
     "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 },
  {
    
     "__alignof",	RID_ALIGNOF,	0 },
  {
    
     "__alignof__",	RID_ALIGNOF,	0 },
  {
    
     "__asm",		RID_ASM,	0 },
  {
    
     "__asm__",		RID_ASM,	0 },
  {
    
     "__attribute",	RID_ATTRIBUTE,	0 },
  {
    
     "__attribute__",	RID_ATTRIBUTE,	0 },
  {
    
     "__auto_type",	RID_AUTO_TYPE,	D_CONLY },
  {
    
     "__bases",          RID_BASES, D_CXXONLY },
  {
    
     "__builtin_addressof", RID_ADDRESSOF, D_CXXONLY },
  {
    
     "__builtin_bit_cast", RID_BUILTIN_BIT_CAST, D_CXXONLY },
  {
    
     "__builtin_call_with_static_chain",
    RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
  {
    
     "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
  {
    
     "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
  {
    
     "__builtin_convertvector", RID_BUILTIN_CONVERTVECTOR, 0 },
  {
    
     "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
  {
    
     "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
  {
    
     "__builtin_assoc_barrier", RID_BUILTIN_ASSOC_BARRIER, 0 },
  {
    
     "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
  {
    
     "__builtin_shufflevector", RID_BUILTIN_SHUFFLEVECTOR, 0 },
  {
    
     "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
  {
    
     "__builtin_offsetof", RID_OFFSETOF, 0 },
  {
    
     "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
  {
    
     "__builtin_va_arg",	RID_VA_ARG,	0 },
  {
    
     "__complex",	RID_COMPLEX,	0 },
  {
    
     "__complex__",	RID_COMPLEX,	0 },
  {
    
     "__const",		RID_CONST,	0 },
  {
    
     "__const__",	RID_CONST,	0 },
  {
    
     "__constinit",	RID_CONSTINIT,	D_CXXONLY },
  {
    
     "__decltype",       RID_DECLTYPE,   D_CXXONLY },
  {
    
     "__direct_bases",   RID_DIRECT_BASES, D_CXXONLY },
  {
    
     "__extension__",	RID_EXTENSION,	0 },
  {
    
     "__func__",		RID_C99_FUNCTION_NAME, 0 },
  {
    
     "__has_nothrow_assign", RID_HAS_NOTHROW_ASSIGN, D_CXXONLY },
  {
    
     "__has_nothrow_constructor", RID_HAS_NOTHROW_CONSTRUCTOR, D_CXXONLY },
  {
    
     "__has_nothrow_copy", RID_HAS_NOTHROW_COPY, D_CXXONLY },
  {
    
     "__has_trivial_assign", RID_HAS_TRIVIAL_ASSIGN, D_CXXONLY },
  {
    
     "__has_trivial_constructor", RID_HAS_TRIVIAL_CONSTRUCTOR, D_CXXONLY },
  {
    
     "__has_trivial_copy", RID_HAS_TRIVIAL_COPY, D_CXXONLY },
  {
    
     "__has_trivial_destructor", RID_HAS_TRIVIAL_DESTRUCTOR, D_CXXONLY },
  {
    
     "__has_unique_object_representations", RID_HAS_UNIQUE_OBJ_REPRESENTATIONS,
					D_CXXONLY },
  {
    
     "__has_virtual_destructor", RID_HAS_VIRTUAL_DESTRUCTOR, D_CXXONLY },
  {
    
     "__imag",		RID_IMAGPART,	0 },
  {
    
     "__imag__",		RID_IMAGPART,	0 },
  {
    
     "__inline",		RID_INLINE,	0 },
  {
    
     "__inline__",	RID_INLINE,	0 },
  {
    
     "__is_abstract",	RID_IS_ABSTRACT, D_CXXONLY },
  {
    
     "__is_aggregate",	RID_IS_AGGREGATE, D_CXXONLY },
  {
    
     "__is_base_of",	RID_IS_BASE_OF, D_CXXONLY },
  {
    
     "__is_class",	RID_IS_CLASS,	D_CXXONLY },
  {
    
     "__is_empty",	RID_IS_EMPTY,	D_CXXONLY },
  {
    
     "__is_enum",	RID_IS_ENUM,	D_CXXONLY },
  {
    
     "__is_final",	RID_IS_FINAL,	D_CXXONLY },
  {
    
     "__is_layout_compatible", RID_IS_LAYOUT_COMPATIBLE, D_CXXONLY },
  {
    
     "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
  {
    
     "__is_pointer_interconvertible_base_of",
			RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, D_CXXONLY },
  {
    
     "__is_pod",		RID_IS_POD,	D_CXXONLY },
  {
    
     "__is_polymorphic",	RID_IS_POLYMORPHIC, D_CXXONLY },
  {
    
     "__is_same",     RID_IS_SAME_AS, D_CXXONLY },
  {
    
     "__is_same_as",     RID_IS_SAME_AS, D_CXXONLY },
  {
    
     "__is_standard_layout", RID_IS_STD_LAYOUT, D_CXXONLY },
  {
    
     "__is_trivial",     RID_IS_TRIVIAL, D_CXXONLY },
  {
    
     "__is_trivially_assignable", RID_IS_TRIVIALLY_ASSIGNABLE, D_CXXONLY },
  {
    
     "__is_trivially_constructible", RID_IS_TRIVIALLY_CONSTRUCTIBLE, D_CXXONLY },
  {
    
     "__is_trivially_copyable", RID_IS_TRIVIALLY_COPYABLE, D_CXXONLY },
  {
    
     "__is_union",	RID_IS_UNION,	D_CXXONLY },
  {
    
     "__label__",	RID_LABEL,	0 },
  {
    
     "__null",		RID_NULL,	0 },
  {
    
     "__real",		RID_REALPART,	0 },
  {
    
     "__real__",		RID_REALPART,	0 },
  {
    
     "__restrict",	RID_RESTRICT,	0 },
  {
    
     "__restrict__",	RID_RESTRICT,	0 },
  {
    
     "__signed",		RID_SIGNED,	0 },
  {
    
     "__signed__",	RID_SIGNED,	0 },
  {
    
     "__thread",		RID_THREAD,	0 },
  {
    
     "__transaction_atomic", RID_TRANSACTION_ATOMIC, 0 },
  {
    
     "__transaction_relaxed", RID_TRANSACTION_RELAXED, 0 },
  {
    
     "__transaction_cancel", RID_TRANSACTION_CANCEL, 0 },
  {
    
     "__typeof",		RID_TYPEOF,	0 },
  {
    
     "__typeof__",	RID_TYPEOF,	0 },
  {
    
     "__underlying_type", RID_UNDERLYING_TYPE, D_CXXONLY },
  {
    
     "__volatile",	RID_VOLATILE,	0 },
  {
    
     "__volatile__",	RID_VOLATILE,	0 },
  {
    
     "__GIMPLE",		RID_GIMPLE,	D_CONLY },
  {
    
     "__PHI",		RID_PHI,	D_CONLY },
  {
    
     "__RTL",		RID_RTL,	D_CONLY },
  {
    
     "alignas",		RID_ALIGNAS,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "alignof",		RID_ALIGNOF,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "asm",		RID_ASM,	D_ASM },
  {
    
     "auto",		RID_AUTO,	0 },
  {
    
     "bool",		RID_BOOL,	D_CXXONLY | D_CXXWARN },
  {
    
     "break",		RID_BREAK,	0 },
  {
    
     "case",		RID_CASE,	0 },
  {
    
     "catch",		RID_CATCH,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "char",		RID_CHAR,	0 },
  {
    
     "char8_t",		RID_CHAR8,	D_CXX_CHAR8_T_FLAGS | D_CXXWARN },
  {
    
     "char16_t",		RID_CHAR16,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "char32_t",		RID_CHAR32,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "class",		RID_CLASS,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "const",		RID_CONST,	0 },
  {
    
     "consteval",	RID_CONSTEVAL,	D_CXXONLY | D_CXX20 | D_CXXWARN },
  {
    
     "constexpr",	RID_CONSTEXPR,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "constinit",	RID_CONSTINIT,	D_CXXONLY | D_CXX20 | D_CXXWARN },
  {
    
     "const_cast",	RID_CONSTCAST,	D_CXXONLY | D_CXXWARN },
  {
    
     "continue",		RID_CONTINUE,	0 },
  {
    
     "decltype",         RID_DECLTYPE,   D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "default",		RID_DEFAULT,	0 },
  {
    
     "delete",		RID_DELETE,	D_CXXONLY | D_CXXWARN },
  {
    
     "do",		RID_DO,		0 },
  {
    
     "double",		RID_DOUBLE,	0 },
  {
    
     "dynamic_cast",	RID_DYNCAST,	D_CXXONLY | D_CXXWARN },
  {
    
     "else",		RID_ELSE,	0 },
  {
    
     "enum",		RID_ENUM,	0 },
  {
    
     "explicit",		RID_EXPLICIT,	D_CXXONLY | D_CXXWARN },
  {
    
     "export",		RID_EXPORT,	D_CXXONLY | D_CXXWARN },
  {
    
     "extern",		RID_EXTERN,	0 },
  {
    
     "false",		RID_FALSE,	D_CXXONLY | D_CXXWARN },
  {
    
     "float",		RID_FLOAT,	0 },
  {
    
     "for",		RID_FOR,	0 },
  {
    
     "friend",		RID_FRIEND,	D_CXXONLY | D_CXXWARN },
  {
    
     "goto",		RID_GOTO,	0 },
  {
    
     "if",		RID_IF,		0 },
  {
    
     "inline",		RID_INLINE,	D_EXT89 },
  {
    
     "int",		RID_INT,	0 },
  {
    
     "long",		RID_LONG,	0 },
  {
    
     "mutable",		RID_MUTABLE,	D_CXXONLY | D_CXXWARN },
  {
    
     "namespace",	RID_NAMESPACE,	D_CXXONLY | D_CXXWARN },
  {
    
     "new",		RID_NEW,	D_CXXONLY | D_CXXWARN },
  {
    
     "noexcept",		RID_NOEXCEPT,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "nullptr",		RID_NULLPTR,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "operator",		RID_OPERATOR,	D_CXXONLY | D_CXXWARN },
  {
    
     "private",		RID_PRIVATE,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "protected",	RID_PROTECTED,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "public",		RID_PUBLIC,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "register",		RID_REGISTER,	0 },
  {
    
     "reinterpret_cast",	RID_REINTCAST,	D_CXXONLY | D_CXXWARN },
  {
    
     "restrict",		RID_RESTRICT,	D_CONLY | D_C99 },
  {
    
     "return",		RID_RETURN,	0 },
  {
    
     "short",		RID_SHORT,	0 },
  {
    
     "signed",		RID_SIGNED,	0 },
  {
    
     "sizeof",		RID_SIZEOF,	0 },
  {
    
     "static",		RID_STATIC,	0 },
  {
    
     "static_assert",    RID_STATIC_ASSERT, D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "static_cast",	RID_STATCAST,	D_CXXONLY | D_CXXWARN },
  {
    
     "struct",		RID_STRUCT,	0 },
  {
    
     "switch",		RID_SWITCH,	0 },
  {
    
     "template",		RID_TEMPLATE,	D_CXXONLY | D_CXXWARN },
  {
    
     "this",		RID_THIS,	D_CXXONLY | D_CXXWARN },
  {
    
     "thread_local",	RID_THREAD,	D_CXXONLY | D_CXX11 | D_CXXWARN },
  {
    
     "throw",		RID_THROW,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "true",		RID_TRUE,	D_CXXONLY | D_CXXWARN },
  {
    
     "try",		RID_TRY,	D_CXX_OBJC | D_CXXWARN },
  {
    
     "typedef",		RID_TYPEDEF,	0 },
  {
    
     "typename",		RID_TYPENAME,	D_CXXONLY | D_CXXWARN },
  {
    
     "typeid",		RID_TYPEID,	D_CXXONLY | D_CXXWARN },
  {
    
     "typeof",		RID_TYPEOF,	D_ASM | D_EXT },
  {
    
     "union",		RID_UNION,	0 },
  {
    
     "unsigned",		RID_UNSIGNED,	0 },
  {
    
     "using",		RID_USING,	D_CXXONLY | D_CXXWARN },
  {
    
     "virtual",		RID_VIRTUAL,	D_CXXONLY | D_CXXWARN },
  {
    
     "void",		RID_VOID,	0 },
  {
    
     "volatile",		RID_VOLATILE,	0 },
  {
    
     "wchar_t",		RID_WCHAR,	D_CXXONLY },
  {
    
     "while",		RID_WHILE,	0 },
  {
    
     "__is_assignable", RID_IS_ASSIGNABLE, D_CXXONLY },
  {
    
     "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY },
  {
    
     "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY },
  {
    
     "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, D_CXXONLY },

  /* C++ transactional memory.  */
  {
    
     "synchronized",	RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },
  {
    
     "atomic_noexcept",	RID_ATOMIC_NOEXCEPT, D_CXXONLY | D_TRANSMEM },
  {
    
     "atomic_cancel",	RID_ATOMIC_CANCEL, D_CXXONLY | D_TRANSMEM },
  {
    
     "atomic_commit",	RID_TRANSACTION_ATOMIC, D_CXXONLY | D_TRANSMEM },

  /* Concepts-related keywords */
  {
    
     "concept",		RID_CONCEPT,	D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
  {
    
     "requires", 	RID_REQUIRES,	D_CXX_CONCEPTS_FLAGS | D_CXXWARN },

  /* Modules-related keywords, these are internal unspellable tokens,
     created by the preprocessor.  */
  {
    
     "module ",		RID__MODULE,	D_CXX_MODULES_FLAGS | D_CXXWARN },
  {
    
     "import ",		RID__IMPORT,	D_CXX_MODULES_FLAGS | D_CXXWARN },
  {
    
     "export ",		RID__EXPORT,	D_CXX_MODULES_FLAGS | D_CXXWARN },

  /* Coroutines-related keywords */
  {
    
     "co_await",		RID_CO_AWAIT,	D_CXX_COROUTINES_FLAGS | D_CXXWARN },
  {
    
     "co_yield",		RID_CO_YIELD,	D_CXX_COROUTINES_FLAGS | D_CXXWARN },
  {
    
     "co_return", 	RID_CO_RETURN,	D_CXX_COROUTINES_FLAGS | D_CXXWARN },

  /* These Objective-C keywords are recognized only immediately after
     an '@'.  */
  {
    
     "compatibility_alias", RID_AT_ALIAS,	D_OBJC },
  {
    
     "defs",		RID_AT_DEFS,		D_OBJC },
  {
    
     "encode",		RID_AT_ENCODE,		D_OBJC },
  {
    
     "end",		RID_AT_END,		D_OBJC },
  {
    
     "implementation",	RID_AT_IMPLEMENTATION,	D_OBJC },
  {
    
     "interface",	RID_AT_INTERFACE,	D_OBJC },
  {
    
     "protocol",		RID_AT_PROTOCOL,	D_OBJC },
  {
    
     "selector",		RID_AT_SELECTOR,	D_OBJC },
  {
    
     "finally",		RID_AT_FINALLY,		D_OBJC },
  {
    
     "optional",		RID_AT_OPTIONAL,	D_OBJC },
  {
    
     "required",		RID_AT_REQUIRED,	D_OBJC },
  {
    
     "property",		RID_AT_PROPERTY,	D_OBJC },
  {
    
     "package",		RID_AT_PACKAGE,		D_OBJC },
  {
    
     "synthesize",	RID_AT_SYNTHESIZE,	D_OBJC },
  {
    
     "dynamic",		RID_AT_DYNAMIC,		D_OBJC },
  /* These are recognized only in protocol-qualifier context
     (see above) */
  {
    
     "bycopy",		RID_BYCOPY,		D_OBJC },
  {
    
     "byref",		RID_BYREF,		D_OBJC },
  {
    
     "in",		RID_IN,			D_OBJC },
  {
    
     "inout",		RID_INOUT,		D_OBJC },
  {
    
     "oneway",		RID_ONEWAY,		D_OBJC },
  {
    
     "out",		RID_OUT,		D_OBJC },
  /* These are recognized inside a property attribute list */
  {
    
     "assign",		RID_ASSIGN,		D_OBJC },
  {
    
     "atomic",		RID_PROPATOMIC,		D_OBJC },
  {
    
     "copy",		RID_COPY,		D_OBJC },
  {
    
     "getter",		RID_GETTER,		D_OBJC },
  {
    
     "nonatomic",	RID_NONATOMIC,		D_OBJC },
  {
    
     "readonly",		RID_READONLY,		D_OBJC },
  {
    
     "readwrite",	RID_READWRITE,		D_OBJC },
  {
    
     "retain",		RID_RETAIN,		D_OBJC },
  {
    
     "setter",		RID_SETTER,		D_OBJC },
  /* These are Objective C implementation of nullability, accepted only in
     specific contexts.  */
  {
    
     "null_unspecified", RID_NULL_UNSPECIFIED,	D_OBJC },
  {
    
     "nullable",		RID_NULLABLE,		D_OBJC },
  {
    
     "nonnull",		RID_NONNULL,		D_OBJC },
  {
    
     "null_resettable",	RID_NULL_RESETTABLE,	D_OBJC },
};

1.2.5 Type PRAGMA (c_token::pragma_kind)

Lorsqu'un symbole lexical CPP_PRAGMAest , cela signifie que le symbole lexical est un indicateur de guidage de compilation, qui est utilisé pour guider la compilation. gccLes symboles de guidage de compilation et les valeurs de symbole pour toutes cles langues sont définis dans les variables d'énumération du fichier ./gcc/c-family/c-pragma.h .enum pragma_kind

//# gcc-12.1.0/gcc/c-family/c-pragma.h	##------## enum pragma_kind

/* Pragma identifiers built in to the front end parsers.  Identifiers
   for ancillary handlers will follow these.  */
enum pragma_kind {
    
    
  PRAGMA_NONE = 0,

  PRAGMA_OACC_ATOMIC,
  PRAGMA_OACC_CACHE,
  PRAGMA_OACC_DATA,
  PRAGMA_OACC_DECLARE,
  PRAGMA_OACC_ENTER_DATA,
  PRAGMA_OACC_EXIT_DATA,
  PRAGMA_OACC_HOST_DATA,
  PRAGMA_OACC_KERNELS,
  PRAGMA_OACC_LOOP,
  PRAGMA_OACC_PARALLEL,
  PRAGMA_OACC_ROUTINE,
  PRAGMA_OACC_SERIAL,
  PRAGMA_OACC_UPDATE,
  PRAGMA_OACC_WAIT,

  /* PRAGMA_OMP__START_ should be equal to the first PRAGMA_OMP_* code.  */
  PRAGMA_OMP_ALLOCATE,
  PRAGMA_OMP__START_ = PRAGMA_OMP_ALLOCATE,
  PRAGMA_OMP_ATOMIC,
  PRAGMA_OMP_BARRIER,
  PRAGMA_OMP_CANCEL,
  PRAGMA_OMP_CANCELLATION_POINT,
  PRAGMA_OMP_CRITICAL,
  PRAGMA_OMP_DECLARE,
  PRAGMA_OMP_DEPOBJ,
  PRAGMA_OMP_DISTRIBUTE,
  PRAGMA_OMP_ERROR,
  PRAGMA_OMP_END_DECLARE_TARGET,
  PRAGMA_OMP_FLUSH,
  PRAGMA_OMP_FOR,
  PRAGMA_OMP_LOOP,
  PRAGMA_OMP_NOTHING,
  PRAGMA_OMP_MASKED,
  PRAGMA_OMP_MASTER,
  PRAGMA_OMP_ORDERED,
  PRAGMA_OMP_PARALLEL,
  PRAGMA_OMP_REQUIRES,
  PRAGMA_OMP_SCAN,
  PRAGMA_OMP_SCOPE,
  PRAGMA_OMP_SECTION,
  PRAGMA_OMP_SECTIONS,
  PRAGMA_OMP_SIMD,
  PRAGMA_OMP_SINGLE,
  PRAGMA_OMP_TARGET,
  PRAGMA_OMP_TASK,
  PRAGMA_OMP_TASKGROUP,
  PRAGMA_OMP_TASKLOOP,
  PRAGMA_OMP_TASKWAIT,
  PRAGMA_OMP_TASKYIELD,
  PRAGMA_OMP_THREADPRIVATE,
  PRAGMA_OMP_TEAMS,
  /* PRAGMA_OMP__LAST_ should be equal to the last PRAGMA_OMP_* code.  */
  PRAGMA_OMP__LAST_ = PRAGMA_OMP_TEAMS,

  PRAGMA_GCC_PCH_PREPROCESS,
  PRAGMA_IVDEP,
  PRAGMA_UNROLL,

  PRAGMA_FIRST_EXTERNAL
};

1.2.6. Pointeur de nœud d'arbre (valeur)

Pour certains symboles lexicaux, il faut faire attention non seulement à son type, mais aussi à sa valeur. Par exemple, le symbole de constante de chaîne de caractères a un type de symbole lexical de CPP_STRING, et la valeur de la constante de chaîne de caractères est donnée par le nœud d'arbre constant de chaîne valuepointé par .

1.2.7. Informations de localisation (localisation)

locationIl est utilisé pour décrire l'emplacement de l'unité lexicale dans le code source, permettant aux débogueurs de localiser rapidement l'emplacement de l'erreur. La structure pour obtenir les positions des symboles est définie dans le fichiergcc ./libcpp/include/line-map.h et la fonction dans ./gcc/input.hexpanded_locationextern expanded_location expand_location (location_t);

//# gcc-12.1.0/libcpp/include/line-map.h	##------## expanded_location
typedef struct
{
    
    
  /* The name of the source file involved.  */
  const char *file;

  /* The line-location in the source file.  */
  int line;

  int column;

  void *data;

  /* In a system header?. */
  bool sysp;
} expanded_location;

//# gcc-12.1.0/gcc/input.h
extern expanded_location expand_location (location_t);

1.2.8. Exemples

Un exemple simple de traitement d'analyseur lexical est donné ci-dessous.
analyse lexicale

1.3. Analyse syntaxique et analyse sémantique

En fait, l'analyse grammaticale est effectuée avec l'analyse lexicale.Lorsque l'analyseur lexical a besoin d'une nouvelle unité tokenlexicale , il obtiendra une nouvelle tokentséquence d'unités lexicales de l'analyseur lexical, confirmera que la séquence peut être générée par la grammaire, et si la grammaire est correcte, générer une analyse grammaticale Le nœud d'arbre génère finalement un arbre syntaxique complet, c'est-à-dire l'arbre syntaxique abstrait ( , Abstract Syntax Tree) AST, gccle processus de génération d'un arbre d'analyse syntaxique et de sa conversion en code assembleur sera décrit en détail ci-dessous , et voici un point mentionné en premier. gccL'analyse grammaticale de la paire intermédiaire est principalement gcc-12.1.0/gcc/c/c-parser.cccomplétée par chaque fonction du fichier, et struct c_parserla structure est utilisée pour enregistrer l'état de l'analyse grammaticale et les informations de contexte, les symboles lexicaux pré-lus actuels (jusqu'à deux) et la fonction d'entrée de l'analyse grammaticale c_parse_file().

c_parserLes informations spécifiques de la structure sont les suivantes :

//# gcc-12.1.0/gcc/c/c-parser.cc	##------## struct c_parser

/* A parser structure recording information about the state and
   context of parsing.  Includes lexer information with up to two
   tokens of look-ahead; more are not needed for C.  */
struct GTY(()) c_parser {
    
    
  /* The look-ahead tokens.  */
  c_token * GTY((skip)) tokens;
  /* Buffer for look-ahead tokens.  */
  c_token tokens_buf[4];
  /* How many look-ahead tokens are available (0 - 4, or
     more if parsing from pre-lexed tokens).  */
  unsigned int tokens_avail;
  /* Raw look-ahead tokens, used only for checking in Objective-C
     whether '[[' starts attributes.  */
  vec<c_token, va_gc> *raw_tokens;
  /* The number of raw look-ahead tokens that have since been fully
     lexed.  */
  unsigned int raw_tokens_used;
  /* True if a syntax error is being recovered from; false otherwise.
     c_parser_error sets this flag.  It should clear this flag when
     enough tokens have been consumed to recover from the error.  */
  BOOL_BITFIELD error : 1;
  /* True if we're processing a pragma, and shouldn't automatically
     consume CPP_PRAGMA_EOL.  */
  BOOL_BITFIELD in_pragma : 1;
  /* True if we're parsing the outermost block of an if statement.  */
  BOOL_BITFIELD in_if_block : 1;
  /* True if we want to lex a translated, joined string (for an
     initial #pragma pch_preprocess).  Otherwise the parser is
     responsible for concatenating strings and translating to the
     execution character set as needed.  */
  BOOL_BITFIELD lex_joined_string : 1;
  /* True if, when the parser is concatenating string literals, it
     should translate them to the execution character set (false
     inside attributes).  */
  BOOL_BITFIELD translate_strings_p : 1;

  /* Objective-C specific parser/lexer information.  */

  /* True if we are in a context where the Objective-C "PQ" keywords
     are considered keywords.  */
  BOOL_BITFIELD objc_pq_context : 1;
  /* True if we are parsing a (potential) Objective-C foreach
     statement.  This is set to true after we parsed 'for (' and while
     we wait for 'in' or ';' to decide if it's a standard C for loop or an
     Objective-C foreach loop.  */
  BOOL_BITFIELD objc_could_be_foreach_context : 1;
  /* The following flag is needed to contextualize Objective-C lexical
     analysis.  In some cases (e.g., 'int NSObject;'), it is
     undesirable to bind an identifier to an Objective-C class, even
     if a class with that name exists.  */
  BOOL_BITFIELD objc_need_raw_identifier : 1;
  /* Nonzero if we're processing a __transaction statement.  The value
     is 1 | TM_STMT_ATTR_*.  */
  unsigned int in_transaction : 4;
  /* True if we are in a context where the Objective-C "Property attribute"
     keywords are valid.  */
  BOOL_BITFIELD objc_property_attr_context : 1;

  /* Whether we have just seen/constructed a string-literal.  Set when
     returning a string-literal from c_parser_string_literal.  Reset
     in consume_token.  Useful when we get a parse error and see an
     unknown token, which could have been a string-literal constant
     macro.  */
  BOOL_BITFIELD seen_string_literal : 1;

  /* Location of the last consumed token.  */
  location_t last_token_location;
};

La fonction d'entrée de l'analyse syntaxique c_parse_file()est la suivante :

//#gcc-12.1.0/gcc/c/c-parse.cc	##------## c_parse_file()

void
c_parse_file (void)
{
    
    
  /* Use local storage to begin.  If the first token is a pragma, parse it.
     If it is #pragma GCC pch_preprocess, then this will load a PCH file
     which will cause garbage collection.  */
  c_parser tparser;

  memset (&tparser, 0, sizeof tparser);
  tparser.translate_strings_p = true;
  tparser.tokens = &tparser.tokens_buf[0];
  the_parser = &tparser;

  /* 这里预读取一个测法符号,如果是预处理符号,则进行编译的预处理 */
  if (c_parser_peek_token (&tparser)->pragma_kind == PRAGMA_GCC_PCH_PREPROCESS)
    c_parser_pragma_pch_preprocess (&tparser);	/* 预处理 */
  else
    c_common_no_more_pch ();

  the_parser = ggc_alloc<c_parser> ();		/* 创建 c_parser 结构体 */
  *the_parser = tparser;
  if (tparser.tokens == &tparser.tokens_buf[0])
    the_parser->tokens = &the_parser->tokens_buf[0];

  /* Initialize EH, if we've been told to do so.  */
  if (flag_exceptions)
    using_eh_for_cleanups ();

  c_parser_translation_unit (the_parser);	/* 这里从C语言的语法中的翻译单元 (translation unit) 非终结符开始语法分析 */
  the_parser = NULL;
}

1.3.1. Comment sortir AST (Abstract Syntax Tree)

Voici un morceau de code extrêmement simple, et vous pouvez voir d'un coup d'œil qu'il resultva être réaffecté 12.

imaginemiracle@ubuntu:abstract_syntax_tree$ cat ast.c
int main(int argc, char **argv)
{
    
    
    int a = 10;
    int b = 20;

    int result = 0;

    result = a + b / a;

    return 0;
}

gccUtilisez d'abord le fichierAST généré par l'outil, c'est-à-dire le fichier suivant, et exécutez la commande comme suit :dumpast.c.004t.original

imaginemiracle@ubuntu:abstract_syntax_tree$ gcc -fdump-tree-original-raw ast.c 
imaginemiracle@ubuntu:abstract_syntax_tree$ ls
a.out  ast.c  ast.c.004t.original

En fait, le fichier généré ici est déjà le fichier d'arbre de syntaxe abstraite analysé par l'analyseur, mais il n'est pas commode pour nous de le lire. Les étapes suivantes ne servent qu'à générer des résultats graphiques plus faciles à comprendre pour nous. Vérifions-le d'abord . Contenu de ce fichier :

imaginemiracle@ubuntu:abstract_syntax_tree$ cat ast.c.004t.original 

;; Function main (null)
;; enabled by -tree-original

@1      statement_list   0   : @2       1   : @3      
@2      bind_expr        type: @4       vars: @5       body: @6      
@3      return_expr      type: @4       expr: @7      
@4      void_type        name: @8       algn: 8       
@5      var_decl         name: @9       type: @10      scpe: @11     
                         srcp: ast.c:3                 init: @12     
                         size: @13      algn: 32       used: 1       
@6      statement_list   0   : @14      1   : @15      2   : @16     
                         3   : @17      4   : @18     
@7      modify_expr      type: @10      op 0: @19      op 1: @20     
@8      type_decl        name: @21      type: @4      
@9      identifier_node  strg: a        lngt: 1       
@10     integer_type     name: @22      size: @13      algn: 32      
                         prec: 32       sign: signed   min : @23     
                         max : @24     
@11     function_decl    name: @25      type: @26      srcp: ast.c:1      
                         args: @27      link: extern  
@12     integer_cst      type: @10     int: 10
@13     integer_cst      type: @28     int: 32
@14     decl_expr        type: @4      
@15     decl_expr        type: @4      
@16     decl_expr        type: @4      
@17     modify_expr      type: @10      op 0: @29      op 1: @30     
@18     return_expr      type: @4       expr: @31     
@19     result_decl      type: @10      scpe: @11      srcp: ast.c:1      
                         note: artificial              size: @13     
                         algn: 32      
@20     integer_cst      type: @10     int: 0
@21     identifier_node  strg: void     lngt: 4       
@22     type_decl        name: @32      type: @10     
@23     integer_cst      type: @10     int: -2147483648
@24     integer_cst      type: @10     int: 2147483647
@25     identifier_node  strg: main     lngt: 4       
@26     function_type    size: @33      algn: 8        retn: @10     
                         prms: @34     
@27     parm_decl        name: @35      type: @10      scpe: @11     
                         srcp: ast.c:1                 argt: @10     
                         size: @13      algn: 32       used: 0       
@28     integer_type     name: @36      size: @37      algn: 128     
                         prec: 128      sign: unsigned min : @38     
                         max : @39     
@29     var_decl         name: @40      type: @10      scpe: @11     
                         srcp: ast.c:6                 init: @20     
                         size: @13      algn: 32       used: 1       
@30     plus_expr        type: @10      op 0: @41      op 1: @5      
@31     modify_expr      type: @10      op 0: @19      op 1: @20     
@32     identifier_node  strg: int      lngt: 3       
@33     integer_cst      type: @28     int: 8
@34     tree_list        valu: @10      chan: @42     
@35     identifier_node  strg: argc     lngt: 4       
@36     identifier_node  strg: bitsizetype             lngt: 11      
@37     integer_cst      type: @28     int: 128
@38     integer_cst      type: @28     int: 0
@39     integer_cst      type: @28     int: -1
@40     identifier_node  strg: result   lngt: 6       
@41     trunc_div_expr   type: @10      op 0: @43      op 1: @5      
@42     tree_list        valu: @44      chan: @45     
@43     var_decl         name: @46      type: @10      scpe: @11     
                         srcp: ast.c:4                 init: @47     
                         size: @13      algn: 32       used: 1       
@44     pointer_type     size: @48      algn: 64       ptd : @49     
@45     tree_list        valu: @4      
@46     identifier_node  strg: b        lngt: 1       
@47     integer_cst      type: @10     int: 20
@48     integer_cst      type: @28     int: 64
@49     pointer_type     size: @48      algn: 64       ptd : @50     
@50     integer_type     name: @51      size: @33      algn: 8       
                         prec: 8        sign: signed   min : @52     
                         max : @53     
@51     type_decl        name: @54      type: @50     
@52     integer_cst      type: @50     int: -128
@53     integer_cst      type: @50     int: 127
@54     identifier_node  strg: char     lngt: 4

Préparez deux fichiers de script ci-dessous :
pre.awkfichier, le contenu est le suivant :

imaginemiracle@ubuntu:abstract_syntax_tree$ cat pre.awk
#! /usr/bin/gawk -f
/^[^;]/{
    
    
    gsub(/^@/, "~@", $0);
    gsub(/( *):( *)/, ":", $0);
    print;                                                                                                                           
    }         

graphviz.awkfichier, le contenu est le suivant :

imaginemiracle@ubuntu:abstract_syntax_tree$ cat graphviz.awk
#! /usr/bin/gawk -f

BEGIN {
    
    RS = "~@"; printf "digraph G {
    
    \n node [shape = record];";}
/^[0-9]/{
    
    
s = sprintf("%s [label = \"{%s | {", $1, $1);
for(i = 2; i < NF - 1; i++)
    s = s sprintf("%s | ", $i);
    s = s sprintf("%s}}\"];\n", $i);
    $0 = s;
    while (/([a-zA-Z]+):@([0-9]+)/){
    
    
        format = sprintf("\\1 \\3\n %s:\\1 -> \\2;", $1);
        $0 = gensub(/([a-zA-Z]+):@([0-9]+)(.*)$/, format, "g");
    };
    printf " %s\n", $0;
}
END {
    
    print "}"}

Écrivez un autre script run.shpour appeler ces deux scripts pre.awket tree.awk, son contenu est le suivant :

imaginemiracle@ubuntu:abstract_syntax_tree$ cat run.sh 
./pre.awk $1.* | ./graphviz.awk > $1.dot

Une fois le fichier prêt, vous devez accorder des autorisations exécutables à ces scripts. La commande est la suivante :

imaginemiracle@ubuntu:abstract_syntax_tree$ chmod 755 *.awk
imaginemiracle@ubuntu:abstract_syntax_tree$ chmod 755 run.sh

Afin d'éviter la panique causée par certains amis qui n'ont pas installé gawket ne peuvent pas exécuter le script en raison d'erreurs, les commandes d'installation des deux logiciels sont fournies ci-dessous :graphviz

imaginemiracle@ubuntu:abstract_syntax_tree$ sudo apt install gawk
imaginemiracle@ubuntu:abstract_syntax_tree$ sudo apt-get install graphviz

Lancez l'exécution run.shdu script et *.dotle fichier sera généré, le voici ast.c.dot:

imaginemiracle@ubuntu:abstract_syntax_tree$ bash run.sh ast.c
imaginemiracle@ubuntu:abstract_syntax_tree$ ls
a.out  ast.c  ast.c.004t.original  ast.c.dot  pre.awk  run.sh  tree.awk

Utilisez ensuite dotl'outil pour l'exporter sous forme PNGd'image PDFou de fichier :

imaginemiracle@ubuntu:abstract_syntax_tree$ dot -Tpng ast.c.dot -o ast.png
imaginemiracle@ubuntu:abstract_syntax_tree$ ls
a.out  ast.c  ast.c.004t.original  ast.c.dot  ast.png  pre.awk  run.sh  tree.awk

Ce qui suit est le diagramme d'arbre de syntaxe abstraite ( ) correspondant au code source ci-dessus AST. Vous pouvez comparer ast.c.004t.originalle contenu du fichier et vous constaterez que le contenu à l'intérieur est décrit sous une forme plus claire de diagrammes de blocs et de flèches, ce qui permet aux utilisateurs de interpréter. Bien que le code source soit très simple, si les amis "pas de fondement" et "pas prêt" sont encore éblouis lorsqu'ils voient l'arbre de syntaxe abstraite correspondant.
arbre de syntaxe abstraite
Qu'à cela ne tienne, l'auteur essaiera d'étudier et d'analyser ASTavec tout le monde cet éblouissant arbre syntaxique abstrait ( ). Nous analysons ast.c.004t.originalle fichier et l'arbre de syntaxe ci-dessus en même temps par analogie, afin que nous puissions comprendre les deux.

1.3.2 Interprétation simple de l'AST (Abstract Syntax Tree)

Si vous avez besoin de comprendre ce que chaque nœud de l'arbre de syntaxe abstraite signifie, c'est-à-dire ce que représente chaque case de la figure ci-dessus, vous devez commencer par le code gccsource )gcc pour décrire En fait, cela est fait pour implémenter un nom descriptif commun pour chaque nœud, et chaque structure ajoutée à l'intérieur est utilisée comme structure de stockage de fonctionnalités réelle, qui est un nom collectif pour toutes ces structures de stockage de fonctionnalités. Examinons les nœuds qui représentent différentes significations contenues dans l'arbre de syntaxe abstraite ( ) défini dans .tree_nodeunionASTASTtree_nodetree_nodegccAST
[注]:一时没有找到 tree_node 定义位置的读者也不要慌张。新版本的 union tree_node 的定义被写在 ./gcc/tree-core.h中,而旧版本的 tree_node 的定义写在 ./gcc/tree.h里。

//# gcc-12.1.0/gcc/tree-core.h	##------## union tree_node

/* Define the overall contents of a tree node.
   It may be any of the structures declared above
   for various types of node.  */
union GTY ((ptr_alias (union lang_tree_node),
	    desc ("tree_node_structure (&%h)"), variable_size)) tree_node {
    
    
  /* 树节点的基类结构体 */
  struct tree_base GTY ((tag ("TS_BASE"))) base;
  /* 类型节点 */
  struct tree_typed GTY ((tag ("TS_TYPED"))) typed;
  /* 树节点的共有基本信息 */
  struct tree_common GTY ((tag ("TS_COMMON"))) common;
  /* 整型常量节点 */
  struct tree_int_cst GTY ((tag ("TS_INT_CST"))) int_cst;
  struct tree_poly_int_cst GTY ((tag ("TS_POLY_INT_CST"))) poly_int_cst;
  /* 实数常量节点 */
  struct tree_real_cst GTY ((tag ("TS_REAL_CST"))) real_cst;
  /* 定点数常量节点 */
  struct tree_fixed_cst GTY ((tag ("TS_FIXED_CST"))) fixed_cst;
  /* 向量常量节点 */
  struct tree_vector GTY ((tag ("TS_VECTOR"))) vector;
  /* 字符串常量节点 */
  struct tree_string GTY ((tag ("TS_STRING"))) string;
  /* 复数常量节点 */
  struct tree_complex GTY ((tag ("TS_COMPLEX"))) complex;
  /* 标识符节点 */
  struct tree_identifier GTY ((tag ("TS_IDENTIFIER"))) identifier;
  /* 声明的基类 */
  struct tree_decl_minimal GTY((tag ("TS_DECL_MINIMAL"))) decl_minimal;
  struct tree_decl_common GTY ((tag ("TS_DECL_COMMON"))) decl_common;
  /* 具有rtl属性的声明 */
  struct tree_decl_with_rtl GTY ((tag ("TS_DECL_WRTL"))) decl_with_rtl;
  /* 非一般声明的基类 */
  struct tree_decl_non_common  GTY ((tag ("TS_DECL_NON_COMMON")))
    decl_non_common;
  /* 参数声明的基类 */
  struct tree_parm_decl  GTY  ((tag ("TS_PARM_DECL"))) parm_decl;
  /* 具有可见性声明的基类 */
  struct tree_decl_with_vis GTY ((tag ("TS_DECL_WITH_VIS"))) decl_with_vis;
  /* 变量声明 */
  struct tree_var_decl GTY ((tag ("TS_VAR_DECL"))) var_decl;
  /* 字段声明 */
  struct tree_field_decl GTY ((tag ("TS_FIELD_DECL"))) field_decl;
  /* 标签声明节点 */
  struct tree_label_decl GTY ((tag ("TS_LABEL_DECL"))) label_decl;
  /* 返回值声明节点 */
  struct tree_result_decl GTY ((tag ("TS_RESULT_DECL"))) result_decl;
  /* 常量声明节点 */
  struct tree_const_decl GTY ((tag ("TS_CONST_DECL"))) const_decl;
  /* 类型声明节点 */
  struct tree_type_decl GTY ((tag ("TS_TYPE_DECL"))) type_decl;
  /* 函数声明节点 */
  struct tree_function_decl GTY ((tag ("TS_FUNCTION_DECL"))) function_decl;
  /* 翻译单元声明节点 */
  struct tree_translation_unit_decl GTY ((tag ("TS_TRANSLATION_UNIT_DECL")))
    translation_unit_decl;
  struct tree_type_common GTY ((tag ("TS_TYPE_COMMON"))) type_common;
  struct tree_type_with_lang_specific GTY ((tag ("TS_TYPE_WITH_LANG_SPECIFIC")))
    type_with_lang_specific;
  struct tree_type_non_common GTY ((tag ("TS_TYPE_NON_COMMON")))
    type_non_common;
  /* 列表节点 */
  struct tree_list GTY ((tag ("TS_LIST"))) list;
  /* 向量节点 */
  struct tree_vec GTY ((tag ("TS_VEC"))) vec;
  /* 表达式节点 */
  struct tree_exp GTY ((tag ("TS_EXP"))) exp;
  /* 静态单赋值 SSA_NAME 节点 */
  struct tree_ssa_name GTY ((tag ("TS_SSA_NAME"))) ssa_name;
  /* 块信息节点 */
  struct tree_block GTY ((tag ("TS_BLOCK"))) block;
  /* 块信息节点 */
  struct tree_binfo GTY ((tag ("TS_BINFO"))) binfo;
  /* 语句列表节点 */
  struct tree_statement_list GTY ((tag ("TS_STATEMENT_LIST"))) stmt_list;
  struct tree_constructor GTY ((tag ("TS_CONSTRUCTOR"))) constructor;
  struct tree_omp_clause GTY ((tag ("TS_OMP_CLAUSE"))) omp_clause;
  struct tree_optimization_option GTY ((tag ("TS_OPTIMIZATION"))) optimization;
  struct tree_target_option GTY ((tag ("TS_TARGET_OPTION"))) target_option;
};

Ci-dessus, l'auteur a donné quelques notes sur la signification des nœuds.Nous listons ci-dessous les nœuds nécessaires à cette analyse, ce qui peut être vu de manière plus intuitive.

Type de structure dans tree_node nom de structure décrire
struct tree_identifier GTY ((balise ("TS_IDENTIFIER"))) identifiant nœud d'identification
struct tree_function_decl GTY ((balise ("TS_FUNCTION_DECL"))) function_decl nœud de déclaration de fonction
struct tree_var_decl GTY ((balise ("TS_VAR_DECL"))) var_decl nœud de déclaration de variable
struct tree_exp GTY ((balise ("TS_EXP"))) exp noeud d'expression
struct tree_result_decl GTY ((balise ("TS_RESULT_DECL"))) result_decl nœud de valeur de retour

Revenez d'abord au ast.ccode source :

int main(int argc, char **argv)
{
    
    
    int a = 10;
    int b = 20;

    int result = 0;

    result = a + b / a;

    return 0;
}

Le code source est très simple, il n'y a qu'une seule mainfonction , qui définit trois intvariables de type a, b, resultet affecte une valeur initiale, et une ligne d'instruction d'affectation d'expression d'opération result = a + b / a. Comme le montre le tableau ci-dessus, ales définitions b, , et les expressions du code source sont principalement utilisées dans l'arbre de syntaxe abstraite. Les nœuds de l'arbre stockent les nœuds de déclaration de variable et les nœuds utilisés pour stocker les expressions . Si vous les connaissez, vous pouvez les voir via Générer l'arbre de syntaxe abstraite correspondant .resultresult = a + b / astruct tree_var_declstruct tree_expgcc -fdump-tree-original-rawast.c.004t.original

1.3.2.1 Structure du nœud AST

Tout d'abord, nous devons savoir ce que signifie chaque champ d'une ligne ast.c.004t.originaldans . Chaque nœud est composé de trois unités de la dernière version. Les informations de base d'un nœud (une ligne, ou plusieurs lignes en raison de la longueur) est la suivante .

champ Structure de stockage de fonctionnalités correspondante décrire
étiquette ( TREE_CODE) enum tree_code Énumération de l'ID de nœud d'arbre TREE_CODE
nom ( NAME) const char * const tree_code_name[] Tableau de chaînes de noms de nœuds d'arbre
taper ( TREE_CODE_CLASS) enum tree_code_class
const char * const tree_code_class_strings[]
const enum tree_code_class tree_code_type[]
Énumération des types de nœuds d'arbre
Tableau de chaînes de noms de types de
nœuds d'arbre Tableau de types de nœuds d'arbre indexés par TREE_CODE
longueur de l'opérande ( len) const caractère non signé tree_code_length[] Nombre d'opérandes de nœud d'arbre

1.3.2.2 Analyse de chaque nœud d'AST

@1 nœud
Effacez le contenu ci-dessus, analysez-le maintenant à partir du premier nœud du ast.c.004t.original
fichier .

@1      statement_list   0   : @2       1   : @3
# 与该节点对应的存储结构为:
# struct tree_statement_list GTY ((tag ("TS_STATEMENT_LIST"))) stmt_list;

On peut voir que l'étiquette du nœud TREE_CODEest @1, c'est-à-dire qu'il s'agit du nœud 1N° , et que le nom du nœud NAMEest statement_list. De là, on peut voir que la structure de stockage caractéristique du nœud d'arbre syntaxique correspondant au nœud est tree_node.tree_statement_list, et le nœud décrit une liste d'instructions. On peut voir qu'il y a deux instructions 0instruction et 1instruction dans la liste d'instructions, 0l'instruction pointe vers @2le nœud et 1l'instruction pointe vers @3le nœud . Sa structure de diagramme est la suivante.
insérez la description de l'image ici

@2 nœuds

@2      bind_expr        type: @4       vars: @5       body: @6
# 与该节点对应的存储结构为:
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;

@1Examinez d'abord 0l'instruction dans la liste d'instructions décrite par le nœud. Vous pouvez voir que l'étiquette du nœud TREE_CODEest @2le nom du nœud NAMEest bind_expr. À partir de là, on peut voir que le nœud décrit une expression et sa caractéristique correspondante La structure de stockage est tree_node.tree_exp, la structure D'autres types d'expression pouvant être décrits peuvent être trouvés dans le fichier ./gcc/tree.def dans le répertoire du code source.
Tout d'abord, nous avons précisé que le nœud décrit une expression de 1, typele champ décrit le type valeur de l'expression, et typepointe vers @4le nœud , et ce dernier décrit les opérandes de l'expression. total Il y a deux opérandes qui varspointent vers @5le nœud et bodypointent vers @6le nœud. Sa structure de diagramme est la suivante.

insérez la description de l'image ici

Nous pouvons vérifier le type d'opérande du nœud dans le fichier ./gcc/tree.def :bind_exprBIND_EXPR

  • BIND_EXPR_VARS : pointe sur le nœud décrivant la variable ;
  • BIND_EXPR_BODY : pointe vers le corps de la fonction d'expression utilisée pour calculer la variable ;
  • BIND_EXPR_BLOCK : L'opérande de bloc est généralement utilisé pour le débogage et ne sera pas analysé ici.

En voyant ici, nous pouvons déjà correspondre au contenu du nœud.

champ de nœud noeud pointant décrire
taper @4 le type de valeur de l'expression
vars @5 opérande d'expression, variable ( BIND_EXPR_VARS)
corps @6 le corps de l'expression ( BIND_EXPR_BODY)

@4 node
Ensuite, analysez les éléments de ce node un par un, regardez d'abord @4le node ,

@4      void_type        name: @8       algn: 8
# 与该节点对应的存储结构为:
# struct tree_typed GTY ((tag ("TS_TYPED"))) typed;

Le nom du nœud NAMEest void_type, on peut voir que le nœud décrit un type (en fait, on voit déjà d'après le nom que la description est voidun type ), et une brève analyse en est donnée ci-dessous.

champ de nœud noeud pointant décrire
nom @8 le nom du genre
quelques - Généralement, ce champ pointe vers un nœud de constante entière. Ici, la valeur 8signifie que la précision est 8bit, et il est 8bitaligné

@8 nœuds et @21 nœuds

@8      type_decl        name: @21      type: @4
# 与该节点对应的存储结构为:
# struct tree_type_decl GTY ((tag ("TS_TYPE_DECL"))) type_decl;

En regardant à nouveau @8le nœud NAME, type_declc'est un nœud d'un type déclaré, et son namechamp indique que le nom de ce type pointe vers @21le nœud, et typele champ pointe vers le nœud type de ce type, qui peut pointer vers @4le nœud . En regardant @21le nœud , vous pouvez intuitivement voir que le nœud est un nœud d'identifiant et que le nom de caractère de l'identifiant est void. Sa structure de diagramme est la suivante.

# 标识符节点
@21     identifier_node  strg: void     lngt: 4
# 与该节点对应的存储结构为:
# struct tree_identifier GTY ((tag ("TS_IDENTIFIER"))) identifier;

Petite connaissance : Voici une petite explication pour le noeud identifiant.

champ identifier_node décrire
ctrl stringReprésente le nom de l'identifiant sous forme de chaîne ( )
lngt longueur de l'identifiant ( length)

insérez la description de l'image ici

Le nœud @5
revient ensuite et analyse le champ suivant du @2nœud qui est son premier opérande vars: @5.

@5      var_decl         name: @9       type: @10      scpe: @11     
                         srcp: ast.c:3                 init: @12     
                         size: @13      algn: 32       used: 1
# 与该节点对应的存储结构为:                         
# struct tree_var_decl GTY ((tag ("TS_VAR_DECL"))) var_decl;                         

Peu de connaissances : Voici le résultat de l'analyse détaillée du ème @5nœud var_decl, qui est le nœud. Les partenaires intéressés peuvent également obtenir le résultat basé sur l'analyse de l'arbre de syntaxe abstraite.

champ var_decl décrire @5valeur de nœud
nom Le nom de la variable décrite par ce nœud a
taper le type de variable décrit par ce nœud int
scpe Portée ( scope) main()portée de la fonction
srcp programme source ( source progarm) à la ast.csource 3ligne ligne
initialiser valeur initiale ( initial) La valeur initiale est10
taille taille variable 32bit
quelques bits d'alignement ( align) Appuyez pour 32bitaligner
utilisé Citations 1Secondaire

À partir du nom du nœud var_decl, nous savons que le nœud décrit la déclaration d'une variable. Bien que le nœud comporte de nombreux champs, en fait, lorsque nous analysons la déclaration d'une variable, nous ne nous soucions souvent que de trois choses, à savoir le nom de la variable, le type et valeur initiale de la variable. , c'est-à-dire le contenu des trois champs de name, typeet dans initce nœud.
@10 node
Selon les habitudes habituelles, regardons d'abord le type de la variable type: @10. L'analyse du type ayant été suivie et vérifiée en détail ci-dessus, je ne vais pas l'expliquer en détail ici. La méthode d'analyse est la même que ci-dessus. La liste suivante contient plusieurs informations sur les nœuds clés qui s'y rapportent.

# 类型节点——声明整型类型节点
@10     integer_type     name: @22      size: @13      algn: 32      
                         prec: 32       sign: signed   min : @23     
                         max : @24
# 与该节点对应的存储结构为:
# struct tree_typed GTY ((tag ("TS_TYPED"))) typed;
@22     type_decl        name: @32      type: @10  
# 与该节点对应的存储结构为:
# struct tree_type_decl GTY ((tag ("TS_TYPE_DECL"))) type_decl;   
@32     identifier_node  strg: int      lngt: 3    
# 与该节点对应的存储结构为:
# struct tree_identifier GTY ((tag ("TS_IDENTIFIER"))) identifier;              

Peu de connaissances : Voici le résultat de l'analyse détaillée du ème @10nœud integer_type, qui est le nœud. Les partenaires intéressés peuvent également obtenir le résultat basé sur l'analyse de l'arbre de syntaxe abstraite.

champ type_entier décrire @10valeur de nœud
nom Le nom de la description du nœud int
taille la taille du type décrit par ce nœud 32bit
quelques bits d'alignement ( align) Appuyez pour 32bitaligner
perc La précision du type Le type est 32bitla précision
signe Avec ou sans signe signedcaractères signés
min valeur minimum -2^31 = -2147483648
maximum valeur maximum 2^31 = 2147483648

On peut voir à partir des nœuds ci-dessus que la variable de ce @5nœud est intune variable de type. Regardons le nom de variable de la variable name: @9On peut voir intuitivement que le nom de la variable devrait être a.

# 标识符节点
@9      identifier_node  strg: a        lngt: 1
# 与该节点对应的存储结构为:
# struct tree_identifier GTY ((tag ("TS_IDENTIFIER"))) identifier;

Regardons la valeur initiale de cette variable init: @12, qui est le 12nœud . On voit clairement que le nœud est un nœud constant entier integer_cstdont la valeur est int: 10. Sa structure de diagramme est la suivante.

# 整型常量节点
@12     integer_cst      type: @10     int: 10
# 与该节点对应的存储结构为:
# struct tree_int_cst GTY ((tag ("TS_INT_CST"))) int_cst;

insérez la description de l'image ici

到这里我们就清楚了 @5 节点描述的是一个类型为 int 型,名为 a,初始值为 10的整型变量,也就是源码中的 int a = 10;

# C语言定义
int a = 10;
# ===========================分隔符===========================
# 抽象语法树对其的描述
@5      var_decl         name: @9       type: @10      scpe: @11     
                         srcp: ast.c:3                 init: @12     
                         size: @13      algn: 32       used: 1
@9      identifier_node  strg: a        lngt: 1       
@10     integer_type     name: @22      size: @13      algn: 32      
                         prec: 32       sign: signed   min : @23     
                         max : @24     
@11     function_decl    name: @25      type: @26      srcp: ast.c:1      
                         args: @27      link: extern  
@12     integer_cst      type: @10     int: 10
@13     integer_cst      type: @28     int: 32

@22     type_decl        name: @32      type: @10 

@32     identifier_node  strg: int      lngt: 3

到这里我们分析的内容,笔者在抽象语法树的图中摘了出来,可以直观的梳理分析过的思路。
int a = 10 ;

@6 节点
继续来分析 @2 节点的下一个操作数 body: @6,可以看得出 @6 节点是描述一段语句的节点,其语句包含节点 0: @141: @152: @163: @174: @18

@6      statement_list   0   : @14      1   : @15      2   : @16     
                         3   : @17      4   : @18
# 与该节点对应的存储结构为:
# struct tree_statement_list GTY ((tag ("TS_STATEMENT_LIST"))) stmt_list;                         

ast.c.004t.original 文件里可以看到节点 @14@15@16 这三个几点是同一类型节点,且类型指向也相同 @4,三个节点均是描述的是一个空的表达式。因此这里这三个节点是没什么实际意义的。

@14     decl_expr        type: @4      
@15     decl_expr        type: @4      
@16     decl_expr        type: @4

@17 节点

@17     modify_expr      type: @10      op 0: @29      op 1: @30
# 与该节点对应的存储结构为:
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;

从该节点名 modify_expr 可以看出该节点描述的是一段赋值表达式,type 字段表示该表达式的取值类型指向节点 @10,由于上文已经对其类似的分析过了,这里就不在赘述,该表达式的取值类型为 int,其它字段含义可以在 ./gcc/tree.def 文件中索引找到,如下。
modifier_expr
在该文件中指出,MODIFY_EXPR 表达式有两个操作数,其中 Operand 0 是需要被赋值的操作数,Operand 1 就是将要赋值给 Operand 0 的具体值。用 c 语言表达就相当于是 op_0 = op_1;,这样子的一条简单的赋值语句。图示结构如下。
insérez la description de l'image ici

@29 节点

@29     var_decl         name: @40      type: @10      scpe: @11     
                         srcp: ast.c:6                 init: @20     
                         size: @13      algn: 32       used: 1 
# 与该节点对应的存储结构为:
# struct tree_var_decl GTY ((tag ("TS_VAR_DECL"))) var_decl;                          

那么首先来看 op 0: @29。该节点 NAMEvar_decl,可以看出它是描述一个变量的节点,其分析方法可参考上文 @5 节点的分析过程,这里不再赘述,直接给出分析结果。@29 节点描述的是一个名为 result,类型 int 型,初始值为 0 的一个整型变量。即 ast.c 源码中对 result 变量的定义, int result = 0;
@30 节点

@30     plus_expr        type: @10      op 0: @41      op 1: @5 
# 与该节点对应的存储结构为:
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;

从该节点名 plus_expr 可以看出该节点描述的是一段简单的加法运算表达式,type 字段同样描述的是该表达式的取值类型为 int,其后跟的两个操作数为两个加数,即 op 0op 1 需要相加(op_0 + op_1)。首先来看 op 0: @41
@41 节点

@41     trunc_div_expr   type: @10      op 0: @43      op 1: @5 
# 与该节点对应的存储结构为:
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;

该节点名为 trunc_div_expr,是描述一段整数的除法运算(即只保留商的整数结果的除法运算),其中 op 0 为被除数,op 1 为除数 (op_0 / op_1)。其图示结构如下。
insérez la description de l'image ici
@43 节点

@43     var_decl         name: @46      type: @10      scpe: @11     
                         srcp: ast.c:4                 init: @47     
                         size: @13      algn: 32       used: 1  
# 与该节点对应的存储结构为:
# struct tree_var_decl GTY ((tag ("TS_VAR_DECL"))) var_decl;                          

@41 节点的 op0 指向的节点 @43 节点名 NAMEvar_decl,同样是描述一个变量定义,同 @5 节点的分析过程一样此处直接给出分析结果,该节点事实上是描述的源码 ast.c 中的 b 变量的定义以及初始化,int b = 20;
@41 节点的 op0 指向的节点 @5,我们已经分析过了,是int a = 10;,因此 @41 节点描述的表达式即为 (int) b / (int) a;@30 节点的 op 0 的值为 (int)20 / (int)10 = (int)2
@30 节点的 op 1 指向 @5 节点,因此 @30 节点描述的表达是则为 (op_0 + op_1) == ((int)2 + (int)10),因此 @30 节点的值为 (int) 12
到这里我们就清楚了 @17 节点表达式描述的是,(int) result = (int) 20 / (int) 10 + (int) 10 = 12;,此时 result 变量应被赋值为 12
@18 节点 && @31 节点
而这时候 @6 节点描述的语句列表也被我们分析的只剩 @18 节点。该节点较为简单,下面将其有关的几个节点均列出来。

@18     return_expr      type: @4       expr: @31 
# 与该节点对应的存储结构为:
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;
@31     modify_expr      type: @10      op 0: @19      op 1: @20
# 与该节点对应的存储结构为:
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;
@19     result_decl      type: @10      scpe: @11      srcp: ast.c:1      
                         note: artificial              size: @13     
                         algn: 32
# 与该节点对应的存储结构为:                         
# struct tree_result_decl GTY ((tag ("TS_RESULT_DECL"))) result_decl;
@20     integer_cst      type: @10     int: 0
# 与该节点对应的存储结构为: 
# struct tree_int_cst GTY ((tag ("TS_INT_CST"))) int_cst; 

经过上面对一系列的节点分析,相信才华横溢的大家已经可以很轻松的读懂该节点的内容,首先该节点描述的是取值类型为 @4 节点即 void 类型,也就是该表达式无返回值,表达式主体指向的是 @31 节点,一段赋值一句,将一个整型常量 0 赋值给定义的函数返回值,即表示源码 ast.c 中的最后一条语句 return 0;,其图示结构如下。
renvoie 0 ;
@3 节点
现在来观察第三个节点 return_expr

@3      return_expr      type: @4       expr: @7 
# 与该节点对应的存储结构为:               
# struct tree_exp GTY ((tag ("TS_EXP"))) exp;

可以看的出来该节点与我们刚刚分析过的 @18 节点所描述的表达式是完全相同的,只是对应节点不同而已,其含义同样是 return 0;,其描述的事实上是 int main() 函数的默认返回值是 0,有好奇心的小伙伴可以将 ast.c 中最后一条 return 0; 改为 return 666; (数字随意),再生成抽象语法树 AST 查看就会更加清楚了。下面笔者附上修改后生成 AST 文件,作为对比学习。

// 修改后的 ast.c
imaginemiracle@ubuntu:test$ cat ast.c
int main(int argc, char **argv)
{
    
    
    int a = 10;
    int b = 20;

    int result = 0;

    result = a + b / a;

    return 666;
}

这里只附上关键几个节点信息,足够用来明确两个 return_expr 节点分别描述的内容。

// 修改后的 ast.c.004t.original
@1      statement_list   0   : @2       1   : @3      
@2      bind_expr        type: @4       vars: @5       body: @6      
@3      return_expr      type: @4       expr: @7      

@6      statement_list   0   : @14      1   : @15      2   : @16     
                         3   : @17      4   : @18  
@7      modify_expr      type: @10      op 0: @19      op 1: @20 

@18     return_expr      type: @4       expr: @31 
@19     result_decl      type: @10      scpe: @11      srcp: test.c:1      
                         note: artificial              size: @13     
                         algn: 32      
@20     integer_cst      type: @10     int: 0

@31     modify_expr      type: @10      op 0: @19      op 1: @42

@42     integer_cst      type: @10     int: 666

到此整个 ast.c 源码的代码主体部分已经完全分析完成,只剩下一个 main 函数的声明节点 @ 26 未作分析,事实上对该节点分析并不难,感兴趣的小伙伴可以自行进行分析,参考源码和 gcc 目录下 ./gcc/tree-core.h中定义的 tree_node,以及 ./gcc/tree.def 文件来分析。综合以上分析的内容以及 main 函数的定义节点即构成下面这副完整的 ast.c 源码的抽象语法树(AST)图示结果。
insérez la description de l'image ici

1.4. 中间代码生成 (GIMPLE)

在语法&语义分析后的下一个步骤就是生成中间代码,语法分析阶段输出的表达式或程序语句是以语法树 (AST/GENERIC) 的形式存储,编译器并无法直接将其编译生成汇编程序,此时需要将其转换为一种中间代码。中间代码 (GIMPLE) 是编译过程中的一种临时代码,中间代码是 gcc 为了处理不同的前端语言而引入的一种与平台无关的代码。
gcc 中将完成对抽象语法树 (AST/GENERIC) 转换为中间代码 (GIMPLE) 这一过程命名为 passgcc 就是利用 pass 这一个抽象的名词代表了这一转换过程中的各种具体方法。在 gcc-12.1.0 中将 pass 的核心结构定义在了 gcc-12.1.0/gcc/tree-pass.h 中的 class opt_pass类中。

/* An instance of a pass.  This is also "pass_data" to minimize the
   changes in existing code.  */
class opt_pass : public pass_data
{
    
    
public:
  virtual ~opt_pass () {
    
     }

  /* Create a copy of this pass.

     Passes that can have multiple instances must provide their own
     implementation of this, to ensure that any sharing of state between
     this instance and the copy is "wired up" correctly.

     The default implementation prints an error message and aborts.  */
  virtual opt_pass *clone ();
  virtual void set_pass_param (unsigned int, bool);

  /* This pass and all sub-passes are executed only if the function returns
     true.  The default implementation returns true.  */
  virtual bool gate (function *fun);

  /* This is the code to run.  If this is not overridden, then there should
     be sub-passes otherwise this pass does nothing.
     The return value contains TODOs to execute in addition to those in
     TODO_flags_finish.   */
  virtual unsigned int execute (function *fun);

protected:
  opt_pass (const pass_data&, gcc::context *);

public:
  /* A list of sub-passes to run, dependent on gate predicate.  */
  opt_pass *sub;

  /* Next in the list of passes to run, independent of gate predicate.  */
  opt_pass *next;

  /* Static pass number, used as a fragment of the dump file name.  */
  int static_pass_number;

protected:
  gcc::context *m_ctxt;
};

为了说明问题,我们为 ast.c 代码增加一点内容,下面我们看一下 ast.c 生成的语法树 (AST/GENERIC),即上文看到 ast.c.004t.original 文件([注]:此处的该文件是语法树的 GENERIC 表达形式),和所生成的中间代码 (GIMPLE),此处的中间代码形式为三地址码形式。
添加内容后的 ast.c 文件:

imaginemiracle:ast$ cat ast.c 
int main(int argc, char **argv)
{
    
    
    int a = 10;
    int b = 20;

    int result = 0;

    result = a + b / a;

    if (result > (a + b)) {
    
    
        result -= 1;
    } else {
    
    
        result += 1;
    }

    return 0;
}

1.4.1. 语法树的GENERIC形式

ast.c 生成的语法树 (AST/GENERIC),GENERIC 表达形式:

imaginemiracle:ast$ gcc -fdump-tree-original-all ast.c 
imaginemiracle:ast$ ls
ast.c.004t.original a.out  ast.c
imaginemiracle:ast$ cat ast.c.004t.original 

;; Function main (null)
;; enabled by -tree-original


{
    
    
  int a = 10;
  int b = 20;
  int result = 0;

    int a = 10;
    int b = 20;
    int result = 0;
  result = b / a + a;
  if (a + b < result)
    {
    
    
      result = result + -1;
    }
  else
    {
    
    
      result = result + 1;
    }
  return 0;
}
return 0;

1.4.2. 中间代码 (GIMPLE)——三地址码形式

ast.c 生成的中间代码——三地址码形式:

imaginemiracle:ast$ gcc -fdump-tree-gimple ast.c 
imaginemiracle:ast$ ls
ast.c.004t.original  ast.c.005t.gimple  a.out  ast.c
imaginemiracle:ast$ cat ast.c.005t.gimple 
int main (int argc, char * * argv)
{
    
    
 int D.1953;

 {
    
    
   int a;
   int b;
   int result;

   a = 10;
   b = 20;
   result = 0;
   _1 = b / a;
   result = a + _1;
   _2 = a + b;
   if (result > _2) goto <D.1950>; else goto <D.1951>;
   <D.1950>:
   result = result + -1;
   goto <D.1952>;
   <D.1951>:
   result = result + 1;
   <D.1952>:
   D.1953 = 0;
   return D.1953;
 }
 D.1953 = 0;
 return D.1953;
}

1.4.3. AST/GENERIC 与 GIMPLE 的区别

中间代码 (GIMPLE) 和语法树 (AST/GENERIC) 的区别(也可以说是优点):

  • 与语言无关的中间表示
      语法树 (AST) 形式与前端的编程语言是高度相关的,即每种语言通过对应的词法&语法分析后生成的 AST 是异构的(即不同语言生成的语法树结构是不同的,如Java、C++两种语言即便描述的是同一个表达式、语句或者是同功能的代码,但使用其对应的编译器所生成的语法树也是不同的,即异构)。而中间代码 (GIMPLE) 表示形式则是与语言无关的,任何语言再转换成中间代码 (GIMPLE) 形式时,都需遵从 GIMPLE 规范,从而最终将转换为同构的中间代码。
  • 具有线性序列特征
      语法树 (AST) 是树形表达结构,难以进行编译处理([注]: 其树形结构只是难处理的一个原因,还包括语法树本身的异构性等原因并不能直接对语法树直接编译生成汇编代码),而中间代码 (GIMPLE) 本质上是一种线性的表达序列,编译器能够更加方便、高效做后续的编译处理,即编译成目标代码。
    [注]:虽然中间代码是一种线性表达序列,但依然会使用树形结构来描述表达式中的操作符、操作数等元素。但其宏观上讲还是一种线性结构,可以说是利用了树节点的便利性。

1.4.4. GIMPLE 的特点

Grâce à la comparaison ci-dessus, le code intermédiaire ( GIMPLE) est une sorte de code temporaire avec un isomorphisme plus facile à gérer pour le compilateur. Les codes intermédiaires courants incluent le "code à trois adresses", le "code P", etc.

  • GIMPLEIntroduisez une variable temporaire pour enregistrer le résultat intermédiaire et divisez ASTl'expression en un tuple de pas plus de trois opérandes ( Tuples);
  • Étant donné que GIMPLEle formulaire est essentiellement une séquence de code linéaire, toutes les expressions de calcul sont exprimées sous la forme d'une série d'opérations de base. ASTLes structures de contrôle dans (par exemple, if-else, for, whileetc. GIMPLE) sont converties en instructions de saut conditionnel ( gotocomplétées à l'aide de ) dans la représentation ;
  • ASTLa portée lexicale ( Lexical Scopes) GIMPLEdu est supprimée.

1.5 Génération de code d'assemblage

1.6. Génération de code objet

2. Phase de liaison

3. Phase de relocalisation

Acho que você gosta

Origin blog.csdn.net/qq_36393978/article/details/124604885
Recomendado
Clasificación