MSVC中__assume(0)/std::unreachable()为何未优化?能否用std::unexpected()优化?
std::unreachable()优化失效的替代方案 测试代码
以下是用MSVC编译的测试代码:
#include <mmintrin.h> #include <utility> static auto bit_width(unsigned long x) { unsigned long i; _BitScanReverse(&i, x); ++i; if (x == 0) { i = 0; } return i; } auto foo(unsigned long x) { __assume(x > 0); return bit_width(x); } auto bar(unsigned long x) { if (x > 0) { return bit_width(x); } else { std::unreachable(); // 在MSVC中实现为__assume(0) } }
生成的汇编代码
编译后生成的汇编如下:
bit_width(unsigned long) bsr eax, ecx xor edx, edx inc eax test ecx, ecx cmovne edx, eax mov eax, edx ret 0 foo(unsigned long) PROC bsr eax, ecx inc eax ret 0 bar(unsigned long) PROC bsr eax, ecx xor edx, edx inc eax test ecx, ecx cmovne edx, eax mov eax, edx ret 0
可以看到,foo函数因前置的__assume(x>0)被成功优化,而bar函数虽然在else分支使用了std::unreachable()(MSVC中映射为__assume(0)),却未得到优化,汇编输出和通用的零检查函数完全一致。
按照MSVC文档,__assume(0)作为不可达断言应当帮助编译器优化,但实际未生效。针对这个问题,相关解答如下:
能否用std::unexpected()优化这类代码?
不能。std::unexpected()是C++异常处理机制的一部分,设计目的是当抛出的异常类型不在函数异常规格说明的列表中时被调用,它会触发异常处理流程,而非向编译器传递“代码不可达”的优化提示,因此无法用来实现这类断言式优化。
针对MSVC的可行优化方案
直接使用
__assume断言前置条件
延续foo函数的写法,在函数开头直接用__assume(x>0)断言输入条件,MSVC能直接利用该信息删除冗余的零检查逻辑,优化效果稳定。缺点是该语法仅MSVC支持,缺乏可移植性。封装跨编译器的断言宏
为了兼顾可移植性,可以定义一个适配不同编译器的宏:#ifdef _MSC_VER #define ASSUME(expr) __assume(expr) #elif defined(__clang__) || defined(__GNUC__) #define ASSUME(expr) __builtin_assume(expr) #else #define ASSUME(expr) ((void)0) // 无对应支持的编译器下为空操作 #endif之后修改
bar函数为:auto bar(unsigned long x) { ASSUME(x > 0); return bit_width(x); }这样既保留了MSVC下的优化能力,又能在Clang、GCC等编译器中正常工作。
检查编译器版本与优化选项
确保使用的MSVC版本支持C20(std::unreachable()是C20引入的特性),并且开启了足够的优化等级(如/O2)。部分旧版MSVC对std::unreachable()的优化支持不完善,升级编译器版本可能解决问题。
补充说明:__assume(x>0)这类断言是影响正确性的前置条件声明,编译器会完全信任该断言并据此优化;而GNU C的__builtin_expect(x>0, 1)仅为编译器提供分支概率提示(类似C++20的[[likely]]属性),不会改变代码的正确性语义,二者本质不同。
内容的提问来源于stack exchange,提问作者user541686

