Visual C++ 嵌入汇编代码

虽然最近很忙,不过庆祝博客开业,发一篇水文吧。

一、为什么很少使用嵌入式汇编?

很多语言都可以嵌入汇编语言,这并不是一个秘密……而且很多对硬件有了解的童鞋都已经这样做了,但在桌面电脑行业,这种行为依然被采用的不多,但为什么?

 

有很多原因,其中的一条是,这样你会丧失可移植性。汇编语言与机器语言有密切关系,而嵌入了汇编语言,那一部分代码就会与你所在的处理器绑死,至少是很难移植到不同的处理器架构上去。使用了一些特定指令集的代码特别如此。当然,我们有时候依然会需要这么做,而这个时候,就需要一些检测代码来检测是否支持相关的特性。

 

同时,各个编译器嵌入汇编代码的语法不同,这样你会丧失编译器之间的可移植性——不过现在也只有最基本的只使用标准库的代码才在编译器之间有可移植性;加上平台api的各种不可移植,这种情况并不特别糟糕。

 

另一个原因是,嵌入汇编的代码让优化器优化起来更困难。毕竟不是每个人都是专家,也很少有人能打包票保证自己的代码比编译器优化产生的代码要好……即使他们真的能这么保证,那么或许混合环境下优化器产生代码的质量会远远不如单纯只使用visual c++代码产生的质量。拙劣的目标代码抵消了汇编的优势,可能产生的代码的效率远远比你想的更加糟糕。

 

而且,你确定你对cpu的分支执行预测、缓存等影响优化的方面知道的比编译器清楚?

 

二、何时使用嵌入式汇编?

这是一个很难界定的话题,但有些时候仍然需要使用嵌入式汇编……对于我等鸟人而言,其实最重要的用途之一,是用来练习写32位/64位汇编代码而避免写冗长的启动和终止代码。

 

同时,在需要巨大的运算(CPU密集型)操作时,一些嵌入式汇编能让你更加充分的发挥硬件的特性……毕竟,一般而言,硬件总是比软件快得多。

 

据《UNIX操作系统设计》说,c语言性能只有汇编的60-80%左右,有时候,或许嵌入式汇编能起到一些关键的作用。但是在目前的硬件和常用的程序而言,关系不大——cpu总是很快的,而且大量的时间在等着用户输入呢,他们不会注意到这么一点点性能的提升的。

三、如何使用嵌入式汇编?

这是一个简单的话题,虽然能从中引出更多更加深奥的东西……比如参数入栈和返回的方式等,但我们只讨论最简单的,就是如何在函数中嵌入汇编,而将这些乱七八糟的互操作留到以后可能有的另一些文章。

 

我们将以ROR指令(循环右移指令)为例,来说明这个话题。

 

任何对C++语言有所了解的同学都知道,C语言里头提供了移位操作,不过循环移位呢?则需要额外的处理,毕竟没有原生的运算符或标准的函数啊。

 

用原生提供的工具写,得有如下的小手段:

 

1、取得要移出的位(丫到底是0还是1)

unsigned int bl=input&0x80000000;

 

2、进行移位,移位后移入的位为0,高位被舍去。

input<<=1;

 

3、将移出来的位和最后一位取或。

input|=(bl>>31);

 

很简单不是吗?不过毕竟大部分处理器还是提供了循环移位的功能的……一个互操作的程序如下:

 

unsigned int ROL(unsigned int input)
{
    __asm
    {
        mov eax,input
        rol eax,1
        mov input,eax
    }
    return input;
}

 

貌似还是简单不少,不是吗?

 

如果你有兴趣看看这段代码的反汇编,你会发现如下的东西:

 

unsigned int ROL(unsigned int input)
{
004113E0  push        ebp
004113E1  mov         ebp,esp
004113E3  sub         esp,0C0h
004113E9  push        ebx
004113EA  push        esi
004113EB  push        edi
004113EC  lea         edi,[ebp-0C0h]
004113F2  mov         ecx,30h
004113F7  mov         eax,0CCCCCCCCh
004113FC  rep stos    dword ptr es:[edi]
    __asm
    {
        mov eax,input
004113FE  mov         eax,dword ptr [input]
        rol eax,1
00411401  rol         eax,1
        mov input,eax
00411403  mov         dword ptr [input],eax
    }
    return input;
00411406  mov         eax,dword ptr [input]
}
00411409  pop         edi
0041140A  pop         esi
0041140B  pop         ebx
0041140C  add         esp,0C0h
00411412  cmp         ebp,esp
00411414  call        @ILT+315(__RTC_CheckEsp) (411140h)
00411419  mov         esp,ebp
0041141B  pop         ebp
0041141C  ret

 

这就是我说的为什么不提参数入栈和返回的方式之类的东西,真的是很繁琐不是吗?虽然实际有效的就几句话……

 

无论如何,这篇小文写到这里~开业水一水吧~

posted @ 2012-04-05 15:57  荣耀属于跪拜猫  Views(2046)  Comments(0Edit  收藏  举报