title: Looping over arrays with inline assembly
body: <p><strong>When looping over an array with inline assembly should I use the register modifier "r" or he memory modifier "m"?</strong></p>
  
  <p>Let's consider an example which adds two float arrays <code>x</code>, and <code>y</code> and writes the results to <code>z</code>. Normally I would use intrinsics to do this like this</p>
  
  <pre><code>for(int i=0; i&lt;n/4; i++) {
      __m128 x4 = _mm_load_ps(&amp;x[4*i]);
      __m128 y4 = _mm_load_ps(&amp;y[4*i]);
      __m128 s = _mm_add_ps(x4,y4);
      _mm_store_ps(&amp;z[4*i], s);
  }
  </code></pre>
  
  <p>Here is the inline assembly solution I have come up with using the register modifier "r"</p>
  
  <pre><code>void add_asm1(float *x, float *y, float *z, unsigned n) {
      for(int i=0; i&lt;n; i+=4) {
          __asm__ __volatile__ (
              "movaps   (%1,%%rax,4), %%xmm0\n"
              "addps    (%2,%%rax,4), %%xmm0\n"
              "movaps   %%xmm0, (%0,%%rax,4)\n"
              :
              : "r" (z), "r" (y), "r" (x), "a" (i)
              :
          );
      }
  }
  </code></pre>
  
  <p>This generates similar assembly to GCC. The main difference is that GCC adds 16 to the index register and uses a scale of 1 whereas the inline-assembly solution adds 4 to the index register and uses a scale of 4.</p>
  
  <p><strong>I was not able to use a general register for the iterator. I had to specify one which in this case was <code>rax</code>. Is there a reason for this?</strong></p>
  
  <p>Here is the solution I came up with using the memory modifer "m"</p>
  
  <pre><code>void add_asm2(float *x, float *y, float *z, unsigned n) {
      for(int i=0; i&lt;n; i+=4) {
          __asm__ __volatile__ (
              "movaps   %1, %%xmm0\n"
              "addps    %2, %%xmm0\n"
              "movaps   %%xmm0, %0\n"
              : "=m" (z[i])
              : "m" (y[i]), "m" (x[i])
              :
              );
      }
  }
  </code></pre>
  
  <p>This is less efficient as it does not use an index register and instead has to add 16 to the base register of each array.  The generated assembly is (gcc (Ubuntu 5.2.1-22ubuntu2) with <code>gcc -O3 -S asmtest.c</code>):</p>
  
  <pre><code>.L22
      movaps   (%rsi), %xmm0
      addps    (%rdi), %xmm0
      movaps   %xmm0, (%rdx)
      addl    $4, %eax
      addq    $16, %rdx
      addq    $16, %rsi
      addq    $16, %rdi
      cmpl    %eax, %ecx
      ja      .L22
  </code></pre>
  
  <p><strong>Is there a better solution using the memory modifier "m"? Is there some way to get it to use an index register?</strong> The reason I asked is that it seemed more logical to me to use the memory modifer "m" since I am reading and writing memory. Additionally, with the register modifier "r" I never use an output operand list which seemed odd to me at first.</p>
  
  <p>Maybe there is a better solution than using "r" or "m"?</p>
  
  <p>Here is the full code I used to test this</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;x86intrin.h&gt;
  
  #define N 64
  
  void add_intrin(float *x, float *y, float *z, unsigned n) {
      for(int i=0; i&lt;n; i+=4) {
          __m128 x4 = _mm_load_ps(&amp;x[i]);
          __m128 y4 = _mm_load_ps(&amp;y[i]);
          __m128 s = _mm_add_ps(x4,y4);
          _mm_store_ps(&amp;z[i], s);
      }
  }
  
  void add_intrin2(float *x, float *y, float *z, unsigned n) {
      for(int i=0; i&lt;n/4; i++) {
          __m128 x4 = _mm_load_ps(&amp;x[4*i]);
          __m128 y4 = _mm_load_ps(&amp;y[4*i]);
          __m128 s = _mm_add_ps(x4,y4);
          _mm_store_ps(&amp;z[4*i], s);
      }
  }
  
  void add_asm1(float *x, float *y, float *z, unsigned n) {
      for(int i=0; i&lt;n; i+=4) {
          __asm__ __volatile__ (
              "movaps   (%1,%%rax,4), %%xmm0\n"
              "addps    (%2,%%rax,4), %%xmm0\n"
              "movaps   %%xmm0, (%0,%%rax,4)\n"
              :
              : "r" (z), "r" (y), "r" (x), "a" (i)
              :
          );
      }
  }
  
  void add_asm2(float *x, float *y, float *z, unsigned n) {
      for(int i=0; i&lt;n; i+=4) {
          __asm__ __volatile__ (
              "movaps   %1, %%xmm0\n"
              "addps    %2, %%xmm0\n"
              "movaps   %%xmm0, %0\n"
              : "=m" (z[i])
              : "m" (y[i]), "m" (x[i])
              :
              );
      }
  }
  
  int main(void) {
      float x[N], y[N], z1[N], z2[N], z3[N];
      for(int i=0; i&lt;N; i++) x[i] = 1.0f, y[i] = 2.0f;
      add_intrin2(x,y,z1,N);
      add_asm1(x,y,z2,N);
      add_asm2(x,y,z3,N);
      for(int i=0; i&lt;N; i++) printf("%.0f ", z1[i]); puts("");
      for(int i=0; i&lt;N; i++) printf("%.0f ", z2[i]); puts("");
      for(int i=0; i&lt;N; i++) printf("%.0f ", z3[i]); puts("");
  }
  </code></pre>