如果有的话,编译器何时会在指针解引用优化方面保持保守? [英] When would the compiler be conservative regarding pointer dereferencing optimization, if at all?

查看:72
本文介绍了如果有的话,编译器何时会在指针解引用优化方面保持保守?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,最近,我对编译器(正在讨论的问题)gcc (GCC) 4.8.3在优化指针和指针方面的表现很感兴趣.

So, I recently took an interest in how well the compiler (gcc (GCC) 4.8.3 being the one in question) is in optimizing pointers and pointers.

最初,我创建了一个简单的整数和一个整数指针,并实现了 对其进行操作,以便我将其打印出来.不出所料,所有操作 不论是否通过取消引用的指针,对经过硬编码的代码都进行了优化.

Initially I created a simple integer and an integer pointer and realized operations on it so I could print it out. As expected, all the operations that were hard coded were optmized, through dereferenced pointer or not.

call    __main
leaq    .LC0(%rip), %rcx
movl    $1, %edx
call    printf

即使创建了一个带有int指针的函数, 取消引用并对其进行更改,使其仍然是最佳选择.

And even after creating a function that takes in an int pointer, dereferences it and changes it it still was perfectly optmized.

call    __main
leaq    .LC0(%rip), %rcx
movl    $-1, %edx
call    printf

现在,当我将指针视为空白并进行更改时 通过将其转换为char并取消引用它,实际上它仍然是优化的 完美(因为我最初将其视为8字节,所以是额外" mov调用 值,然后将其作为1个字节的值用于指针解引用)

Now, when I treated my pointer as a void and made changes by casting it to char and dereferencing it, it actually still optmized perfectly (an 'extra' mov call since I initially treated it as an 8 byte value, and then as a 1 byte value for pointer dereferencing)

call    __main
movl    $4, 44(%rsp)
movb    $2, 44(%rsp)
leaq    .LC0(%rip), %rcx
movl    44(%rsp), %eax
leal    1(%rax), %edx
call    printf

关于我的问题:

  1. 关于指针解引用的编译器优化的一致性如何?在某些情况下会选择保守?

  1. How consistent is compiler optimization regarding pointer dereferencing? What would be some cases where it would chose to be conservative?

如果我在项目中的所有指针都是使用strict关键字声明的,我是否可以像根本没有使用任何指针"那样对它进行优化?

If all of my pointers in a project were declared with the restrict keyword, could I trust it would be as well optimized as if 'no pointers were being used at all'?

(假设没有volatile个案例)

Ps¹.:我知道编译器通常做得很好,并且 程序员担心在次要优化中帮助编译器的原因是 一般的,没有生产力的(正如很多人在stackoverflow中指出的那样, 有关优化的问题).但是我对此事仍然有好奇心.

Ps¹.: I am aware the compiler generally does a good enough job, and that a programmer worrying about aiding the compiler in minor optimizations is, in general, unproductive (as so many point out in stackoverflow answers to questions regarding optimization). Yet I still have curiosity regarding the matter.

Ps².: gcc -O3 -S -c main.c是用于生成汇编代码的命令

Ps².: gcc -O3 -S -c main.c was the command used to generate the assembly code

C代码:(根据要求)

1:

#include <stdio.h>

int main (void)
{
    int a = 4;
    int *ap = &a;

    *ap = 0;
    a += 1;

    printf("%d\n", a);
    return 0;
}

2:

#include <stdio.h>

void change(int *p) {
    *p -= 2;
}

int main (void)
{
    int a = 4;
    int *ap = &a;

    *ap = 0;
    change(ap);
    a += 1;

    printf("%d\n", a);
    return 0;
}

3:

#include <stdio.h>

void change(void *p) {
    *((char*)p) += 2;
}

int main (void)
{
    int a = 4;
    void *ap = (void*) &a;

    *((char*)(ap)) = 0;
    change(ap);
    a += 1;

    printf("%d\n", a);
    return 0;
}

推荐答案

LLVM和GCC都发出静态单辅助表单代码,这是优化分析的一部分. SSA代码的有用特性之一是,它精确地显示了赋值的影响流程-也就是说,它知道哪些赋值导致了其他赋值,因此可以检测哪些值可以影响所有其他赋值.

LLVM and GCC both emit static-single-assigment form code as a part of optimization analysis. One of the useful properties of SSA code is that precisely shows the flow of influence for assignment -- that is, it knows what assignments lead to other assignments and so can detect which values can influence all others.

第一个影响链看起来像

a 1 ->常量(0)-> ap-> a 2

a1 -> constant(0) -> ap -> a2

第二个: a 1 ->常量(0)-> ap-> p-> a 2

The second: a1 -> constant(0) - > ap -> p -> a2

第三个与第二个非常相似. (对不起,这个表示法是虚构的,但我希望它能说明我的观点.)

The third being pretty similar to the second. (Sorry, this notation is pretty much made-up but i hope it illustrates my point.)

因为很容易证明a对ap的影响是确定性的,所以可以随时取消引用"early"并将指令合并为一个(尽管在前两种情况下,这不是最准确的陈述)因为常量会覆盖原始引用,并让编译器证明原始赋值不会流到代码的末尾.

Because it is fairly simple to prove that the influence of a on ap is deterministic, it will feel free to dereference 'early' and combine the instructions into one (though in the first two cases this isn't the most accurate statement since the constant overwrites the original reference and lets the compiler prove that the original assignment does not flow to the end of the code.

使编译器在取消引用时更加保守,这将涉及足够复杂以逃避编译器的理解(我认为这对静态程序很困难),或者更有可能导致编译器在SSA的过程中调用phi函数(在非专业人士的指导下)术语,以使该赋值以不确定性的方式受到多个先前赋值的影响.

Causing the compiler to be more conservative about dereferencing would involve getting complicated enough to escape the compiler's understanding (difficult in a static program i think) or more likely causing the compiler to invoke a phi function in the process of SSA (in laymen's terms, to cause the assignment to be influenced by multiple previous assignments) in a nondeterministic way.

restrict关键字的目的是向编译器提示两个指针是不同的.如果产生该指针的代码仍然具有不确定的源(例如,如果运行时创建的数据影响了对指针值的选择的选择,那么这将不会限制在运行时使用取消引用),我认为如果序列化可能会发生这种情况.指针是从外部来源发送到程序中的?)

The restrict keyword has the purpose of hinting to the compiler that two pointers are different. This wouldn't restrict the use of dereference at runtime if the code which produced that pointer still had a nondeterministic source (for example, if runtime-created data influenced the choice of what pointer value was dereferenced- i think this could happen if a serialized pointer was sent into the program from an external source?)

这篇关于如果有的话,编译器何时会在指针解引用优化方面保持保守?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆