为什么 GCC 包含一个“空的"异或 [英] Why does GCC include an "empty" XOR

查看:63
本文介绍了为什么 GCC 包含一个“空的"异或的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下一段代码:

typedef struct {
        int x;
        int y;
        int z;
        int w;
} s32x4;

s32x4
f() {
        s32x4 v;
        v.x = 0

        return v;
}

生成 (gcc -O2):

which generates (gcc -O2):

f:
        xor     eax, eax
        xor     edx, edx          ; this line is questionable
        ret

其中 clang 输出 (clang -O2):

where clang outputs (clang -O2):

f:                                      # @f
        xor     eax, eax
        ret

问题

  • GCC 在那里插入 XOR 是否有原因?
  • 如果没有充分的理由:我能以某种方式摆脱它吗?
  • Questions

    • Is there a reason why GCC inserts an XOR there?
    • If there isn't a good reason for it: can I somehow get rid of it?
    • 推荐答案

      您读取了一个部分未初始化的 struct 对象以返回它,即 (可以说) 现场未定义行为,即使调用者不使用返回值.

      You read a partly uninitialized struct object to return it, which is (arguably) Undefined Behaviour on the spot, even if the caller doesn't use the return value.

      在 x86-64 System V ABI 中的 RDX:RAX 中返回 16 字节结构(任何更大的结构,它将通过让调用者传递指向返回值对象的指针来返回).GCC 将未初始化的部分归零,clang 会留下任何垃圾.

      The 16-byte struct is returned in RDX:RAX in the x86-64 System V ABI (any larger and it would be returned by having the caller pass a pointer to a return-value object). GCC is zeroing the uninitialized parts, clang is leaving whatever garbage was there.

      GCC 喜欢在任何可能将错误依赖耦合到某些东西中的风险时打破依赖关系.(例如 pxor xmm0,xm​​m0 在使用设计不佳的 cvtsi2sd xmm0, eax 之前).Clang 更激进"忽略这一点,有时即使这样做只有很小的代码大小好处,例如使用 mov al, 1 代替 mov eax,1mov al, [rdi] 代替 movzx eax, byte ptr[rdi])

      GCC loves to break dependencies any time there might be a risk of coupling a false dependency into something. (e.g. pxor xmm0,xmm0 before using the badly-designed cvtsi2sd xmm0, eax). Clang is more "aggressive" in leaving that out, sometimes even when there's only a tiny code-size benefit for doing so, e.g. using mov al, 1 instead of mov eax,1, or mov al, [rdi] instead of movzx eax, byte ptr [rdi])

      您所看到的最简单的形式是返回一个未初始化的普通 int,
      GCC 和 clang 代码生成之间的相同区别:

      The simplest form of what you're seeing is returning an uninitialized plain int,
      same difference between GCC and clang code-gen:

      int foo(){
          int x;
          return x;
      }
      

      (Godbolt)

      # clang 11.0.1 -O2
      foo:
              # leaving EAX unwritten
              ret
      
      
      # GCC 10.2 -O2
      foo:
              xor     eax, eax        # return 0
              ret
      

      这里clang到达"省略整个指令.当然,这是未定义的行为(读取未初始化的对象),因此标准实际上允许任何事情,包括 ud2(保证引发非法指令异常),甚至省略 ret假设此代码路径无法访问,即永远不会调用该函数.或者返回 0xdeadbeef,或者调用任何其他函数,如果你有一个恶意的 DeathStation 9000 C 实现.

      Here clang "gets to" leave out a whole instruction. Of course it's undefined behaviour (reading an uninitialized object), so the standard allows literally anything, including ud2 (guaranteed to raise an illegal instruction exception), or omitting even the ret on the assumption that this code-path is unreachable, i.e. the function will never be called. Or to return 0xdeadbeef, or call any other function, if you have a malicious DeathStation 9000 C implementation.

      这篇关于为什么 GCC 包含一个“空的"异或的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆