版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lidan113lidan/article/details/119942740
系列文章:
- GCC源码分析(一) — 输入参数解析
- GCC源码分析(二) — 词法分析
- GCC源码分析(三) — 词法符号转语法符号
- GCC源码分析(四) — 语法/语义分析之声明说明符的解析
- GCC源码分析(五) — 语法/语义分析之声明符解析(上)
- GCC源码分析(六) — 符号绑定,作用域与block树节点
- GCC源码分析(七) — 语法/语义分析之声明符解析(下)
- GCC源码分析(八) — 语法/语义分析之声明与函数定义的解析
- GCC源码分析(九) — gcc全局符号表与符号的分析(gimplify)
- GCC源码分析(十) — 函数节点的gimple高端化
- GCC源码分析(十一) — 函数节点的gimple低端化
- GCC源码分析(十二) — gimplify之后的基本流程
- GCC源码分析(十三) — 机器描述文件
- GCC源码分析(十四) — rtx结构体,指令与栈分配
- GCC源码分析(十五) — gimple转RTL(pass_expand)(上)
- GCC源码分析(十六) — gimple转RTL(pass_expand)(下)
- GCC源码分析(十七) — rtl expand之后
只以编译阶段为例的话,gcc的参数解析分为两部分:
-
一部分是参数从命令行到gcc
-
一部分是参数从gcc到最终的编译程序cc1
1. 从命令行到gcc
编译出来的gcc二进制程序实际上只是起到一个桩代码的作用,或者说只是一个编译的驱动,其内部实际上是要分别调用cc1,as,collect来进行编译,汇编与连接。gcc的基本流程如下图:
gcc在解析了输入参数后,会为每一个输入文件启动一次cc1来做具体的编译,而cc1一次只能处理一个源码的编译,如果输入多个文件则会报错,如:
$ ./cc1 main.c asm.c
cc1: error: too many filenames given. Type cc1 --help for usage
gcc和cc1的参数解析实际上用的是同一套代码,其首先依赖于编译期间生成的用户参数数组:
#./build-gcc/gcc/options.c //注意这个是gcc编译的输出目录,此文件是编译期间生成的
const struct cl_option cl_options[] =
{
/* [0] = */ {
"-###",
NULL,
NULL,
NULL,
NULL, NULL, N_OPTS, N_OPTS, 3, /* .neg_idx = */ -1,
CL_DRIVER,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(unsigned short) -1, 0, CLVC_BOOLEAN, 0, -1, -1 },
/* [1] = */ {
"--all-warnings",
NULL,
NULL,
NULL,
NULL, NULL, OPT_Wall, N_OPTS, 13, /* .neg_idx = */ -1,
CL_Ada | CL_AdaSCIL | CL_AdaWhy | CL_C | CL_CXX | CL_Fortran | CL_ObjC | CL_ObjCXX | CL_WARNING,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(unsigned short) -1, 0, CLVC_BOOLEAN, 0, -1, -1 },
......
这个数组共有上千项,其中记录了在gcc和cc1中所有可用的参数,参数分为以下几大类:
##./build-gcc/gcc/options.h //具体语言相关的参数
#define CL_Ada (1U << 0)
#define CL_AdaSCIL (1U << 1)
#define CL_AdaWhy (1U << 2)
#define CL_BRIG (1U << 3)
#define CL_C (1U << 4)
#define CL_CXX (1U << 5)
#define CL_D (1U << 6)
#define CL_Fortran (1U << 7)
#define CL_Go (1U << 8)
#define CL_LTO (1U << 9)
#define CL_ObjC (1U << 10)
#define CL_ObjCXX (1U << 11)
#define CL_LANG_ALL ((1U << 12) - 1)
##./gcc/opts.h //非语言相关的参数
#define CL_PARAMS (1U << 16) /* Fake entry. Used to display --param info with --help. */
#define CL_WARNING (1U << 17) /* Enables an (optional) warning message. */
#define CL_OPTIMIZATION (1U << 18) //优化相关的参数
#define CL_DRIVER (1U << 19) //仅供gcc使用的参数
#define CL_TARGET (1U << 20) //目标平台特有参数
#define CL_COMMON (1U << 21) //通用参数
数组中cl_optons[n].flags 为CL_DRIVER的参数,即为gcc driver使用的参数,这样的参数如 -Wl, --entry等,除了CL_DRIVER外,其他的参数基本都是要传给cc1的。
2. 从gcc到cc1
在cc1中参数解析一共经历了三个阶段:
-
argvs => save_decoded_options
-
save_decoded_options =>global_options等真正影响编译的flags
-
对global_options等参数的处理
其中:
-
argvs => save_decoded_options
save_decoded_options[]数组中的每一个元素都是一个cl_decoded_option结构体,其记录了输入参数的字符串,参数值,与此参数相关的cl_options数组索引等,此索引与枚举类型
opt_code的下标一一对应。
struct cl_decoded_option
{
size_t opt_index; //代表当前参数的类型的index,也是此参数在 cl_options 数组中的索引,如 OPT_SPECIAL_input_file 代表此参数是输入文件名
const char *warn_message; //使用此参数是否要先提示个warning信息
const char *arg; //解析出的此参数的具体字符串, 如"-fdump-passes"
const char *orig_option_with_args_text;
const char *canonical_option[4];
size_t canonical_option_num_elements;
HOST_WIDE_INT value; //参数的值,对于大部分没有值的参数会是0/1
int errors; //代表检查出此参数设置错误
};
struct cl_decoded_option *save_decoded_options;
unsigned int save_decoded_options_count; //save_decoded_options数组的元素个数
这一步实际上是在decode_cmdline_options_to_array中完成的,其调用路径为:
## toplev::main => decode_cmdline_options_to_array_default_mask => decode_cmdline_options_to_array
最终实际上调用的函数展开后是:
decode_cmdline_options_to_array(argc, argv, CL_C|CL_COMMON|CL_TARGET, &save_decoded_options, &save_decoded_options_count);
2.save_decoded_options =>global_options等真正影响编译的flags
save_decoded_options只是用来记录输入参数相关信息的一个数组,输入参数最终都是以flag的形式在编译过程中起作用的,而global_options中则记录了大部分参数相关的flags,这一步的作用就是将所有参数翻译为各种flags.
这一步是在decode_options中完成的,其调用路径为: toplev::main => decode_options
decode_options的参数如下,其将saved_decoded_options中的大部分参数都转化为global_options中的flags
decode_options (&global_options, &global_options_set, save_decoded_options, save_decoded_options_count,
UNKNOWN_LOCATION, global_dc, targetm.target_option.override);
此外handle_common_deferred_options ();函数中会对部分标记为CLVC_DEFER的参数做统一的解析:
## toplev::main =>handle_common_deferred_options
3.对global_options等参数的处理
这里包括了对要解析的编译单元文件的读取(=>parse_in),检查输入文件个数等,其他后续补充:
## toplev::main => do_compile => process_options