为内联汇编参数打开立即值传播的特定 GCC 标志是啥?

Posted

技术标签:

【中文标题】为内联汇编参数打开立即值传播的特定 GCC 标志是啥?【英文标题】:What is the specific GCC flag that turns on immediate value propagation for inline assembly parameters?为内联汇编参数打开立即值传播的特定 GCC 标志是什么? 【发布时间】:2012-07-17 07:45:26 【问题描述】:

考虑以下 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 优化选项的捷径,这些选项记录在 fine GCC manual 中。不幸的是,我找不到启用此行为的特定 GCC 标志。

有什么想法吗?

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

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

void foo (void)

  int i = 6;

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

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

【问题讨论】:

c 支持内联吗?如果是,您使用的是哪个版本?几乎在所有情况下,您都可以使用内联和宏来做任何事情。而且,优化后你期望什么? (这么小的代码段没有奇迹) @BasileStarynkevitch 原因:我想知道 @gcc C 从标准的 C99 版本开始支持内联。 GCC 实际上早在标准之前就支持 C 内联函数。这里的优化很明显 - 执行函数调用的指令数与内联案例中的单条指令。 如果真正的用例是某个自定义处理器上的专用指令,您真的应该考虑将其设为内置指令(如我的回答中所建议的那样)。这是内置函数的典型用法。 【参考方案1】:

看起来像 -ftree-ter(替换 SSA 中的临时表达式->正常传递 - 不管是什么)都可以解决问题:

gcc -ftree-ter test.c   # no errors

我是这样确定的:

    gcc -Q --help=optimizers 告诉您默认启用/禁用哪些优化(某些已启用) gcc -O -Q --help=optimizers 告诉您为 -O 启用/禁用了哪些优化 将这些命令的输出重定向到文件并进行比较。 尝试仅在指定-O 时启用的优化,直到其中一个起作用。

【讨论】:

感谢您的回答。我已经尝试过 -ftree-ter,以及 GCC 手册中记录的所有其他优化选项都可以通过 -O 启用,但它们都没有成功。事实上,我还对 -O 和不带 -O ​​的“gcc -Q --help=optimizer”的输出进行了比较,并一起尝试了 diff 中出现的所有选项(这应该与传递 -O1 相同),即使这样做了不工作... @gby:嗯。我在 GCC 4.6.1 上进行了测试。它是 Windows 上的 MinGW 发行版,以防造成差异(TDM x86-64 发行版)。 -v 选项表明 GCC 也在使用内置规范中的 -mtune=generic-march=x86-64 选项,这可能是一个因素,也可能不是。 有趣。我在 x86_32 4.4.3 (Ubuntu 4.4.3-4ubuntu5) 版本上对此进行了测试,现在确实可以工作,但 -O1 可以。 从谷歌搜索 -ftree-ter 看来这确实是正确的标志。现在我只需要弄清楚为什么它在我的版本上不起作用......谢谢! @gby: 也许在 GCC 4.4.3 中除了-ftree-ter 之外还需要启用两个或更多-f 选项?也许尝试从另一个方向接近 - 传递 all -O 启用的 -f 选项(希望这将使编译正常),然后开始删除它们直到你到达集合没有更多可以删除的地方。【参考方案2】:

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

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

【讨论】:

为了帮助编译器,声明 i const 可能是个好主意? 感谢您的回答。代码示例本身确实没有意义。但是,我的实际使用涉及自定义处理器的指令,该指令必须将立即数作为操作数,因此需要“i”约束。希望这能解释我正在努力做得更好。 但是 inline 函数不是正确的工具,因为它必须以某种方式不知道函数参数的来源。我会选择一个宏,我会用__builtin_constant_p 保护它不会与非常量表达式一起使用。 实际上,似乎一个宏(甚至是对内联 asm 进行手工编码)遇到了同样的问题。【参考方案3】:

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 都进行了很好的处理)。

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

最近的 GCC(特别是 4.6 和 4.7)接受插件(如果它们已配置为 --enable-plugins)。如果您的特定 GCC 接受插件,请与 gcc -v 联系。一些发行版不喜欢 GCC 插件的想法(例如 Suse 和 Redhat),所以不包含 GCC 接受插件。

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

在 Debian 或 Ubuntu 上,我建议安装 gcc-4.6-plugin-devgcc-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 就是这样一个插件(它是一个 meta 插件,因为melt.so 正在做一些dlopen)。一旦你有一个 GCC 接受插件并安装了一些插件(例如 MELT),使用它只是运行 gcc -fplugin=melt 和一些其他插件特定选项(例如 -fplugin-arg-melt-mode=your-melt-mode 用于 MELT )。

【讨论】:

我的端口中提到了 gcc 无法在禁用优化的情况下组装我的代码这一事实(gcc test.c 和 gcc -O0 test.c 是一回事)。它不是 GCC 4.7 版独有的——这实际上是我要问的。我想要做的是包装自定义处理器的指令,该指令必须接收立即操作数作为 C 级构造。宏确实可以工作,但会受到宏的所有已知缺点的影响,因此我尽量避免使用它。 使用内置而不是内联汇编是一个非常好的主意。但是,使用 MELT 扩展对我来说是有问题的,因为它需要 GCC 的非主线分支,除非我错过了一些东西,这将需要我扩展 GCC,唉,这非常困难:-) 不,MELT(或任何其他插件)可在任何启用插件的 GCC 上使用。您无需更改任何 GCC 即可使用 MELT(或任何其他 GCC 插件)。你只需要一个启用插件的 GCC。使用gcc -v 进行检查(在 Debian 和 Ubuntu 和 Mandriva 上启用了 GCC 4.6 和 4.7 的插件,但 Suse 和 Redhat 可能还没有启用它们)。在 debian 或 ubuntu 上,为 gcc-4.6 等安装 gcc-4.6-plugin-dev 包...

以上是关于为内联汇编参数打开立即值传播的特定 GCC 标志是啥?的主要内容,如果未能解决你的问题,请参考以下文章

gcc 内联汇编行为异常

GCC 内联汇编的副作用

GCC 内联汇编中的标签

x86 内联汇编器标志

这个 GCC 内联汇编中的参数列表有啥问题?

gcc 内联汇编中的 min