在文本段中创建具有给定大小的C函数

时间:2014-02-10 15:44:47

标签: c gcc memory-alignment 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个函数

我也愿意探索以一种方式放置一组选定函数的全新技术,这样它们就不会映射到相同的缓存行。链接描述文件可以这样做吗?

4 个答案:

答案 0 :(得分:4)

使用GCC的__attribute__(( aligned(size) ))

或者,在GCC命令行上传递-falign-functions=n

答案 1 :(得分:3)

也许 linker scripts 是可行的方法。我认为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

答案 2 :(得分:3)

如果您愿意付出一些努力,可以使用

__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")

这将确保热路径函数ab彼此相邻,并且都适合在4 KiB边界上对齐的4 KiB的同一块,因此您可以简单地将该页面锁定到缓存中;如果代码不合适,则会出错。

您甚至可以使用

NOCROSSREFS(.text.hotpath.one .text)

禁止调用其他函数的热路径函数。

答案 3 :(得分:2)

假设您正在使用GCC和GAS,这可能是一个简单的解决方案:

void high_freq1(void)
{
   ...
}
asm(".org .+288"); /* Advance location by 288 bytes */
void high_freq2(void)
{
   ...
}

您甚至可以使用它来设置函数的绝对位置,而不是使用地址中的相对增量,这样可以使您在修改函数时将函数与大小改变的后果隔离开来。

当然,这不是纯粹的C89,但它可能不如使用虚拟功能那么难看。 :)

(然后,应该提到链接器脚本也没有标准化。)

编辑:正如评论中所述,在这种情况下将-fno-toplevel-reorder标志传递给GCC似乎很重要。