Why does a const int not get optimized by the compiler (through the symbol table) if another pointer points to its reference?

喜欢而已 提交于 2019-12-24 10:38:59

问题


This is a follow up on an answer in this question: What kind of optimization does const offer in C/C++? (if any)

In the top voted answer, the following is stated:

When you declare a const in your program,

int const x = 2;

Compiler can optimize away this const by not providing storage to this variable rather add it in symbol table. So, subsequent read just need indirection into the symbol table rather than instructions to fetch value from memory.

NOTE:- If you do something like below:-

const int x = 1;
const int* y = &x;

Then this would force compiler to allocate space for 'x'. So, that degree of optimization is not possible for this case.

Why is this the case? Looks like you will never be able to change x anyway, no?


回答1:


IMHO, Peter provided the explanation in his comment:

If a pointer is initialised to contain the address of a variable, and that pointer can be accessed from another compilation unit, then it would be reasonable for the compiler to allow for the possibility that the pointer IS dereferenced after being initialised in some compilation unit that is not visible to the compiler. One consequence of that is not optimising the pointer or the variable out of existence. There are numerous other reasoning approaches that might lead to the same outcome, depending on what code the compiler can actually see.

and this is exactly what I think too.

The const in C++ is a little bit confusing. It looks like the abbreviation of “constant” but actually it means “read-only”.

This in mind, I never wondered why the following code is legal in C:

enum { N = 3 };
static int a[N]; /* legal in C: N is a constant. */

but this not:

const int n = 3;
static int b[n]; /* illegal in C: n is a read-only variable */

When I switched to C++, I assumed the above for C++ until I realized in a discussion with a colleague that I was wrong. (Not that this broke any written code of mine, but I hate it to be wrong.) ;-)

const int n = 3;
static int b[n]; // legal in C++

and Const propagation is the technique which makes it legal.

However, even with const propagation const int x; is still a read-only variable which might be addressed.

The OP provided a link about this topic (which might explain it even better than above):

SO: why the size of array as a constant variable is not allowed in C but allowed in C++?

To make this a ful-featured answer I tried to prepare a sample which illustrates the differences:

#include <iostream>

const int x1 = 1;
static const int x1s = 11;
extern const int x1e = 12;

const int x2 = 2;
extern const int *const pX2 = &x2;

const int x3 = 3;
static const int *const pX3 = &x3;

int main()
{
  // make usage of values (to have a side-effect)
  std::cout << x1;
  std::cout << x1s;
  std::cout << x1e;
  std::cout << x2;
  std::cout << pX2;
  std::cout << x3;
  std::cout << pX3;
  // done
  return 0;
}

and the outcome of gcc 8.2 with -O3:

; int main()
main:
; {
 sub rsp, 8
;   // make usage of values (to have a side-effect)
;   std::cout << x1;
  mov esi, 1
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << x1s;
  mov esi, 11
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << x1e;
  mov esi, 12
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << x2;
  mov esi, 2
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << pX2;
  mov esi, OFFSET FLAT:_ZL2x2
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSo9_M_insertIPKvEERSoT_
;   std::cout << x3;
  mov esi, 3
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << pX3;
  mov esi, OFFSET FLAT:_ZL2x3
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSo9_M_insertIPKvEERSoT_
;   // done
;   return 0;
; }
  xor eax, eax
  add rsp, 8
  ret

and the IMHO most interesting part – the global variables:

; const int x3 = 3;
_ZL2x3:
  .long 3
; extern const int *const pX2 = &x2;
pX2:
  .quad _ZL2x2
; const int x2 = 2;
_ZL2x2:
  .long 2
; extern const int x1e = 12;
x1e:
  .long 12
  1. x1, x1s, and pX3 have been optimized away because they are const and not remarked for external linkage.

  2. x1e and pX2 have been allocated because they are remarked for external linkage.

  3. x2 has been allocated because it is referred by pX2 which is remarked for external linkage. (Something from extern may access x2 via pX2.)

  4. x3 was the most tricky one for me. pX3 has been used (in std::cout << pX3;). Although, its value itself is inlined it refers to x3. Furthermore, although the access to x3 (in std::cout << x3;) was inlined as well, the usage of the pointer pX3 initialized with &x3 prevented to optimize this storage away.

Live Demo on godbolt (which has a nice colored dual-view to make it easy to explore)

I did the same with clang 7.0.0 and the outcome was similar.

(I tried it also with msvc v19.15 but I was not able (not patient enough) to evaluate the outcome.)


Concerning 4., I tried additionally:

const int x4 = 4;
static const int *const pX4 = &x4;

and added to main():

  std::cout << x4;
  // No: std::cout << pX4;

In this case, there was no storage allocated – neither for x4 nor for pX4. (pX4 was optimized away, leaving no “reference” to x4 which in turn was optimized away as well.)

It's amazing...



来源:https://stackoverflow.com/questions/53348432/why-does-a-const-int-not-get-optimized-by-the-compiler-through-the-symbol-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!