title: GCC inline assembly: "g" constraint and parameter size
body: <h3>Background</h3>
  
  <p>I am aware that solving the following problem with inline assembly is a bad idea. I'm currently learning inline assembly as part of a class on the linux kernel, and this was part of an assignment for that class.</p>
  
  <h1>The Setup</h1>
  
  <p>The begin with, below is a snippet of code that is almost correct, but instead segfaults. It is a function that copies the substring of <code>src</code> starting at index <code>s_idx</code> and ending (exclusively) at index <code>e_idx</code> into the pre-allocated <code>dest</code> using only inline assembly.</p>
  
  <pre><code>static inline char *asm_sub_str(char *dest, char *src, int s_idx, int e_idx) {
    asm("addq %q2, %%rsi;"  /* Add start index to src (ptrs are 64-bit) */
        "subl %k2, %%ecx;"  /* Get length of substr as e - s (int is 32-bit) */
        "cld;"              /* Clear direction bit (force increment) */
        "rep movsb;"        /* Move %ecx bytes of str at %esi into str at %edi */
        : /* No Ouputs */
        : "S" (src), "D" (dest), "g" (s_idx), "c" (e_idx)
        : "cc", "memory"
        );
  
    return dest;
  }
  </code></pre>
  
  <p>The issue with this code is the constraint for the second input parameter. When compiled with <code>gcc</code>s default optimization and <code>-ggdb</code>, the following assembly is generated:</p>
  
  <pre><code>Dump of assembler code for function asm_sub_str:
     0x00000000004008e6 &lt;+0&gt;:     push   %rbp
     0x00000000004008e7 &lt;+1&gt;:     mov    %rsp,%rbp
     0x00000000004008ea &lt;+4&gt;:     mov    %rdi,-0x8(%rbp)
     0x00000000004008ee &lt;+8&gt;:     mov    %rsi,-0x10(%rbp)
     0x00000000004008f2 &lt;+12&gt;:    mov    %edx,-0x14(%rbp)
     0x00000000004008f5 &lt;+15&gt;:    mov    %ecx,-0x18(%rbp)
     0x00000000004008f8 &lt;+18&gt;:    mov    -0x10(%rbp),%rax
     0x00000000004008fc &lt;+22&gt;:    mov    -0x8(%rbp),%rdx
     0x0000000000400900 &lt;+26&gt;:    mov    -0x18(%rbp),%ecx
     0x0000000000400903 &lt;+29&gt;:    mov    %rax,%rsi
     0x0000000000400906 &lt;+32&gt;:    mov    %rdx,%rdi
     0x0000000000400909 &lt;+35&gt;:    add    -0x14(%rbp),%rsi
     0x000000000040090d &lt;+39&gt;:    sub    -0x14(%rbp),%ecx
     0x0000000000400910 &lt;+42&gt;:    cld    
     0x0000000000400911 &lt;+43&gt;:    rep movsb %ds:(%rsi),%es:(%rdi)
     0x0000000000400913 &lt;+45&gt;:    mov    -0x8(%rbp),%rax
     0x0000000000400917 &lt;+49&gt;:    pop    %rbp
     0x0000000000400918 &lt;+50&gt;:    retq 
  </code></pre>
  
  <p>This is identical to the assembly that is generated when the second input parameter's constraint is set to <code>"m"</code> instead of <code>"g"</code>, leading me to believe the compiler is effectively choosing the <code>"m"</code> constraint. In stepping through these instructions with gdb, I found that the offending instruction is <code>+35</code> which adds the starting offset index <code>s_idx</code> to the <code>src</code> pointer in <code>%rsi</code>. The problem of course is that <code>s_idx</code> is only 32-bits and the upper 4 bytes of a 64-bit integer at that location on the static is not necessarily 0. On my machine, it is in fact nonzero and causes the addition to muddle the upper 4 bytes of <code>%rsi</code> which leads to a segfault in instruction <code>+43</code>. </p>
  
  <h1>The Question</h1>
  
  <p>Of course the solution to the above is to change the constraint of parameter <code>2</code> to <code>"r"</code> so it's placed in its own 64-bit register where the top 4 bytes are correctly zeroed and call it a day. Instead, my question is why does gcc resolve the <code>"g"</code> constraint as <code>"m"</code> instead of <code>"r"</code> in this case when the expression <code>"%q2"</code> indicates the value of parameter <code>2</code> will be used as a 64-bit value?</p>
  
  <p>I don't know much about how gcc parses inline assembly, and I know there's not really a sense of typing in assembly, but I would think that gcc could recognize the effectively implicit cast of <code>s_idx</code> to a <code>long</code> when it's used as a 64-bit value in the first inline instruction. FWIW, if I explicitly change <code>"g" (s_idx)</code> to <code>"g" ((long) s_idx)</code>, gcc then resolves the <code>"g"</code> constraint to <code>"r"</code> since <code>(long) s_idx</code> is a temporary value. I would think gcc could do that implicitly as well?</p>