在常量值上强制 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可能想知道为什么我们会这样做。密码学家提出规范,有时这些规范与底层硬件或标准机构并不一致。我们没有更改密码学家的规范,而是尝试逐字提供它以简化审核。
我不知道 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 怎么样?
这与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可能想知道为什么我们会这样做。密码学家提出规范,有时这些规范与底层硬件或标准机构并不一致。我们没有更改密码学家的规范,而是尝试逐字提供它以简化审核。
我不知道 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 怎么样?