您的位置:首页 > 其它

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")])
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: