首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于clang -O3优化的ARM模块化算法(Apple -O3)的错误结果

基于clang -O3优化的ARM模块化算法(Apple -O3)的错误结果
EN

Stack Overflow用户
提问于 2021-07-14 02:24:27
回答 1查看 355关注 0票数 1

在过去的几天里,我一直在使用这段“无害”的代码(最小可复制的例子,一个更大的模乘例程的一部分):

代码语言:javascript
复制
#include <iostream>
#include <limits>

using ubigint = unsigned long long int;
using bigint = long long int;

void modmul(bigint a, bigint b, ubigint p) {
    ubigint ua = a < 0 ? -a : a;
    ubigint ub = b < 0 ? -b : b;

    ua %= p;
    ub %= p;

    std::cout << "ua: " << ua << '\n';
}

int main() {
    bigint minbigint = std::numeric_limits<bigint>::min();
    bigint maxbigint = std::numeric_limits<bigint>::max();
    std::cout << "minbigint: " << minbigint << '\n';
    std::cout << "maxbigint:  " << maxbigint << '\n';

    modmul(minbigint, maxbigint, 2314); // expect ua: 2036, got ua: 0
}

我正在编译macOS 11.4中的clang12.0,安装自Homebrew

代码语言:javascript
复制
clang version 12.0.0 
Target: arm64-apple-darwin20.5.0 
Thread model:posix 
InstalledDir: /opt/homebrew/opt/llvm/bin

在用clang -O1编译时,程序会输出预期的结果(在本例中,2036年,我已经使用Wolfram MathematicaMod[9223372036854775808, 2314]检查过了,这是正确的)。但是,当我使用clang -O2clang -O3 (完全优化)编译时,变量ua不知何故被归零(其值变为0)。我在这里完全不知所措,不知道为什么会这样。国际海事组织,这段代码中没有UB,也没有溢出,或者任何可疑的东西。我非常感谢你的任何建议,或者如果你能在你这一边重复这个问题。

PS:代码在任何其他平台(包括Windows/Linux/FreeBSD/Solaris)和任何编译器组合上的行为都是预期的。我只在Apple 12上得到这个错误(没有在M1上用其他编译器进行测试)。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-14 03:28:57

UPDATE:正如@哈罗德在评论部分指出的那样,0中的negqsubq完全相同。因此,我下面关于negqsubq的讨论是不正确的。请不要理会这部分,很抱歉在发帖之前没有再检查一遍。

关于最初的问题,我重新编译了一个稍微简单一些的代码哥德波特版本,并发现有问题的编译器的优化是在main而不是modmul中。在main中,clang看到其用于modmul的所有操作数都是常数,因此它决定在编译时计算modmul。在计算ubigint ua = a < 0 ? -a : a;时,clang会发现它是带符号整数溢出UB,因此它决定返回0并打印出来。这似乎是一个激进的做法,但这是合法的,因为UB。此外,由于由于两种恭维系统的限制,没有数学上正确的答案,所以返回0可以说是与任何其他结果一样好(或同样糟糕)。

老字号回答

正如有人在注释部分中指出的那样,代码下面的2行是未定义的行为--有符号整数溢出UB。

代码语言:javascript
复制
    ubigint ua = a < 0 ? -a : a;
    ubigint ub = b < 0 ? -b : b;

如果您想知道clang在引擎盖下到底做了什么才能在两个不同的优化级别上产生两个不同的结果,那么请考虑一个简单的例子如下。

代码语言:javascript
复制
using ubigint = unsigned long long int;
using bigint = long long int;

ubigint
negate(bigint a)
{
    ubigint ua = -a;
    return ua;
}

用-O0编译时

代码语言:javascript
复制
negate(long long):                             # @negate(long long)
        pushq   %rbp
        movq    %rsp, %rbp
        movq    %rdi, -8(%rbp)
        xorl    %eax, %eax
        subq    -8(%rbp), %rax  # Negation is performed here
        movq    %rax, -16(%rbp)
        movq    -16(%rbp), %rax
        popq    %rbp
        retq

用-O3编译

代码语言:javascript
复制
negate(long long):                             # @negate(long long)
        movq    %rdi, %rax
        negq    %rax  # Negation is performed here
        retq

在-O0,clang使用普通的subq指令执行0和%rax的二进制减法,并产生整数环绕行为的结果。

在-O3,clang可以做得更好,它使用negq指令,它只替换操作数和它的两个补码(即翻转所有位并添加1)。但是,您可以看到,只有在有符号整数溢出是未定义的行为(因此编译器可以忽略溢出情况)时,此优化才是合法的。如果标准要求的整数环绕行为,clang必须回到未优化的版本。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68371261

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档