GCC后端及汇编发布(1)
2011-04-09 09:09
846 查看
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")])
相关文章推荐
- GCC后端及汇编发布(45)
- GCC后端及汇编发布(9)
- GCC后端及汇编发布(19)
- GCC后端及汇编发布(27-续)
- GCC后端及汇编发布(46)
- GCC后端及汇编发布(6)
- GCC后端及汇编发布(20)
- GCC后端及汇编发布(22)
- GCC后端及汇编发布(47)
- GCC后端及汇编发布(10)
- GCC后端及汇编发布(7)
- GCC后端及汇编发布(21)
- GCC后端及汇编发布(22 续)
- GCC后端及汇编发布(32)
- GCC后端及汇编发布(37)
- GCC后端及汇编发布 当前目录
- GCC后端及汇编发布(33)
- GCC后端及汇编发布(8)
- GCC后端及汇编发布(23)
- GCC后端及汇编发布(38)