是否`常量T * restrict`保证指向的对象,以不被修改? [英] Does `const T *restrict` guarantee the object pointed-to isn’t modified?

查看:235
本文介绍了是否`常量T * restrict`保证指向的对象,以不被修改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下code:

 无效doesnt_modify(const int的*);INT美孚(INT * N){
    * N = 42;
    doesnt_modify(N);
    回报* N;
}

其中, doesnt_modify 的定义不是编译器可见。因此,它必须假定, doesnt_modify 改变对象的 N 点,必须读 * ñ收益之前(最后一行不能由替换返回42; )。

假设, doesnt_modify 不修改 * N 。我想到了以下允许优化:

  INT foo_r(INT * N){
    * N = 42;
    {/ *新的范围是很重要的,我想。 * /
        const int的*限制n_restr = N;
        doesnt_modify(n_restr);
        返回* n_restr;
    }
}

这都有的 doesnt_modify 调用者告诉编译器 * N 没有被修改的缺点,而不是函数本身可以告诉通过其原型编译器。只要限制在声明中参数-qualifying到 doesnt_modify 不足够,比照。 <一href=\"http://stackoverflow.com/questions/28819855/is-top-level-volatile-or-restrict-significant-in-a-function-prototype\">Is顶级挥发性限制显著[...]?。

在与 GCC编译-std = C99 -O3 -S (或锵使用相同的选项),所有的功能被编译成等价组装,全部重新阅读 42 * N


  1. 请问一个编译器被允许做这种优化(按替换最后一行返回42; )的 foo_r ?如果没有,是否有一个(可移植的,如果可能的话)的方式来告诉编译器 doesnt_modify 不修改它什么参数指向?有没有办法做的编译器理解和利用?


  2. 是否有功能有UB(提供 doesnt_modify 不会修改其参数的指针对象)?


为什么我认为,限制可以帮助这里(从C11(n1570)6.7.3.1的正式定义限制,P4 [EMPH我的。):

[在这种情况下, B foo_r 的内部块P n_restr T const int的 X 是>按,我想。]


  

B的每次执行,让是具有任何左值&安培; →根据 P 。如果用于访问对象 X的值,它指定,和 X 也被修改(通过任何方式),然后应满足以下要求: T 不得常量-qualified 。 [...]


  $铛--version
(基于LLVM 3.5.0)的Ubuntu版本铿锵3.5.0-4ubuntu2(标签/ RELEASE_350 /决赛)
目标:x86_64的-PC-Linux的GNU

gcc版本4.9.2是,在x86 32位的目标。


解决方案

1版似乎清楚地的正式定义指定限制(C11 6.7.3.1)。对于以下code:

  const int的*限制P = N;
doesnt_modify(P);
返回* P;

6.7.3.1中使用的符号是:


  • - code的该块

  • P - 变量 P

  • T - * P const int的
  • X - 的(非const)中等正指向 P

  • - 左值 * P 是我们感兴趣的是

6.7.3.1/4(部分):


  

B的每次执行,让是具有任何左值&安培; →根据 P 。如果用于访问对象 X的值,它指定和 X 也被修改(通过任何方式),那么应满足以下要求: T 不得const限定
  [...]
   如果这些要求得不到满足,则该行为是未定义的。


注意 T 是const限定。因此,如果 X 以任何方式该块(在该块调用函数中包括)期间修改的行为是不确定的。

因此​​编译器可以优化仿佛 doesnt_modify 没有修改 X


2版是编译器有点难度。 6.7.6.3/15说,顶级预选赛没有原型兼容性考虑 - 虽然他们并不完全忽略

所以,虽然原型说:

 无效doesnt_modify2(const int的*限制P);

它仍然可能是因为函数体被声明为无效doesnt_modify2(const int的* P),因此可能会修改 * P

我的结论是,当且仅当编译器可以看到 doesnt_modify2 的定义和确认 P 是声明限制在定义的参数列表那么这将是能够执行优化。

Consider the following code:

void doesnt_modify(const int *);

int foo(int *n) {
    *n = 42;
    doesnt_modify(n);
    return *n;
}

where the definition of doesnt_modify isn’t visible for the compiler. Thus, it must assume, that doesnt_modify changes the object n points to and must read *n before the return (the last line cannot be replaced by return 42;).

Assume, doesnt_modify doesn’t modify *n. I thought about the following to allow the optimization:

int foo_r(int *n) {
    *n = 42;
    { /* New scope is important, I think. */
        const int *restrict n_restr = n;
        doesnt_modify(n_restr);
        return *n_restr;
    }
}

This has the drawback that the caller of doesnt_modify has to tell the compiler *n isn’t modified, rather than that the function itself could tell the compiler via its prototype. Simply restrict-qualifying the parameter to doesnt_modify in the declaration doesn’t suffice, cf. "Is top-level volatile or restrict significant [...]?".

When compiling with gcc -std=c99 -O3 -S (or Clang with the same options), all functions are compiled to equivalent assembly, all re-reading the 42 from *n.

  1. Would a compiler be allowed to do this optimization (replace the last line by return 42;) for foo_r? If not, is there a (portable, if possible) way to tell the compiler doesnt_modify doesn’t modify what its argument points to? Is there a way compilers do understand and make use of?

  2. Does any function have UB (provided doesnt_modify doesn’t modify its argument’s pointee)?

Why I think, restrict could help here (From C11 (n1570) 6.7.3.1 "Formal definition of restrict", p4 [emph. mine]):

[In this case, B is the inner block of foo_r, P is n_restr, T is const int, and X is the object denoted by *n, I think.]

During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. […]

$ clang --version
Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
Target: x86_64-pc-linux-gnu

Gcc version is 4.9.2, on an x86 32bit target.

解决方案

Version 1 seems clearly specified by the formal definition of restrict (C11 6.7.3.1). For the following code:

const int *restrict P = n;
doesnt_modify(P);
return *P;

the symbols used in 6.7.3.1 are:

  • B - that block of code
  • P - the variable P
  • T - the type of *P which is const int
  • X - the (non-const) int being pointed to by P
  • L - the lvalue *P is what we're interested in

6.7.3.1/4 (partial):

During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified [...] If these requirements are not met, then the behavior is undefined.

Note that T is const-qualified. Therefore, if X is modified in any way during this block (which includes during the call to a function in that block), the behaviour is undefined.

Therefore the compiler can optimize as if doesnt_modify did not modify X.


Version 2 is a bit more difficult for the compiler. 6.7.6.3/15 says that top-level qualifiers are not considered in prototype compatibility -- although they aren't ignored completely.

So although the prototype says:

void doesnt_modify2(const int *restrict p);

it could still be that the body of the function is declared as void doesnt_modify2(const int *p) and therefore might modify *p.

My conclusion is that if and only if the compiler can see the definition for doesnt_modify2 and confirm that p is declared restrict in the definition's parameter list then it would be able to perform the optimization.

这篇关于是否`常量T * restrict`保证指向的对象,以不被修改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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