title: Why does Clang generate different code for reference and non-null pointer arguments?
body: <p>This is related to <a href="/q/66263263">Why can't GCC generate an optimal operator== for a struct of two int32s?</a>. I was playing around with the code from that question at godbolt.org and noticed this odd behavior.</p>
  <pre><code>struct Point {
      int x, y;
  };
  
  bool nonzero_ptr(Point const* a) {
      return a-&gt;x || a-&gt;y;
  }
  
  bool nonzero_ref(Point const&amp; a) {
      return a.x || a.y;
  }
  </code></pre>
  <p><a href="https://godbolt.org/z/e49h6d" rel="noreferrer">https://godbolt.org/z/e49h6d</a></p>
  <p>For <code>nonzero_ptr</code>, clang -O3 (all versions) produces this or similar code:</p>
  <pre class="lang-none prettyprint-override"><code>    mov     al, 1
      cmp     dword ptr [rdi], 0
      je      .LBB0_1
      ret
  .LBB0_1:
      cmp     dword ptr [rdi + 4], 0
      setne   al
      ret
  </code></pre>
  <p>This strictly implements the short-circuiting behavior of the C++ function, loading the <code>y</code> field only if the <code>x</code> field is zero.</p>
  <p>For <code>nonzero_ref</code>, clang 3.6 and earlier generate the same code as they do for <code>nonzero_ptr</code>, but clang 3.7 through 11.0.1 produce</p>
  <pre class="lang-none prettyprint-override"><code>    mov     eax, dword ptr [rdi + 4]
      or      eax, dword ptr [rdi]
      setne   al
      ret
  </code></pre>
  <p>which loads <code>y</code> unconditionally. No version of clang is willing to do that when the parameter is a pointer. Why?</p>
  <p>The only situation I can think of (on the x64 platform) where the behavior of the branching code would be observably different is when there's no memory mapped at <code>[rdi+4]</code>, but I'm still unsure why clang would consider that case important for pointers and not references. My best guess is that there is some language-lawyery argument that references must be to &quot;full objects&quot; and pointers needn't be:</p>
  <pre><code>char* p = alloc_4k_page_surrounded_by_guard_pages();
  int* pi = reinterpret_cast&lt;int*&gt;(p + 4096 - sizeof(int));
  Point* ppt = reinterpret_cast&lt;Point*&gt;(pi);  // ok???
  ppt-&gt;x = 42;  // ok???
  Point&amp; rpt = *ppt;  // UB???
  </code></pre>
  <p>But if the spec implies that, I'm not seeing how.</p>