clang OpenMP 循环继续计数超出循环条件限制

clang OpenMP loop continues counting beyond loop condition limit

提问人:nh2 提问时间:10/30/2023 更新时间:10/30/2023 访问量:34

问:

我有以下有趣的问题:

#include <iostream>
#include <omp.h>

using namespace std;

int main(const int, const char **) {

  const size_t n = 13; // triggers the issue
  // const uint32_t n = 13; // fixes the issue

#pragma omp parallel for
  for (uint32_t i = 0; i < n; i += 2) { // `+= 1` fixes the issue
    cerr << "i = " << i << ", n = " << n << endl;
  }

}

尽管我的 ,这在 上打印了无限系列的数字:i < nn = 13clang++ -fopenmp

i = 0, n = 13
i = 2, n = 13
i = 4, n = 13
i = 6, n = 13
i = 8, n = 13
i = 10, n = 13
i = 12, n = 13     <- this should be impossible
i = 14, n = 13
...

这怎么可能?

clang++ 11、14 和 16 会发生这种情况,但 GCC 不会。如果被删除,它也会被修复。-fopenmp

如果我这样做而不是,问题就会消失。+= 1+= 2

根据 OPenMP 循环增量,“大于 1 的循环增量”是合法的。

如果我将类型从 更改为 ,问题也会消失。nsize_tuint32_t

关于警告的观察

使用 和 打印以下警告:-Wall+= 2clang++

clang-openmp-for-loop-test.cpp:49:3: warning: implicit conversion from 'unsigned long' to 'unsigned int' changes value from 9223372034707292167 to 2147483655 [-Wconstant-conversion]
  for (uint32_t i = 0; i < n; i += 2) {
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
clang-openmp-for-loop-test.cpp:49:3: warning: implicit conversion from 'unsigned long' to 'unsigned int' changes value from 9223372034707292167 to 2147483655 [-Wconstant-conversion]
  for (uint32_t i = 0; i < n; i += 2) {
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
clang-openmp-for-loop-test.cpp:49:3: warning: implicit conversion from 'unsigned long' to 'unsigned int' changes value from 9223372034707292167 to 2147483655 [-Wconstant-conversion]
  for (uint32_t i = 0; i < n; i += 2) {
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.

关于这些警告,我不明白很多事情:

  1. 为什么相同的警告(在同一代码位置)发出 3 次?
  2. 价值从何而来?这是一个警告,当然它应该警告我实际使用的一些常量。该值为 .9223372034707292167-Wconstant-conversion2147483655maximum 32-bit-signed integer + 8
  3. 当我更改为 ?这两个文字的类型肯定是一样的吗?+= 2+= 1

环境

Ubuntu 22.04 中。

sudo apt install clang14 libomp-14-dev gcc+12 libstdc++-12-dev

(只是为了工作而需要的,因为 clang 使用 GCC 的 stdlib 标头。gcc+12 libstdc++-12-dev#include <iostream>

编译:

clang++ myprogram.cpp -o myprogram -fopenmp -O2 -Wall -Wextra

跑:

OMP_NUM_THREADS=1 ./myprogram 2>&1 | head -n20

编译器错误,还是我遗漏了什么?

嚓咔 OpenMP 咔嚓�

评论

0赞 Victor Eijkhout 10/30/2023
您正在混合有符号和无符号数量。例如,停止检验和递增具有整数。如果你把所有东西都签了会怎样?
0赞 nh2 10/30/2023
@VictorEijkhout 停止测试未签名:两者均未签名。警告还没有提到签名的数量。你的增量提示仍然很好:更改为或修复错误,但奇怪的是没有修复错误。因此,它不能只是 incerment 的标志。size_tuint32_t+= 22lu2llu2u

答:

3赞 PierU 10/30/2023 #1

将测试更改为显式强制转换可修复 clang (17) 的问题。i < (uint32_t)n

但是显式强制转换会导致编译错误:(size_t)i < n

condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'.

我不太清楚,但我对 OpenMP 5.2 规范(最新的规范,但我不知道使用哪个 clang)的理解是 C/C++ OpenMP 循环的测试表达式应单独涉及 lhs 或 rhs 上的变量(规范的第 88 页):i

enter image description here

因为 和 是不同的类型,编译器必须隐式强制转换其中之一,并且因为范围更广,所以这就是强制转换。那么测试不再单独涉及,而是 的函数。inniii

因此,最初的问题可能是这是一个非法的 OpenMP 代码,即使 gcc 将其作为 OpenMP 规范的扩展进行处理。不过,这也是一个 clang 编译器错误:clang 最初进入隐式强制转换路由,然后搞砸了循环(它应该因为非法代码而产生编译错误,或者像 gcc 一样作为扩展正确处理它)。

评论

0赞 nh2 10/31/2023
谢谢,我已经在以下位置提交了一个 clang 错误:github.com/llvm/llvm-project/issues/70687