4

考虑以下 x86 代码示例:

#include <stdlib.h>

static int i;

static inline __attribute__((always_inline)) test(int x)
{
    asm volatile("mov %1, %0" : "=r"(i): "i"(x));
}

int main(void)
{
    test(5);

    return i;
}

如果我用以下方式构建它:

gcc -O test.c

它构建良好。

如果我用(没有优化)构建它:

gcc test.c

它在组装阶段失败,因为该值'5'没有作为立即值传播到内联函数测试,所以我们没有通过约束。

我希望能够在不打开其他无关优化的情况下编译此代码,以使调试更容易。

从理论上讲,-O这只是一次启用大量 GCC 优化选项的捷径,这些选项记录在精美的 GCC 手册中。不幸的是,我找不到打开此行为的特定 GCC 标志。

有任何想法吗?

澄清:为了消除任何疑问,代码片段只是一个示例。除了展示我正在尝试做的事情之外,它本身并没有多大意义。实际用例涉及自定义处理器上的指令,该指令只能将立即数作为参数,我试图将其包装在 C 构造中。宏确实可以解决问题,但会遇到宏的所有常见缺点,因此我试图避免它。

更新:对于那些想知道的人,宏也不起作用。看来内联函数在这里根本不起作用。例如,这也不起作用:

void foo (void)
{
  int i = 6;

  asm volatile ("" : : "i" (i));
}

我还修复了问题标题以反映这一点。

4

3 回答 3

6

看起来 -ftree-ter (替换 SSA-> 正常通行证中的临时表达式 - 无论是什么)都可以解决问题:

gcc -ftree-ter test.c   # no errors

这是我确定的方法:

  1. gcc -Q --help=optimizers告诉您默认启用/禁用哪些优化(某些已启用)
  2. gcc -O -Q --help=optimizers告诉您启用/禁用哪些优化-O
  3. 将这些命令的输出重定向到文件并区分它们。
  4. 尝试仅在-O指定时启用的优化,直到一个工作。
于 2012-07-17T08:01:09.330 回答
2

always_inline是一个奇怪的属性,非常特定于 GCC,并且可能特定于 GCC 版本(因此详细行为可能与 GCC 4.5 和 GCC 4.7 不同)。

GCC 通过运行大量优化通道来工作(即使在-O0其中一些通道​​正在运行,否则不会发出任何代码)。通常,GCC-O1编译运行 200 次优化传递。

使用gcc-4.7您的代码甚至不编译-O0

alw.c: In function ‘main’:
alw.c:7:5: warning: asm operand 1 probably doesn’t match constraints [enabled by default]
alw.c:7:5: error: impossible constraint in ‘asm’

要了解更多 GCC 正在做什么,您可以运行它,gcc -fdump-tree-all您将获得大多数 GCC 通道的所谓“转储文件”(通过通道转换的一些内部表示的文本表示)。请注意,您将获得数百个此类转储文件(遗憾的是,转储文件名称中的数字并不重要)。

我不明白你为什么要这样做。我建议要么让你test成为一个宏,要么总是优化(最近的 GCC 很好地处理了-g-O1)。

一种可能的替代方法是使用插件扩展 GCC ,或者更好的是MELT 扩展(MELT 是扩展 GCC 的高级域特定语言,作为 GPLv3 许可的 GCC 插件实现)。然后你可以让你的test函数成为你自己的 GCC内置函数,因为GCC 可以扩展为添加内置函数和 pragma。然后,您的扩展程序将安装您的特定内置插件并插入一些特定的通行证以适当地处理它们。(这意味着需要几天的工作,即使您非常了解 GCC 内部结构)。请注意,内置函数通常用于连接额外的目标处理器特定指令(就像您的用例一样)。

最近的 GCC(特别是 4.6 和 4.7)接受插件(如果它们已经配置了--enable-plugins)。检查gcc -v您的特定 GCC 是否接受插件。一些发行版不喜欢 GCC 插件的想法(例如 Suse 和可能是 Redhat),因此不包含接受插件的 GCC。

如果您的特定 Linux 发行版(最近的发行版)尚不支持 GCC 插件,我建议您打开一个错误报告以请求在 GCC 中启用插件。如果您的 GCC 交叉编译器供应商不支持插件,我还建议您查询该功能,该功能在 FSF GNU Gcc 中已经存在好几年了,例如从 4.5 开始!

在 Debian 或 Ubuntu 上,我建议安装gcc-4.6-plugin-devorgcc-4.7-plugin-dev包。然后,您将能够构建和使用MELT插件(我正在努力尽快发布适用于 GCC 4.6 和 4.7 的 MELT 0.9.6,即 2012 年 7 月)。

带有 GCC 4.6 或 4.7 的最新发行版(Debian、Ubuntu、Mandriva、ArchLinux 等)具有可通过插件扩展的 GCC。MELT 就是这样一个插件(它是一个插件,因为melt.so它自己在做一些事情dlopen)。一旦你有一个 GCC 接受插件并安装了一些插件(例如 MELT),使用它只是运行gcc -fplugin=melt一些其他插件特定选项(例如用于 MELT-fplugin-arg-melt-mode=的 your-melt-mode )。

于 2012-07-17T08:05:59.087 回答
2

可能您只是在滥用"i"约束。如果您不进行优化,编译器将无法“知道”这将是一个立即结束。

我认为你应该让 gcc 来决定可以优化多少。我只是"g"用作约束而不是"i". 我很确定,当您使用优化进行编译时,一切都会立即解决。但是你最好检查一下生产出来的汇编器来确定。

于 2012-07-17T08:09:50.440 回答