在文本段中创建具有给定大小的C函数
我正在编写一个嵌入式powerpc 32系统,它带有一个32 KB的8路组关联L2指令缓存。 为了避免高速缓存抖动,我们以一种方式对齐函数,使得以高频率调用的一组函数的文本(想想中断代码)最终在单独的高速缓存集中。 我们通过根据需要插入虚函数来实现这一点,例如
void high_freq1(void) { ... } void dummy(void) { __asm__(/* Silly opcodes to fill ~100 to ~1000 bytes of text segment */); } void high_freq2(void) { ... }
这让我感到丑陋和不理想。 我想做的是
- 完全避免使用
__asm__
并使用纯C89(可能是C99) - 找到一种方法来创建GCC优化器不接触的所需的
dummy()
间隔 -
dummy()
spacer的大小应该可以配置为4个字节的倍数。 典型的间隔物为260至1000个字节。 - 对于总共500个函数中的一组约50个函数应该是可行的
我也愿意探索以一种方式放置一组选定函数的全新技术,这样它们就不会映射到相同的缓存行。 链接器脚本可以这样做吗?
也许链接器脚本是可行的方法。 我认为GNU链接器可以使用这些…我已经在AVR和MQX上使用了LD文件,我们使用基于GCC的编译器…可能有帮助……
你可以定义你的记忆部分等,以及在哪里…每次我来写一个它已经很久了,因为我不得不再读一遍…
搜索SVR3样式的命令文件到gem。
免责声明:以下是一个非常具体的编译器示例…但类似SVR3的格式非常通用…您必须阅读系统
例如,你可以使用像……这样的命令
ApplicationStart = 0x...; MemoryBlockSize = 0x...; ApplicationDataSize = 0x...; ApplicationLength = MemoryBlockSize - ApplicationDataSize; MEMORY { RAM: ORIGIN = 0x... LENGTH = 1M ROM: ORIGIN = ApplicationStart LENGTH = ApplicationLength }
这为链接器定义了三个内存部分。 然后你可以说像
SECTIONS { GROUP : { .text : { * (.text) * (.init , '.init$*') * (.fini , '.fini$*') } .my_special_text ALIGN(32): { * (.my_special_text) } .initdat ALIGN(4): // Blah blah } > ROM // SNIP }
SECTIONS
命令告诉链接器如何将输入节映射到输出节,以及如何将输出节放在存储器中……这里我们说的是ROM输出节中的内容,我们在上面的MEMORY
定义中定义了它。 你可能感兴趣的是.my_special_text
。 在您的代码中,您可以执行以下操作:
__attribute__ ((section(".my_special_text"))) void MySpecialFunction(...) { .... }
链接器会将__attribute__
语句前面的任何函数放入my_special_text
部分。 在上面的例子中,它被放置在text
部分之后的下一个4字节对齐边界的ROM中,但是你可以随意放置它。 所以你可以为你描述的每个函数创建几个部分,并确保地址不会导致冲突……
您可以使用表单的链接器定义变量来获取节的大小和内存位置
extern char_fsection_name[]; // Set to the address of the start of section_name extern char_esection_name[]; // Set to the first byte following section_name
所以对于这个例子……
extern char _fmy_special_text[]; // Set to the address of the start of section_name extern char _emy_special_text[]; // Set to the first byte following section_name
使用GCC的__attribute__(( aligned(size) ))
。
或者,在GCC命令行上传递-falign-functions=n
。
- GCCfunction属性
- GCC优化选项
如果你愿意付出一些努力,你可以使用
__attribute__((section(".text.hotpath.a")))
将函数放入单独的部分,然后在自定义链接描述文件中显式放置函数。
这比简单地要求对齐函数提供了更细粒度的控制,但需要更多的手持。
例如,假设您要将4KiB锁定到缓存中:
SECTIONS { .text.hotpath.one BLOCK(0x1000) { *(.text.hotpath.a) *(.text.hotpath.b) } } ASSERT(SIZEOF(.text.hotpath.one) <= 0x1000, "Hot Path functions do not fit into 4KiB")
这将确保热路径函数a
和b
彼此相邻,并且都适合在4 KiB边界上对齐的4 KiB的同一块,因此您可以简单地将该页面锁定到缓存中; 如果代码不合适,则会出错。
你甚至可以使用
NOCROSSREFS(.text.hotpath.one .text)
禁止调用其他函数的热路径函数。
假设您正在使用GCC和GAS,这可能是一个简单的解决方案:
void high_freq1(void) { ... } asm(".org .+288"); /* Advance location by 288 bytes */ void high_freq2(void) { ... }
您甚至可以使用它来设置函数的绝对位置,而不是使用地址中的相对增量,这样可以使您在修改它们时由于函数大小改变而导致的后果无法使用。
当然,它不是纯粹的C89,但它可能不如使用虚拟function那么难看。 🙂
(然后,应该提到链接器脚本也没有标准化。)
编辑 :如评论中所述,在这种情况下,将-fno-toplevel-reorder
标志传递给GCC似乎很重要。