【问题标题】:GCC PowerPC avoiding .rodata section for floatsGCC PowerPC 避免浮动的 .rodata 部分
【发布时间】:2017-08-19 10:45:41
【问题描述】:

我正在编写C 代码并将其编译为PowerPC 架构。也就是说C 代码包含浮点变量常量,我希望将其放在.text 部分而不是.rodata,因此函数代码是自包含的。

这个问题是在PowerPCthe only way to move a floating point value into a floating point register is by loading it from memory。这是指令集限制。

为了说服GCC 帮助我,我尝试将浮动声明为static const。没有不同。使用指针,结果相同。对函数使用__attribute__((section(".text"))),结果相同,每个浮点变量单独使用:

error: myFloatConstant causes a section type conflict with myFunction

我还尝试通过 #pragma GCC push_options 禁用优化 #pragma GCC optimize("O0")#pragma GCC pop_options。再加上假装我有一个unsigned int 工作:

unsigned int *myFloatConstant = (unsigned int *) (0x11000018);
*myFloatConstant = 0x4C000000;

使用浮动:

float theActualFloat = *(float *) myFloatConstant;

我仍然想保留-O3,但它再次使用.rodata,因此可能的答案将包括which optimization flag causes the floats to be placed in .rodata,因为从-O1开始,这正在发生吗?

最好的情况是我可以在代码中“正常”使用浮点数以及最大优化,并且它们根本不会放在 .rodata 中。

我想GCC 可能做的是通过混合数据和代码将浮点常量放在代码之间,从该位置加载到浮点寄存器并继续。我相信这可以手动编写,但如何让GCC 做到这一点?强制每个变量的属性会导致上面的错误,但从技术上讲这应该是可行的。

【问题讨论】:

  • POWER ABI 有点搞笑;请参阅 man gcc 和 POWER -msdata 选项。在 GCC 开发邮件列表中,有人提到将 -G 0 添加到 gcc 选项“修复”此问题;你能试试这个并报告这是否让 gcc 做你喜欢的事情吗?
  • 为什么要“自包含”代码?
  • @fuz 我想优化缓存使用,减少 TLB 未命中,缓存错误等?
  • @fuz 也许代码以非标准方式使用(例如注入微控制器 RAM 并在那里执行的代码),这需要绝对与位置无关。
  • @AnttiHaapala:现代 CPU(包括 x86 和 PowerPC)具有分离的 L1 缓存和单独的第一级 TLB,用于指令和数据。但是,从当前正在执行的同一缓存行加载数据可能会在 L2 中命中。 (除非您的 L2 是 L1D 独有的,例如 AMD Bulldozer 系列)。 dTLB 也可能会丢失。在 x86 上,L2 TLB 保存从 iTLB 和 dTLB 中驱逐的条目是很常见的,但是当前页面的条目将在 iTLB 中,并且没有理由期望它在 L2 TLB 中,因此 dTLB 很可能会触发页面浏览。

标签: c gcc assembly floating-point powerpc


【解决方案1】:

使用 GCC 7.1.0 powerpc-eabi(Linux 下的交叉编译器),以下代码对我有用:

float test(void)
{
    int x;
    volatile float y;
    float theActualFloat;

    *(float *)&x = 1.2345f;
    *(int *)&y = x;
    theActualFloat = y;

    return theActualFloat;
}

生成的汇编代码:

test:
    stwu 1,-24(1)
    lis 9,0x3f9e
    ori 9,9,0x419
    stw 9,8(1)
    lfs 1,8(1)
    addi 1,1,24
    blr

说明:

*(float *)&x = value 行中写入一个整数,该整数将被编译器优化。编译器将执行一个整数运算,它不会访问.rodata 中的浮点值。

*(int *)&y = x 行无论如何都是纯整数运算。

theActualFloat = y 行由于volatile 而无法优化,因此编译器必须将整数写入堆栈上的变量,并且必须从变量中读取结果。

【讨论】:

  • 这很好,但是通用函数/宏返回一个浮点数而不是完全复制它并更改*(float *)&x = 1.2345f 行呢?
  • 使用指针转换的类型双关语违反了严格的别名。如果可能,请使用union(保证可以在 C99 及更高版本以及 GNU89 和 GNU C++ 中工作)。我想您可以使union volatile` 强制编译器将来自立即数的数据存储到它,而不是优化编译时常量。不过,如果有一种方法可以在不强制编译器在将此函数内联到循环后每次都重做它的情况下获得它,那就太好了。我想这并不重要,因为 OP 想要一个独立的功能。
  • @BullyWiiPlaza 和 Martin:IIRC,PowerPC 真的不喜欢存储/重新加载,尤其是在 FPU 和整数之间。我试图用谷歌搜索一些关于这个的东西,但大部分都找到了像alex-simon.blogspot.ca/2010/04/load-hit-store.html 这样的东西,它有一些有用的 C++ 编程建议,但在微架构细节上似乎很模糊。 (显然,当负载不比以前的存储宽时,至少一些 PowerPC uarches 可以像 x86 一样进行存储命中加载转发,用于整数存储/重新加载。gcc.gnu.org/bugzilla/show_bug.cgi?id=71310
【解决方案2】:

我找到了另一个解决方案,它避免了 stack frame 创建和 .rodata 使用,但需要一个绝对内存地址来存储浮点数:

static inline volatile float *getFloatPointer(int address, float value) {
    float *pointer = (float *) address;
    *pointer = value;

    return pointer;
}

它是这样使用的:

volatile float *myFloat = getFloatPointer(0x12345678, 30.f);
printf("%f", *myFloat);

重要的是不要创建本地 float 变量,只创建 volatile 指针,这样它就不会再次使用 .rodata

【讨论】:

  • 您肯定希望在循环之外执行float foo = *myFloat;,因为每次使用*myFloat 时编译器都必须实际发出加载指令,因为它是指向易失性的指针。您只需要阻止编译器一直执行常量传播到编译时常量float,它将放入.rodata。如果将常量保存在整个函数的寄存器中,则不需要或不想停止。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多