编译原理 第7章 目标代码生成
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
①已定位的机器语言代码 编译后可立即执行。 ②待装配的机器语言代码模块 目前大多数编译系统所产生的目标代码均为可重定位的机器指令。在执行前, 将它和系统函数(源程序中使用)的机器指令连接成一个程序,对未确定的地 址进行定位(假设起始地址为0),形成可执行的机器语言代码程序。 在执行时,操作系统可将目标代码装入内存的任意位置。 ③汇编语言代码 需经汇编程序翻译,将其转换成可执行的机器语言代码。 由于硬件厂商提供机器的汇编程序,所以编译程序的目标代码通常为汇编语 言。
第7章 目标代码生成
7.1 汇编语言虚拟计算机模型 7.2 语法制导翻译在汇编程序自动构造中的 应用 7.3 从四元式到汇编语言的翻译
7.4 一个简单目标代码生成器
7.5 静态地址分配
1
㈠任务
将中间代码变换成目标代码,实现源程序的最后翻译。由于涉及到目标机器 的系统结构,此阶段的工作最复杂。
㈡目标代码的形式
P→I{ //<指令串> →<指令> output I.val;P.val←-1 //按十六进制输出指令 } P→P(1)I{ //<指令串> →<指令串><指令> output I.val;P.val←-1 } I→w{ // <指令> →操作码(例如RET) I.val ←pval*16*16*16 //pval指单词(操作码)的值 }
10
S→r{ // <第二地址>→<寄存器> S.val←rval //rval表示寄存器号 S.val←S.val|0x0000 S.val←S.val|0x0100 //寄存器寻址 } S→@r{ // <第二地址>→@<寄存器> S.val←rval //rval表示寄存器号 S.val←S.val|0x0010 //寄存器间接寻址 S.val←S.val|0x0100 //寄存器寻址 } S→d{ // <第二地址>→十六进制数字 S.val←dval //dval表示十六进制数字的值 S.val←S.val|0x0200 //直接数访问 } S→d1d0{ // <第二地址>→十六进制数字 十六进制数字 S.val←d1val*16+d0val //dval表示十六进制数字的值 S.val←S.val|0x0200 //直接数访问 } S→d[r]{ // <第二地址>→十六进制数字 [寄存器] S.val←dval //dval表示十六进制数字的值 S.val←S.val|0x0300 //变址寻址 }
④指令分类 零地址指令 Ret、Halt A型一地址指令(无第二地址) Write、Read B型一地址指令(无第一地址) Call、Jmp、JmpNeg、JmpZero、JmpPos 二地址指令 Load、Store、Add、Sub、Mul、Div、Cmp
上述计算机模型机已用高级语言虚拟实现,执行文件名为: MachineByLR.exe
7 <第二地址>→m 十六进制数 S →md //直接地址寻址 8 <第二地址>→m 十六进制数 十六进制数 S →mdd //直接地址寻 址 9 <第二地址>→ 寄存器 S →r //寄存器直接寻址 10 <第二地址>→ @寄存器 S →@r //寄存器间接寻址 11 <第二地址>→十六进制数 S →d //直接数访问 12 <第二地址>→十六进制数 十六进制数 S →dd //直接数访问 13 <第二地址>→十六进制数 [寄存器] S →d[r] //变址寻址 14 <第二地址>→十六进制数 十六进制数 [寄存器] S →dd[r] //变 址寻址 文法G构造的SLR(1)分析表见教材185页图7.2。
㈢目标代码生成要求
①目标代码数与中间代码数的比值尽可能小(即目标代码尽可能少)。 ②充分利用寄存器,减少访问内存的次数。 ③删除不必要的临时变量。
2
7.1 汇编语言虚拟计算机模型
㈠内存
①共有64k个字,1个字有16个二进制位(2Byte),内存地址标 识为0-65535。 ②划分为256页,页号0-255,每页的长度为256个字,页内位移 为0-255。 ③地址计算:页号*256+页内位移。 ④255页用作系统堆栈区。
6
7.2 语法制导翻译在汇编程序自动构造中的 应用
汇编语言的构造远较高级语言简单,将LR分析法制导的语义 分析用于自动构造汇编程序是绰绰有余。 7.2.1 汇编语言文法和分析表构造 虚拟机汇编语言的语法结构用文法G描述如下: 0 <程序>→<指令串> S’ →P 1 <指令串>→<指令> P →I 2 <指令串>→ <指令串> <指令> P →PI 3 <指令>→ 操作码0 I →w //零地址指令 4 <指令>→ 操作码1 寄存器 I →xr //A型一地址指令 5 <指令>→ 操作码2 寄存器,<第二地址> I →yr,S //二地址指令 6 <指令>→ 操作码3 <第二地址> I →zS //B型一地址指令 7
8
7.2.2 单词编码表和词法分析 汇编语言的词法比较简单,共有42个单词(包括单词 “#”)。除操作码和寄存器外,其余均为单字符单词。 汇编语言词法分析器的执行文件名为Lexical.exe。 源程序约定存放在文件source.txt中。 词法分析的结果约定存放在文件lex_r.txt中。 计算两个长度为10的向量积的汇编语言源程序如图7.3 (187页),词法分析结果如图7.4。 7.2.3 汇编语言语义和语法制导翻译 根据虚拟机的系统结构,语义子程序设计如下:
11
S→ d1d0[r]{ // <第二地址>→十六进制数字 十六进制数字 [寄存器] S.val←d1val*16+d0val //dval表示十六进制数字的值 S.val←S.val|0x0300 //变址寻址 }
汇编语言的语法制导翻译器的文件名为Parse.exe。 源程序的单词二元式序列约定存放在文件lex_r.txt中。 语法制导翻译结果存放在文件par_r.txt中。
15-12 11-10 9-8 7-0 ①操作码 Read 从键盘读一个字到第一地址。 Write 从第一地址写一个字到屏幕。 Load 从第二地址将字装入第一地址。 Store 将第一地址中的字存放到第二地址。 Call 控制转移到第二地址指定的内存单元,断点保留在系统堆栈中。 Ret 由系统堆栈获得断点,返回。 Add 将第一地址中的字加上第二地址中的字,结果保留第一地址中。 Sub Mul Div Cmp 将第一地址中的字和第二地址中的字比较,由系统置位FlagReg。 FlagReg=-1,表示第一地址中的字小于第二地址中的字。 FlagReg=1,表示第一地址中的字大于第二地址中的字。 4 FlagReg=0,表示第一地址中的字等于第二地址中的字。
255*256+255=65535 系统堆栈区 255 255*256=65280 无符号实常数表 254 无符号整常数表 254*256=65024 符号表 253 253*256=64768 临时变量表 252
虚拟内存示wk.baidu.com图
(共64K、分255页、每页255字节)
实常数表的起始地址为 254*256+255 整常数表的起始地址为 254*256 符号表起始地址为 253*256+255
㈡单个四元式的翻译
四元式的翻译关键是确定地址,约定如下: 指令的第一地址使用R0。 指令的第二地址使用R3(兼作变址寄存器)。 ARG1、ARG2为变量地址或常数地址,RESULT一定是变量地址。 C1=ARG1/256、D1=ARG1%256。 C2=ARG2/256、D2=ARG2%256。 C3= RESULT/256、D3= RESULT %256。 注:C1、C2、C3表示页码,D1、D2、D3表示页内位移。 例1:(+,ARG1,ARG2, RESULT)其中ARG1是变量在符号表入口,ARG2是 常数地址。 解: Load R3,D1 //第一操作数(变量在符号表入口) Load R0,C1[R3] Load R0,@R0 //第一运算量的值在R0中 Load R3,D2 //第二操作数(常数地址) Add R0,C2[R3] Load R3,D3 //结果保存(符号表或临时变量表入口) Load R3,C3[R3]
JMP 无条件转移到第二地址指定的内存单元。 JMPNEG 若FlagReg中的值<0,转移到第二地址指定的内存单元。 JMPPOS 若FlagReg中的值>0,转移到第二地址指定的内存单元。 JMPZERO 若FlagReg中的值=0,转移到第二地址指定的内存单元。 HALT 终止程序执行。 ②第一地址为寄存器(R0-R3) ③寻址方式和第二地址 直接地址寻址(M) Load Ri , MXX (XX)→Ri XX范围0-255(第0页),用16进制表示。 寄存器寻址(R) Load Ri , Rj (Rj)→Ri 间址寄存器寻址(@R) Load Ri , @Rj ((Rj))→Ri 直接数访问(D) Load Ri , XX XX→Ri XX范围0-255,用16进制表示。 变址寻址(C [R3]) Load Ri , C[R3] (C*256+(R3))→Ri 5 C为常数(范围0-255),用16进制表示。
临时变量表起始地址为 252*256 252*256=64512
14
设源程序为:a=2+(+b+1),它的四元式代码为 + &b 0 &T1 + &T1 &1 &T2 + &2 &T2 &T3 = &T3 0 &a 假设 整常数1在无符号整常数表中的地址为65024(254*256+0) 整常数2在无符号整常数表中的地址为65025(254*256+1) 变量a的符号表入口为65023(253*256+255) 变量b的符号表入口为65019(253*256+251) 临时变量T1的临时变量表入口为64512(252*256+0) 临时变量T2的临时变量表入口为64516(252*256+4) 临时变量T3的临时变量表入口为64520(252*256+8) 实际四元式代码序列应为 + 65019 0 64512 + 64512 65024 64516 + 65025 64516 64520 15 = 64520 0 65023
12
7.3 从四元式到汇编语言的翻译
㈠符号表、常数表和临时变量表的内存位置
四元式中含有变量在符号表和临时变量表中的入口,以及常数在 常数表中的地址,所以先确定符号表、临时变量表和常数表在内存 中的位置,然后再讨论四元式的翻译。 ①无符号实常数表(第254页,按地址递减使用) 起始地址:254*256+255=65279,每个实常数占用2个字(4Byte)。 ②无符号整常数表(第254页,按地址递增使用) 起始地址:254*256=65024,每个整常数占用1个字(2Byte)。 ③符号表(第252、253页,按地址递减使用) 起始地址:253*256+255=65023,每个标识符占用4个字(8Byte)。 ④临时变量表(第252、253页,按地址递增使用) 起始地址:252*256=64512,每个临时变量占用4个字(8Byte)。 注:四元式中最多允许出现512/4=128个不同的变量,包括临时变 13 量在内。
㈡寄存器
①通用寄存器(2字节) 共有4个,分别标记为R0、R1、R2、R3,用于存放操作数和 计算结果,R3还可用于变址寻址。 ②标志寄存器FlagReg 保存CMP指令比较结果。 ③堆栈寄存器TopReg(2字节) 3 TopReg用作系统栈顶指针。
㈢指令系统
指令格式:
操作 码
第一 地址
第二地址 寻址方式
9
I→xr{ // <指令> →操作码 寄存器 (例如READ) I.val ←pval*16*16*16 //pval指单词(操作码)的值 t←rval*16*16*4 //rval指单词(寄存器)的值 I.val←I.val|t } I→yr,S{ // <指令> →操作码 寄存器 ,<第二地址>(例如LOAD) I.val ←pval*16*16*16 //pval指单词(操作码)的值 t←rval*16*16*4 //rval指单词(寄存器)的值 I.val←I.val|t I.val←I.val|S.val } I→zS{ // <指令> →操作码 <第二地址>(例如CALL) I.val ←pval*16*16*16 //pval指单词(操作码)的值 I.val←I.val|S.val } S→md{ // <第二地址>→m十六进制数字 S.val←dval //dval表示十六进制数字的值 S.val←S.val|0x0000 } S→md1d0{ // <第二地址>→m十六进制数字 十六进制数字 S.val←d1val*16+d0val //dval表示十六进制数字的值 S.val←S.val|0x0000 }
第7章 目标代码生成
7.1 汇编语言虚拟计算机模型 7.2 语法制导翻译在汇编程序自动构造中的 应用 7.3 从四元式到汇编语言的翻译
7.4 一个简单目标代码生成器
7.5 静态地址分配
1
㈠任务
将中间代码变换成目标代码,实现源程序的最后翻译。由于涉及到目标机器 的系统结构,此阶段的工作最复杂。
㈡目标代码的形式
P→I{ //<指令串> →<指令> output I.val;P.val←-1 //按十六进制输出指令 } P→P(1)I{ //<指令串> →<指令串><指令> output I.val;P.val←-1 } I→w{ // <指令> →操作码(例如RET) I.val ←pval*16*16*16 //pval指单词(操作码)的值 }
10
S→r{ // <第二地址>→<寄存器> S.val←rval //rval表示寄存器号 S.val←S.val|0x0000 S.val←S.val|0x0100 //寄存器寻址 } S→@r{ // <第二地址>→@<寄存器> S.val←rval //rval表示寄存器号 S.val←S.val|0x0010 //寄存器间接寻址 S.val←S.val|0x0100 //寄存器寻址 } S→d{ // <第二地址>→十六进制数字 S.val←dval //dval表示十六进制数字的值 S.val←S.val|0x0200 //直接数访问 } S→d1d0{ // <第二地址>→十六进制数字 十六进制数字 S.val←d1val*16+d0val //dval表示十六进制数字的值 S.val←S.val|0x0200 //直接数访问 } S→d[r]{ // <第二地址>→十六进制数字 [寄存器] S.val←dval //dval表示十六进制数字的值 S.val←S.val|0x0300 //变址寻址 }
④指令分类 零地址指令 Ret、Halt A型一地址指令(无第二地址) Write、Read B型一地址指令(无第一地址) Call、Jmp、JmpNeg、JmpZero、JmpPos 二地址指令 Load、Store、Add、Sub、Mul、Div、Cmp
上述计算机模型机已用高级语言虚拟实现,执行文件名为: MachineByLR.exe
7 <第二地址>→m 十六进制数 S →md //直接地址寻址 8 <第二地址>→m 十六进制数 十六进制数 S →mdd //直接地址寻 址 9 <第二地址>→ 寄存器 S →r //寄存器直接寻址 10 <第二地址>→ @寄存器 S →@r //寄存器间接寻址 11 <第二地址>→十六进制数 S →d //直接数访问 12 <第二地址>→十六进制数 十六进制数 S →dd //直接数访问 13 <第二地址>→十六进制数 [寄存器] S →d[r] //变址寻址 14 <第二地址>→十六进制数 十六进制数 [寄存器] S →dd[r] //变 址寻址 文法G构造的SLR(1)分析表见教材185页图7.2。
㈢目标代码生成要求
①目标代码数与中间代码数的比值尽可能小(即目标代码尽可能少)。 ②充分利用寄存器,减少访问内存的次数。 ③删除不必要的临时变量。
2
7.1 汇编语言虚拟计算机模型
㈠内存
①共有64k个字,1个字有16个二进制位(2Byte),内存地址标 识为0-65535。 ②划分为256页,页号0-255,每页的长度为256个字,页内位移 为0-255。 ③地址计算:页号*256+页内位移。 ④255页用作系统堆栈区。
6
7.2 语法制导翻译在汇编程序自动构造中的 应用
汇编语言的构造远较高级语言简单,将LR分析法制导的语义 分析用于自动构造汇编程序是绰绰有余。 7.2.1 汇编语言文法和分析表构造 虚拟机汇编语言的语法结构用文法G描述如下: 0 <程序>→<指令串> S’ →P 1 <指令串>→<指令> P →I 2 <指令串>→ <指令串> <指令> P →PI 3 <指令>→ 操作码0 I →w //零地址指令 4 <指令>→ 操作码1 寄存器 I →xr //A型一地址指令 5 <指令>→ 操作码2 寄存器,<第二地址> I →yr,S //二地址指令 6 <指令>→ 操作码3 <第二地址> I →zS //B型一地址指令 7
8
7.2.2 单词编码表和词法分析 汇编语言的词法比较简单,共有42个单词(包括单词 “#”)。除操作码和寄存器外,其余均为单字符单词。 汇编语言词法分析器的执行文件名为Lexical.exe。 源程序约定存放在文件source.txt中。 词法分析的结果约定存放在文件lex_r.txt中。 计算两个长度为10的向量积的汇编语言源程序如图7.3 (187页),词法分析结果如图7.4。 7.2.3 汇编语言语义和语法制导翻译 根据虚拟机的系统结构,语义子程序设计如下:
11
S→ d1d0[r]{ // <第二地址>→十六进制数字 十六进制数字 [寄存器] S.val←d1val*16+d0val //dval表示十六进制数字的值 S.val←S.val|0x0300 //变址寻址 }
汇编语言的语法制导翻译器的文件名为Parse.exe。 源程序的单词二元式序列约定存放在文件lex_r.txt中。 语法制导翻译结果存放在文件par_r.txt中。
15-12 11-10 9-8 7-0 ①操作码 Read 从键盘读一个字到第一地址。 Write 从第一地址写一个字到屏幕。 Load 从第二地址将字装入第一地址。 Store 将第一地址中的字存放到第二地址。 Call 控制转移到第二地址指定的内存单元,断点保留在系统堆栈中。 Ret 由系统堆栈获得断点,返回。 Add 将第一地址中的字加上第二地址中的字,结果保留第一地址中。 Sub Mul Div Cmp 将第一地址中的字和第二地址中的字比较,由系统置位FlagReg。 FlagReg=-1,表示第一地址中的字小于第二地址中的字。 FlagReg=1,表示第一地址中的字大于第二地址中的字。 4 FlagReg=0,表示第一地址中的字等于第二地址中的字。
255*256+255=65535 系统堆栈区 255 255*256=65280 无符号实常数表 254 无符号整常数表 254*256=65024 符号表 253 253*256=64768 临时变量表 252
虚拟内存示wk.baidu.com图
(共64K、分255页、每页255字节)
实常数表的起始地址为 254*256+255 整常数表的起始地址为 254*256 符号表起始地址为 253*256+255
㈡单个四元式的翻译
四元式的翻译关键是确定地址,约定如下: 指令的第一地址使用R0。 指令的第二地址使用R3(兼作变址寄存器)。 ARG1、ARG2为变量地址或常数地址,RESULT一定是变量地址。 C1=ARG1/256、D1=ARG1%256。 C2=ARG2/256、D2=ARG2%256。 C3= RESULT/256、D3= RESULT %256。 注:C1、C2、C3表示页码,D1、D2、D3表示页内位移。 例1:(+,ARG1,ARG2, RESULT)其中ARG1是变量在符号表入口,ARG2是 常数地址。 解: Load R3,D1 //第一操作数(变量在符号表入口) Load R0,C1[R3] Load R0,@R0 //第一运算量的值在R0中 Load R3,D2 //第二操作数(常数地址) Add R0,C2[R3] Load R3,D3 //结果保存(符号表或临时变量表入口) Load R3,C3[R3]
JMP 无条件转移到第二地址指定的内存单元。 JMPNEG 若FlagReg中的值<0,转移到第二地址指定的内存单元。 JMPPOS 若FlagReg中的值>0,转移到第二地址指定的内存单元。 JMPZERO 若FlagReg中的值=0,转移到第二地址指定的内存单元。 HALT 终止程序执行。 ②第一地址为寄存器(R0-R3) ③寻址方式和第二地址 直接地址寻址(M) Load Ri , MXX (XX)→Ri XX范围0-255(第0页),用16进制表示。 寄存器寻址(R) Load Ri , Rj (Rj)→Ri 间址寄存器寻址(@R) Load Ri , @Rj ((Rj))→Ri 直接数访问(D) Load Ri , XX XX→Ri XX范围0-255,用16进制表示。 变址寻址(C [R3]) Load Ri , C[R3] (C*256+(R3))→Ri 5 C为常数(范围0-255),用16进制表示。
临时变量表起始地址为 252*256 252*256=64512
14
设源程序为:a=2+(+b+1),它的四元式代码为 + &b 0 &T1 + &T1 &1 &T2 + &2 &T2 &T3 = &T3 0 &a 假设 整常数1在无符号整常数表中的地址为65024(254*256+0) 整常数2在无符号整常数表中的地址为65025(254*256+1) 变量a的符号表入口为65023(253*256+255) 变量b的符号表入口为65019(253*256+251) 临时变量T1的临时变量表入口为64512(252*256+0) 临时变量T2的临时变量表入口为64516(252*256+4) 临时变量T3的临时变量表入口为64520(252*256+8) 实际四元式代码序列应为 + 65019 0 64512 + 64512 65024 64516 + 65025 64516 64520 15 = 64520 0 65023
12
7.3 从四元式到汇编语言的翻译
㈠符号表、常数表和临时变量表的内存位置
四元式中含有变量在符号表和临时变量表中的入口,以及常数在 常数表中的地址,所以先确定符号表、临时变量表和常数表在内存 中的位置,然后再讨论四元式的翻译。 ①无符号实常数表(第254页,按地址递减使用) 起始地址:254*256+255=65279,每个实常数占用2个字(4Byte)。 ②无符号整常数表(第254页,按地址递增使用) 起始地址:254*256=65024,每个整常数占用1个字(2Byte)。 ③符号表(第252、253页,按地址递减使用) 起始地址:253*256+255=65023,每个标识符占用4个字(8Byte)。 ④临时变量表(第252、253页,按地址递增使用) 起始地址:252*256=64512,每个临时变量占用4个字(8Byte)。 注:四元式中最多允许出现512/4=128个不同的变量,包括临时变 13 量在内。
㈡寄存器
①通用寄存器(2字节) 共有4个,分别标记为R0、R1、R2、R3,用于存放操作数和 计算结果,R3还可用于变址寻址。 ②标志寄存器FlagReg 保存CMP指令比较结果。 ③堆栈寄存器TopReg(2字节) 3 TopReg用作系统栈顶指针。
㈢指令系统
指令格式:
操作 码
第一 地址
第二地址 寻址方式
9
I→xr{ // <指令> →操作码 寄存器 (例如READ) I.val ←pval*16*16*16 //pval指单词(操作码)的值 t←rval*16*16*4 //rval指单词(寄存器)的值 I.val←I.val|t } I→yr,S{ // <指令> →操作码 寄存器 ,<第二地址>(例如LOAD) I.val ←pval*16*16*16 //pval指单词(操作码)的值 t←rval*16*16*4 //rval指单词(寄存器)的值 I.val←I.val|t I.val←I.val|S.val } I→zS{ // <指令> →操作码 <第二地址>(例如CALL) I.val ←pval*16*16*16 //pval指单词(操作码)的值 I.val←I.val|S.val } S→md{ // <第二地址>→m十六进制数字 S.val←dval //dval表示十六进制数字的值 S.val←S.val|0x0000 } S→md1d0{ // <第二地址>→m十六进制数字 十六进制数字 S.val←d1val*16+d0val //dval表示十六进制数字的值 S.val←S.val|0x0000 }