注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

吉大释然

0101 0100 0010 1011

 
 
 

日志

 
 

Lab 3:ARM指令  

2013-04-10 00:34:27|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

ARM 与 Thumb

较新的ARM处理器有一种16-bit指令模式,叫做Thumb,也许跟每个条件式运行指令均耗用4位的情形有关。在Thumb模式下,较小的opcode有更少的功能性。例如,只有分支可以是条件式的,且许多opcode无法访问所有CPU的暂存器。然而,较短的opcode提供整体更佳的编码密度(注:意指代码在存储器中占的空间),即使有些运算需要更多的指令。特别在存储器端口或总线宽度限制在32以下的情形时,更短的Thumb opcode能更有效地使用有限的存储器带宽,因而提供比32位代码更佳的性能。典型的嵌入式硬件仅具有较小的32-bit datapath寻址范围以及其他更窄的16 bits寻址(例如Game Boy Advance)。在这种情形下,通常可行的方案是编译成Thumb代码,并自行优化一些使用(非Thumb)32位指令集的CPU相关程序区,因而能将它们置入受限的32-bit总线宽度的存储器中。维基百科

生成了Thumb指令还是ARM指令,如何通过编译参数改变

首先写一个最简单的代码


int main()
{
int a;
a = 10;
a = a + 2;
}

在编译时,加上-S选项查看产生的汇编,查看产生的汇编文件


.arch armv6
.eabi_attribute 27, 3
.eabi_attribute 28, 1
.fpu vfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 18, 4
.file "1.c"
.text
.align 2
.global main
.type main, %function
...

并没有明确的说是否是ARM。(没有类似于.code 32或者.arm的伪指令)。

当然,可以使用objdump查看生成的.o文件,确定指令长度。


pi@chenyzpi ~/expCHEN/lab3 $ gcc 1.c -c -o 1.o
pi@chenyzpi ~/expCHEN/lab3 $ objdump -d 1.o

1.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <main>:
0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4: e28db000 add fp, sp, #0
8: e24dd00c sub sp, sp, #12
c: e3a0300a mov r3, #10
10: e50b3008 str r3, [fp, #-8]
14: e51b3008 ldr r3, [fp, #-8]
18: e2833002 add r3, r3, #2
1c: e50b3008 str r3, [fp, #-8]
20: e1a00003 mov r0, r3
24: e28bd000 add sp, fp, #0
28: e8bd0800 pop {fp}
2c: e12fff1e bx lr

可以看出指令是32位

然后,查询如何生成Thumb指令。使用gcc的帮助


pi@chenyzpi ~/expCHEN/lab3 $ gcc --target-help | less

会发现有这样一条:


-mthumb Compile for the Thumb not the ARM

这意味着使用-mthumb选项,是“生成Thumb而非ARM,也就是说缺省生成的是ARM”。

使用-mthumb选项生成一个新的汇编:


pi@chenyzpi ~/expCHEN/lab3 $ gcc -mthumb -msoft-float 1.c -S -o 1_1.s

注意,在编译的时候还要加上-msoft-float 因为系统没有实现硬件浮点的ABI

对两个得到的源文件进行比较(摘录部分)


...

> .fpu softvfp
12a11
> .code 16
16a16,17
> .code 16
> .thumb_func
...

也就是说,加入了伪指令`.code 16'表明是thumb指令。ARM伪指令

对于ARM指令,能否产生条件执行的指令

ARM指令可以通过添加适当的条件码前缀来达到条件执行的目的。
这样可以提高代码密度,减少分支跳转指令数目,提高性能;
比如:

    CMP  r3,#0            ---比较r3和0
    BEQ  skip                ---相等就跳到skip
    ADD  r0,r1,r2        ---不相等就执行 r0 = r1 + r2

可以优化成:

    CMP  r3,#0            ---比较r3和0
    ADDNE   r0,r1,r2    ---加了NE后缀,不相等就执行r0 = r1 + r2

这也就是条件执行,于是设计相应的函数来测试。选用函数是为了防止编译器优化过度,下同

#include <stdio.h>

int f(int x,int y)
{
   int ret = 10;

   if (x < y)
    ret += 123;
   if (x == y - 100)
    ret -= 10086;

   return ret;
}

产生的汇编文件可以看到:

pi@chenyzpi ~/expCHEN/lab3 $ gcc -c condition.c -o condition.o -O3
pi@chenyzpi ~/expCHEN/lab3 $ objdump condition.o -d

condition.o:     file format elf32-littlearm

Disassembly of section .text:

00000000 <f>:
   0:   e59f2024    ldr r2, [pc, #36]   ; 2c <f+0x2c>
   4:   e59f3024    ldr r3, [pc, #36]   ; 30 <f+0x30>
   8:   e1500001    cmp r0, r1
   c:   e2411064    sub r1, r1, #100    ; 0x64
  10:   b1a03002    movlt   r3, r2
  14:   b3a02085    movlt   r2, #133    ; 0x85
  18:   a3a0200a    movge   r2, #10
  1c:   e1510000    cmp r1, r0
  20:   11a00002    movne   r0, r2
  24:   01a00003    moveq   r0, r3
  28:   e12fff1e    bx  lr
  2c:   ffffd91f    .word   0xffffd91f
  30:   ffffd8a4    .word   0xffffd8a4

其中的movlt movge movne moveq都是条件执行,优化了代码结构,加快了速度。

设计C的代码场景,观察是否产生了寄存器移位寻址

总共设计了三个版本的代码,但是只有最后一个成功产生了寄存器移位寻址:


int f(int* a,int ind)
{
a[ind] = 2;

return 0;
}

生成.o文件并检查之

这里必须采用Level 1优化,不然会被转成先做左移,再寻址。同时,Level 1优化有使得有确定值的数组和下标被直接优化掉,所以需要用函数的形式确保不被优化


pi@chenyzpi ~/expCHEN/lab3 $ gcc 3.c -c -o 3.o -O1
pi@chenyzpi ~/expCHEN/lab3 $ objdump 3.o -d

3.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <f>:
0: e3a03002 mov r3, #2
4: e7803101 str r3, [r0, r1, lsl #2]
8: e3a00000 mov r0, #0
c: e12fff1e bx lr

有命令 str r3, [r0, r1, lsl #2]对应了a[ind] = 2需要使用寄存器移位寻址是因为整型数是32位的,故:

Addr(Byte) = INT \times 4

这样就需要左移INT2位。

设计C的代码场景,观察一个复杂的32位数是如何装载到寄存器的

这里,用一个函数来装载一个32位数字即可看到:


int load32()
{
return 0xFEDCBA98;
}

int main()
{
int a;
a = load32();
return 0;
}

编译并查看


pi@chenyzpi ~/expCHEN/lab3 $ gcc 4.c -c -o 4.o -O0
pi@chenyzpi ~/expCHEN/lab3 $ objdump 4.o -d

4.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <load32>:
0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4: e28db000 add fp, sp, #0
8: e59f300c ldr r3, [pc, #12] ; 1c <load32+0x1c>
c: e1a00003 mov r0, r3
10: e28bd000 add sp, fp, #0
14: e8bd0800 pop {fp}
18: e12fff1e bx lr
1c: fedcba98 .word 0xfedcba98

00000020 <main>:
20: e92d4800 push {fp, lr}
24: e28db004 add fp, sp, #4
28: e24dd008 sub sp, sp, #8
2c: ebfffffe bl 0 <load32>
30: e50b0008 str r0, [fp, #-8]
34: e3a03000 mov r3, #0
38: e1a00003 mov r0, r3
3c: e24bd004 sub sp, fp, #4
40: e8bd8800 pop {fp, pc}

可见,ARM是将这个32位数,使用伪指令存放在指令序列中的,并使用ldr命令将其载入寄存器。

写一个C的多重函数调用的程序,观察和分析:

  1. 调用时的返回地址在哪里?

  2. 传入的参数在哪里?

  3. 本地变量的堆栈分配是如何做的?

  4. 寄存器是caller保存还是callee保存?是全体保存还是部分保存?


当然,还是要写一个函数,为了防止优化,在设计函数的时候给出足够多的参数,并多重调用。


#include <stdio.h>

void print(char* c)
{
printf("%s",c);
}
int f1(int a,int b,int c,int d,int e,int f)
{
print("Wow");
b = b + 1;
c = e * f - a;
e = f * f;
d = a + c * b;
int x = a + b +c + d + e + f;
return x;
}

int f2(int a,int b,int c,int d,int e,int f,int g,int h,int i)
{
i = f1(a,b,c,d,e,f);
h = i + g;

return h;
}

编译并查看其指令:

可以看到:

  • 调用的返回地址放在lr中,因为调用结束的时候,会把lr给pop到pc当中
  • 传入的参数在r0 ~ r3当中,当不够,会在保存后使用更高的寄存器(r4+)
  • 对本地变量,会先使用r0~r3寄存器(优化得太厉害,看不太清楚)
  • 寄存器是callee保存的,部分保存(r0~r3不存)

MLA是带累加的乘法,尝试要如何写C的表达式能编译得到MLA指令。

MLA 指令执行有符号或无符号操作数 op1 与 op2 的乘法。乘法的 32 位乘积加到第三个操作数 op3 上,结果写入 dest。condition 必须是有效值;否则将指令当作 NOP。MLA指令%20User's%20Guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/xscinstruct_hh/INST_MLA.htm)

代码就简单了:

int f(int a,int b,int c) { return a * b + c; }

查看一下,就可看到MLA指令啦。

pi@chenyzpi ~/expCHEN/lab3 $ gcc 6.c -c -o 6.o -O1
pi@chenyzpi ~/expCHEN/lab3 $ objdump -d 6.o

6.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <f>:
 0: e0202091 mla r0, r1, r0, r2
 4: e12fff1e bx lr
  评论这张
 
阅读(145)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018