Force Clang在常数值上"尽早执行数学"

jww*_*jww 6 c++ llvm clang constexpr

这与如何通过内联函数强制const传播有关Clang有一个集成的汇编程序; 它不使用系统的汇编程序(通常是GNU AS(GAS)).Non-Clang提前完成了数学运算,一切都"正常".

我说"早",因为@nm反对将其描述为"由预处理器执行的数学".但是这个想法是在编译时知道值,并且应该提前评估它,就像预处理器评估a时一样#if (X % 32 == 0).

下面,Clang 3.6抱怨违反约束.看起来这个常数并没有在整个过程中传播:

$ export CXX=/usr/local/bin/clang++
$ $CXX --version
clang version 3.6.0 (tags/RELEASE_360/final)
Target: x86_64-apple-darwin12.6.0
...
$ make
/usr/local/bin/clang++ -DNDEBUG -g2 -O3 -Wall -fPIC -arch i386 -arch x86_64 -pipe -Wno-tautological-compare -c integer.cpp
In file included from integer.cpp:8:
In file included from ./integer.h:7:
In file included from ./secblock.h:7:
./misc.h:941:44: error: constraint 'I' expects an integer constant expression
        __asm__ ("rolb %1, %0" : "+mq" (x) : "I" ((unsigned char)(y%8)));
                                                  ^~~~~~~~~~~~~~~~~~~~
./misc.h:951:44: error: constraint 'I' expects an integer constant expression
...
Run Code Online (Sandbox Code Playgroud)

上述函数是内联模板特化:

template<> inline byte rotrFixed<byte>(byte x, unsigned int y)
{
    // The I constraint ensures we use the immediate-8 variant of the
    // shift amount y. However, y must be in [0, 31] inclusive. We
    // rely on the preprocessor to propoagte the constant and perform
    // the modular reduction so the assembler generates the instruction.
    __asm__ ("rorb %1, %0" : "+mq" (x) : "I" ((unsigned char)(y%8)));
    return x;
}
Run Code Online (Sandbox Code Playgroud)

它们是用const值调用的,因此旋转量在编译时是已知的.典型的调用者可能看起来像:

unsigned int x1 =  rotrFixed<byte>(1, 4);
unsigned int x2 =  rotrFixed<byte>(1, 32);
Run Code Online (Sandbox Code Playgroud)

如果GCCClang提供了在近恒定时间内执行旋转的内在函数,则不需要这些[可疑]技巧.我甚至满足于"执行旋转",因为他们甚至没有.

让Clang继续执行const值的预处理需要什么技巧?


rotrFixed<byte>(1, 32)如果使用传统的C/C++旋转,精明的读者会认识到可能是未定义的行为.因此,我们进入汇编以避免C/C++限制并享受1指令加速.

好奇的读者可能想知道为什么我们会这样做.密码学家提出了规范,有时那些规范并不同情底层硬件或标准组织.我们不是改变密码学家的规范,而是试图逐字提供,以使审核更容易.


针对此问题打开了一个错误:LLVM错误24226 - 常量未传播到内联汇编中,导致"约束"我需要一个整数常量表达式".

我不知道Clang有什么保证,但我知道编译器和集成汇编程序声称与GCC和GNU的汇编程序兼容.并且GCC和GAS提供恒定值的传播.

Bre*_*ale 2

由于由于设计决策而尝试强制进行持续评估似乎不太走运,因此该ror r/m8, cl形式可能是一个很好的折衷方案:

__asm__ ("rorb %b1, %b0" : "+q,m" (x) : "c,c" (y) : "cc");
Run Code Online (Sandbox Code Playgroud)

由于 clang 的问题,多种替代约束语法是为了“促进”寄存器使用而不是内存使用,此处介绍。不知道这个问题在以后的版本中是否得到了解决。gcc 往往更擅长约束匹配和避免溢出。

这确实需要加载(y)rcx/ecx/cl寄存器中,但编译器可能会将其隐藏在另一个延迟后面。此外,不存在范围问题(y)rorb有效地利用(%cl % 8). "cc"不需要破坏者。


如果表达式常量,gcc 和 clang 都可以使用__builtin_constant_p

if (__builtin_constant_p(y))
    __asm__("rorb %1, %b0" : "+q,m" (x) : "N,N" ((unsigned char) y) : "cc");
else
    ... non-constant (y) ...
Run Code Online (Sandbox Code Playgroud)

或如邮件列表中提到的:

if (__builtin_constant_p(y))
{
    if ((y &= 0x7) != 0)
        x = (x >> y) | (x << (8 - y)); /* gcc generates rotate. */
}
Run Code Online (Sandbox Code Playgroud)