GCC后端及汇编发布(1)_gcc的后端芯片架构-程序员宅基地

技术标签: input  汇编  file  c  gcc  constants  GCC后端及汇编发布  

1.        概览

为了使 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 的源代码(就像我们在前端中看到的那些),连同这里产生的源代码,产生编译器。

2.        genconditions 工具

2.1. 代码输出的准备

在研究 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);

2.1.1. 读入 rtx 形式的定义

注意,下面的 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")])

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wuhui_gdnt/article/details/6311208

智能推荐

数据库新增幂等操作_接口幂等性-程序员宅基地

文章浏览阅读417次。含义:接口可重复调用后,在调用方多次调用的情况下,接口最终得到的结果是一致的。有些接口天然具备幂等性,如查询接口。除查询外,增加、更新、删除都要保证幂等性。如何保证幂等性?1.全局唯一ID(防止新增脏数据)如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在来判断这个操作是否已经执行,如果不存在则把全局唯一ID存储到存储系统中,比如数据库,re..._如何保证数据库增加的幂等

java为什么要学习数组_为什么数组要从零开始?-程序员宅基地

文章浏览阅读354次。如题,数组第一个元素为什么要从零开始,而不从一开始?感觉这很反人类呀,正常来讲,一个集合的开始,不应该从一吗?对于这个问题,我觉得可以从以下两方面来考虑。设计层面我们先了解一下数组最基本的结构和寻址方式(即实现方式)。现在市面上无论是C、Java、PHP,还是Go或者其他编程语言,他们数组的实现方式,应该都是一样的:一段连续的内存。数组在分配内存的时候,我们会知道数组的开始地址(PS:在目前下标为..._java 为什么使用数组

魔兽世界plus怎么收费/怎么充值 手把手教你充值-程序员宅基地

文章浏览阅读295次,点赞8次,收藏4次。魔兽世界plus怎么收费/怎么充值 手把手教你充值。魔兽世界plus怎么收费/怎么充值 手把手教你充值。,也就是我们玩家要需要。

表格内容的展开和折叠效果《超实用的JavaScript代码段》笔记_html设置table展开怎么设置-程序员宅基地

文章浏览阅读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展开怎么设置

PTA 6-2 顺序表操作集 (20 分)-容易做错-程序员宅基地

文章浏览阅读1.6k次,点赞9次,收藏33次。综述: 该题为数据结构顺序表基本操作相关的题目,其中包括插入、寻找、删除以及创建线性表共4个要求,题目给了函数接口的定义,要求你写相关函数,我开始在做这个题的时候对这个题的意思有了点误解,题目说Last保存最后一个元素的位置,然后它要开MAXSIZE个int大小的空间,因为这个Last的描述我开始忽略了顺序表的特性,让我难受了好久。本篇博客大致讲解一下该题目的做法。题目:本题要求实现顺序表的操作集。函数接口定义:List MakeEmpty();..._6-2 顺序表操作集

ABBYY2024免费永久版OCR图片文字识别软件_orc识别软件-程序员宅基地

文章浏览阅读316次,点赞3次,收藏10次。总体来说,ABBYY FineReader 15是一款功能强大、易于使用的OCR图片文字识别软件,适合个人和企业用户在多种场景下使用。然而,用户也需要考虑其价格和资源消耗等因素是否适合自己的需求。_orc识别软件

随便推点

Windows下安装Hive(包安装成功)_windows安装hive-程序员宅基地

文章浏览阅读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

无偏估计+求样本的方差为何除n-1_设总体x~n(θ,1),gθ=θ2,试求gθ的无偏估计的方差下界-程序员宅基地

文章浏览阅读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∑1n​xi​​S2=n−1∑1n​(x−xi​)2​​均值方差​求样本方差为什么除n−1?求样本方差_设总体x~n(θ,1),gθ=θ2,试求gθ的无偏估计的方差下界

Linux学习教程,Linux入门教程(超详细)| 网址推荐_linux中大量使用脚本语言,而不是c语言!-程序员宅基地

文章浏览阅读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语言!

c语言函数base,c中base的用法-程序员宅基地

文章浏览阅读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是什么意思

为什么接口中变量要用final修饰_接口参数都定义为final的作用-程序员宅基地

文章浏览阅读6.7k次,点赞3次,收藏7次。今天碰到这个问题时候,还真不好理解,只知道interface中的变量默认是被public static final 修饰的,接口中的方法是被public和abstrct修饰的。查阅了很多资料,做了些例子,得出以下结论,不足的地方希望大家指出。 Java代码 /* * 关于抽象类和接口 * * 1_接口参数都定义为final的作用

【R语言学习笔记】6、List列表详解_r语言怎么查看list有几个项目-程序员宅基地

文章浏览阅读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有几个项目

推荐文章

热门文章

相关标签