title: Loading and storing long doubles in x86-64
body: <p>I noticed a weird thing today. When copying a <code>long double</code><sup>1</sup> all of <code>gcc</code>, <code>clang</code> and <code>icc</code> generate <code>fld</code> and <code>fstp</code> instructions, with <code>TBYTE</code> memory operands.</p>
  
  <p>That is, the following function:</p>
  
  <pre><code>void copy_prim(long double *dst, long double *src) {
      *src = *dst;
  }
  </code></pre>
  
  <p><a href="https://godbolt.org/g/yjyjWg" rel="nofollow noreferrer">Generates</a> the following assembly:</p>
  
  <pre><code>copy_prim(long double*, long double*):
    fld TBYTE PTR [rdi]
    fstp TBYTE PTR [rsi]
    ret
  </code></pre>
  
  <p>Now according to <a href="http://www.agner.org/optimize/#manual_instr_tab" rel="nofollow noreferrer">Agner's tables</a> this is a poor choice for performance, as <code>fld</code> takes four uops (none fused) and <code>fstp</code> takes a whopping <strong>seven</strong> uops (none fused) versus say a single fused uop each for <code>movaps</code> to/from an <code>xmm</code> register.</p>
  
  <p>Interestingly, <code>clang</code> starts using <code>movaps</code> as soon as you put the <code>long double</code> in a <code>struct</code>. The following code:</p>
  
  <pre><code>struct long_double {
      long double x;
  };
  
  void copy_ld(long_double *dst, long_double *src) {
      *src = *dst;
  }
  </code></pre>
  
  <p><a href="https://godbolt.org/g/yjyjWg" rel="nofollow noreferrer">Compiles</a> to the same assembly with <code>fld</code>/<code>fstp</code> as previously shown for <code>gcc</code> and <code>icc</code> but <code>clang</code> now uses:</p>
  
  <pre><code>copy_ld(long_double*, long_double*):
    movaps xmm0, xmmword ptr [rdi]
    movaps xmmword ptr [rsi], xmm0
    ret
  </code></pre>
  
  <p>Oddly, if you stuff an additional <code>int</code> member into the <code>struct</code> (which <em>doubles</em> its size to 32 bytes due to alignment), all compilers generate SSE-only copy code:</p>
  
  <pre><code>copy_ldi(long_double_int*, long_double_int*):
    movdqa xmm0, XMMWORD PTR [rdi]
    movaps XMMWORD PTR [rsi], xmm0
    movdqa xmm0, XMMWORD PTR [rdi+16]
    movaps XMMWORD PTR [rsi+16], xmm0
    ret
  </code></pre>
  
  <p>Is there any functional reason to copy floating point values with <code>fld</code> and <code>fstp</code> or is just a missed optimization?</p>
  
  <hr>
  
  <p><sup>1</sup> Although a <code>long double</code> (i.e., x86 extended precision float) is nominally 10 bytes on x86, it has <code>sizeof == 16</code> and <code>alignof == 16</code> since alignments have to be a power of two and the size <a href="https://stackoverflow.com/q/46457449/149138">must usually be at least as large as the alignment</a>.</p>