Proceso de compilación de GCC: del código fuente al programa ejecutable——Análisis del principio de compilación

Continuará》》》
Por favor espere~~~

Cuando estabas en la escuela, ¿escuchaste un curso llamado "Principios de compilación" y el profesor te preguntó en clase: "¿Sabes cómo el código que escribiste se convierte en un archivo ejecutable para ejecutarlo?" mucha "compilación", "preprocesamiento", "expansión de definición de macros", "árbol de sintaxis abstracta", "ensamblaje", "desensamblaje", etc. Una gran cantidad de contenido embriagador disuade la motivación para aprenderlo.
Repasemos el proceso de compilación de programas.La compilación que solemos decir generalmente se refiere al proceso de generar archivos ejecutables a partir de uno o más archivos de código fuente a través de un compilador. El proceso de compilación del programa se puede dividir en tres etapas:

  • (1) Etapa de compilación
  • (2) Etapa de vinculación
  • (3) Fase de reubicación
    proceso de compilación

Dirección de descarga de mapas de alta definición PNG: https://download.csdn.net/download/qq_36393978/85329800 Dirección de descarga de archivos
de ultra alta definición : https://download.csdn.net/download/qq_36393978/85329905PDF

1. Fase de compilación

La cadena de herramientas de compilación de uso común gccnecesita llamar al "preprocesador", "compilador" y "ensamblador" en la cadena de herramientas durante la fase de compilación. El proceso de procesamiento incluye el siguiente flujo.
[注]:GIMPLE代码也可称为中间代码,寄存器传输语言(Register Transfer Language,RTL)
inserte la descripción de la imagen aquí

1.1 Preprocesamiento del programa

Es decir, el procesamiento de cada comando de preprocesamiento en el archivo fuente (inclusión de archivos de encabezado, expansión de macros personalizadas y macros integradas, compilación condicional, instrucciones de control para compilación de programas), eliminación de comentarios, adición de números de línea y nombre de archivo identificadores, qué macros integradas se incluyen específicamente y qué comandos de preprocesamiento deben procesarse, podemos ver en el código fuente;
gcc-12.1.0las macros integradas proporcionadas son las siguientes:

//# 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.0Los comandos de preprocesamiento que deben procesarse son los siguientes, y las funciones de procesamiento correspondientes se colocan en btableesta matriz de estructura:

//# 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 Pequeño conocimiento (macro polimorfismo)

BLos pequeños amigos talentosos aquí pueden definir las macros aquí , como Destas, ¿por qué #definedespués de definirlas, quedan #undefindefinidas? ?
Esto es para lograr polimorfismo de macros, es decir, cuando se referencian en diferentes lugares y en diferentes momentos, pueden tener diferentes propiedades y producir diferentes resultados. Si aún no lo entiende, puede mirar el ejemplo simple a continuación y quedará claro de un vistazo.

#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;
}

Los resultados de su ejecución son los siguientes:

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

1.2 Análisis léxico

1.2.1 Unidad léxica (token)

El analizador léxico lee el código fuente, identifica cada símbolo léxico en el código fuente y construye una unidad léxica tokenpara guardar el símbolo. Este proceso se _cpp_lex_token()completa principalmente con la función (la función se encuentra gcc12.1.0/libcpp/lex.ccen ), y luego cada uno tokense verificará y generar tokenuna secuencia de . En el código fuente del analizador léxico, tokenen realidad representado por una estructura. En la gcc-12.1.0actualidad definición dec la unidad léxica del lenguaje es que la estructura se utiliza para almacenar una unidad léxica básica. Existe una definición de la estructura , que consiste en agregar una representación , que en sí misma es una macro definición. Se utiliza principalmente en el mecanismo de gestión de basura posterior y no se tratará aquí;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++Y tokenla definición de la unidad léxica del lenguaje es struct GTY(()) cpp_token. Este artículo utiliza principalmente cel lenguaje como ejemplo para analizar todo el proceso de compilación.

//# 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 estructura incluye principalmente tipo de símbolo ( type), tipo de identificador ( id_kind), palabra clave ( keyword), PRAGMAtipo ( pragma_kind), valor de símbolo ( value) y ( ) utilizados para describir la posición del símbolo en el archivo de origen location.

1.2.2 Tipo de token (c_token::type)

gccUna serie de símbolos de operación se definen en , como operadores regulares =, , +, y -, etc> . , separadores , , , , etc . Las definiciones y símbolos de tales símbolos de operación se especifican mediante macros , y algunos otros tipos de símbolos se especifican por la macro , incluye principalmente , , , , etc. Todos estos símbolos están definidos en las variables de enumeración el archivo ./libcpp/iclude/cpplib.h .<:,;{ }valueOPTKEOFNAMECHARSTRINGPRAGMAenum cpp_ttype

1.2.3 Tipo de identificador (c_token::id_kind)

Del comentario a la definición de struct c_tokentla estructura , podemos entender que cuando typeel valor de no es CPP_NAME, el valor de id_kindes C_IN_NONE, indicando que el tipo no es un identificador; por el contrario, cuando typeel valor de CPP_NAMEes, id_kindserá ser válido (es decir, se le asignará un valor de significado), lo que significa que el tipo es un tipo identificador. CPP_NAMELos tipos de incluyen identificadores ordinarios ( C_ID_ID), nombres de tipos ( C_ID_TYPENAME), nombres de objetos en Objective-C ( C_ID_CLASSNAME) y tipos de espacio de direcciones ( 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 Identificación de palabras clave (c_token::keyword)

Cuando sea necesario utilizar un identificador para representar una palabra clave ( ) en el código Key Workds, se deben determinar los valores específicos de estas palabras clave, a fin de determinar el significado específico representado por la palabra clave. cTodas las palabras clave y los valores correspondientes utilizados por el idioma se definen en las variables de enumeración en el archivo ./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
};

Como se puede ver en el código anterior, se escribe una enumeración GCCpara todas las palabras clave admitidas para representar el valor específico de la palabra clave correspondiente, y la declaración de la palabra clave se coloca en ./gcc/c-family/c -En la matriz c_common_reswordsde estructura archivo 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 Tipo PRAGMA (c_token::pragma_kind)

Cuando un símbolo léxico CPP_PRAGMAes , significa que el símbolo léxico es un indicador de guía de compilación, que se utiliza para guiar la compilación. gccLos símbolos de guía de compilación y los valores de símbolo para todos clos idiomas se definen en las variables de enumeración en el archivo ./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 Puntero de nodo de árbol (valor)

Para algunos símbolos léxicos, debemos prestar atención no solo a su tipo, sino también a su valor. Por ejemplo, el símbolo de constante de cadena de caracteres tiene un tipo de símbolo léxico de CPP_STRING, y el valor de la constante de cadena de caracteres viene dado por el nodo del árbol de constantes de cadena valueal que apunta .

1.2.7 Información de ubicación (ubicación)

locationSe utiliza para describir la ubicación de la unidad léxica en el código fuente, lo que permite a los depuradores localizar rápidamente la ubicación del error. La estructura para obtener las posiciones de los símbolos se define en el archivogcc ./libcpp/include/line-map.h y la función en ./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 Ejemplos

A continuación se proporciona un ejemplo de procesamiento de analizador léxico simple.
análisis léxico

1.3 Análisis de sintaxis y análisis semántico

De hecho, el análisis gramatical se lleva a cabo con el análisis léxico, tokencuando , obtendrá una nueva tokentsecuencia de unidades léxicas del analizador léxico, confirmará que la secuencia puede ser generada por la gramática, y si la gramática es correcta, generar un análisis gramatical El nodo del árbol finalmente genera un árbol de sintaxis completo, es decir, el árbol de sintaxis abstracta ( , Abstract Syntax Tree) AST, gccel proceso de generar un árbol de análisis de sintaxis y convertirlo a código ensamblador se describirá en detalle a continuación , y aquí hay un punto mencionado primero. gccEl análisis gramatical del par medio se gcc-12.1.0/gcc/c/c-parser.cccompleta principalmente con cada función en el archivo, y struct c_parserla estructura se usa para guardar el estado del análisis gramatical y la información de contexto, los símbolos léxicos preleídos actuales (hasta dos) y la función de entrada de el análisis gramatical c_parse_file().

c_parserLa información específica de la estructura es la siguiente:

//# 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 función de entrada del análisis de sintaxis c_parse_file()es la siguiente:

//#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 Cómo generar AST (árbol de sintaxis abstracta)

Aquí hay una pieza de código extremadamente simple, y puede ver de un vistazo que será resultreasignada 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;
}

Primero gccuse el archivoAST generado por la herramienta, es decir, el siguiente archivo, y ejecute el comando de la siguiente manera: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

De hecho, el archivo generado aquí ya es el archivo de árbol de sintaxis abstracta analizado por el analizador, pero no es conveniente para nosotros leerlo. Los siguientes pasos son solo para generar resultados gráficos que sean más fáciles de entender para nosotros. Vamos a comprobarlo primero. Contenido de este archivo:

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

Prepare dos archivos de script a continuación:
pre.awkarchivo, el contenido es el siguiente:

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

graphviz.awkarchivo, el contenido es el siguiente:

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 "}"}

Escriba otro script run.shpara llamar a estos dos scripts pre.awky tree.awk, su contenido es el siguiente:

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

Una vez que el archivo esté listo, debe otorgar permisos de ejecución a estos scripts. El comando es el siguiente:

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

Para evitar el pánico causado por algunos amigos que no han instalado gawky no pueden ejecutar el script debido a errores, a continuación se proporcionan los comandos de instalación de los dos paquetes de software:graphviz

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

Comience a ejecutar run.shel script y *.dotse generará el archivo, aquí está 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

Luego use dotla herramienta para exportarlo como PNGuna imagen o PDFcomo un archivo:

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

El siguiente es el diagrama de árbol de sintaxis abstracta ( ) correspondiente al código fuente anterior AST. Puede comparar ast.c.004t.originalel contenido del archivo y encontrará que el contenido interno se describe en una forma más clara de diagramas de bloques y flechas, lo que facilita que las personas interpretar. Aunque el código fuente es muy simple, si los amigos "sin fundamento" y "no listo" todavía se deslumbran cuando ven el árbol de sintaxis abstracta correspondiente.
árbol de sintaxis abstracta
No importa, el autor intentará estudiar y analizar este deslumbrante árbol de sintaxis abstracta ( AST) con todos. Analizamos ast.c.004t.originalel archivo y el árbol de sintaxis anterior al mismo tiempo por analogía, para que podamos entender ambos.

1.3.2 Interpretación simple de AST (Abstract Syntax Tree)

Si necesita averiguar qué significa cada nodo en el árbol de sintaxis abstracta, es decir, qué representa cada cuadro en la figura anterior, debe comenzar con el código gccfuente )gcc para describe De hecho, esto se hace para implementar un nombre descriptivo común para cada nodo, y cada estructura agregada dentro se usa como la estructura de almacenamiento de características real, que es un nombre colectivo para todas estas estructuras de almacenamiento de características. Echemos un vistazo a los nodos que representan diferentes significados contenidos en el árbol de sintaxis abstracta ( ) definido en .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;
};

Arriba, el autor dio algunas notas sobre el significado de los nodos. A continuación, enumeramos los nodos necesarios para este análisis, que se pueden ver de manera más intuitiva.

Tipo de estructura en tree_node nombre de la estructura describir
struct tree_identifier GTY ((etiqueta ("TS_IDENTIFIER"))) identificador nodo identificador
estructura árbol_función_decl GTY ((etiqueta ("TS_FUNCTION_DECL"))) function_decl nodo de declaración de función
estructura tree_var_decl GTY ((etiqueta ("TS_VAR_DECL"))) var_decl nodo de declaración de variables
estructura tree_exp GTY ((etiqueta ("TS_EXP"))) Exp nodo de expresión
estructura tree_result_decl GTY ((etiqueta ("TS_RESULT_DECL"))) result_decl nodo de valor de retorno

Primero vuelve al ast.ccódigo fuente :

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

    int result = 0;

    result = a + b / a;

    return 0;
}

El código fuente es muy simple, solo hay una mainfunción , que define tres intvariables de tipo a, by resultasigna un valor inicial y una declaración de asignación de expresión de línea de operación result = a + b / a. Como se puede ver en la tabla anterior, alas definiciones y expresiones en el código fuente se utilizan principalmente en el árbol de bsintaxis abstracta. Los nodos del árbol almacenan los nodos de declaración de variables y los nodos utilizados para almacenar expresiones . Si los conoce, puede verlos a través de Genere el árbol de sintaxis abstracta correspondiente .resultresult = a + b / astruct tree_var_declstruct tree_expgcc -fdump-tree-original-rawast.c.004t.original

1.3.2.1 Estructura del nodo AST

En primer lugar, necesitamos saber qué significa cada campo en una línea ast.c.004t.originalen . Cada nodo está compuesto por tres unidades de la última versión. La información básica de un nodo (una línea o varias líneas debido a la longitud) es el siguiente .

campo Estructura de almacenamiento de características correspondiente describir
etiqueta ( TREE_CODE) enumerar código_árbol Enumeración del ID de nodo de árbol TREE_CODE
nombre ( NAME) const char * const tree_code_name[] Matriz de cadenas de nombres de nodos de árbol
tipo ( TREE_CODE_CLASS) enum tree_code_class
const char * const tree_code_class_strings[]
const enum tree_code_class tree_code_type[]
Enumeración de tipos de nodos de árboles
Matriz de cadenas de nombres de tipos de
nodos de árboles Matriz de tipos de nodos de árboles indexados por TREE_CODE
longitud del operando ( len) const char sin firmar tree_code_length[] Número de operandos de nodo de árbol

1.3.2.2 Análisis de cada nodo de AST

@1 nodo
Borre el contenido anterior, ahora analícelo desde el primer nodo del ast.c.004t.original
archivo .

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

Se puede ver que la etiqueta del nodo TREE_CODEes @1, es decir, es el nodo 1No. , y el nombre del nodo NAMEes statement_list.De esto, se puede ver que la estructura de almacenamiento característica del nodo del árbol de sintaxis correspondiente al nodo es tree_node.tree_statement_list, y el nodo describe una lista de sentencias. Se puede ver que hay dos sentencias 0sentencia y 1sentencia en la lista de sentencias, 0la sentencia apunta @2al nodo y 1la sentencia apunta @3al nodo . La estructura de su diagrama es la siguiente.
inserte la descripción de la imagen aquí

@2 nodos

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

Primero @1mire 0la declaración en la lista de declaraciones descrita por el nodo. Puede ver que la etiqueta del nodo TREE_CODEes @2el nombre del nodo NAMEes bind_expr. A partir de esto, se puede ver que el nodo describe una expresión y su característica correspondiente la estructura de almacenamiento es tree_node.tree_exp, la estructura Puede encontrar más tipos de expresiones que se pueden describir en el archivo ./gcc/tree.def en el directorio del código fuente.
En primer lugar, hemos dejado en claro que el nodo describe una expresión de 1, typeel campo describe el tipo de valor de la expresión y typeapunta al @4nodo y este último describe los operandos de la expresión. total Hay dos operandos que varsapuntan al @5nodo y bodyapuntan al @6nodo. La estructura de su diagrama es la siguiente.

inserte la descripción de la imagen aquí

Podemos verificar el tipo de operando del nodo en el archivo ./gcc/tree.def :bind_exprBIND_EXPR

  • BIND_EXPR_VARS: apunta al nodo que describe la variable;
  • BIND_EXPR_BODY: apunta al cuerpo de la función de expresión utilizado para calcular la variable;
  • BIND_EXPR_BLOCK: el operando de bloque generalmente se usa para la depuración y no se analizará aquí.

Viendo aquí ya podemos corresponder al contenido del nodo.

campo de nodo nodo señalador describir
tipo @4 el tipo de valor de la expresión
vars @5 operando de expresión, variable ( BIND_EXPR_VARS)
cuerpo @6 el cuerpo de la expresión ( BIND_EXPR_BODY)

@4 nodo
A continuación, analice los elementos de este nodo uno por uno, primero mire @4el nodo ,

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

El nombre del nodo NAMEes void_type, se puede ver que el nodo describe un tipo (de hecho, ya se ve por el nombre que la descripción es voidun tipo ), y a continuación se hace un breve análisis del mismo.

campo de nodo nodo señalador describir
nombre @8 el nombre del tipo
alguno - Generalmente, este campo apunta a un nodo de constante entera.Aquí, el valor 8significa que la precisión es 8bit, y está 8bitalineado

@8 nodo y @21 nodo

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

Mirando de nuevo @8el nodo NAME, type_decles un nodo de un tipo declarado, y su namecampo indica que el nombre de este tipo apunta @21al nodo, y typeel campo apunta al nodo de tipo de este tipo, que puede apuntar de nuevo @4al nodo . Mirando @21el nodo , puede ver intuitivamente que el nodo es un nodo identificador y el nombre del carácter del identificador es void. La estructura de su diagrama es la siguiente.

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

Poco conocimiento: aquí hay una pequeña explicación para el nodo identificador.

campo nodo_identificador describir
control stringRepresenta el nombre del identificador como una cadena ( )
largo longitud del identificador ( length)

inserte la descripción de la imagen aquí

El nodo @5
luego retrocede y analiza el siguiente campo del @2nodo , que es su primer operando 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;                         

Poco conocimiento: Aquí está el resultado del análisis detallado del @5nodo var_decl, que es el nodo. Los socios interesados ​​también pueden obtener el resultado basado en el análisis del árbol de sintaxis abstracta.

campo var_decl describir @5valor de nodo
nombre El nombre de la variable descrita por este nodo. a
tipo el tipo de variable descrito por este nodo int
ámbito Alcance ( scope) main()alcance de la función
srcp programa fuente ( source progarm) en ast.cfuente 3línea línea
en eso valor inicial ( initial) El valor inicial es10
tamaño tamaño variable 32bit
alguno bits de alineación ( align) Presione para 32bitalinear
usado Citas 1de segunda categoría

Por el nombre del nodo var_decl, sabemos que el nodo describe la declaración de una variable. Aunque el nodo tiene muchos campos, de hecho, cuando analizamos la declaración de una variable, a menudo solo nos importan tres cosas, a saber, el nombre de la variable, el tipo y valor inicial de la variable. , es decir, el contenido de los tres campos de name, typey en initeste nodo.
@10 nodo
De acuerdo con los hábitos habituales, primero veamos el tipo de la variable type: @10. Dado que el análisis del tipo se ha seguido y verificado en detalle anteriormente, no lo explicaré en detalle aquí. El método de análisis es el mismo que La siguiente lista incluye información de varios nodos clave relacionados con ella.

# 类型节点——声明整型类型节点
@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;              

Poco conocimiento: Aquí está el resultado del análisis detallado del @10nodo integer_type, que es el nodo. Los socios interesados ​​también pueden obtener el resultado basado en el análisis del árbol de sintaxis abstracta.

campo tipo_entero describir @10valor de nodo
nombre El nombre de la descripción del nodo. int
tamaño el tamaño del tipo descrito por este nodo 32bit
alguno bits de alineación ( align) Presione para 32bitalinear
perc La precisión del tipo. El tipo es 32bitde precisión.
firmar Con o sin signo signedtipo firmado
min valor mínimo -2^31 = -2147483648
máximo valor máximo 2^31 = 2147483648

Se puede ver en los nodos anteriores que la variable de este @5nodo es intuna variable de tipo. Veamos el nombre de la variable de la variable name: @9.Se puede ver intuitivamente que el nombre de la variable debe ser a.

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

Veamos el valor inicial de esta variable init: @12, que es el 12nodo . Se puede ver claramente que el nodo es un nodo constante entero integer_cstcuyo valor es int: 10. La estructura de su diagrama es la siguiente.

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

inserte la descripción de la imagen aquí

到这里我们就清楚了 @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 文件中索引找到,如下。
modificar_expr
在该文件中指出,MODIFY_EXPR 表达式有两个操作数,其中 Operand 0 是需要被赋值的操作数,Operand 1 就是将要赋值给 Operand 0 的具体值。用 c 语言表达就相当于是 op_0 = op_1;,这样子的一条简单的赋值语句。图示结构如下。
inserte la descripción de la imagen aquí

@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)。其图示结构如下。
inserte la descripción de la imagen aquí
@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;,其图示结构如下。
devolver 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)图示结果。
inserte la descripción de la imagen aquí

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 的特点

A través de la comparación anterior, el código intermedio ( GIMPLE) es un tipo de código temporal con isomorfismo que es más fácil de manejar para el compilador. Los códigos intermedios comunes incluyen "código de tres direcciones", "código P", etc.

  • GIMPLEIntroduzca una variable temporal para guardar el resultado intermedio y divida ASTla expresión en una tupla de no más de tres operandos ( Tuples);
  • Dado que GIMPLEla forma es esencialmente una secuencia de código lineal, todas las expresiones computacionales se expresan como una serie de operaciones básicas. ASTLas estructuras de control en (p. ej., if-else, for, whileetc. GIMPLE) se convierten en instrucciones de salto condicional ( gotocompletadas con ) en la representación;
  • ASTSe suprime el alcance léxico ( Lexical Scopes) GIMPLEen .

1.5 Generación de código ensamblador

1.6 Generación de código de objeto

2. Fase de vinculación

3. Fase de reubicación

Supongo que te gusta

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