lcc源代码解析之x86后端

在前面的文章中已经介绍过,lcc中跟硬件平台相关的配置由src中*.md配置,本文以x86为例,详解这一部分的工作机制。

熟悉汇编的同学都知道32位x86机器有八个通用寄存器:

eax ebx ecx edx esi edi esp ebp

而ebp和esp两个寄存器是有固定作用的,其保存的帧指针和栈指针是组成栈帧的基本组成部分,所以这两个寄存器不参与分配。

所以参与分配的通用寄存器只有六个,lcc用枚举将这两个寄存器编号:

enum { EAX=0, ECX=1, EDX=2, EBX=3, ESI=6, EDI=7 };


在x86.md的最下面是一个Interface结构体的填充,关于Interface结构体中字段的详细作用,请看这里。

其代码如下:

Interface x86IR = {
        1, 1, 0,  /* char */
        2, 2, 0,  /* short */
        4, 4, 0,  /* int */
        4, 4, 0,  /* long */
        4, 4, 0,  /* long long */
        4, 4, 1,  /* float */
        8, 4, 1,  /* double */
        8, 4, 1,  /* long double */
        4, 4, 0,  /* T * */
        0, 1, 0,  /* struct */
        1,        /* little_endian */
        0,        /* mulops_calls */
        0,        /* wants_callb */
        1,        /* wants_argb */
        0,        /* left_to_right */
        0,        /* wants_dag */
        0,        /* unsigned_char */
        address,
        blockbeg,
        blockend,
        defaddress,
        defconst,
        defstring,
        defsymbol,
        emit,
        export,
        function,
        gen,
        global,
        import,
        local,
        progbeg,
        progend,
        segment,
        space,
        0, 0, 0, 0, 0, 0, 0,
        {1, rmap,
            blkfetch, blkstore, blkloop,
            _label,
            _rule,
            _nts,
            _kids,
            _string,
            _templates,
            _isinstruction,
            _ntname,
            emit2,
            doarg,
            target,
            clobber,
}
};

这个结构体填充之后,一个完整的编译器就可以工作了,前端就可以通过通过函数指针驱动后端生成代码了。


这里要插入一个知识点Iburg的介绍,lburg是一个代码生成器的生成器,以lburg规范为语法,类似yacc和lex可以生成词法分析器和语法分析器一样。

lburg接收紧缩规范并产生一个用C语言编写的树分析程序,该程序为目标机器选择指令。

lburg规范的核心是树文法,与常规的文法类似,树文法也是一个规则列表,每个规则的左边是一个非终结符号,右边是终结符号和非终结符号组成的模式。

具体EBNF表示如下(term表终结符号,nonterm表示非终结符号):

grammar:

'%{' configration '%}' { dcl } %% {rule} [%% C code]

dcl:

%start nonterm

%term { term = integer }

rule:

nonterm: tree template [ C expression ]

tree:

term[ '(' tree [, tree] ')']

nonterm

template:

" { any charactor except double quote} "

lburg以行为单位,%{  %[  %%必须单独一行,configuration是C语言代码,每个dcl和rule必须出现在一行上。


lburg翻译程序由lburg文件夹编译生成,在一级目录下执行make lburg即可,makefile中相关代码如下:

lburg:	$Blburg$E
$Blburg$E:	$Blburg$O $Bgram$O;	$(LD) $(LDFLAGS) -o $@ $Blburg$O $Bgram$O 

$Blburg$O $Bgram$O:	lburg/lburg.h

$Blburg$O:	lburg/lburg.c;	$(CC) $(CFLAGS) -c -Ilburg -o $@ lburg/lburg.c
$Bgram$O:	lburg/gram.c;	$(CC) $(CFLAGS) -c -Ilburg -o $@ lburg/gram.c
由于这一些列文章主要是研究编译器本身的,对于lburg程序就不在继续深入分享,有兴趣的同学可以自己深入理解。

有了lburg,就可以将*.md翻译.c文件,makefile中相关部分代码如下:

$Bdagcheck.c:	$Blburg$E src/dagcheck.md; $Blburg src/dagcheck.md $@
$Balpha.c:	$Blburg$E src/alpha.md;    $Blburg src/alpha.md    $@
$Bmips.c:	$Blburg$E src/mips.md;     $Blburg src/mips.md     $@
$Bsparc.c:	$Blburg$E src/sparc.md;    $Blburg src/sparc.md    $@
$Bx86.c:	$Blburg$E src/x86.md;      $Blburg src/x86.md      $@
$Bx86linux.c:	$Blburg$E src/x86linux.md; $Blburg src/x86linux.md $@

可以看到x86.md和x86linux.md被翻译成x86.c和x86linux.c,这就是本篇blog重点分析的部分。

下面就是生成的x86linux.c,这个文件比较长,原因在于计算指令耗损的_label函数占用了较大的篇幅,为了便于理解,下面的只挑出来一些重要的部分进行源码分析。


你可能感兴趣的:(cpp&compliers)