前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >gcc 嵌入式汇编(asm)实现bsr(位扫描)指令

gcc 嵌入式汇编(asm)实现bsr(位扫描)指令

作者头像
10km
发布2022-05-07 09:57:49
7070
发布2022-05-07 09:57:49
举报
文章被收录于专栏:10km的专栏10km的专栏

在c/c++语言中,如果你想获取一个二进制数为1的最高位的位置(比如40的最高位位置是5,1的最高位位置是0),该怎么办?

c语言实现

最笨的办法就是下面的代码

代码语言:javascript
复制
//对一个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位整数,代码也差不多

代码语言:javascript
复制
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_

代码语言:javascript
复制
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官网链接),在这些函数中我们发现一个对解决这个问题有用的函数

代码语言:javascript
复制
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

代码语言:javascript
复制
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汇编实现

代码语言:javascript
复制
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体系的指令,并不适用于其他平台,所以如果考虑代码跨平台开发,还是要把上面所有的代码结合起来用预编译宏重新封装。

跨平台封装

代码语言:javascript
复制
#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++封装

代码语言:javascript
复制
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 ;

然后就可以这样方便的调用

代码语言:javascript
复制
int res=MyUtils._bsr(88080LLU);//调用static __int8 _bsr(unsigned __int64 num)

看完这些内容,如果你想实现bsf(正向位扫描)指令,也可以在这个基础上如法炮制了。

请注意以上代码在mingw gcc 64位编译器下实现,在32位系统下,需要做相应修改。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • c语言实现
  • gcc内建函数实现
  • asm汇编实现
  • 跨平台封装
  • c++封装
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档