热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GCC–structure/union前端解析说明

  以GCC8.2.0版本为例,介绍gcc语法解析器(p


  以GCC8.2.0版本为例,介绍gcc语法解析器(parser)对声明即函数定义的解析过程以及structure/union的简单解析说明。


1. GCC中声明和定义的解析过程


1.1 解析入口 c_parse_file

  GCC中gcc/c/c-parser.c文件主要负责解析GNU C的完整语法。其中单个源码文件的解析入口在void c_parse_file (void)函数中,函数代码如下:
parse1
  先对c_parser struct进行初始化,再判断其pragma_kind。代码中的c_parser_translation_unit函数主要为解析当前translation unit(TU)。最后每一个parser解析完成时会被置为NULL,又开始解析下一个源文件。
  其中一个c_parser对应着一个.c文件,c_parser struct解析器结构记录了相关解析状态和上下文的信息,以及词法分析器信息等,其代码如下:
parse2


1.2 解析TU c_parser_translation_unit

  translation unit是gcc整个语法产生式的开始点,其注释说明如下:

/* Parse a translation unit (C90 6.7, C99 6.9, C11 6.9).
translation-unit:
external-declarations
external-declarations:
external-declaration
external-declarations external-declaration
GNU extensions:
translation-unit:
empty
*/

  函数中ggc_collect函数进行一些顶级标记和清除处理,c_parser_external_declaration函数中进行非终结符语法解析,代码如下:
parse3


1.3 外部声明 c_parser_external_declaration

  根据解析c_parser的&parser->tokens[0]->type执行不同规则的流程,即不同的语法产生式会走不同的执行路径。部分代码如下:
parse4


1.4 声明解析 c_parser_declaration_or_fndef

  该方法首先会收集构建成声明或函数定义的信息,然后将它们组合成一个声明。其主要信息包含声明说明符(c_declspecs)、声明符(c_declarator),初始化值,参数列表等。该方法中首先根据build_null_declspecs方法创建一个structure的空declaration specifiers list,其后进行一系列的解析和处理操作。总体来说一个完整的声明包括start_decl、finish_decl两部分,其中一个非常重要的结构体是c_declspecs,主要用于在解析过程中存放声明信息。
build_null_declspecs函数代码如下:
parse5
c_declspecs struct的定义如下:

/* A sequence of declaration specifiers in C. When a new declaration
specifier is added, please update the enum c_declspec_word above
accordingly. */

struct c_declspecs {
source_location locations[cdw_number_of_elements];
/* The type specified, if a single type specifier such as a struct,
union or enum specifier, typedef name or typeof specifies the
whole type, or NULL_TREE if none or a keyword such as "void" or
"char" is used. Does not include qualifiers. */

tree type;
/* Any expression to be evaluated before the type, from a typeof
specifier. */

tree expr;
/* The attributes from a typedef decl. */
tree decl_attr;
/* When parsing, the attributes. Outside the parser, this will be
NULL; attributes (possibly from multiple lists) will be passed
separately. */

tree attrs;
/* The pass to start compiling a __GIMPLE or __RTL function with. */
char *gimple_or_rtl_pass;
/* The base-2 log of the greatest alignment required by an _Alignas
specifier, in bytes, or -1 if no such specifiers with nonzero
alignment. */

int align_log;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
/* For the _FloatN and _FloatNx declspec, this stores the index into
the floatn_nx_types array. */

int floatn_nx_idx;
/* The storage class specifier, or csc_none if none. */
enum c_storage_class storage_class;
/* Any type specifier keyword used such as "int", not reflecting
modifiers such as "short", or cts_none if none. */

ENUM_BITFIELD (c_typespec_keyword) typespec_word : 8;
/* The kind of type specifier if one has been seen, ctsk_none
otherwise. */

ENUM_BITFIELD (c_typespec_kind) typespec_kind : 3;
/* Whether any expressions in typeof specifiers may appear in
constant expressions. */

BOOL_BITFIELD expr_const_operands : 1;
/* Whether any declaration specifiers have been seen at all. */
BOOL_BITFIELD declspecs_seen_p : 1;
/* Whether something other than a storage class specifier or
attribute has been seen. This is used to warn for the
obsolescent usage of storage class specifiers other than at the
start of the list. (Doing this properly would require function
specifiers to be handled separately from storage class
specifiers.) */

BOOL_BITFIELD non_sc_seen_p : 1;
/* Whether the type is specified by a typedef or typeof name. */
BOOL_BITFIELD typedef_p : 1;
/* Whether the type is explicitly "signed" or specified by a typedef
whose type is explicitly "signed". */

BOOL_BITFIELD explicit_signed_p : 1;
/* Whether the specifiers include a deprecated typedef. */
BOOL_BITFIELD deprecated_p : 1;
/* Whether the type defaulted to "int" because there were no type
specifiers. */

BOOL_BITFIELD default_int_p : 1;
/* Whether "long" was specified. */
BOOL_BITFIELD long_p : 1;
/* Whether "long" was specified more than once. */
BOOL_BITFIELD long_long_p : 1;
/* Whether "short" was specified. */
BOOL_BITFIELD short_p : 1;
/* Whether "signed" was specified. */
BOOL_BITFIELD signed_p : 1;
/* Whether "unsigned" was specified. */
BOOL_BITFIELD unsigned_p : 1;
/* Whether "complex" was specified. */
BOOL_BITFIELD complex_p : 1;
/* Whether "inline" was specified. */
BOOL_BITFIELD inline_p : 1;
/* Whether "_Noreturn" was speciied. */
BOOL_BITFIELD noreturn_p : 1;
/* Whether "__thread" or "_Thread_local" was specified. */
BOOL_BITFIELD thread_p : 1;
/* Whether "__thread" rather than "_Thread_local" was specified. */
BOOL_BITFIELD thread_gnu_p : 1;
/* Whether "const" was specified. */
BOOL_BITFIELD const_p : 1;
/* Whether "volatile" was specified. */
BOOL_BITFIELD volatile_p : 1;
/* Whether "restrict" was specified. */
BOOL_BITFIELD restrict_p : 1;
/* Whether "_Atomic" was specified. */
BOOL_BITFIELD atomic_p : 1;
/* Whether "_Sat" was specified. */
BOOL_BITFIELD saturating_p : 1;
/* Whether any alignment specifier (even with zero alignment) was
specified. */

BOOL_BITFIELD alignas_p : 1;
/* Whether any __GIMPLE specifier was specified. */
BOOL_BITFIELD gimple_p : 1;
/* Whether any __RTL specifier was specified. */
BOOL_BITFIELD rtl_p : 1;
/* The address space that the declaration belongs to. */
addr_space_t address_space;
};

  c_parser_declaration_or_fndef函数中还有两个相关的比较重要的函数分别为:c_parser_declspecs、finish_declspecs。部分代码如下:
parse6


2. 结构体、联合体的解析说明

  类型的解析是在c_parser_declspecs函数中,无论是系统默认的类型还是用户自定义的类型,在解析过程中c_parser_declspecs函数会被递归调用,直到解析完最后一行以分号结尾的代码为止。


2.1 c_parser_declspecs

  该方法中依照语法产生式循环解析各种说明符,包括解析前的各种错误和警告检查,当所有的检查通过时来到具体说明符的真正解析位置,通过c_parser_peek_token (parser)函数解析出parser->token[0]信息,并根据其keyword关键字来判断走哪一个switch case。解析完成之后会将此说明符解析出来的信息放置在声明说明符c_declspecs中。
例如结构体、联合体的keyword判断为:
parse7
  以上代码中包含两个重要函数:c_parser_struct_or_union_specifier、declspecs_add_type。c_parser_struct_or_union_specifier函数主要是解析结构或联合说明符,declspecs_add_type函数是把存储类说明符或函数说明符信息添加到声明说明符数据结构中。


2.2 c_parser_struct_or_union_specifier

  解析结构体或联合体说明符,struct/union的处理相关函数有:start_struct(解析struct/union的定义,并在解析组件之前启动标签的作用域,准备相关的数据结构)、c_parser_struct_declaration(以分号为单位,解析出一个声明列表)、chainon(将c_parser_struct_declaration解析出来的声明列表和之前的串联起来)、finish_struct(完成结构体的定义,这包括布局结构体的空间,实际分配空间给各成员,计算出结构体的对齐方式、机器模式、大小)、parser_xref_tag(在声明结构体变量的时候会被执行)。
  部分代码如下所示:

struct c_typespec ret;
tree attrs;
tree ident = NULL_TREE;
location_t struct_loc;
location_t ident_loc = UNKNOWN_LOCATION;
enum tree_code code;
switch (c_parser_peek_token (parser)->keyword)
{
case RID_STRUCT:
code = RECORD_TYPE;
break;
case RID_UNION:
code = UNION_TYPE;
break;
default:
gcc_unreachable ();
}
//...
/* Parse a struct or union definition. Start the scope of the
tag before parsing components. */

struct c_struct_parse_info *struct_info;
tree type = start_struct (struct_loc, code, ident, &struct_info);
//...
/* Parse some comma-separated declarations, but not the
trailing semicolon if any. */

decls = c_parser_struct_declaration (parser);
contents = chainon (decls, contents);
//...
ret.spec = finish_struct (struct_loc, type, nreverse (contents),
chainon (attrs, postfix_attrs), struct_info);
ret.kind = ctsk_tagdef;
ret.expr = NULL_TREE;
ret.expr_const_operands = true;
timevar_pop (TV_PARSE_STRUCT);
return ret;
}
else if (!ident)
{
c_parser_error (parser, "expected %<{%>");
ret.spec = error_mark_node;
ret.kind = ctsk_tagref;
ret.expr = NULL_TREE;
ret.expr_const_operands = true;
return ret;
}
ret = parser_xref_tag (ident_loc, code, ident);
return ret;

2.3 c_parser_struct_declaration

  函数c_parser_struct_declaration处理是以分号为单位的结构体成员声明。也就是说这些声明符共用一个声明说明符,但声明符和位域都是需要分别处理,其注释说明如下:
parse8
  该方法中主要调用的方法有:c_parser_declspecs、finish_declspecs、grokfield。部分代码如下:
parse9


2.4 finish_declspecs

  在获取关于一个声明的所有说明信息后,调用函数finish_declspecs按照c_declspecs中的各布尔变量给出相应的类型节点, 比如unsigned int a; gcc在获得unsigned int信息后,调用函数finish_declspecs,将specs->type 置为unsigned_type_node。即函数finish_declspecs用来确定一个声明的类型信息。只有经过该函数给出节点的类型信息之后,其后的grokfield函数才能正确将所有声明整合成一个真正的GCC declare。
  部分代码如下所示:
parse10


2.5 grokfield

  grokfield函数的作用便是把函数c_parser_struct_declaration处理以分号为单位的结构体成员声明,通过grokfield函数将这些分别处理的声明符和位域整合成一个真正的GCC声明。我们获得了声明说明符和声明符(可能会有位宽),于是我们有足够的信息去合成一个结构体成员。其代码如下:
parse11
  该函数的主要处理步骤为:如果是普通的标识符则进入此条件的语句块中;通过grokdeclarator糅合成一个FIELD_DECL声明;完成结构体的定义


2.6 示例

对于如下示例结构体:

typedef struct _PixelPacket{
char rt, gt, bt, ot;
}PixelPacket;

  结构体的声明会在c_parser_declaration_or_fndef中进行处理,结构体的定义作为一个类型会在c_parser_declspec函数中进行解析,对于结构体定义他会直接跳转到函数c_parser_declaration_or_fndef。函数c_parser_declspecs会解析struct定义并将解析出来的类型放入声明说明符结构体c_declspec中,finish_declspecs会将声明说明符结构体中的内容进行解析,例如基本类型int 在c_declspec中是cst_int,会在函数finish_declspecs中找到其对应的树类型节点。
  在c_parser_struct_or_union_specifier函数中c_parser_struct_declaration方法执行结束之后整个主要的类型解析过程基本也快结束了,此时得到的tree decls通过gdb,我们可以看到其类型已经是一个FIELD_DECL了,如下:
parse12
  此时我们再从decls->decl_common.common.common.typed.type中继续查找,但由于此时的数据内存我们不能访问,所以我们gdb往后执行多遍之后,便能找到其中的一些信息,如下:
parse13
  在这便能通过decls得到structure的field type信息,同样field declare信息也可以通过decls->decl_minimal.name->identifier.id.str的chainon链表循环获得。

以上相关函数在解析过程中的调用层次如下图所示:
parse14



推荐阅读
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
  • 本文详细解析了 Android 系统启动过程中的核心文件 `init.c`,探讨了其在系统初始化阶段的关键作用。通过对 `init.c` 的源代码进行深入分析,揭示了其如何管理进程、解析配置文件以及执行系统启动脚本。此外,文章还介绍了 `init` 进程的生命周期及其与内核的交互方式,为开发者提供了深入了解 Android 启动机制的宝贵资料。 ... [详细]
  • 本文探讨了如何利用Java代码获取当前本地操作系统中正在运行的进程列表及其详细信息。通过引入必要的包和类,开发者可以轻松地实现这一功能,为系统监控和管理提供有力支持。示例代码展示了具体实现方法,适用于需要了解系统进程状态的开发人员。 ... [详细]
  • 在当前的软件开发领域,Lua 作为一种轻量级脚本语言,在 .NET 生态系统中的应用逐渐受到关注。本文探讨了 Lua 在 .NET 环境下的集成方法及其面临的挑战,包括性能优化、互操作性和生态支持等方面。尽管存在一定的技术障碍,但通过不断的学习和实践,开发者能够克服这些困难,拓展 Lua 在 .NET 中的应用场景。 ... [详细]
  • 在Ubuntu上安装MySQL时解决缺少libaio.so.1错误及libaio在MySQL中的重要性分析
    在Ubuntu系统上安装MySQL时,遇到了缺少libaio.so.1的错误。本文详细介绍了如何解决这一问题,并深入探讨了libaio库在MySQL性能优化中的重要作用。对于初学者而言,理解这些依赖关系和配置步骤是成功安装和运行MySQL的关键。通过本文的指导,读者可以顺利解决相关问题,并更好地掌握MySQL在Linux环境下的部署与管理。 ... [详细]
  • 每年,意甲、德甲、英超和西甲等各大足球联赛的赛程表都是球迷们关注的焦点。本文通过 Python 编程实现了一种生成赛程表的方法,该方法基于蛇形环算法。具体而言,将所有球队排列成两列的环形结构,左侧球队对阵右侧球队,首支队伍固定不动,其余队伍按顺时针方向循环移动,从而确保每场比赛不重复。此算法不仅高效,而且易于实现,为赛程安排提供了可靠的解决方案。 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
  • Java并发机制详解及其在数据安全性保障中的应用方案 ... [详细]
  • V8不仅是一款著名的八缸发动机,广泛应用于道奇Charger、宾利Continental GT和BossHoss摩托车中。自2008年以来,作为Chromium项目的一部分,V8 JavaScript引擎在性能优化和技术创新方面取得了显著进展。该引擎通过先进的编译技术和高效的垃圾回收机制,显著提升了JavaScript的执行效率,为现代Web应用提供了强大的支持。持续的优化和创新使得V8在处理复杂计算和大规模数据时表现更加出色,成为众多开发者和企业的首选。 ... [详细]
  • 在安装 iOS 开发所需的 CocoaPods 时,用户可能会遇到多种问题。其中一个常见问题是,在执行 `pod setup` 命令后,系统无法连接到 GitHub 以更新 CocoaPods/Specs 仓库。这可能是由于网络连接不稳定、GitHub 服务器暂时不可用或本地配置错误等原因导致。为解决此问题,建议检查网络连接、确保 GitHub API 限制未被触发,并验证本地配置文件是否正确。 ... [详细]
  • 2018 HDU 多校联合第五场 G题:Glad You Game(线段树优化解法)
    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6356在《Glad You Game》中,Steve 面临一个复杂的区间操作问题。该题可以通过线段树进行高效优化。具体来说,线段树能够快速处理区间更新和查询操作,从而大大提高了算法的效率。本文详细介绍了线段树的构建和维护方法,并给出了具体的代码实现,帮助读者更好地理解和应用这一数据结构。 ... [详细]
  • 分享一款基于Java开发的经典贪吃蛇游戏实现
    本文介绍了一款使用Java语言开发的经典贪吃蛇游戏的实现。游戏主要由两个核心类组成:`GameFrame` 和 `GamePanel`。`GameFrame` 类负责设置游戏窗口的标题、关闭按钮以及是否允许调整窗口大小,并初始化数据模型以支持绘制操作。`GamePanel` 类则负责管理游戏中的蛇和苹果的逻辑与渲染,确保游戏的流畅运行和良好的用户体验。 ... [详细]
author-avatar
萍萍jean
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有