使用GCC的内置功能

时间:2011-12-14 10:48:24

标签: gcc arm built-in

我正在使用带有裸机工具链的皮质m3板,没有libc。

我实现了memcpy,它会逐字节地复制数据,但速度太慢。在GCC手册中,它说它提供了__builtin_memcpy,我决定使用它。所以这是使用__builtin_memcpy的实现。

#include <stddef.h>

void *memcpy(void *dest, const void *src, size_t n)
{
    return __builtin_memcpy(dest,src,n);
}

当我编译这段代码时,它变成了一个永远不会结束的递归函数。

$ arm-none-eabi-gcc -march=armv7-m -mcpu=cortex-m3 -mtune=cortex-m3 \
  -O2 -ffreestanding -c memcpy.c -o memcpy.o
$ arm-none-eabi-objdump -d memcpy.o

memcpy.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <memcpy>:
   0:   f7ff bffe       b.w     0 <memcpy>

我做错了吗?如何使用编译器生成的memcpy版本?

2 个答案:

答案 0 :(得分:4)

内置函数不应该用于实现自己:)

应该在应用程序代码中使用内置函数 - 然后编译器可能生成或不生成一些特殊 insn序列或对底层实函数的调用

比较

int a [10], b [20];

void
foo ()
{
  __builtin_memcpy (a, b, 10 * sizeof (int));
}

这导致:

foo:
    stmfd   sp!, {r4, r5}
    ldr     r4, .L2
    ldr     r5, .L2+4
    ldmia   r4!, {r0, r1, r2, r3}
    mov     ip, r5
    stmia   ip!, {r0, r1, r2, r3}
    ldmia   r4!, {r0, r1, r2, r3}
    stmia   ip!, {r0, r1, r2, r3}
    ldmia   r4, {r0, r1}
    stmia   ip, {r0, r1}
    ldmfd   sp!, {r4, r5}
    bx      lr

可是:

void
bar (int n)
{
  __builtin_memcpy (a, b, n * sizeof (int));
}

导致调用memcpy函数:

bar:
    mov     r2, r0, asl #2
    stmfd   sp!, {r3, lr}
    ldr     r1, .L5
    ldr     r0, .L5+4
    bl      memcpy
    ldmfd   sp!, {r3, lr}
    bx      lr

答案 1 :(得分:1)

理论上,库不是C编译器的一部分,也不是工具链的一部分。 因此,如果您编写memcpy(&a,&b,sizeof(a))编译器必须生成子例程调用。

__builtin的想法:告知编译器,该函数是标准的并且可以进行优化。因此,如果您编写__builtin_memcpy(&a,&b,sizeof(a))编译器可以生成子例程调用,但在大多数情况下它不会发生。例如,如果在编译时将size称为4,则只会生成一个mov命令。 (另一个优点 - 即使在子程序调用编译器被通知的情况下,该库函数也没有副作用)。

因此,使用__builtin_memcpy而不是memcpy总是更好。在现代图书馆中,它仅由#define memcpy __builtin_memcpy在string.h中完成

但是你仍然需要在某处实现memcpy,调用将在诡辩的地方生成。对于ARM上的字符串函数,严格建议使用4字节实现。