【问题标题】:How to provide an implementation of memcpy如何提供 memcpy 的实现
【发布时间】:2021-07-16 12:21:36
【问题描述】:

我正在尝试编写一些带有memset 样式循环的裸机代码:

for (int i = 0; i < N; ++i) {
  arr[i] = 0;
}

它是用 GCC 编译的,而 GCC 足够聪明,可以将其转换为对 memset() 的调用。不幸的是,因为它是裸机,所以我没有 memset()(通常在 libc 中)所以我收到链接错误。

 undefined reference to `memset'

似乎进行这种转换的优化是-ftree-loop-distribute-patterns

对可以通过调用库生成的代码执行循环分布。默认情况下,此标志在 -O2 及更高版本以及-fprofile-use-fauto-profile 启用。

所以one person's solution 只是降低了优化级别。不是很满意。

我还发现 this really helpful page 解释了 -ffreestanding 不足以让 GCC 不这样做,基本上没有选择,只能提供您自己的 memcpymemmovememsetmemcmp。我很乐意这样做,但是怎么做呢?

如果我只写memset,编译器将检测其中的循环并将其转换为对 memset 的调用!事实上,在我使用的 CPU 供应商提供的代码中,我实际上发现了这条评论:

/*
// This is commented out because the assembly code that the compiler generates appears to be
// wrong.  The code would recursively call the memset function and eventually overruns the
// stack space.
void * memset(void *dest, int ch, size_t count)
...

所以我认为这是他们遇到的问题。

我如何提供memset 的 C 实现,而不需要编译器将其优化为对其自身的调用,并且不禁用该优化?

【问题讨论】:

  • 奇怪,-ffreestandingworks for me
  • @Lundin:我不明白您为什么需要了解目标系统。我想你误会了。
  • @EricPostpischil 事实证明,在初始化/复制large structs 时,即使使用-ffreestanding,编译器也确实插入了memsetmemcpy 调用。 gcc documentation中也提到了这一点:GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.
  • @supercat 我认为这里提供的答案是“正确”的方式,因为 glibc 就是这样做的(AFAIK 他们与 gcc 携手合作)。

标签: c gcc memset


【解决方案1】:

啊哈,我签入了the glibc code,有一个inhibit_loop_to_libcall 修饰符听起来应该这样做。是defined like this:

/* Add the compiler optimization to inhibit loop transformation to library
   calls.  This is used to avoid recursive calls in memset and memmove
   default implementations.  */
#ifdef HAVE_CC_INHIBIT_LOOP_TO_LIBCALL
# define inhibit_loop_to_libcall \
    __attribute__ ((__optimize__ ("-fno-tree-loop-distribute-patterns")))
#else
# define inhibit_loop_to_libcall
#endif

【讨论】:

  • 不,您不可能在裸机系统上运行 glibc。我认为其他更适合 Linux 风格系统的库的尝试已经存在,例如 uClibc。我自己没用过,所以不能保证。
  • @Lundin 下摆。这解决方案,即__attribute__ ((__optimize__ ("-fno-tree-loop-distribute-patterns")))修饰符Timmmm的自己的定义memset等。
  • @AnttiHaapala 这取决于 gcc 目标端口。此外,如果您要推出自己的标准库替代品,显然您不应该同时使用另一个标准库...
  • 我没有使用任何 libc 实现。我想你可能误解了这个问题。
  • @Lundin,这里的重点是,由于glibc 提供了memset 的实现,它需要有一些方法来抑制GCC 的“用memset 的调用替换此代码”优化,而不降低全局优化级别。
【解决方案2】:

您在问题中提到:

似乎进行这种转换的优化是-ftree-loop-distribute-patterns

关闭此优化所需要做的就是将-fno-tree-loop-distribute-patterns 传递给编译器。这会关闭全局优化。

【讨论】:

  • 这比我的回答更糟糕,它只是为memset 关闭它。
  • @Timmmm 这是你想要的答案,而不是你认为你想要的答案;假设您的 memset 与天真的 C 实现没有任何不同,它可能比让编译器简单地在代码中生成一个循环要慢。编译器实际上只生成对memset 的调用,因为它假定(并且在大多数情况下是正确的)memset 比它自己生成的任何东西都要快。
  • 这是一个很好的观点,但是您的解决方案通常仍然无法正常工作,因为即使使用此标志,编译器仍然可以生成对memset() 的调用。见ssbssa's example
  • @Timm 你的答案使用相同的标志,所以这是一个有争议的问题
  • 我的仅将标志用于特定函数,这些函数不执行任何其他操作来触发 GCC 插入对 memset()/memcpy() 的调用。
猜你喜欢
  • 1970-01-01
  • 2011-12-13
  • 2013-10-11
  • 2023-03-21
  • 1970-01-01
  • 2023-03-15
  • 2013-07-04
  • 1970-01-01
  • 2013-10-12
相关资源
最近更新 更多