技术标签: input 汇编 file c gcc constants GCC后端及汇编发布
为了使 GCC 以高效、方便的形式移植到其它机器(架构上), GCC 需要芯片的机器描述文件( MD 文件)。为了描述芯片,一系列称为模式( pattern )的定义被引入。通常,我们需要从两方面来描述芯片。
首先是在 RTL 形式定义的指令集——包括,指令看起来像什么( define_insn 模式);哪个指令序列比其它等效的指令序列效率更高( define_peephole 及 define_peephole2 模式);如何把一个复杂指令分解成多个简单些的指令,然后其中一个可以被放入延迟槽( delay slot ),或填充流水线(模式 define_split 及 define_insn_split ,填充延迟槽及流水线的考量,参考后面的 genattrtab工具 );如何把一个复杂指令分解成多个简单指令,使得 define_insn 模式能够被匹配( define_expand )。
其次是架构的描述;我们知道同一系列的不同芯片可能具有不同的功能单元,不同的流水线结构。为了尽可能挖掘芯片的能力,我们需要告诉编译器这些细节。这个描述同样以 RTL 语言来定义。
因此有两个大致上人类可读的描述形式,为了把描述转换为一个可以被包括在 GCC 源代码中的形式,其开发者设计了一系列的工具。这些工具是如此的重要,没有它们后端什么也做不了。它们不仅对汇编代码的产生起重要的作用,而且向接近机器层面的优化的执行提供基础(对于 V4 版本,引入了名为 SSA 的一层,来增强编译器接近源代码层面的优化。通过机器描述文件, V3 版本能够在低层面提供强大的优化,然而在这一低级形式下,能用于依赖分析的信息,比如类型与别名,都被剥除了。它使得编译器不能抓住优化的机会)。
在下面,我们将学习处理机器描述文件的大多数工具,来理解后端是如何组织的。在编译 GCC 时,这些工具将被首先编译出来,然后运行,解析目标机器的描述文件,产生相应的源代码。随后,才是编译 GCC 的源代码(就像我们在前端中看到的那些),连同这里产生的源代码,产生编译器。
在研究 genrecog 工具之前,首先需要研究 genconditions 工具,因为它产生被 genrecog 使用的文件 insn-conditions.c 。
182 int
183 main (int argc, char **argv) in genconditions.c
184 {
185 rtx desc;
186 int pattern_lineno; /* not used */
187 int code;
188
189 progname = "genconditions";
190
191 if (argc <= 1)
192 fatal ("No input file name.");
193
194 if (init_md_reader (argv[1]) != SUCCESS_EXIT_CODE)
195 return (FATAL_EXIT_CODE);
196
197 condition_table = htab_create (1000, hash_c_test, cmp_c_test, NULL);
注意,下面的 condition_table 与上面的 condition_table 是两个不同的静态变量,它们分别被声明在 genconditions.c 及 gensupport.c 文件中。 init_md_reader 将从机器描述文件读入 rtx 对象,它是一个公共函数,也被其他工具调用。
935 int
936 init_md_reader (const char *filename) in gensupport.c
937 {
938 FILE *input_file;
939 int c;
940 size_t i;
941 char *lastsl;
942
943 lastsl = strrchr (filename, '/');
944 if (lastsl != NULL)
945 base_dir = save_string (filename, lastsl - filename + 1 );
946
947 read_rtx_filename = filename;
948 input_file = fopen (filename, "r");
949 if (input_file == 0)
950 {
951 perror (filename);
952 return FATAL_EXIT_CODE;
953 }
954
955 /* Initialize the table of insn conditions. */
956 condition_table = htab_create (n_insn_conditions ,
957 hash_c_test, cmp_c_test, NULL);
958
959 for (i = 0; i < n_insn_conditions ; i++)
960 *(htab_find_slot (condition_table , &insn_conditions [i], INSERT))
961 = (void *) &insn_conditions [i];
962 obstack_init (rtl_obstack );
963 errors = 0;
964 sequence_num = 0;
965
966 /* Read the entire file. */
967 while (1)
968 {
969 rtx desc;
970 int lineno;
971
972 c = read_skip_spaces (input_file);
973 if (c == EOF)
974 break ;
975
976 ungetc (c, input_file);
977 lineno = read_rtx_lineno ;
978 desc = read_rtx (input_file);
979 process_rtx (desc, lineno);
980 }
981 fclose (input_file);
982
983 /* Process define_cond_exec patterns. */
984 if (define_cond_exec_queue != NULL)
985 process_define_cond_exec ();
986
987 return errors ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
988 }
read_skip_spaces 跳过空格及注释,获取一个有效字符。
102 int
103 read_skip_spaces (FILE *infile) in read-rtl.c
104 {
105 int c;
106
107 while (1)
108 {
109 c = getc (infile);
110 switch (c)
111 {
112 case '/n':
113 read_rtx_lineno ++;
114 break ;
115
116 case ' ': case '/t': case '/f': case '/r':
117 break ;
118
119 case ';':
120 do
121 c = getc (infile);
122 while (c != '/n' && c != EOF);
123 read_rtx_lineno ++;
124 break ;
125
126 case '/':
127 {
128 int prevc;
129 c = getc (infile);
130 if (c != '*')
131 fatal_expected_char (infile, '*', c);
132
133 prevc = 0;
134 while ((c = getc (infile)) && c != EOF)
135 {
136 if (c == '/n')
137 read_rtx_lineno ++;
138 else if (prevc == '*' && c == '/')
139 break ;
140 prevc = c;
141 }
142 }
143 break ;
144
145 default :
146 return c;
147 }
148 }
149 }
对于 md 文件,行开头的“ ; ” 表示该行是注释。同时,“ /* ”及“ */ ”对也作为注释使用。那么 read_rtx ,在 read_skip_spaces 的协助下,根据机器描述文件中的 rtx 定义,构建 rtx 对象。
509 rtx
510 read_rtx (FILE *infile) in read-rtl.c
511 {
512 int i, j;
513 RTX_CODE tmp_code;
514 const char *format_ptr;
515 /* tmp_char is a buffer used for reading decimal integers
516 and names of rtx types and machine modes.
517 Therefore, 256 must be enough. */
518 char tmp_char[256];
519 rtx return_rtx;
520 int c;
521 int tmp_int;
522 HOST_WIDE_INT tmp_wide;
523
524 /* Obstack used for allocating RTL objects. */
525 static struct obstack rtl_obstack;
526 static int initialized ;
527
528 /* Linked list structure for making RTXs: */
529 struct rtx_list
530 {
531 struct rtx_list *next;
532 rtx value; /* Value of this node. */
533 };
534
535 if (!initialized ) {
536 obstack_init (&rtl_obstack);
537 initialized = 1;
538 }
539
540 again:
541 c = read_skip_spaces (infile); /* Should be open paren. */
542 if (c != '(')
543 fatal_expected_char (infile, '(', c);
544
545 read_name (tmp_char, infile);
546
547 tmp_code = UNKNOWN;
548
549 if (! strcmp (tmp_char, "define_constants"))
550 {
551 read_constants (infile, tmp_char);
552 goto again;
553 }
554 for (i = 0; i < NUM_RTX_CODE; i++)
555 if (! strcmp (tmp_char, GET_RTX_NAME (i)))
556 {
557 tmp_code = (RTX_CODE) i; /* get value for name */
558 break ;
559 }
560
561 if (tmp_code == UNKNOWN)
562 fatal_with_file_and_line (infile, "unknown rtx code `%s'", tmp_char);
563
564 /* (NIL) stands for an expression that isn't there. */
565 if (tmp_code == NIL)
566 {
567 /* Discard the closeparen. */
568 while ((c = getc (infile)) && c != ')')
569 ;
570
571 return 0;
572 }
573
574 /* If we end up with an insn expression then we free this space below. */
575 return_rtx = rtx_alloc (tmp_code);
576 format_ptr = GET_RTX_FORMAT (GET_CODE (return_rtx));
在机器描述文件里,每个指令都被包括在括号对中, 542 行确保了这一点。然后 read_name 被调用来得到指令的名字。
154 static void
155 read_name (char *str, FILE *infile) in read-rtl.c
156 {
157 char *p;
158 int c;
159
160 c = read_skip_spaces (infile);
161
162 p = str;
163 while (1)
164 {
165 if (c == ' ' || c == '/n' || c == '/t' || c == '/f' || c == '/r')
166 break ;
167 if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/'
168 || c == '(' || c == '[')
169 {
170 ungetc (c, infile);
171 break ;
172 }
173 *p++ = c;
174 c = getc (infile);
175 }
176 if (p == str)
177 fatal_with_file_and_line (infile, "missing name or number");
178 if (c == '/n')
179 read_rtx_lineno ++;
180
181 *p = 0;
182
183 if (md_constants )
184 {
185 /* Do constant expansion. */
186 struct md_constant *def;
187
188 p = str;
189 do
190 {
191 struct md_constant tmp_def;
192
193 tmp_def.name = p;
194 def = htab_find (md_constants , &tmp_def);
195 if (def)
196 p = def->value;
197 } while (def);
198 if (p != str)
199 strcpy (str, p);
200 }
201 }
上面,在 183 行, md_constants 是一个哈希表实例( htab ),而在 186 行, md_constant 是两个 char 指针成员 name 及 value 的简单 struct 。
在机器描述文件中,有时会定义一些用在指令定义中的常量。这些常量的定义由 define_constants 表示。在上面 read_rtx 的 551 行,调用 read_constants 来处理这些定义。
421 static void
422 read_constants (FILE *infile, char *tmp_char) in read-rtl.c
423 {
424 int c;
425 htab_t defs;
426
427 c = read_skip_spaces (infile);
428 if (c != '[')
429 fatal_expected_char (infile, '[', c);
430 defs = md_constants ;
431 if (! defs)
432 defs = htab_create (32, def_hash, def_name_eq_p, (htab_del) 0);
433 /* Disable constant expansion during definition processing. */
434 md_constants = 0;
435 while ( (c = read_skip_spaces (infile)) != ']')
436 {
437 struct md_constant *def;
438 void **entry_ptr;
439
440 if (c != '(')
441 fatal_expected_char (infile, '(', c);
442 def = xmalloc (sizeof (struct md_constant));
443 def->name = tmp_char;
444 read_name (tmp_char, infile);
445 entry_ptr = htab_find_slot (defs, def, TRUE);
446 if (! *entry_ptr)
447 def->name = xstrdup (tmp_char);
448 c = read_skip_spaces (infile);
449 ungetc (c, infile);
450 read_name (tmp_char, infile);
451 if (! *entry_ptr)
452 {
453 def->value = xstrdup (tmp_char);
454 *entry_ptr = def;
455 }
456 else
457 {
458 def = *entry_ptr;
459 if (strcmp (def->value, tmp_char))
460 fatal_with_file_and_line (infile,
461 "redefinition of %s, was %s, now %s",
462 def->name, def->value, tmp_char);
463 }
464 c = read_skip_spaces (infile);
465 if (c != ')')
466 fatal_expected_char (infile, ')', c);
467 }
468 md_constants = defs;
469 c = read_skip_spaces (infile);
470 if (c != ')')
471 fatal_expected_char (infile, ')', c);
472 }
常量定义的形式就像: (define_constants [(name value)…(name value)]) , name 及 value 对将被提取出来,并保存入结构体 md_constant 中,这个结构体又被哈希表 md_constants 记录。
其他机器模式模式包括 define_insn , define_attr , define_peephole , define_split , define_expand , define_insn_and_split 等等。它们同样出现在文件 rtl.def 里。它们都是特殊的 rtx 对象,对于 i386 系统,我们找到如下的定义
192 DEF_RTL_EXPR(DEFINE_INSN, "define_insn", "sEsTV", 'x') in rtl.def
在这个定义中,我们知道第一个参数是 rtx 码,第二个参数是名字,第三个参数是格式,这里“ sEsTV ”表示该 rtx 对象最多有 5 个孩子,并且最后一个参数是 rtx 类别。让我们看一下格式及类别字符的含义。
对于格式,我们有:
"0" 该域是不使用的(或者依赖于阶段( a phase-dependent manner )),不输出任何东西
"i" 一个整数,输出整数
"n" 类似 "i" ,但从 `note_insn_name' 输出项
"w" 一个宽度为 HOST_BITS_PER_WIDE_INT 的整数,输出整数
"s" 一个字符串指针,输出该字符串
"S" 类似 "s" ,但是可选的:包含( containing ) rtx 可能在这个操作数之前结束
"T" 类似 "s" ,但被 RTL 输入器特殊处理;仅出现在机器描述模式中。
"e" 一个 rtl 表达式的指针,输出这个表达式
"E" 一个指向一组 rtl 表达式的 vector 的指针,输出这些 rtl 表达式的列表
"V" 类似 "E" ,但是可选的:包含( containing ) rtx 可能在这个操作数之前结束
"u" 指向另一个 insn 的指针,输出这个 insn 的 uid
"b" 一个位图( bitmap )头的指针
"B" 一个基本块( basic block )指针
"t" 一个树指针
对于类别,我们有:
"o" 一个可用于代表一个对象(比如 REG , MEM )的 rtx 码
"<" 一个表示比较 ( 比如 , EQ , NE , LT ) 的 rtx 码
"1" 用于一个一元算术表达式(比如, NEG , NOT )的 rtx 码
"c" 用于一个符合交换律的二元操作(比如, PLUS , MULT )的 rtx 码
"3" 用于一个非位域三输入操作( IF_THEN_ELSE )的 rtx 码
"2" 用于一个不符合交换律的二元操作(比如, MINUS , DIV )的 rtx 码
"b" 用于一个位域操作( ZERO_EXTRACT , SIGN_EXTRACT )的 rtx 码
"i" 用于一个机器 insn ( INSN , JUMP_INSN , CALL_INSN )的 rtx 码
"m" 用于表示 insn 中所匹配物件(比如, MATCH_DUP )的 rtx 码
"g" 用于集合 insn (比如, GROUP_PARALLEL )的 rtx 码
"a" 用于自增取址模式(比如, POST_DEC )的 rtx 码
"x" 其它
而其它相关的指令的 rtl 定义有:
200 DEF_RTL_EXPR(DEFINE_PEEPHOLE, "define_peephole", "EsTV", 'x')
211 DEF_RTL_EXPR(DEFINE_SPLIT, "define_split", "EsES", 'x')
239 DEF_RTL_EXPR(DEFINE_INSN_AND_SPLIT, "define_insn_and_split", "sEsTsESV", 'x')
243 DEF_RTL_EXPR(DEFINE_PEEPHOLE2, "define_peephole2", "EsES", 'x')
247 DEF_RTL_EXPR(DEFINE_COMBINE, "define_combine", "Ess", 'x')
260 DEF_RTL_EXPR(DEFINE_EXPAND, "define_expand", "sEss", 'x')
276 DEF_RTL_EXPR(DEFINE_DELAY, "define_delay", "eE", 'x')
因此在上面函数 read_rtx 的 554 行 , 在机器描述文件中的其它指令通过名字来组织 ,并提前出相应的 rtx 码。在 565 行,被 rtl 输入器及输出器使用的 NIL 代表一个空指针。这些指令被分配作 rtx 对象。然后 read_rtx 来处理这些指令。
以 i386.md 中的一个指令为例:
467 (define_insn "cmpdi_ccno_1_rex64" in i386.md
468 [(set (reg 17)
469 (compare (match_operand:DI 0 "nonimmediate_operand" "r,?mr")
470 (match_operand:DI 1 "const0_operand" "n,n")))]
471 "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)"
472 "@
473 test{q}/t{%0, %0|%0, %0}
474 cmp{q}/t{%1, %0|%0, %1}"
475 [(set_attr "type" "test,icmp")
476 (set_attr "length_immediate" "0,1")
477 (set_attr "mode" "DI")])
文章浏览阅读417次。含义:接口可重复调用后,在调用方多次调用的情况下,接口最终得到的结果是一致的。有些接口天然具备幂等性,如查询接口。除查询外,增加、更新、删除都要保证幂等性。如何保证幂等性?1.全局唯一ID(防止新增脏数据)如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在来判断这个操作是否已经执行,如果不存在则把全局唯一ID存储到存储系统中,比如数据库,re..._如何保证数据库增加的幂等
文章浏览阅读354次。如题,数组第一个元素为什么要从零开始,而不从一开始?感觉这很反人类呀,正常来讲,一个集合的开始,不应该从一吗?对于这个问题,我觉得可以从以下两方面来考虑。设计层面我们先了解一下数组最基本的结构和寻址方式(即实现方式)。现在市面上无论是C、Java、PHP,还是Go或者其他编程语言,他们数组的实现方式,应该都是一样的:一段连续的内存。数组在分配内存的时候,我们会知道数组的开始地址(PS:在目前下标为..._java 为什么使用数组
文章浏览阅读295次,点赞8次,收藏4次。魔兽世界plus怎么收费/怎么充值 手把手教你充值。魔兽世界plus怎么收费/怎么充值 手把手教你充值。,也就是我们玩家要需要。
文章浏览阅读902次。<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>表格内容的展开和折._html设置table展开怎么设置
文章浏览阅读1.6k次,点赞9次,收藏33次。综述: 该题为数据结构顺序表基本操作相关的题目,其中包括插入、寻找、删除以及创建线性表共4个要求,题目给了函数接口的定义,要求你写相关函数,我开始在做这个题的时候对这个题的意思有了点误解,题目说Last保存最后一个元素的位置,然后它要开MAXSIZE个int大小的空间,因为这个Last的描述我开始忽略了顺序表的特性,让我难受了好久。本篇博客大致讲解一下该题目的做法。题目:本题要求实现顺序表的操作集。函数接口定义:List MakeEmpty();..._6-2 顺序表操作集
文章浏览阅读316次,点赞3次,收藏10次。总体来说,ABBYY FineReader 15是一款功能强大、易于使用的OCR图片文字识别软件,适合个人和企业用户在多种场景下使用。然而,用户也需要考虑其价格和资源消耗等因素是否适合自己的需求。_orc识别软件
文章浏览阅读5.1k次,点赞12次,收藏28次。Hive 的Hive_x.x.x_bin.tar.gz 高版本在windows 环境中缺少 Hive的执行文件和运行程序。配置文件目录(%HIVE_HOME%\conf)有4个默认的配置文件模板拷贝成新的文件名。可以发现,自动连接MySQL去创建schema hive,并执行脚本。可以通过访问namenode和HDFS的Web UI界面(以及resourcemanager的页面(先在Hive安装目录下建立。根据自己的Hive安装路径(根据自己的Hive安装路径(请严格按照版本来安装。在Hadoop管理台(_windows安装hive
文章浏览阅读464次。$$f(x)={xˉ=∑1nxin均值S2=∑1n(x−xi)2n−1方差f(x)=\begin{cases} \bar{x}=\frac{ \sum_{1}^{n} x_{i} }{n}& \text{均值}\\S^2= \frac{ \sum_{1}^{n} (x- x_{i} )^2 }{n-1} & \text{方差}\end{cases}f(x)={xˉ=n∑1nxiS2=n−1∑1n(x−xi)2均值方差求样本方差为什么除n−1?求样本方差_设总体x~n(θ,1),gθ=θ2,试求gθ的无偏估计的方差下界
文章浏览阅读1.3k次,点赞2次,收藏51次。1Linux简介2Linux安装3Linux文件和目录管理4Linux打包(归档)和压缩5Vim文本编辑器6Linux文本处理(Linux三剑客)7Linux软件安装8Linux用户和用户组管理9Linux权限管理10Linux文件系统管理11Linux高级文件系统管理12Linux系统管理13Linux备份与恢复14Linux系统服务管理15Linux系统日志管理16Linux启动管理17LAMP环境搭建和LNMP环境搭建18SEL..._linux中大量使用脚本语言,而不是c语言!
文章浏览阅读1.7k次。c中base的用法的用法你知道吗?下面小编就跟你们详细介绍下c中base的用法的用法,希望对你们有用。c中base的用法的用法如下:1、调用基类中的重名方法[csharp]public class Person{protected string ssn = "444-55-6666";protected string name = "John L. Malgraine";public virtua..._c语言中base=16是什么意思
文章浏览阅读6.7k次,点赞3次,收藏7次。今天碰到这个问题时候,还真不好理解,只知道interface中的变量默认是被public static final 修饰的,接口中的方法是被public和abstrct修饰的。查阅了很多资料,做了些例子,得出以下结论,不足的地方希望大家指出。 Java代码 /* * 关于抽象类和接口 * * 1_接口参数都定义为final的作用
文章浏览阅读4.4k次,点赞3次,收藏25次。创建一个list列表> mylist <- list(stud.id = 1234,+ stud.name = "Tom",+ stud.marks = c(12, 3, 14, 25, 19))> mylist$stud.id[1] 1234$stud.name[1] "Tom"$stud.marks[1] 12 3 14 25 19取列表的值注..._r语言怎么查看list有几个项目