【问题标题】:Force Clang to "perform math early" on constant values强制 Clang 对常量值“尽早执行数学运算”
【发布时间】:2015-07-23 03:18:46
【问题描述】:

这与How to force const propagation through an inline function? Clang 有一个集成的汇编器有关;并且它不使用系统的汇编程序(通常是 GNU AS (GAS))。 Non-Clang 很早就完成了数学运算,一切都“正常运行”。

我说“早”是因为@n.m。反对将其描述为“由预处理器执行的数学运算”。但想法是该值在编译时是已知的,并且应该尽早评估它,就像预处理器评估 #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;
}

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

unsigned int x1 =  rotrFixed<byte>(1, 4);
unsigned int x2 =  rotrFixed<byte>(1, 32);

如果GCCClang 提供了执行rotate in near constant time 的内在函数,则不需要这些[可疑] 技巧。我什至会满足于“执行旋转”,因为他们甚至没有。

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


精明的读者会认识到,如果使用传统的 C/C++ 循环,rotrFixed&lt;byte&gt;(1, 32) 可能是未定义的行为。所以我们进入汇编程序以避免 C/C++ 的限制并享受 1 指令加速。

好奇的读者可能想知道我们为什么要这样做。密码学家提出了规范,有时这些规范并不赞同底层硬件或标准机构。我们没有更改密码学家的规范,而是尝试逐字提供以使审计更容易。


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

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

【问题讨论】:

  • 您一直在谈论预处理算法,但您的代码中没有任何#define 常量。
  • 您可以通过-no-integrated-as使用系统汇编器。
  • 如果y在comoile时间是已知的,为什么不把它作为模板参数呢?
  • @n.m. - 两个原因。首先,这会改变 ABI。 (我们正在为过去的罪孽付出代价)。其次,它不一定适用于 Clang/LLVM。对于 (2),请参阅 Sean 在LLVM Bug 24226 的评论。 (Sean 是 LLVM 维护者之一)。
  • 无论如何它是内联和模板。这里没有 ABI 问题。 API 可能。

标签: c++ clang llvm constexpr


【解决方案1】:

由于设计决策而试图强制进行持续评估似乎不太走运,ror r/m8, cl 表单可能是一个不错的折衷方案:

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

由于 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. */
}

【讨论】:

  • "rorb effectively uses (%cl % 8)" - 我知道这可以利用,但我不知道如何表达......谢谢。
  • @jww - 顺便说一句,对于 8 位无符号立即数,您可以使用 "N" 约束,而不是 "I",并让 ror 处理模 8 的范围。
【解决方案2】:

如果 8 位的 'N' 约束,那么 16/32/64 怎么样?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2019-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多