浙江大学计算机科学与技术学院 / 课程:嵌入式系统 / 指导教师:翁恺
树莓派与ARM指令
一、实验目的和要求
通过C代码和反汇编工具研究:
- 生成了Thumb指令还是ARM指令,如何通过编译参数改变;
- 对于ARM指令,能否产生条件执行的指令;
- 设计C的代码场景,观察是否产生了寄存器移位寻址;
- 设计C的代码场景,观察一个复杂的32位数是如何装载到寄存器的;
- 写一个C的多重函数调用的程序,观察和分析:
a) 调用时的返回地址在哪里?
b) 传入的参数在哪里?
c) 本地变量的堆栈分配是如何做的?
d) 寄存器是caller保存还是callee保存?是全体保存还是部分保存?
- MLA是带累加的乘法,尝试要如何写C的表达式能编译得到MLA指令。
二、实验内容和原理
最新的gcc编译器有优化功能!ARM除了普通的32位指令集之外还有16位的THUMB指令集!
三、主要仪器设备
创见8G C10 SD卡
树莓派Model B
PL2303 USB-to-Serial
其它配件
四、操作方法和实验步骤
1. 生成了Thumb指令还是ARM指令,如何通过编译参数改变
测试代码arm_thumb.c如下:
#include <stdio.h>int main(){
int i, j, k; k = 65535; for(i = 0; i < 5; i++){ j = 0; do{ printf(“%d”, j); j++; }while(j < 5); if ((j % 2) != 0){ printf(“%d”, k); } } return 0; } |
编译,不优化。
gcc –o arm_arm –c arm_thumb.cobjdump –d arm_thumb.o |
可以看见所有的指令都是4字节32位的,即ARM指令。
编译,加入参数-mthumb –mfloat-abi=softfp,使gcc使用thumb指令。
gcc –o arm_thumb.o –c arm_thumb.c –mthumb –mfloat-abi=softfpobjdum –d arm_thumb.o |
可以看见大多数指令都是2字节16位了,这些是THUMB指令。
2. 对于ARM指令,能否产生条件执行的指令
本题使用的测试代码与第一题相同。编译时加入-O1优化选项。
gcc –o arm_thumb.o –c arm_thumb.c –O1objdump –d arm_thumb.o |
根据上表,我们可以看出ARM指令的高4位如果为0x0Eh则为无条件执行,下图相比于第一题中的ARM指令的汇编代码图(所有的开头都为0x0Eh),可以看出优化后的反汇编代码中有一条高四位为0x1h的代码(事实上在之后的代码中还有),这条指令是条件执行的,执行条件是NE。
3. 设计C的代码场景,观察是否产生了寄存器移位寻址
本题的测试代码如下:
#include <stdio.h>int reg(int i, int j[])
{ return j[i]; } |
编译,开启优化。
gcc –o lab3_3.o –c lab3_3.c –O1objdump –d lab3_3.o |
我们可以看出指令ldr r0, [r1, r0, ls1 #2]这条指令是寄存器位移寻址。它将寄存器r0中的值左移两位后将上寄存器r1中的值,得到实际的数据地址并从该地址中将输入读取到寄存器r0中。
4. 设计C的代码场景,观察一个复杂的32位数是如何装载到寄存器的
本体的测试代码如下:
#include <stdio.h>int main(){
int b = 0xB4965AC3; return b; } int main2(){ int a = 0xFFFFFFFF; return a; } |
编译,并开启优化。
gcc –o lab3_4.o –c lab3_4.c –O1objdump –d lab3_4.o |
我们可以看出对于0xB4965AC3h这个算是比较复杂的数(1011 0100 1001 0110 0101 1010 1100 0011)编译器直接把它以word(4字节32为)的形式扔到了寄存器中;而对于0xFFFFFFFFh这个相当简单的数,编译器用了一个mvn r0, #0的方法,即移入0并取反,获得了全1。
5. 写一个C的多重函数调用的程序,观察和分析
5.1调用时的返回地址在哪里
5.2传入的参数在哪里
5.3本地变量的堆栈分配是如何做的
5.4寄存器是caller保存还是callee保存?是全体保存还是部分保存
本题的测试程序如下:
#include <stdio.h>int min(int op1, int op2){
if (op1 < op2) return op1; else return op2; } int queue(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p){ int t1 = a + b; int t2 = c + d; int t3 = e + f; int t4 = g + h; int t5 = i + j; int t6 = k + l; int t7 = m + n; int t8 = o + p; int t0 = min(t4, t8); t1 = t2 * t3; t4 = t5 * t6; t7 = t8 / t0; return t0; } int main(){ queue(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); return 0; } |
编译,不开启优化
gcc –o lab3_5.o –c lab3_5.cobjdump –d lab3_5.o |
汇编代码如下(好长!!!!!!!!!)
5.1返回地址保存在lr寄存器中,并且每一个子程序的第一句都是将lr压入堆栈保护起来;
5.2参数传递是通过使用r0-r3四个寄存器完成的,当传递的参数超过四个的时候会通过堆栈进行传递(大片大片的ldr,str……);
5.3本地变量的堆栈分配是按照声明的先后顺序压入堆栈的;
5.4寄存器由caller保护(主调保护),保护方式为部分保护,即只对使用到的寄存器进行保护。
6. MLA是带累加的乘法,尝试要如何写C的表达式能编译得到MLA指令
本题测试代码如下:
#include <stdio.h>int mla(int op1, int op2, int op3){
return op1 * op2 + op3; } |
编译,开启优化
gcc –o lab3_6.o –c lab3_6.c –O1objdump –d lab3_6.o |
这题一开始一开始失误没用优化,然后出了好长一串代码,优化之后只有两条指令!这缩水的的真厉害啊!然后我们就可以看见mla(带累加的乘法)指令了。
五、实验数据记录和处理
那堆反汇编如果也算实验数据的话……
六、实验结果与分析
-O1的影响很大,-O1之后从反汇编的代码可以看出得到的目标文件会变短很多,而且会有一些ARM优势的指令被采用,比如条件执行,MLA这类的。
七、讨论、心得
Gcc开了-O1之后看上去好像很厉害的样子。通过mvn进行移入并取反来获得0xFFFFFFFF的方法显然应该会比从内存中读取一个word要快得多,但是之前也有反汇编出来过return 0它并没有通过类似的方法移入取反,而是通过写一个0x00000000的word。之后尝试了int a = 0; return a;发现这个时候它是用mov的方式移入0然后return,没有用word来保存0x000000000,或许以后要return 0的时候用这种方法程序的执行效率会更高?(笑)
Comments