__builtin___clear_cache是如何工作的?

5

在查看gcc文档时,我发现了内置函数__builtin___clear_cache

— 内置函数:void __builtin___clear_cache (char *begin, char* end) 此函数用于清除处理器指令高速缓存中begin(含)和end(不含)之间的存储区域。某些目标需要在修改包含代码的内存后刷新指令高速缓存,以获得确定性行为。

如果目标不需要刷新指令高速缓存,则__builtin___clear_cache无效果。否则,将内联发出指令以清除指令高速缓存,或调用libgcc中的__clear_cache函数。

我觉得这很有趣,但也让人惊讶。在许多情况下,当前堆栈的大量指令存储在L1高速缓存(指令高速缓存)中。因此乍一看,这个内置函数似乎会显著破坏我们程序的流程,因为它会清除堆栈上的下一个指令。

这个指令是否还会重新填充在L1缓存中的堆栈部分呢?

这似乎不太可能。如果不会重新填充,则用户需要使用正确的beginend参数,以避免破坏我们的进程。在实践中,人们该如何找到正确的beginend参数呢?


你为什么要问这个问题?你的程序实际上在做什么? - Basile Starynkevitch
3
栈上没有指令。 - molbdnilo
3
请注意,__builtin___clear_cache 不会擦除指令,它只是刷新/清空缓存,处理器会重新填充它。 - nos
问题不明确,提问者可能对缓存有一些误解。 - undefined
2个回答

5

它只是在目标处理器上发出一些奇怪的机器指令(x86不需要这样做)。

__builtin___clear_cache看作是一种“便携式”(适用于GCC和兼容编译器)清除指令缓存的方式(例如在某些JIT库中)。

在实践中,如何找到正确的起始位置和结束位置?

为了安全起见,我会在一些页面范围内使用它(例如使用sysconf(_SC_PAGESIZE)获得的...),通常是4K字节对齐的内存范围(4K字节的倍数)。否则,你需要一些特定于目标的技巧来找到缓存行宽度...

在Linux上,您可以读取/proc/cpuinfo并使用cache_alignmentcache_size行来获取更精确的缓存行大小和对齐方式。

顺便说一下,使用__builtin__clear_cache的代码很可能是(出于其他原因)针对特定的目标机器,因此它具有或知道一些机器参数(其中应包括缓存大小和对齐方式)。


2
原来在x86上需要使用__builtin___clear_cache作为编译器内存屏障,以使gcc意识到“死”存储实际上是在写入代码,并且需要在转换为函数指针之前发生。请参见https://codegolf.stackexchange.com/questions/160100/the-repetitive-byte-counter/160236#160236,了解我为什么编写了https://godbolt.org/g/pGXn3B,它重复M次的`dec eax然后附加一个ret。如果没有__builtin___clear_cache或类似于asm("" :::“memory”);的编译器内存屏障,则memset`将被优化掉,因此只是调用malloc返回而不进行存储。 - Peter Cordes
我认为__builtin___clear_cache应该只在你想要执行的确切字节范围上使用,这样它才能正常工作。如果它需要了解缓存行大小,它会自己计算出来,对吗? - Peter Cordes
@PeterCordes 看起来这实际上与使用 malloc 分配内存有关,而不是与在 x86 上运行有关。特别是,如果我用 mmap(NULL, M+1, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 替换 malloc(M+1),那么即使没有 __builtin___clear_cache,死代码优化也不会发生。 - Joseph Sible-Reinstate Monica
2
@Joseph 不行。Gcc 知道 malloc(私有内存,没有其他指针指向)。这使得它可以进行优化,如果使用 mmap 不能证明是安全的。但是偶然工作并不等于完全安全和正确。 - Peter Cordes
我不认为__builtin___clear_cache()具有内存屏障效果!这是不正确的。 - undefined

0

在x86上,对__builtin___clear_cache的调用编译为零条汇编指令,但它确实可以防止死存储消除和类似的影响,这可能会导致正确性问题。(特别是如果您在堆栈上分配内存并且由于-zexecstack而可执行,因此GCC知道数组对此函数调用是私有的,并且您没有将指针传递到函数外部。)https://godbolt.org/z/5671x3MYn显示了一个在x86-64上没有`__builtin___clear_cache`的测试程序失败的示例。您的答案对AArch64有用,但对x86-64是错误的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接