GCC-3.4.6源代码学习笔记

大约4年前,我加入了GDNT - 北电网络在中国的合资企业,参与3G UMTS无线接入网的研发工作。与GCC有了第一次亲密的接触(之前使用的是MS的VC)。彼时,北电在其诸如,UMTS、CDMA、及自行开发的众多工具等项目中(此后,在4G项目,Wimax及Lte中),将GCC作为标准编译器来使用。每周我都需要进行数次的loadbuild,编译出load文件进行测试,以验证我对一些bug的修正代码。每次loadbuild,编译的文件数以千计,最后的执行文件可至百兆的尺寸。而GCC的运行令人惊异地稳定。这些年来,我只碰到过2次GCC的崩溃,其中一次还是因为我为了达到模板分离编译(separate compilation of template)的效果(GCC至今未支持这一特性。据我所知,目前只有EDG的前端能做到。而且在C++标准稳定下来之前【ISO-IEC - 14882-1998】,EDG的前端是实际的标准。在http://www.edg.com/可以找到有关EDG的情况),使用了奇怪的代码(含有错误的C++代码),而导致生成错误的中间树代码(很遗憾,GCC未能检出语法错误)。从而触发了GCC内部的断言。因此GCC虽然退出,但给出了详细的错误转储。还算退出得比较体面。

这个神奇的工具令人称奇!虽然以前我也学过编译原理,但是面对GCC,我觉得对它知之甚少。多亏GCC是开源的,使我得以窥探其神秘面纱后的容颜。这些年,虽然自觉对GCC已有相当的了解,但远还未到究竟。借此博客,我将和大家分享这些年来学习GCC的笔记(所关注的GCC为:3.4.6版,C++前端,运行平台:x86/Linux,目标机器:x86/Linux)。而这个笔记还在增长中,还远未结束。

参考文献

[1] Programming language pragmatics, 2nd edition

[2] gccint, version 3.4.6

[3] ISO-IEC-14882-2003

[4] The C Preprocessor April 2001, for GCC V3

[5] cppinternals

[6] Using the GNU Compiler Collection

[7] Inside The C++ Object Model, by Stanley B.Lippman

[8] GCC Complete Reference

[9] The design and evolution of C++, by Bjarne Stroustrup

[10] Linkers & Loaders, by John R. Levine

[11] Efficient Instruction Scheduling Using Finite State Automata, by Vasanth Bala, Norman Rubin

[12] Compilers: Principles, Techniques, and Tools, 2nd edition

其中,[2]和[5]保存在目录“YOUR-GCC-SOURCE-DIR/gcc/doc”下。[1],[7],[9],[10]和[12]给出了一些有用的背景知识。

准备工作

一些重要的GCC源码是由GCC自带工具生成的。在深入看代码前,我们首先需要编译GCC,生成这些源码。至于如何下载源代码(http://gcc.gnu.org/mirrors.html是官方的下载地址),编译前配置及编译,网上有丰富的资料,在此略过(注:如果已经装有GCC,使用g++ -###可查看GCC的配置命令)。

GCC的架构

GCC可简单地分为2部分:前端和后端。预处理器(如果存在)、词法分析器和语法分析器在前端实现,前端的作用是将源语言写的程序转换成与语言无关的中间形式。因此,理论上,引入对新语言的支持,只需要实现预处理器、词法分析器和语法分析器。但实际上,一般的我们还需要写一些代码以确立运行时环境。

在GCC 3.4.6,这个通用的中间语言是RTL(register transfer language)。RTL是一种简单的语言,很容易就能翻译成汇编代码。因此,直接将源语言转换为RTL,不太合适。事实上,前端首先会将代码转换成中间树并进行大量的处理,然后再进一步变换为RTL,并送入后端。

后端的作用则是生成汇编代码。作为广泛使用的编译器,GCC可以支持各种流行的平台。为了实现这个目的,GCC采用了机器描述文件,对目标机器的指令集、流水线结构、甚至架构本身带来的优化机会进行描述。对于目标机器(配置GCC时需指定),这些描述文件将被多个工具处理,生成相应的源代码,然后用于编译GCC。因此,引入新机器的主要工作,在于提供机器描述文件(一般而言还需要定义一些处理函数提供必要的处理逻辑)。

在以下地址,http://www.ibm.com/developerworks/cn/linux/l-gcc4/?ca=dwcn-newsletter-linux,可以找到关于GCC较新版本的信息。

前端

当我们调用 ”gcc –o xxx xxx.c” 时,事实上我们的请求会转交给一个shell,这个shell会解析这个调用命令,并根据它所能认识的命令行选项,在所运行的平台上进行相应的准备。然后根据源文件的后缀,调用相匹配的编译器,并将其不能识别的命令行选项一并传入。这里我们不关心这个shell,我们专注于真正的编译器。

1.      概览

前端读入源程序进行语法分析,然后将其转换为语言无关形式。理论上,每个前端可以使用独特的形式。不过在GCC中,为了尽可能地重用代码,不同前端生成的中间树都使用同一组树节点。显然,这组节点需要足够的多样性以满足不同语言的要求。这组树节点对于C/C++前端的意义非同寻常,我们首先需要对其着手。

1.1. 前端中树的表达形式

为了支持已经存在及以后将会加入的前端,GCC定义了数十种的树节点(不是所有节点都能作为叶子节点)。所有的节点,在其结构体定义中,以下面的结构作为第一个成员。

 

129  struct tree_common GTY(())                                                                              in tree.h

130  {

131    tree chain;

132    tree type;

133 

134    ENUM_BITFIELD(tree_code) code : 8;

135 

136    unsigned side_effects_flag : 1;

137    unsigned constant_flag : 1;

138    unsigned addressable_flag : 1;

139    unsigned volatile_flag : 1;

140    unsigned readonly_flag : 1;

141    unsigned unsigned_flag : 1;

142    unsigned asm_written_flag: 1;

143    unsigned unused_0 : 1;

144 

145    unsigned used_flag : 1;

146    unsigned nothrow_flag : 1;

147    unsigned static_flag : 1;

148    unsigned public_flag : 1;

149    unsigned private_flag : 1;

150    unsigned protected_flag : 1;

151    unsigned deprecated_flag : 1;

152    unsigned unused_1 : 1;

153 

154    unsigned lang_flag_0 : 1;

155    unsigned lang_flag_1 : 1;

156    unsigned lang_flag_2 : 1;

157    unsigned lang_flag_3 : 1;

158    unsigned lang_flag_4 : 1;

159    unsigned lang_flag_5 : 1;

160    unsigned lang_flag_6 : 1;

161    unsigned unused_2 : 1;

162  };

 

上面,在134行,在3.4.6版,ENUM_BITFIELD将被扩展为”__extension__ enum”, 131行的chain,如果需要,能将该节点链入树。

下面给出了,结构体中部分的标识域的含义,及访问它们的宏定义(红字部分)。

Ø         TREE_TYPE ((NODE)->common.type)

用于所有表示表达式的节点,代表该表达式的数据类型。

²        在POINTER_TYPE节点中,代表该指针指向的类型。

²        在ARRAY_TYPE节点中,代表数组元素的类型。

²        在VECTOR_TYPE节点中,代表vector元素的类型(某些芯片的寄存器足够大,足以存放多个标量,这一组标量称为vector类型)。

Ø         TREE_ADDRESSABLE((NODE)->common.addressable_flag)

²        在VAR_DECL节点中,非零值表示该节点代表的变量的地址,在程序的其他地方被使用,不能产生将该变量置入寄存器的代码。

²        在FUNCTION_DECL节点中,非零值表示该节点代表的函数的地址,在程序的其他地方被使用。该函数必须被编译出来即便它是内联函数(在被调用的地方,内联函数被展开为函数体,故不需要单独编译出来)。

²        在FIELD_DECL节点中,非零值表示该节点代表的成员的地址允许被程序员设定。彼时,该标识用于别名识别:参见函数record_component_aliases

²        在CONSTRUCTOR节点中,非零值表示该构造函数必须在内存中创建对象(而不是在寄存器中)。

²        在LABEL_DECL节点中,非零值表示一条目标为该lable的goto语句,出现在所有执行跳转到该label的goto语句,需要恢复栈的绑定域外。

²        在所有*_TYPE节点中,非零值表示该类型的所有对象必须是完全取址的。这意味着,例如:这些对象任何一部分都不能放入寄存器中。

²        在IDENTIFIER_NODE节点中,非零值表示某些同名的外部声明的地址已被引用。这关系到内联函数。

Ø         TREE_STATIC ((NODE)->common.static_flag)

²        在VAR_DECL节点中,非零值表示该变量为静态类型。

²        在FUNCTION_DECL节点中,非零值表示该函数已被定义。

²        In a CONSTRUCTOR, nonzero means allocate static storage.

Ø         TREE_VIA_VIRTUAL ((NODE)->common.static_flag)

²        在TREE_LIST或TREE_VEC节点中,非零值表示所对应的类,其派生关系为虚继承(virtual)。

Ø         TREE_CONSTANT_OVERFLOW ((NODE)->common.static_flag)

²        在INTEGER_CST,REAL_CST,COMPLEX_CST或 VECTOR_CST节点中,非零值表示在常量折叠中发生溢出。它与TREE_OVERFLOW的区别在于,如果常量表达式发生溢出,ANSI C要求给出诊断信息。

Ø         TREE_SYMBOL_REFERENCED

(IDENTIFIER_NODE_CHECK (NODE)->common.static_flag)

²        在IDENTIFIER_NODE节点中,非零值表示该字串作为参数调用了函数assemble_name

Ø         CLEANUP_EH_ONLY ((NODE)->common.static_flag)

²        在TARGET_EXPR,WITH_CLEANUP_EXPR,CLEANUP_STMT节点,或块(block)的清理(cleanup)链的节点中,非零值表示相关的清理只在异常抛出时执行,而在正常退出时不需要执行。

Ø         TREE_OVERFLOW ((NODE)->common.public_flag)

²        在INTEGER_CST,REAL_CST,COMPLEX_CST或VECTOR_CST节点中,非零值表示在常量折叠中发生溢出,并且这部分的警告还没有发出。TREE_OVERFLOW同时意味着TREE_CONSTANT_OVERFLOW,但反之不成立。

Ø         TREE_PUBLIC((NODE)->common.public_flag)

²        在VAR_DECL或FUNCTION_DECL节点中,非零值表示该名字可从模块(module)外访问。在IDENTIFIER_NODE节点中,非零值表示在域内(inner scope),已有该名字所代表的,能从模块(module)外访问的,外部链接性的声明(external declaration)。

Ø         TREE_PRIVATE ((NODE)->common.private_flag)

²        在C++里,在类中使用。

Ø         CALL_EXPR_HAS_RETURN_SLOT_ADDR ((NODE)->common.private_flag)

²        在CALL_EXPR节点中,非零值表示返回值的地址是参数链的一部分。

Ø         TREE_PROTECTED ((NODE)->common.protected_flag)

²        在C++中用于类。在BLOCK节点中,这是个BLOCK_HANDLER_BLOCK节点。

Ø         CALL_FROM_THUNK_P ((NODE)->common.protected_flag)

²        在CALL_EXPR节点中,非零值表示该函数调用是从thunk到thunk目标函数的跳转。

Ø         TREE_SIDE_EFFECTS ((NODE)->common.side_effects_flag)

²        在所有的表达式中,非零值表示该表达式含有副作用(side effects)或者对其每次求值(reevaluation of the whole expression)将产生不同的值。如果其子表达式是一个函数调用、对一个volatile变量的引用或含有副作用,这个标识符将被设置。

²        In a *_DECL, this is set only if the declaration said `volatile'.

Ø         TREE_THIS_VOLATILE ((NODE)->common.volatile_flag)

²        非零值表示该表达式在C的含义下(in the C sense)是volatile:它的地址应该是类型`volatile WHATEVER *'。换而言之,所声明的项(item)是volatile修饰的(volatile qualified)。该标识符用于*_DECL和*_REF节点中。

²        在*_TYPE节点中,非零值表示其对应的类型为volatile修饰的(volatile-qualified)。不过如果节点代表一个类型,应该使用TYPE_VOLATILE而不是这个宏。因为,以后这2个宏会访问不同的位(现在是相同的,见下一条)。如果这个位被设上,TREE_SIDE_EFFECTS也应该同时被设上。

Ø         TYPE_VOLATILE (TYPE_CHECK (NODE)->common.volatile_flag)

²        非零值表示该类型作为整体是volatile的。

Ø         TREE_READONLY ((NODE)->common.readonly_flag)

²        在VAR_DECL,PARM_DECL或者FIELD_DECL,或者所有*_REF类型节点中,非零值表示它不能作为左值(the lhs of an assignment)。

²        在*_TYPE节点中,非零值表示其对应的类型为常量(const-qualified)类型(但是如果节点表示类型则应该使用宏TYPE_READONLY)。

Ø         TYPE_READONLY (TYPE_CHECK (NODE)->common.readonly_flag)

²        非零值表示该节点代表的类型为常量类型。

Ø         TREE_CONSTANT ((NODE)->common.constant_flag)

²        非零值表示表达式的值为常量。在所有的*_CST节点中设置。也可能出现在数学表达式中,ADDR_EXPR或者CONSTRUCTOR节点中,如果它们的值为常量。

Ø         TREE_UNSIGNED ((NODE)->common.unsigned_flag)

²        在INTEGER_TYPE或者ENUMERAL_TYPE节点中,非零值表示其对应类型为无符号类型。在FIELD_DECL节点中,则表示为无符号域(unsigned bit field)。

Ø         TREE_ASM_WRITTEN ((NODE)->common.asm_written_flag)

²        在VAR_DECL节点中表示汇编代码也被写入。

²        在FUNCTION_DECL节点中表示该函数已被编译。在内联函数中,这个位很有意义,因为内联函数可能不需要独立编译(作为函数)。

²        在RECORD_TYPE,UNION_TYPE,QUAL_UNION_TYPE或者ENUMERAL_TYPE节点中,表示有关类型的sdb调试信息已经写入。

²        在BLOCK节点中,非零值表示在这个分段(block)内,reorder_block节点已发现。

Ø         TREE_USED ((NODE)->common.used_flag)

²        在*_DECL节点中表示,在其作用域中,相应的名字被引用(is used in its scope)。

²        在表达式节点中,表示如果其值未被使用,不发出警告。

²        在IDENTIFIER_NODE节点中,表示同名的外部声明已被引用(was used)。

Ø         TREE_NOTHROW ((NODE)->common.nothrow_flag)

²        在FUNCTION_DECL节点中,表示该函数不会抛出异常。在CALL_EXPR节点中,则表示该次调用不会抛出异常。

Ø         TYPE_ALIGN_OK (TYPE_CHECK (NODE)->common.nothrow_flag)

²        用在表示类型的节点中,非零值表示所有该类型的对象由语言或前端保证,被正确地对齐。因此,我们可以得知该类型的MEM节点(RTL节点)的对齐量不少于该类型的对齐量,尽管它可能看起来不是这样。在面向对象语言中,这种情况发生在一个要求更多对齐量量的变种类型的对象上(in object-oriented languages where a tag field may show this is an object of a more-aligned variant of the more generic type)。

Ø         TREE_DEPRECATED ((NODE)->common.deprecated_flag)

²        在IDENTIFIER_NODE节点中,该名字的使用是被__attribute__((deprecated))所不推荐的。

列表 1:tree_common中的标识符

1.1.1. 树节点的定义

以下是中间树的节点的定义。

 

45    typedef union tree_node *tree;                                                              in coretypes.h

 

1772 union tree_node GTY ((ptr_alias (union lang_tree_node),                              intree.h

1773                   desc ("tree_node_structure (&%h)")))

1774 {

1775   struct tree_common GTY ((tag ("TS_COMMON"))) common;

1776   struct tree_int_cst GTY ((tag ("TS_INT_CST"))) int_cst;

1777   struct tree_real_cst GTY ((tag ("TS_REAL_CST"))) real_cst;

1778   struct tree_vector GTY ((tag ("TS_VECTOR"))) vector;

1779   struct tree_string GTY ((tag ("TS_STRING"))) string;

1780   struct tree_complex GTY ((tag ("TS_COMPLEX"))) complex;

1781   struct tree_identifier GTY ((tag ("TS_IDENTIFIER"))) identifier;

1782   struct tree_decl GTY ((tag ("TS_DECL"))) decl;

1783   struct tree_type GTY ((tag ("TS_TYPE"))) type;

1784   struct tree_list GTY ((tag ("TS_LIST"))) list;

1785   struct tree_vec GTY ((tag ("TS_VEC"))) vec;

1786   struct tree_exp GTY ((tag ("TS_EXP"))) exp;

1787   struct tree_block GTY ((tag ("TS_BLOCK"))) block;

1788 };

 

显然,这个节点需要定义为union。注意第1772行的ptr_alias,它告诉GTY (GCC的废料回收系统(garbage collection service),我们暂时不理会它),指向tree_node的指针实际上指向lang_tree_node,这个节点由前端通过tree_node追加额外的特定于语言的成员来定义(因此,它可被视为tree_node)。在C++前端中,lang_tree_node有如下定义:

 

472  union lang_tree_node GTY((desc ("cp_tree_node_structure (&%h)"),             incp-tree.h

473         chain_next ("(union lang_tree_node *)TREE_CHAIN (&%h.generic)")))

474  {

475    union tree_node GTY ((tag ("TS_CP_GENERIC"),

476                      desc ("tree_node_structure (&%h)"))) generic;

477    struct template_parm_index_s GTY ((tag ("TS_CP_TPI"))) tpi;

478    struct ptrmem_cst GTY ((tag ("TS_CP_PTRMEM"))) ptrmem;

479    struct tree_overload GTY ((tag ("TS_CP_OVERLOAD"))) overload;

480    struct tree_baselink GTY ((tag ("TS_CP_BASELINK"))) baselink;

481    struct tree_wrapper GTY ((tag ("TS_CP_WRAPPER"))) wrapper;

482    struct tree_default_arg GTY ((tag ("TS_CP_DEFAULT_ARG"))) default_arg;

483    struct lang_identifier GTY ((tag ("TS_CP_IDENTIFIER"))) identifier;

484  };


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/454853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

互联网

2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/u/3127489/blog/1550726

GCC笔记 命令行分析

1984年,Richard Stallman发起了自由软件运动,GNU (Gnus Not Unix)项目应运而生,3年后,最初版的GCC横空出世,成为第一款可移植、可优化、支持ANSI C的开源C编译器。GCC最初的全名是GNU C Compiler,之后,随着…

java 反射用法_Java 反射的概念与使用

一,反射的概念对于一个人来说,了解自己的能力、本事、特点,对于他去干事创业来说,是很重要的。同样的,对于一门面向对象的语言来说,了解类(对象其实就是类的实现)本身也是重要的,可以在很多地方…

关于Unity中的Mesh Collider碰撞器

原来我的场景中有一个平面Plane带Mesh Collider碰撞器组件,一个主角Hero带有一个Box Collider碰撞器和有重力的Rigidbody刚体组件,主角可以放在平面上。 在导入场景后,隐藏平面Plane,给一个地板添加一个Mesh Collider碰撞器&#…

GCC常用选项使用详解

通常所说的GCC是GUN Compiler Collection的简称,除了编译程序之外,它还含其他相关工具,所以它能把易于人类使用的高级语言编写的源代码构建成计算机能够直接执行的二进制代码。GCC是Linux平台下最常用的编译程序,它是Linux平台编译…

java 井字棋 人机_井字游戏 人机对战 java实现

package com.ecnu.Main;/*** 主函数触发游戏*/public class MainApplication {public static void main(String[] args){TicTacToeGame ticTacToeGame new TicTacToeGame();ticTacToeGame.start();}}//TicTacToeGame 方法类import java.util.Scanner;public class TicTacToeGa…

Session(数据)共享的前后端分离Shiro实战

1,前言本文期望描述如何使用Shiro构建基本的安全登录和权限验证。本文实战场景有如下特殊需求:1,在集群和分布式环境实现session共享;2,前端只使用HTML/CSS/JS。因此无法直接使用Shiro提供的SessionManager&#xff0c…

读书笔记(javascript 高级程序设计)

一. 数据类型: 1. undefined: 未声明和未初始化的变量,typeof 操作符返回的结果都是 undefined;(建议未初始化的变量进行显式赋值,这样当 typeof 返回 undefined 时就知道是未声明了,帮助定位问…

关于gcc扩展中的宏定义中用 # 和 ##

关于gcc扩展中的宏定义中用 "#" 和 "##"今天测试了宏定义中的 "#" 和 "##" 的区别。 结果如下: "#" 代表和一个字符串相连接 "##" 代表和一个符号连接,符号可以是变量,或另一…

java 年计算_java实现计算某年某月的天数

在计算某年某月的天数时,需要注意平年闰年。分析:闰年具体的判定方法就要看它的判定条件:四年一闰 , 百年不闰 ,400年再闰。而计算该年该月的天数,又分大月和小月,特殊月份2月之分。(视频教程推…

添加自定义菜单,报错40155

2019独角兽企业重金招聘Python工程师标准>>> 提交的json中,某个自定义菜单对应的URL访问是有问题的,请挨个检查一下。 转载于:https://my.oschina.net/selly1025/blog/1551496

gcc编译流程及中间表示层RTL的探索

gcc编译流程及中间表示层RTL的探索收藏新一篇: 解读VC编程中的文件操作API和CFile类 | 旧一篇: Effective Item21 尽可能使用const 内容摘要 本文将以 C 语言为例,介绍 gcc 在接受一个 .c文件的输入之后,其前端是如何进行处理并得到一个中间表示并转交给…

【bzoj2132】圈地计划 网络流最小割

题目描述 最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地。据了解,这块土地是一块矩形的区域,可以纵横划分为NM块小区域。GDOI要求将这些区域分为商业区和工业区来开发。根据不同的地形环境,每…

python爬虫爬取数据如何将br去掉_Python怎么去除爬取下来的网站中的一些转义字符串 - 收获啦...

基本方法其实用python爬取网页很简单,只有简单的几句话这样就可以获得到页面的内容。接下来再用正则匹配去匹配所需要的内容就行了。但是,真正要做起来,就会有各种各样的细节问题。2.登录这是一个需要登录认证的网站。也不太难,只…

Linux基础

Linux的特点: 系统版本:常见的有debian、Redhat更适合做服务器,更安全和稳定,Ubuntu唯一的优势就是图形界面好,centos目前被redhat收购,红旗已经倒闭。 1、免费的/开源的;2、支持多线程/多用户&…

GCC的编译和调试--入门介绍

编译与调试1.1编译的概念和理解在进行C程序开发时,编译就是将编写的C语言代码变成可执行程序的过程,这一过程是由编译器来完成的。编译器就是完成程序编译工作的软件,在进行程序编译时完成了一系列复杂的过程。1.1.1程序编译的过程在执行这一…

A* a=new B ,会不会产生内存泄露了,露了B-A的部分?

A* anew B ,delete a;会不会产生内存泄露了,露了B-A的部分。其中B为A的子类 析构函数在下边3种情况时被调用:1.对象生命周期结束,被销毁时;2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基…

spring 第一天:1015

对象加强的三种方法:1/继承2/装饰着模式3/动态调用 2:装饰着模式:就是就是1-先建一个基类 ,如咖啡类 。味道很苦2- 再建一个类配料类 也就是说是所欲配料种类的父类。然后写多配料子类个子类继承配料类,。3-子类三个步…

java public 继承_java继承问题

代码:父类:public class Father {public Father() {System.out.println("基类构造函数{");show();new a();System.out.println("}");}public void show() {System.out.println("基类----show");}public class a {public a…

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圆环数(数位DP+恶心细节)

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圆环数 Time Limit: 5 Sec Memory Limit: 64 MBDescription 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺序。她们甚至也不能通过仍硬币的方式。 所以她们通过"round number&q…