如何让GCC将 strcpy内联(字符串拷贝)
我正在使用 www.godbolt.org 网站,选择x86-64 gcc 15.2编译器以及 -O2优化选项,但它仍然在libc中对 strcpy 发出调用。
我希望这个编译器将strcpy操作内联实现,而不是使用内建库API。我该如何使用GCC编译器来实现这一点?
在Godbolt上的示例源代码与编译器输出 on Godbolt,仅在buffer为局部 char buffer[500] 数组时调用 strcpy(buffer, argv[1])。
解决方案
对于长度未知的字符串,对strcpy进行内联可能会很复杂,不同的策略可以针对不同情况进行优化(长字符串、短字符串、不同架构等)
让memcpy实现内联更容易,因此如果可以对显式 strlen 进行内联,并使用 -O2 -minline-all-stringops,如上文评论中的Peter Cordes所建议。下面的做法将把拷贝内联执行。
int main(int argc, char** argv)
{
int age = 100;
char buffer[500];
strcpy(buffer, argv[1]);
printf("strcpy gives %s and age gives %d\n", buffer, age);
int n = strlen(argv[2]);
memcpy(buffer, argv[2], n+1);
printf("strcpy gives %s and age gives %d\n", buffer, age);
return 0;
}
请注意,代码中包含内联、展开等——程序大小会增大,并且可能抵消内联带来的收益。
mov rsi, QWORD PTR [rbx+16]
mov rdx, rsi
mov rax, rsi
and edx, 3
je .L20
cmp rdx, 2
je .L5
ja .L4
cmp BYTE PTR [rsi], 0
je .L3
lea rax, [rsi+1]
.L5:
cmp BYTE PTR [rax], 0
je .L3
add rax, 1
.L4:
cmp BYTE PTR [rax], 0
je .L3
add rax, 1
.L20:
mov ecx, DWORD PTR [rax]
add rax, 4
lea edx, [rcx-16843009]
not ecx
and edx, ecx
and edx, -2139062144
je .L20
mov ecx, edx
shr ecx, 16
test edx, 32896
cmove edx, ecx
lea rcx, [rax+2]
cmove rax, rcx
add dl, dl
sbb rax, 3
.L3:
sub rax, rsi
mov rdi, rsp
add eax, 1
cdqe
cmp rax, 8
jb .L6
mov rcx, rax
shr rcx, 3
rep movsq
.L6:
xor edx, edx
test al, 4
je .L7
mov edx, DWORD PTR [rsi]
mov DWORD PTR [rdi], edx
mov edx, 4
.L7:
test al, 2
je .L8
movzx ecx, WORD PTR [rsi+rdx]
mov WORD PTR [rdi+rdx], cx
add rdx, 2
.L8:
test al, 1
je .L9
movzx eax, BYTE PTR [rsi+rdx]
mov BYTE PTR [rdi+rdx], al
备选方案
另一种选项——在实际的 strcpy 针对短字符串、避免调用带来显著收益的情况下——是将“轻量级”的 strcpy 实现为static inline函数:
#define strcpy inline_strcpy
static inline char *inline_strcpy(char *dest, char *src)
{
for (char *d = dest ; (*d++ = *src++) ; ) ;
return dest ;
}
或者类似做法——GCC将几乎总是遵循内联,并生成一个相对高效的逐字节循环。
main:
sub rsp, 520
xor eax, eax
mov rcx, QWORD PTR [rsi+8]
mov rsi, rsp
.L2:
movzx edx, BYTE PTR [rcx+rax]
mov BYTE PTR [rsi+rax], dl
add rax, 1
test dl, dl
jne .L2
mov edx, 100
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 520
ret
为了更高性能的解决方案,将利用(英特尔)推荐的lodsb/stosb。免责声明:我不是汇编专家——这段汇编是用ChatGPT生成的。
static inline char *strcpy_asm(char *dst, const char *src)
{
char *ret = dst;
#if defined(__x86_64__) || defined(__i386__)
__asm__ volatile (
"1:\n\t"
"lodsb\n\t"
"stosb\n\t"
"testb %%al, %%al\n\t"
"jne 1b"
: "+D"(dst), "+S"(src)
:
: "rax", "memory"
);
#else
while ((*dst++ = *src++) != '\0')
;
#endif
return ret;
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。