据(1)的测试,我们已经知道,编译器会把每个函数的入口地址以及常量的地址都放到一个称为Literal Pool(文字池)的区域。
请注意:Literal Pool里面只存放地址!
假设要操作一个常数,使用如下指令:
MOV.L H'FFFF0000,R0
编译器将这条指令编译成如下:
MOV.L @(H'0010:8,PC),R5
(H'0010:8,PC)就是H'FFFF0000在Literal Pool里面的地址,CPU根据这个地址,取出H'FFFF0000这个值,然后送入R5。
同理,调用函数时,如(1)里面经过编译后的代码:
MOV.L @(H'000C:8,PC),R7
JMP @R7
(H'000C:8,PC)就是被调函数的入口地址在Literal Pool里面的地址,CPU根据这个地址,在Literal Pool里取出被调函数的入口地址,然后去执行被调函数。
另外,一些指令被翻译成机器码之后,只占16或者32位。这样的话,操作数只有4位或者8位,无法直接处理32位的立即数。CPU借助Literal Pool,只需要在操作数里放置偏移量就可以使用一个32位的操作数。
SH2A的指令长度为16位。
试想一下,假设CPU的指令有64位或者更多,那么就可以直接使用一个32位的操作数了。实时上,在CISC体系的CPU中,例如Intel,指令长度可以达到15个Bytes,一条指令往往被编译成占用5个以上BYTE的机器码,一条指令就可以附带一个32位的操作数。但在RISC体系里,显然无法实现该功能。
总结:
我们现在可以回答(1)里面最后的疑问了。
程序在移动时,通常要把代码段和数据段一起移动。因为Literal Pool一般位于代码段的后面,当我们一起COPY这个Literal Pool的时候,程序会正确的找到被调函数的入口地址。见附录。
注意Literal Pool与代码的距离是有个范围的,如果PC的偏移量超过了这个范围,程序一样会跳转错误。这时,我们可以告知编译器,把Literal Pool放在这个范围内。SH2A的编译器一般都会自动完成这一功能(Automatic Literal Pool Gereration Function),或者用.POOL关键字来优化Literal Pool。一般的编译器都会有类似的功能。
附录:
Source program
-------------------------------------------
.SECTION CD1, CODE, LOCATE=H'0000F000
CD1_START:
MOV.L #H'FFFF0000,R0
MOV.W #H'FF00,R1
MOV.L #CD1_START,R2
MOV #H'FF,R3
RTS
MOV R0,R10
.END
--------------------------------------------
Automatic literal pool generation result(source list)
-----------------------------------------------------
1 0000F000 1 .SECTION CD1, CODE, LOCATE=H'0000F000
2 0000F000 2 CD1_START
3 0000F000 D003 3 MOV.L #H'FFFF0000,R0
4 0000F002 9103 4 MOV.W #H'FF00,R1
5 0000F004 D203 5 MOV.L #CD1_START,R2
6 0000F006 E3FF 6 MOV #H'FF,R3
7 0000F008 000B 7 RTS
8 0000F00A 6A03 8 MOV R0,R10
9 **** begin pool ****
10 0000F00C FF00 data for source-line 4
11 0000F00E 0000 alignment code
12 0000F010 FFFF0000 data for source-line 3
13 0000F014 0000F000 data for source-line 5
14 **** end pool ****
15 9 .END
-----------------------------------------------------
文章评论(0条评论)
登录后参与讨论