title: SIMD vector interoperability between LLVM and gcc
body: <p>I would like to accelerate an program I'm working on by dynamically generating code with LLVM's JIT. The algorithm can operate on vectors, and I'd rather like to use the SIMD vector extensions in LLVM to do this (not only does it make some operations faster, but it actually makes the code generation simpler).</p>
  
  <p>Do I stand any chance of having this work in a reasonably portable way?</p>
  
  <p>On the C side of things, I'll be compiling with gcc, clang or, maybe, icc. My vectors are going to be simple <code>float x 4</code> or <code>double x 4</code> things. The de facto standard for non-platform-specific vector operations in this world appears to be the gcc vector extension:</p>
  
  <pre><code>typedef double Vector4 __attribute__ ((vector_size (sizeof(double)*4)));
  </code></pre>
  
  <p>Inspection of generated code shows that clang will pass a <code>double x 4</code> vector in registers, while gcc wants it on the stack --- which is bad. (They both pass <code>float x 4</code> vectors in registers.)</p>
  
  <p>My understanding is that the two systems are supposed to be ABI compatible, but obviously vectors don't count. Can I actually do this?</p>
  
  <p>My example program is::</p>
  
  <pre><code>typedef double real;
  typedef real Vector4 __attribute__ ((vector_size (sizeof(real)*4)));
  
  Vector4 scale(Vector4 a)
  {
      Vector4 s = {2, 2, 2, 2};
      return a*s;
  }
  </code></pre>
  
  <p>This compiles with LLVM into:</p>
  
  <pre><code>scale:
      movapd  .LCPI0_0(%rip), %xmm2
      mulpd   %xmm2, %xmm0
      mulpd   %xmm2, %xmm1
      ret
  </code></pre>
  
  <p>...but gcc produces this horror:</p>
  
  <pre><code>scale:
      subq    $64, %rsp
      movq    %rdi, %rax
      movsd   .LC0(%rip), %xmm0
      movapd  72(%rsp), %xmm1
      movsd   %xmm0, -56(%rsp)
      movsd   %xmm0, -48(%rsp)
      movsd   %xmm0, -72(%rsp)
      movsd   %xmm0, -64(%rsp)
      mulpd   -56(%rsp), %xmm1
      movapd  88(%rsp), %xmm0
      mulpd   -72(%rsp), %xmm0
      movapd  %xmm1, -104(%rsp)
      movq    -104(%rsp), %rdx
      movapd  %xmm1, -24(%rsp)
      movapd  %xmm0, -8(%rsp)
      movq    %rdx, (%rdi)
      movq    -16(%rsp), %rdx
      movq    %rdx, 8(%rdi)
      movq    -8(%rsp), %rdx
      movq    %rdx, 16(%rdi)
      movq    (%rsp), %rdx
      movq    %rdx, 24(%rdi)
      addq    $64, %rsp
      ret
  </code></pre>
  
  <p>If I redefine <code>real</code> to be a <code>float</code>, I get this from both compilers (they produce identical code):</p>
  
  <pre><code>scale:
      mulps   .LCPI0_0(%rip), %xmm0
      ret
  </code></pre>
  
  <p>These were all compiled with <code>$CC -O3 -S -msse test.c</code>.</p>
  
  <p><b>Update:</b> It suddenly occurs to me that the simple solution is to just use LLVM to create a trampoline that the translates from structures to vectors and vice versa. That way the interoperability problem is reduced to pass-by-value structures, which <i>are</i> nailed down by the ABI; the vectors only exist in LLVM-land. It means I only get to use the SIMD stuff inside LLVM, but I can live with that.</p>
  
  <p>However, I would still like to know the answer to the above; vectors are awesome and I'd like to be able to use them more.</p>
  
  <p><b>Update update:</b> Turns out that the way C passes structures by value is insanely... er, insane! A <code>struct { double x, y, z; }</code> is passed by pointer; a <code>struct { float x, y, z }</code> is passed as a <em>pair</em> of %xmm registers: <code>x</code> and <code>y</code> are packed into the first, and the <code>z</code> is in the second...</p>
  
  <p>Simple and unpainful it's not!</p>