gcc 嵌入式汇编(asm)实现bsr(位扫描)指令
来源:互联网 发布:linux的dd命令SD卡 编辑:程序博客网 时间:2024/05/20 14:16
在c/c++语言中,如果你想获取一个二进制数为1的最高位的位置(比如40的最高位位置是5,1的最高位位置是0),该怎么办?
c语言实现
最笨的办法就是下面的代码
//对一个64位无符号整数进行位扫描(从高位到低位)inline __int8 _bsr_int64_(unsigned __int64 num) { __int8 count=(sizeof(num)<<3)-1; for(unsigned __int64 mask=1LLU<<count;!(num&mask)&&count>=0;count--,mask>>=1); return count;}
基本的思路就是用for循环从最高位开始对每一位做与运算,找到第一个为1的位,就中止循环,count中就是结果,如果所有的位都为0,则count为-1;
注意这里1LLU<< count,
LLU限定前面的数字1为long long(64位),U限定为无符号类型(unsigned),就是指1为unsigned long long 型64位无符号整数。
如果是32位整数,代码也差不多
inline __int8 _bsr_int32_(unsigned __int32 num) { __int8 count=(sizeof(num)<<3)-1; for(unsigned __int32 mask=1U<<count;!(num&mask)&&count>=0;count--,mask>>=1); return count;}
如果更懒一点,可以直接调用前面的_bsr_int64_
inline __int8 _bsr_int32_(unsigned __int32 num) { return _bsr_int64_(num);}inline __int8 _bsr_int16_(unsigned __int16 num) { return _bsr_int64_(num);}inline __int8 _bsr_int8_(unsigned __int8 num) { return _bsr_int64_(num);}
gcc内建函数实现
gcc本身提供了丰富有用的内置函数(Built-in Functions)(点击打开gcc官网链接),在这些函数中我们发现一个对解决这个问题有用的函数
int __builtin_clz (unsigned int x)
下面这段是gcc官网的对这个函数的解释:
— Built-in Function: int __builtin_clz (unsigned int x)
Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.//返回前导的0的个数,如果x为0,则返回结果不确定。
— Built-in Function: int __builtin_clzll (unsigned long long)//ll后续的函数是参数为64位整数的版本
Similar to __builtin_clz, except the argument type is unsigned long long.
so,我有了新的想法,用这个函数来实现bsr
inline __int8 _bsr_int64_(unsigned __int64 num) {//num为0时直接返回-1,避免不确定的结果 return num==0?-1:(sizeof(num)<<3)-1-__builtin_clzll(num);}inline __int8 _bsr_int32_(unsigned __int32 num) { return num==0?-1:(sizeof(num)<<3)-1-__builtin_clz(num);}inline __int8 _bsr_int16_(unsigned __int16 num) { return _bsr_int64_(num);}inline __int8 _bsr_int8_(unsigned __int8 num) { return _bsr_int64_(num);}
如果你不追求效率,那么下面的内容就可以跳过了(话说,如果不追求效率,干嘛用c/c++来写程序呢?)
唉,总觉得哪里不对,c语言对位的操作好麻烦。
其实,x86结构的cpu(386以上)的指令集中本身就有用于位扫描的指令bsf,bsr(点击链接百度百科)
bsf用于从低到高位扫描,bsr用于从高位到低位扫描
只用这一条汇编指令就能搞定前面那么多循环才能解决的问题。
于是我们可以用在c/c++内嵌汇编代码的方式实现上面的功能:
asm汇编实现
inline __int8 _bsr_int64_(unsigned __int64 num) { __int64 count; __asm__( "bsrq %1, %0\n\t"//bsr和mov后面的q是指8字节数据宽度,每行汇编代码结尾都要加换行符\n\t "jnz 1f\n\t" //寄存器ZF标志为0,%0中结果有效直接跳转到标号1,f是指向前跳转 "movq $-1,%0\n\t"//寄存器ZF标志为1,代表所有的位都是0,所以返回-1 "1:" :"=q"(count):"q"(num)); return count;}inline __int8 _bsr_int32_(unsigned __int32 num) { __int32 count; __asm__( "bsrl %1, %0\n\t"//bsr和mov后面的l是指4字节数据宽度, "jnz 1f\n\t" "movl $-1,%0\n\t" "1:" :"=r"(count):"r"(num)); return count;}inline __int8 _bsr_int16_(unsigned __int16 num) { __int16 count; __asm__( "bsrw %1, %0\n\t"//bsr和mov后面的w是指2字节数据宽度, "jnz 1f\n\t" "movw $-1,%0\n\t" "1:" :"=r"(count):"r"(num)); return count;}inline __int8 _bsr_int8_(unsigned __int8 num) { return _bsr_int16_(num);}
gcc内嵌的汇编不是我们常见的intel汇编格式,而是at&t汇编格式,关于这方面的知识可以在网上找到很多参考资料如:
gcc内嵌汇编用法(点击打开链接)
AT&T汇编格式与Intel汇编格式的比较(点击打开链接)
因为bsr只是x86体系的指令,并不适用于其他平台,所以如果考虑代码跨平台开发,还是要把上面所有的代码结合起来用预编译宏重新封装。
跨平台封装
#if __x86_64__ //判断是否为x86_64结构,如果是则用内嵌汇编实现 inline __int8 _bsr_int64_(unsigned __int64 num) { __int64 count; __asm__( "bsrq %1, %0\n\t" "jnz 1f\n\t" "movq $-1,%0\n\t" "1:" :"=q"(count):"q"(num)); return count;}inline __int8 _bsr_int32_(unsigned __int32 num) { __int32 count; __asm__( "bsrl %1, %0\n\t" "jnz 1f\n\t" "movl $-1,%0\n\t" "1:" :"=r"(count):"r"(num)); return count;}inline __int8 _bsr_int16_(unsigned __int16 num) { __int16 count; __asm__( "bsrw %1, %0\n\t" "jnz 1f\n\t" "movw $-1,%0\n\t" "1:" :"=r"(count):"r"(num)); return count;}inline __int8 _bsr_int8_(unsigned __int8 num) { return _bsr_int16_(num);}#else #ifdef __GNUC__//gcc 编译时使用内建函数实现inline __int8 _bsr_int64_(unsigned __int64 num) { return num==0?-1:(sizeof(num)<<3)-1-__builtin_clzll(num);}inline __int8 _bsr_int32_(unsigned __int32 num) { return num==0?-1:(sizeof(num)<<3)-1-__builtin_clz(num);}inline __int8 _bsr_int16_(unsigned __int16 num) { return _bsr_int64_(num);}inline __int8 _bsr_int8_(unsigned __int8 num) { return _bsr_int64_(num);}#else//非x86_64体系,也非gcc编译时,则是用c实现,//如果你对cpu平台的汇编指令熟悉,也可以用如上面的方式用内嵌汇编实现inline __int8 _bsr_int64_(unsigned __int64 num) { __int8 count=(sizeof(num)<<3)-1; for(unsigned __int64 mask=1LLU<<count;!(num&mask)&&count>=0;count--,mask>>=1); return count;}inline __int8 _bsr_int32_(unsigned __int32 num) { return _bsr_int64_(num);}inline __int8 _bsr_int16_(unsigned __int16 num) { return _bsr_int64_(num);}inline __int8 _bsr_int8_(unsigned __int8 num) { return _bsr_int64_(num);}#endif#endif
__x86_64__和__GNUC__
为gcc预定义的宏,关于预定义宏请参见:
查看gcc的默认宏定义命令(点击打开链接)
为更方便调用,还可以进一步利用c++的重载特性把这些代码封装成类,
c++封装
class _MyUtils {public: static __int8 _bsr(unsigned __int64 num) { return _bsr_int64_(num); } static __int8 _bsr(unsigned __int32 num) { return _bsr_int32_(num); } static __int8 _bsr(unsigned __int16 num) { return _bsr_int16_(num); } static __int8 _bsr(unsigned __int8 num) { return _bsr_int8_(num); }}MyUtils ;
然后就可以这样方便的调用
int res=MyUtils._bsr(88080LLU);//调用static __int8 _bsr(unsigned __int64 num)
看完这些内容,如果你想实现bsf(正向位扫描)指令,也可以在这个基础上如法炮制了。
请注意以上代码在mingw gcc 64位编译器下实现,在32位系统下,需要做相应修改。
- gcc 嵌入式汇编(asm)实现bsr(位扫描)指令
- 6.asm-BSF、BSR: 位扫描指令(找到最开始的1所在位置)
- 汇编编程->ASM指令
- asm基础——汇编指令之CPU的标识位操作指令
- GCC汇编指令格式
- GCC 嵌入式汇编
- GCC 嵌入式汇编
- MIPS GCC 嵌入式汇编
- Gcc嵌入式汇编
- Gcc嵌入式汇编(转载)
- Gcc嵌入式汇编
- MIPS GCC 嵌入式汇编
- Gcc嵌入式汇编
- MIPS GCC 嵌入式汇编
- x64 ASM 常用汇编指令
- GCC嵌入式ASM快速指南
- GCC Inline ASM GCC内联汇编
- GCC Inline ASM GCC内联汇编
- iOS绘图教程
- 我的BIOS之行(2)-Aptio BIOS Overview
- 智能优化算法小结
- jQuery获取循环出来的DOM节点
- 二级指针
- gcc 嵌入式汇编(asm)实现bsr(位扫描)指令
- HIVE函数
- 训练自己haar-like特征分类器并识别物体(3)
- 在ScrollView中嵌套ListView
- 内存区划分
- [Leetcode] Longest Consecutive Sequence
- 微信支付 for android 集成笔记( 集成遇到问题记录)
- java 校验和
- API21开始getRunningAppProcesses只返回应用本身的进程信息