【问题标题】:Portable way to tell the compiler that alignment is OK without supressing the warning?在不抑制警告的情况下告诉编译器对齐没问题的便携式方法?
【发布时间】:2016-01-23 19:21:08
【问题描述】:

我们运行的测试之一是使用-Wcast-align 进行编译。当浮点数、双精度数和整数之间发生错误转换时,它特别有用(有时会导致 SIGBUS,IIRC)。

我们的代码基本上执行以下操作。实际案例稍微多一些,但这是用法的精髓:

typedef uint64_t word64;

static const size_t SIZE = ...;
word64 buffer[SIZE] = ...;

还有:

DoSomethingWithBuffer(const byte* buff, size_t size)
{
    word64* ptr = (word64*)buff;
    ...
}

缓冲区在 8 或 16 字节边界上对齐。我已经使用手动代码审查和运行时断言验证了对齐方式。

问题在于 GCC 和 Clang 都警告数据未对齐。它做了将近 2000 次,所以我可能会丢失真正的发现。例如:

warning: cast from 'const byte *' (aka 'const unsigned char *') to 'word64 *'
(aka 'unsigned long long *') increases required alignment from 1 to 8 [-Wcast-align]
    word64 tmp = *(word64 *)inBlock ^ roundKeys[0];
                  ^~~~~~~~~~~~~~~~~

使用 Clang,我可以使用 assert 进行检测,编译器有时会将其作为诊断提示。但它似乎不适用于这种情况。也就是说,Clang 没有建立 assert(inBlock % 8 == 0); 表示其对齐的连接。

如何在不抑制警告的情况下向编译器传达缓冲区已对齐?

【问题讨论】:

  • 1) 使用stdint.h 固定宽度类型。 2) C 标准中的alignas 有什么问题? 3) 您的代码可能会调用未定义的行为。
  • @Olaf - 谢谢。我们的测试计划相当全面。我们要求代码通过 Clang 和 GCC Undefined Behavior Sanitizer 的安全门,它们明确测试未对齐的访问。我们还要求它通过 Valgrind、Coverity 和 Microsoft Enterprise Analysis。此外,断言显式检查条件并且不会触发。所以我不相信它在这个特定领域会遭受 UB(我希望不会在任何领域)。
  • 您可以尝试将其与编译器对齐的另一种类型合并,例如double.
  • 你甚至没有检查我假设的 alignas (resp. _Alignas) 说明符是什么。
  • @jww:它也是 C11 以来的 C 标准特性。请记住,只有一个 C 标准。仅供参考,该功能是有意添加到两种语言中的(类似于原子,顺便说一句。)

标签: c gcc casting clang compiler-warnings


【解决方案1】:

由于 OP 现有代码库不需要强类型匹配,因此使用 void* 简单地击败大多数类型匹配,这将消除警告。也参考@Ctx

 void DoSomethingWithBuffer(const byte* buff, size_t size) {
   const word64* ptr = (void*) buff;
   ...
 }

【讨论】:

  • 再次感谢。结果真的很好。我花了两天时间才清除了大约 2500 多个 -Wcast-align 警告(大部分是大片的,比如一次更改 1 个宏可修复 50 个结果)。在 2500 多个左右中,有两个是合法的发现。
【解决方案2】:

这让 Clang 很高兴,但会增加冗余计算(因此,对于发布版本可能不合适):

uint64_t y = *(uint64_t *)((uintptr_t)x & ~7UL);

使用 GCC,您可以使用 __builtin_assume_aligned。引用GCC Manual:

内置功能:

void * __builtin_assume_aligned (const void *exp, size_t align, ...)

这个函数返回它的第一个参数,并允许编译器 假设返回的指针至少对齐字节对齐。这 内置可以有两个或三个参数,如果它有三个, 第三个参数应该是整数类型,如果它是非零意味着 错位偏移。例如:

void *x = __builtin_assume_aligned (arg, 16);

意味着编译器可以假定x,设置为arg,至少是16字节对齐的,而:

void *x = __builtin_assume_aligned (arg, 32, 8);

意味着编译器可以假设x,设置为arg(char *) x - 8 是 32 字节对齐。

还有一个patch 供 LLVM 实现 __builtin_assume_aligned,但尚未合并。英特尔编译器的类似内在函数exists。不确定 Visual Studio。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-29
    • 1970-01-01
    • 2010-11-03
    • 2015-09-24
    • 1970-01-01
    • 2011-02-20
    • 2018-05-23
    相关资源
    最近更新 更多