以下是我一直在玩的基本示例:
#include <limits>
constexpr inline int satadd_ce(int x, int y) {
if (x >= 0 && y >= std::numeric_limits<int>::max() - x)
return std::numeric_limits<int>::max();
else if (x < 0 && y <= std::numeric_limits<int>::min() - x)
return std::numeric_limits<int>::min();
else
return x + y;
}
[[gnu::const]] inline int satadd_asm(int x, int y) {
int result;
__asm__("addl %2, %0\n\t"
"jno 2f\n\t"
"jl 1f\n\t"
"movl $0x7fffffff, %0\n\t"
"jmp 2f\n\t"
"1: movl $0x80000000, %0\n\t"
"2:"
: "=r"(result) : "0"(x), "rmi"(y));
return result;
}
constexpr inline int satadd(int x, int y) {
if (__builtin_constant_p(x) && __builtin_constant_p(y))
return satadd_ce(x, y);
else
return satadd_asm(x, y);
}
extern constexpr int n1 = satadd(2000000000, 1000000000);
extern constexpr int n2 = satadd(2000000000, -1000000000);
extern constexpr int n3 = satadd(-2000000000, 1000000000);
extern constexpr int n4 = satadd(-2000000000, -1000000000);
extern constexpr int n5 = satadd(satadd(2000000000, 1000000000), -1000000000);
GCC documentation让我相信在使用-O0编译时可能无法正常工作:
但是,如果在内联函数中使用它并将函数的参数作为参数传递给内置函数,则在使用字符串常量或复合文字调用内联函数时,GCC永远不会返回1(请参阅复合文字) )并且在将常量数值传递给内联函数时不返回1,除非指定-O选项。 (强调我的)
但实际上,上面的代码与g++-6 -O0
编译得很好。那么GCC文档可能是过时的,还是存在其他可能的问题,其中satadd
在核心常量表达式中不起作用但明确调用satadd_ce
会不会?
(顺便说一下,我似乎记得有些SSE版本有专门的饱和算术指令 - 但这不是真正的重点,我只是想试验内联asm和编译时替代是否可以组合在一起单个函数可以从普通代码和核心常量表达式中调用。)
(另请注意,明确禁止asm语句直接出现在constexpr
函数中,因此至少satadd_asm
必须是一个单独的函数。但是,如果C ++ 17的constexpr if
是在gcc中实现并接受__builtin_constant_p
然后可能有效。)