与 memcpy 相比,为什么 GCC 为按字节复制发出更大的输出?
Why is GCC emitting larger output for a bytewise copy vs memcpy?
以下 C11 程序以两种不同的方式将浮点数的位表示形式提取到 uint32_t 中。
#include <stdint.h>
_Static_assert(sizeof(float) == sizeof(uint32_t));
uint32_t f2i_char(float f) {
uint32_t x;
char const *src = (char const *)&f;
char *dst = (char *)&x;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
return x;
}
uint32_t f2i_memcpy(float f) {
uint32_t x;
memcpy(&x, &f, sizeof(x));
return x;
}
使用 armgcc 10.2.1 (none eabi) 编译的输出程序集非常不同,即使应用了 -Os
或 -O3
优化:
我正在编译:
-mcpu=cortex-m4 -std=c11 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
f2i_char:
sub sp, sp, #16
vstr.32 s0, [sp, #4]
ldr r3, [sp, #4]
strb r3, [sp, #12]
ubfx r2, r3, #8, #8
strb r2, [sp, #13]
ubfx r2, r3, #16, #8
ubfx r3, r3, #24, #8
strb r2, [sp, #14]
strb r3, [sp, #15]
ldr r0, [sp, #12]
add sp, sp, #16
bx lr
f2i_memcpy:
sub sp, sp, #8
vstr.32 s0, [sp, #4]
ldr r0, [sp, #4]
add sp, sp, #8
bx lr
为什么 gcc 不为这两个函数生成相同的程序集?
Why is GCC emitting larger output with -Os than -O3 for this function on Cortex-M4?
为什么不呢?每个选项启用或禁用特定的编译器内部工作。当然,可能会有并且将会有编译器决定使 -O3
产生比 -Os
.
更小的代码
Is there anything specific about the C11 standard or the Armv7E-M that's inhibiting gcc from emitting the smaller assembly at -Os?
没有
Is this gcc missing an optimization opportunity?
是的,你可以这么说。但这可能是故意的 - 可能是导致生成此类代码的优化实际上是编译时间和 CPU 消耗,因此它被禁用。就是这样。
避免手动复制数据。使用 memcpy
。 GCC 非常了解这个函数,如果不需要,根本不会调用它。指针双关也可以打破严格的别名规则,.
在 none-eabi memcpy
中不会发出任何代码,因为 return 值在同一个寄存器中作为参数传递。无需任何操作。
https://godbolt.org/z/q8v39d737
#include <stdint.h>
_Static_assert(sizeof(float) == sizeof(uint32_t));
uint32_t f2i_char(float f) {
uint32_t x;
char const *src = (char const *)&f;
char *dst = (char *)&x;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
return x;
}
uint32_t f2i1(float f) {
uint32_t x;
memcpy(&x, &f, sizeof(x));
return x;
}
f2i_char:
sub sp, sp, #8
ubfx r1, r0, #8, #8
ubfx r2, r0, #16, #8
ubfx r3, r0, #24, #8
strb r0, [sp, #4]
strb r1, [sp, #5]
strb r2, [sp, #6]
strb r3, [sp, #7]
ldr r0, [sp, #4]
add sp, sp, #8
bx lr
f2i1:
bx lr
编辑:
你使用 -mfloat-abi=hard
强制在任何与浮点数相关的操作(甚至不是数学)中使用 FPU。通常,我使用 softfp
来执行硬件浮点指令和软件浮点链接。
https://gcc.godbolt.org/z/z39qnvY1c
The output assembly, compiled with armgcc 10.2.1 (none eabi) is very
different, even with the -Os or -O3 optimizations applied:
您逐字节复制,编译器必须遵循您的代码。当您使用 memcpy
时,编译器会理解您的意图并且不会逐字节复制。需要额外的浮点指令,因为您使用 hard
float ABI 并且 ABI 强制此操作通过内存完成(float 和 int 通过 R0 传递)。
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104344
GCC 无法将展开的版本识别和匹配为 bswap 或 store-merging 模式。
GCC 确实识别循环版本。
以下 C11 程序以两种不同的方式将浮点数的位表示形式提取到 uint32_t 中。
#include <stdint.h>
_Static_assert(sizeof(float) == sizeof(uint32_t));
uint32_t f2i_char(float f) {
uint32_t x;
char const *src = (char const *)&f;
char *dst = (char *)&x;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
return x;
}
uint32_t f2i_memcpy(float f) {
uint32_t x;
memcpy(&x, &f, sizeof(x));
return x;
}
使用 armgcc 10.2.1 (none eabi) 编译的输出程序集非常不同,即使应用了 -Os
或 -O3
优化:
我正在编译:
-mcpu=cortex-m4 -std=c11 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
f2i_char:
sub sp, sp, #16
vstr.32 s0, [sp, #4]
ldr r3, [sp, #4]
strb r3, [sp, #12]
ubfx r2, r3, #8, #8
strb r2, [sp, #13]
ubfx r2, r3, #16, #8
ubfx r3, r3, #24, #8
strb r2, [sp, #14]
strb r3, [sp, #15]
ldr r0, [sp, #12]
add sp, sp, #16
bx lr
f2i_memcpy:
sub sp, sp, #8
vstr.32 s0, [sp, #4]
ldr r0, [sp, #4]
add sp, sp, #8
bx lr
为什么 gcc 不为这两个函数生成相同的程序集?
Why is GCC emitting larger output with -Os than -O3 for this function on Cortex-M4?
为什么不呢?每个选项启用或禁用特定的编译器内部工作。当然,可能会有并且将会有编译器决定使 -O3
产生比 -Os
.
Is there anything specific about the C11 standard or the Armv7E-M that's inhibiting gcc from emitting the smaller assembly at -Os?
没有
Is this gcc missing an optimization opportunity?
是的,你可以这么说。但这可能是故意的 - 可能是导致生成此类代码的优化实际上是编译时间和 CPU 消耗,因此它被禁用。就是这样。
避免手动复制数据。使用 memcpy
。 GCC 非常了解这个函数,如果不需要,根本不会调用它。指针双关也可以打破严格的别名规则,.
在 none-eabi memcpy
中不会发出任何代码,因为 return 值在同一个寄存器中作为参数传递。无需任何操作。
https://godbolt.org/z/q8v39d737
#include <stdint.h>
_Static_assert(sizeof(float) == sizeof(uint32_t));
uint32_t f2i_char(float f) {
uint32_t x;
char const *src = (char const *)&f;
char *dst = (char *)&x;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
return x;
}
uint32_t f2i1(float f) {
uint32_t x;
memcpy(&x, &f, sizeof(x));
return x;
}
f2i_char:
sub sp, sp, #8
ubfx r1, r0, #8, #8
ubfx r2, r0, #16, #8
ubfx r3, r0, #24, #8
strb r0, [sp, #4]
strb r1, [sp, #5]
strb r2, [sp, #6]
strb r3, [sp, #7]
ldr r0, [sp, #4]
add sp, sp, #8
bx lr
f2i1:
bx lr
编辑:
你使用 -mfloat-abi=hard
强制在任何与浮点数相关的操作(甚至不是数学)中使用 FPU。通常,我使用 softfp
来执行硬件浮点指令和软件浮点链接。
https://gcc.godbolt.org/z/z39qnvY1c
The output assembly, compiled with armgcc 10.2.1 (none eabi) is very different, even with the -Os or -O3 optimizations applied:
您逐字节复制,编译器必须遵循您的代码。当您使用 memcpy
时,编译器会理解您的意图并且不会逐字节复制。需要额外的浮点指令,因为您使用 hard
float ABI 并且 ABI 强制此操作通过内存完成(float 和 int 通过 R0 传递)。
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104344
GCC 无法将展开的版本识别和匹配为 bswap 或 store-merging 模式。
GCC 确实识别循环版本。