【问题标题】:C optimization: Why does the compiler treat an object not as constant?C 优化:为什么编译器不将对象视为常量?
【发布时间】:2018-03-22 17:43:34
【问题描述】:

编译下面的C模块

static const int i = 1;

void f (const int *i);

int g (void)
{
  f (&i);
  return i;
}

在 x86_64 处理上使用 gcc -S -O3 会为函数 g 生成以下程序集:

g:
        leaq    i(%rip), %rdi
        subq    $8, %rsp
        call    f@PLT
        movl    $1, %eax           # inlined constant as an immediate
        addq    $8, %rsp
        ret

换句话说,return 语句被编译为将常量$1 移动到返回寄存器%eax,这是有道理的,因为i 被声明为常量。


但是,如果我删除那个 const 以便我拥有

static int i = 1;

void f (const int *i);

int g (void)
{
  f (&i);
  return i;
}

gcc -S -O3 的输出突然变成:

g:
        leaq    i(%rip), %rdi
        subq    $8, %rsp
        call    f@PLT
        movl    i(%rip), %eax     # reload i
        addq    $8, %rsp
        ret

即在调用f之后,显式地从内存中加载返回值。

为什么会这样? f 的参数被声明为指向常量 int 的指针,因此不应允许 f 更改 i。此外,f 无法通过非常量引用调用修改 i 的函数,因为唯一的此类函数可能是 g,因为 i 被声明为静态。

【问题讨论】:

    标签: c gcc assembly compiler-optimization


    【解决方案1】:

    将指向 const 的指针转换为指向非 const 的指针并修改被引用的对象并不是未定义的行为,只要引用的对象未声明为 const。

    6.7.3p6 说:“如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。”

    【讨论】:

    • 那么这是否意味着编译器必须将extern void f(const int *)extern void f(int *) 对待完全相同?
    • 是的。但是,它在函数内部有所不同,因为声明必须匹配,并且第一个函数不能在没有强制转换的情况下通过指针写入。
    • 这只是因为没有函数体。如果身体未知,编译必须视为容易产生副作用
    • @Marc:是的,就 ABI / 调用约定而言,它们完全相同相同。唯一的区别是编译时检查您不会通过指向const * 的指针直接修改指向的对象。有趣的事实:foo(const big_object by_value) 仍然“拥有”对象被复制到的堆栈内存,在 x86-64 和 i386 System V ABI 中。因此它可以使用该堆栈空间作为暂存空间,调用者必须假设它已被破坏并重新复制它以使用与参数相同的对象进行另一个函数调用。 (所以 ABI 也不关心传递 const 对象。)
    【解决方案2】:

    改变情况。身体是已知的。无需特殊操作。

    static int i = 1;
    
    __attribute__((noinline)) void f (int *i)
    {
        *i *=2;
    }
    
    int g (void)
    {
      f (&i);
      return i;
    }
    
    
    f:
      sal DWORD PTR [rdi]
      ret
    g:
      mov edi, OFFSET FLAT:i
      call f
      mov eax, DWORD PTR i[rip]
      ret
    i:
    

    【讨论】:

    • 这是想证明什么? g 在调用后重新加载i,并且没有任何地方的 const。
    猜你喜欢
    • 2012-09-06
    • 2012-10-06
    • 1970-01-01
    • 2021-12-06
    • 2015-09-15
    • 2017-03-30
    • 2015-05-13
    • 1970-01-01
    • 2011-12-13
    相关资源
    最近更新 更多