在常量值上强制 Clang 为 "perform math early"

Force Clang to "perform math early" on constant values

这与How to force const propagation through an inline function? Clang 有一个集成的汇编器有关;而且它不使用系统的汇编程序(通常是 GNU AS (GAS))。非 Clang 很早就执行了数学运算,所有的东西 "just worked".

我说"early"是因为@n.m。反对将其描述为 "math performed by the preprocessor." 但想法是该值在编译时已知,应该尽早评估,就像预处理器评估 #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
...

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

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;
}

它们是用常量值调用的,因此循环量在编译时是已知的。典型的来电者可能看起来像:

unsigned int x1 =  rotrFixed<byte>(1, 4);
unsigned int x2 =  rotrFixed<byte>(1, 32);
如果 GCC or Clang provided an intrinsic to perform the ,则需要

None 这些 [有问题的] 技巧。我什至满足于 "perform the rotate",因为他们甚至没有。

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


如果使用传统的 C/C++ 旋转,

机敏的 readers 会识别出 rotrFixed<byte>(1, 32) 可能是未定义的行为。所以我们进入汇编以避免 C/C++ 限制并享受 1 条指令加速。

好奇reader可能想知道为什么我们会这样做。密码学家提出规范,有时这些规范与底层硬件或标准机构并不一致。我们没有更改密码学家的规范,而是尝试逐字提供它以简化审核。


针对此问题打开了一个错误:LLVM Bug 24226 - Constant not propagated into inline assembly, results in "constraint 'I' expects an integer constant expression"

我不知道 Clang 做了什么保证,但我知道编译器和集成汇编器声称与 GCC 和 GNU 的汇编器兼容。而GCC和GAS提供了常数值的传播。

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

__asm__ ("rorb %b1, %b0" : "+q,m" (x) : "c,c" (y) : "cc");

多个替代约束语法是 'promote' 由于 clang 的问题而注册使用超过内存使用,涵盖 here。不知道以后的版本有没有解决这个问题。 gcc 在约束匹配和避免溢出方面往往更好。

这确实需要将 (y) 加载到 rcx/ecx/cl 寄存器中,但编译器可能会将其隐藏在另一个延迟之后。此外,(y) 没有范围问题。 rorb 有效地使用了 (%cl % 8)"cc" clobber 不是必需的。


如果表达式常量,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) ...

或如邮件列表中所述:

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

Of 'N' constraint for 8 bit , 那么 16/32/64 怎么样?