GCC内联汇编:"g"约束和参数大小

def*_*nst 5 c assembly gcc inline-assembly

背景

我知道使用内联汇编解决以下问题是个坏主意.我目前正在学习内联汇编作为linux内核类的一部分,这是该类的一个赋值的一部分.

安装程序

下面的开头是一段几乎正确的代码片段,而不是段错误.它是一个函数,它将src从索引处开始并在索引处s_idx结束(排他地)的子字符串复制e_idxdest仅使用内联汇编的预分配.

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;
}
Run Code Online (Sandbox Code Playgroud)

此代码的问题是第二个输入参数的约束.使用gccs默认优化编译时-ggdb,会生成以下程序集:

Dump of assembler code for function asm_sub_str:
   0x00000000004008e6 <+0>:     push   %rbp
   0x00000000004008e7 <+1>:     mov    %rsp,%rbp
   0x00000000004008ea <+4>:     mov    %rdi,-0x8(%rbp)
   0x00000000004008ee <+8>:     mov    %rsi,-0x10(%rbp)
   0x00000000004008f2 <+12>:    mov    %edx,-0x14(%rbp)
   0x00000000004008f5 <+15>:    mov    %ecx,-0x18(%rbp)
   0x00000000004008f8 <+18>:    mov    -0x10(%rbp),%rax
   0x00000000004008fc <+22>:    mov    -0x8(%rbp),%rdx
   0x0000000000400900 <+26>:    mov    -0x18(%rbp),%ecx
   0x0000000000400903 <+29>:    mov    %rax,%rsi
   0x0000000000400906 <+32>:    mov    %rdx,%rdi
   0x0000000000400909 <+35>:    add    -0x14(%rbp),%rsi
   0x000000000040090d <+39>:    sub    -0x14(%rbp),%ecx
   0x0000000000400910 <+42>:    cld    
   0x0000000000400911 <+43>:    rep movsb %ds:(%rsi),%es:(%rdi)
   0x0000000000400913 <+45>:    mov    -0x8(%rbp),%rax
   0x0000000000400917 <+49>:    pop    %rbp
   0x0000000000400918 <+50>:    retq 
Run Code Online (Sandbox Code Playgroud)

这与将第二个输入参数的约束设置为"m"而不是使用时生成的程序集相同,这使"g"我相信编译器正在有效地选择"m"约束.在使用gdb单步执行这些指令时,我发现有问题的指令+35会将起始偏移索引添加s_idxsrc指针中%rsi.问题当然是s_idx只有32位,而静态上该位置的64位整数的高4字节不一定是0.在我的机器上,它实际上是非零的并且导致添加混淆上层其中4个字节%rsi导致指令中的段错误+43.

问题

当然,解决上述是改变参数的约束2"r"所以它被放置在其自己的64位寄存器,其中前4个字节被正确地归零,收工.相反,我的问题是为什么gcc解析"g"约束"m"而不是"r"在这种情况下表达式"%q2"指示参数的值2将被用作64位值?

我不太了解gcc如何解析内联汇编,而且我知道在汇编中没有真正的输入感,但我认为gcc可以识别当它被用作64位值时的有效隐式s_idx转换long在第一个内联指令中.FWIW,如果我明确更改"g" (s_idx)"g" ((long) s_idx),则gcc会将"g"约束解析为,"r"因为(long) s_idx它是临时值.我认为gcc也可以隐含地做到这一点?