gcc-O0仍然优化了“输出”;“未使用”;代码。是否有一个编译标志来改变这一点?

gcc-O0仍然优化了“输出”;“未使用”;代码。是否有一个编译标志来改变这一点?,c,gcc,C,Gcc,正如我在中提到的,gcc正在删除(是的,带有-O0)一行代码\u mm\u div\u ss(s1,s2)可能是因为结果未保存。但是,这会触发浮点异常并引发SIGFPE,如果删除调用,则不会发生这种情况 问题:是否有一个或多个标志传递给gcc,以便按原样编译代码?我在想类似于fno-remove unused的东西,但我没有看到类似的东西。理想情况下,这将是一个编译器标志,而不必更改我的源代码,但如果不支持,是否有一些gcc属性/pragma可供使用 我尝试过的事情: $ gcc --help=

正如我在中提到的,gcc正在删除(是的,带有
-O0
)一行代码
\u mm\u div\u ss(s1,s2)可能是因为结果未保存。但是,这会触发浮点异常并引发SIGFPE,如果删除调用,则不会发生这种情况

问题:是否有一个或多个标志传递给gcc,以便按原样编译代码?我在想类似于
fno-remove unused
的东西,但我没有看到类似的东西。理想情况下,这将是一个编译器标志,而不必更改我的源代码,但如果不支持,是否有一些gcc属性/pragma可供使用

我尝试过的事情:

$ gcc --help=optimizers | grep -i remove
没有结果

$ gcc --help=optimizers | grep -i unused
没有结果

并显式禁用所有死代码/消除标志——请注意,没有关于未使用代码的警告:

$gcc-O0-msse2-Wall-Wextra-pedantic-Winline\
-fno dce-fno dse-fno树dce\
-fno树dse-fno树fre-fno比较elim-fno gcse\
-重新加载后fno gcse-fno gcse las-fno循环后重新运行cse\
-fno树内置呼叫dce-fno树cselim a.c
a、 c:在函数“main”中:
a、 c:25:5:警告:ISO C90禁止混合声明和代码[-Wpedantic]
__m128 s1,s2;
^
$

源程序

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <xmmintrin.h>

static void sigaction_sfpe(int signal, siginfo_t *si, void *arg)
{
    printf("%d,%d,%d\n", signal, si!=NULL?1:0, arg!=NULL?1:0);
    printf("inside SIGFPE handler\nexit now.\n");
    exit(1);
}

int main()
{
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sigaction_sfpe;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGFPE, &sa, NULL);

    _mm_setcsr(0x00001D80);

    __m128 s1, s2;
    s1 = _mm_set_ps(1.0, 1.0, 1.0, 1.0);
    s2 = _mm_set_ps(0.0, 0.0, 0.0, 0.0);
    _mm_div_ss(s1, s2);

    printf("done (no error).\n");

    return 0;
}
换线

_mm_div_ss(s1, s2);

产生预期的结果:

$ ./a.out
inside SIGFPE handler

编辑更多细节

这似乎与
\u mm\u div\u ss
上的
\u始终\u内联\u
属性有关

$cat t.c
int
分区(内部b)
{
返回1/b;
}
int main()
{
分区(0);
返回0;
}
$gcc-O0-Wall-Wextra-pedantic-Winline t.c-o.out
$  
(无警告或错误)

$./t.out
浮点异常
$
vs如下(除功能属性外,相同)

$cat t.c
__内联int属性(((总是内联)
分区(内部b)
{
返回1/b;
}
int main()
{
分区(0);
返回0;
}
$gcc-O0-Wall-Wextra-pedantic-Winline t.c-o.out
$   
(无警告或错误)

$./t.out
$
添加函数属性
\uuuuu warn\u unused\u result\uuuu
至少会给出一条有用的消息:

$gcc-O0-Wall-Wextra-pedantic-Winline t.c-o t.out
t、 c:在函数“main”中:
t、 c:9:5:警告:忽略'div'的返回值,用属性warn\u unused\u result[-Wunused result]声明
分区(0);
^
编辑:

关于这个问题的一些讨论。最终,我认为一切都在按预期进行。

GCC在这里没有“优化”任何内容。它不会生成无用的代码。这似乎是一种非常常见的错觉,即编译器应该生成某种纯粹形式的代码,对其进行任何更改都是一种“优化”。没有这样的事

编译器创建一些表示代码含义的数据结构,然后在该数据结构上应用一些转换,并由此生成汇编程序,然后编译成指令。如果编译时没有进行“优化”,这只意味着编译器将尽可能少地生成代码

在这种情况下,整个语句是无用的,因为它什么也不做,会立即被丢弃(在扩展内联线和内置的含义后,它相当于写入
a/b;
,区别在于写入
a/b;
将发出一个关于
语句的警告,但没有任何效果
,而内置的可能不会被相同的警告处理)。这不是一个优化,编译器实际上必须花费额外的努力来为一个无意义的语句创造意义,然后伪造一个临时变量来存储该语句的结果,然后将其丢弃


你要找的不是禁用优化的标志,而是悲观化标志。我认为任何编译器开发人员都不会浪费时间来实现这些标志。也许只是愚人节的玩笑。

我不是
gcc
内部的专家,但你的问题似乎不是通过一些优化来删除死代码s、 最有可能的是,编译器甚至没有考虑在第一时间生成此代码

让我们将您的示例从特定于编译器的内部函数简化为普通的旧添加:

int foo(int num) {
    num + 77;
    return num + 15;
}
:

当其中一个操作数有副作用时,仍然不能在程序集中添加

但将此结果保存到(甚至未使用的)变量中会迫使编译器:

大会:

foo(int):
    push    rbp
    mov     rbp, rsp
    mov     DWORD PTR [rbp-20], edi
    mov     eax, DWORD PTR [rbp-20]
    add     eax, 77
    mov     DWORD PTR [rbp-4], eax
    mov     eax, DWORD PTR [rbp-20]
    add     eax, 15
    pop     rbp
    ret
下面只是一个推测,但根据我在编译器构造方面的经验,不为未使用的表达式生成代码,而不是稍后删除此代码,这是更自然的

我的建议是明确说明您的意图,并将表达式的结果放入(因此,优化器不可删除)变量中

@Matthieu M指出,仅仅防止预计算值是不够的。因此,对于不仅仅是处理信号的事情,您应该使用有文档记录的方法来执行您想要的确切指令(可能是,
volatile
inline assembly)

为什么gcc不发出指定的指令

编译器生成的代码必须具有标准指定的可观察行为。任何不可观察的行为都可以随意更改(和优化),因为它不会更改程序的行为(如指定的)

你怎么能使它屈服呢

诀窍是让编译器相信特定代码段的行为实际上是可以观察到的

由于这是micro benchmark中经常遇到的问题,我建议您看看(例如)Google benchmark是如何解决这一问题的。从我们得到:

其中:

    强制编译器考虑<代码> S1 < /Cord>和<代码> S2< /代码>可能已经被修改了
    int foo(int num) {
        num + 77;
        return num + 15;
    }
    
    foo(int):
            push    rbp
            mov     rbp, rsp
            mov     DWORD PTR [rbp-4], edi
            mov     eax, DWORD PTR [rbp-4]
            add     eax, 15
            pop     rbp
            ret
    
    int foo(int num) {
      int baz = num + 77;
      return num + 15;
    }
    
    foo(int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-20], edi
        mov     eax, DWORD PTR [rbp-20]
        add     eax, 77
        mov     DWORD PTR [rbp-4], eax
        mov     eax, DWORD PTR [rbp-20]
        add     eax, 15
        pop     rbp
        ret
    
    template <class Tp>
    inline void DoNotOptimize(Tp const& value) {
        asm volatile("" : : "g"(value) : "memory");
    }
    
    asm volatile("" : : : "memory");
    
    __m128 result = _mm_div_ss(s1, s2);
    
    asm volatile("" : : "g"(result) : );