您的位置:首页 > 其它

结合实例分析arm指令集中的adds指令与arm内嵌汇编

2017-02-18 16:17 411 查看
qq截图进来的图全部丢失了。。。。。。。。郁闷。。。。。。

以下是ffmpege0.11.1源码中的一个函数,稍微做了点修改

注意:编译器我arm-linux-gcc4.4.1,其他的编译器请读者自行验证

static av_always_inline av_const int32_t av_clipl_int32_arm(int64_t a)

{

    int x, y;

#ifndef MELIS

    __asm__ ("adds   %1, %R2, %Q2, lsr #31  \n\t"

             "itet   ne                     \n\t"   //反汇编之后发现该条指令不起作用,可以删除

             "mvnne  %1, #1<<31             \n\t"

             "moveq  %0, %Q2                \n\t"

             "eorne  %0, %1,  %R2, asr #31  \n\t"

             : "=r"(x), "=&r"(y) : "r"(a):"cc");

#else

    __asm__

    {

            adds y,  (((int*) &a)[1]), (((unsigned*) &a)[0]), lsr #31
mvnne y, #1<<31
moveq x, (((unsigned*) &a)[0])
eorne x, y,  (((int*) &a)[1]), asr #31

    }

#endif

    return x;
}

分析:

__asm__是内嵌汇编的标志,这个不多说。

%0表示x,%1表示y,%2表示a,%R2表示a的高32位,%Q2表示a的低32位。

%R2表示a的高32位,%Q2表示a的低32位是怎么来的呢?

查看编写测试程序查看反汇编代码,测试程序如下:

#include <stdio.h>

int main()

{
long long int a=0x2200LL; //用long long int a=0x110022002200LL测试会发现输出结果是0x7fffffff,请读者自行测试
printf("0x%x\n", av_clipl_int32_arm(a));
return 0;

}

在linux下用arm-linux-gcc反汇编,反汇编的关键代码如下:

000083ec <av_clipl_int32_arm>:

    83ec: e92d0810
push {r4, fp}

    83f0: e28db004
add fp, sp, #4

    83f4: e24dd010
sub sp, sp, #16

    83f8: e50b0014
str r0, [fp, #-20]

    83fc: e50b1010
str r1, [fp, #-16]

    8400: e24b4014
sub r4, fp, #20

    8404: e8940018
ldm r4, {r3, r4}

    8408: e0942fa3
adds r2, r4, r3, lsr #31

    840c: 13e02102
mvnne r2, #-2147483648; 0x80000000

    8410: 01a03003
moveq r3, r3

    8414: 10223fc4
eorne r3, r2, r4, asr #31

    8418: e50b300c
str r3, [fp, #-12]

    841c: e50b2008
str r2, [fp, #-8]

    8420: e51b300c
ldr r3, [fp, #-12]

    8424: e1a00003
mov r0, r3

    8428: e24bd004
sub sp, fp, #4

    842c: e8bd0810
pop {r4, fp}

    8430: e12fff1e
bx lr

00008434 <main>:

    8434: e92d4810
push {r4, fp, lr}

    8438: e28db008
add fp, sp, #8

    843c: e24dd00c
sub sp, sp, #12

    8440: e3a03c22
mov r3, #8704; 0x2200

    8444: e3a04000
mov r4, #0

    8448: e50b3014
str r3, [fp, #-20]

    844c: e50b4010
str r4, [fp, #-16]

    8450: e59f402c
ldr r4, [pc, #44]; 8484 <main+0x50>

    8454: e24b1014
sub r1, fp, #20

    8458: e8910003
ldm r1, {r0, r1}

    845c: ebffffe2
bl 83ec <av_clipl_int32_arm>

    8460: e1a03000
mov r3, r0

    8464: e1a00004
mov r0, r4

    8468: e1a01003
mov r1, r3

    846c: ebffffae
bl 832c <_init+0x48>

    8470: e3a03000
mov r3, #0

    8474: e1a00003
mov r0, r3

    8478: e24bd008
sub sp, fp, #8

    847c: e8bd4810
pop {r4, fp, lr}

    8480: e12fff1e
bx lr

    8484: 0000850c
.word 0x0000850c

根据代码画出函数的栈帧结构图:设sp的初始地址为0x2400000c

push {r4, fp, lr}-----r4,fp,lr依次入栈:那么栈指针sp指向0x24000000,sp、fp指针值应该是系统分配



add fp, sp, #8------fp

sub sp, sp, #12-----sp下移12byte,留出3个空位

mov r3, #8704; 0x2200  ----r3=0x2200

mov r4, #0                       ----r4=0x0

str r3, [fp, #-20]    -------将r3的值0x2200存到fp-20字节(fp下移五格)内存处

str r4, [fp, #-16]   ----r4=0x0的值存到fp下移4格的内存处

注意:从这里就可以看出来arm-linux-gcc 4.4.1编译器处理器对64位数据结构的处理,用两个连续的32位的寄存器进行存放,低地址的寄存器存低32位数据,高地址的寄存器存高32位数据。

ldr r4, [pc, #44]; 8484 <main+0x50>   ----将0x8484内存单元的值赋给r4,r4=0x850c

sub r1, fp, #20                           ------------------------r1变成了指针,指向fp-20内存单元

ldm r1, {r0, r1}                           ------------------------将r1指向的内存单元0x24000000-12开始的连续8个字节内存单元数据依次赋值给r0,r1

此时在看看源码就更清楚了,64字节的a变量,分别用r0,r1来存放。

bl 83ec <av_clipl_int32_arm>   ---跳转到子函数

画出子函数的栈帧:

push {r4, fp}  ---保存main函数的fp指针=0x24000008

add fp, sp, #4   ------fp上移四个字节

sub sp, sp, #16------sp给函数形参空出栈空间

str r0, [fp, #-20]-----将r0的值0x2200存到内存空间0x24000000-12-24处

str r1, [fp, #-16]-----将r1的值0x0存到内存空间

sub r4, fp, #20     -----r4指向r0所在的内存地址

ldm r4, {r3, r4}     -----也就是将r0,r1的值分别赋值给r3,r4那么r3=0x2200,r4=0x0

adds r2, r4, r3, lsr #31  ------此时r3,r4分别等于r0,r1,也即是0x2200逻辑右移31位(为0),0+0=0,由于加了s标志,那么cpu的状态寄存器将改变,计算结果r2为0将设置z标志位,于是eq条件成立。接下来的mvnne和eorne指令将不会运行,只有moveq运行。

    840c: 13e02102 mvnne
r2, #-2147483648; 0x80000000

    8410: 01a03003 moveq
r3, r3

    8414: 10223fc4 eorne
r3, r2, r4, asr #31

    8418: e50b300c str
r3, [fp, #-12]     ------保存r3

    841c: e50b2008 str
r2, [fp, #-8]     -------保存r2

    8420: e51b300c ldr
r3, [fp, #-12]   -------取出r3,r3就是x

    8424: e1a00003 mov
r0, r3             

    8428: e24bd004 sub
sp, fp, #4 ----移动sp,为恢复r4、子函数fp做准备

    842c: e8bd0810 pop
{r4, fp}     -----恢复r4、子函数fp



    8430: e12fff1e bx
lr                      -----跳转到main函数

后面就不分析了,要注意的地方就是r3就是x。也就是子函数的返回值。

如果a=0x110022002200LL呢?

    "adds   %1, %R2, %Q2, lsr #31  \n\t"  // y= 0x1100 +(0x22002200 >> 31)=0x1100,此处cpu状态寄存器z标志没有发生改变,将运行mvnne和eorne指令

    "mvnne  %1, #1<<31             \n\t"       // y = ~0x80000000 = 0x7fffffff

    "moveq  %0, %Q2                \n\t"         //不执行

    "eorne  %0, %1,  %R2, asr #31  \n\t"  //x = y^(0x1100 >>31) = 0x7fffffff^0x0 = 0x7fffffff

     : "=r"(x), "=&r"(y) : "r"(a):"cc"); 

如果a=0x110022002200LL子函数将返回0x7fffffff,也即是说如果a大于32位将返回0x7fffffff,小于32位则返回原值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  arm 内嵌汇编 adds 64位