【问题标题】:evaluating/accessing a structure评估/访问结构
【发布时间】:2013-12-19 00:57:57
【问题描述】:

考虑相同代码的两个略有不同的版本:

struct s
{
  int dummy[1];
};

volatile struct s s;

int main(void)
{
  s;
  return 0;
}

struct s
{
  int dummy[16];
};

volatile struct s s;

int main(void)
{
  s;
  return 0;
}

这是我为他们使用 gcc 4.6.2 的结果:

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        call    ___main
        movl    _s, %eax
        xorl    %eax, %eax
        leave
        ret

        .comm   _s, 4, 2

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        call    ___main
        xorl    %eax, %eax
        leave
        ret

        .comm   _s, 64, 5

请注意在第二种情况下无法访问s

这是编译器的错误,还是我只是在处理 C 标准的以下声明,而 gcc 开发人员只是选择了这种奇怪的实现定义并仍在按规则行事?:

什么构成对具有 volatile 限定类型的对象的访问是实现定义的。

造成这种差异的原因是什么?我自然希望整个结构都被访问(或者不被访问,我不确定),不管它的大小和里面有什么。

附:在这种情况下,您的编译器(非 gcc 或更新的 gcc)会做什么? (如果这是您要解决的唯一部分,请在评论中回答最后一个问题,因为这不是要问的主要问题,而是更多好奇的问题)。

【问题讨论】:

  • 我刚刚尝试使用 GCC 4.8.0,它的效果类似:1 字版本仍然可以单读,但 16 字版本没有。但是,如果我说“s=s”,即使是 16 条目的结构也会得到一个副本。
  • 这很有趣:GCC 4.6.0 手册专门调用了 scalar volatiles,而 GCC 4.0.4 手册没有(我碰巧用 google 很快找到了两个) . 4.6.0:gcc.gnu.org/onlinedocs/gcc-4.6.0/gcc/Volatiles.html 4.0.4:gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Volatiles.html 在您的示例中,具有单个 int 的结构是“标量”,而具有 16 个 int 的结构不是。
  • @JoeZ 我没有访问结构内部的标量,而是访问整个结构(或者我认为是这样)。
  • 对,但我相信 GCC 会将适合寄存器的结构转换为标量访问。 IE。 struct { int x[1]; } 在许多情况下被视为标量。我不认为这是有保证的,请注意,如果你问我,它似乎是一个实现工件。也许你应该在 GCC 开发者名单上问一下?
  • 这可以是related吗?

标签: c gcc struct volatile


【解决方案1】:

对于这个解释发生了什么的问题,C 和 C++ 之间存在差异。

clang-3.4

当将这些 sn-ps 中的任何一个编译为 C++ 时,发出的程序集在任何一种情况下都没有引用 s。事实上,两者都发出了警告:

volatile.c:8:2: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue] s;

在 C99 模式下编译时未发出这些警告。如this blog postthis GCC wiki entry from the question comments 中所述,在此上下文中使用s 会导致C 中的左值到右值转换,但在C++ 中不会。这可以通过检查 C 的 Clang AST 得到证实,因为存在来自 LvalueToRValue 的 ImplicitCastExpr,它在从 C++ 生成的 AST 中不存在。 (AST 不受结构大小的影响)。

对 Clang 源的快速 grep 在聚合表达式的发射中揭示了这一点:

case CK_LValueToRValue:
// If we're loading from a volatile type, force the destination
// into existence.
if (E->getSubExpr()->getType().isVolatileQualified()) {
  EnsureDest(E->getType());
  return Visit(E->getSubExpr());
}

EnsureDest 强制发射堆栈槽,为表达式调整大小和类型。由于不允许优化器删除易失性访问,它们在 IR 和输出 asm 中分别保留为标量加载/存储和 memcpy。鉴于上述情况,这是我所期望的行为。

gcc-4.8.2

在这里,我观察到与问题相同的行为。但是,当我将表达式从 s; 更改为 s.dummy; 时,访问权限不会出现在任一版本中。我不熟悉 gcc 的内部结构,因为我使用 LLVM,所以我无法推测为什么会发生这种情况。但基于以上观察,我会说这是由于不一致导致的编译器错误。

【讨论】:

  • 谢谢。我认为 gcc 开发人员可以摆脱这种不一致,因为评估整个结构(不将它们相互分配)很少发生,实际重要性低(更不用说可能有大量内存或代码或 CPU 周期的事实)需要从内存中读取结构,并且不清楚应该如何读取工会成员)并且标准明确表示 What constitutes an access to an object that has volatile-qualified type is implementation-defined. 我认为这件事可以或需要进一步澄清。
猜你喜欢
  • 1970-01-01
  • 2019-08-01
  • 1970-01-01
  • 2023-02-15
  • 2012-11-22
  • 1970-01-01
  • 2014-07-30
  • 2014-04-01
  • 2011-07-17
相关资源
最近更新 更多