C ++ Can a persistent data class be optimized from the class by the compiler?

I know that constant variables outside of classes can be optimized directly into function calls by the compiler, but is it legal for the compiler to do the same for constant class variables?

If the class is declared like this:

class A {
public:
const int constVar;
    //other, modifiable variables

A(int val): constVar(val) {
         //code to initialize modifiable variables

}
};

      

and I instantiate A and call a function like this:

A obj(-2);
int absoluteVal = std::abs(A.constVar);

      

was the compiler allowed to do this instead and make the class sizeof(int)

smaller ?:

A obj();
int absoluteVal = std::abs(-2);

      

+3


source to share


3 answers


The compiler can emit any code that preserves the "observable behavior" of the program (there is an exception to the copy constructor that can be removed even if it has observable behavior, but it doesn't apply here). It's called as if the rule

struct X { int x; };

auto foo()
{
  X x{24};

  return x.x;
}

      

any decent compiler will optimize the above:

foo():                                # @foo()
        mov     eax, 24
        ret

      

As you can see, this has nothing to do with constant (well, almost), just observable behavior. You can try to play with adding complex code and see how smart the compiler is when calculating, it can remove the code associated with the class instance. Hint: This is very smart.




It is not clear to me what you mean by this:

is a compiler to do this instead and make the sizeof (int) class smaller ?:

I can tell you that for a type X

and an object of X

that type, it is sizeof(x)

always = sizeof(X)

independent of class instances. In other words, the size of a class is determined when the class is defined, and as such it does not depend on possible instances or not. Class size is the sum of all non-static item sizes plus padding. A shim is an implementation defined and usually can be somewhat controlled (e.g. packed structures). Thus, no, the size of a class can never be less than the sum of the sizes of all non-static members without reference.

+9


source


It would be perfectly legal for the compiler to generate

int absoluteVal = 2;

      



If abs

it has no side effects. It all depends on the "observable behavior" ( as-if rule ). If you cannot tell from the outside that the compiler has done some conversion, then the compiler must do that conversion.

+2


source


Code optimization and object memory layout do not obey the same rules

The C ++ standard specifies the following object memory location :

1.8 / 2: Objects can contain other objects called sub-objects. A subobject can be a member subobject, a base class subobject, or an array element. (...)

9.2 / 13: Non-stationary data members of a (non-unit) class with the same access control are allocated so that later members have higher addresses within the class object. The distribution order of non-static data members with different access controls is undefined. Implementation alignment requirements may cause two adjacent members not to be allocated immediately after each other; so the space requirements for managing virtual functions and virtual base classes.

This ensures that non-static constant members (which are data members, even if they are constants) are contained within the object. Therefore, the compiler is not allowed to shrink the object size.

However, the compiler is free to perform code optimizations such as persistent propagation and removal of dead code, reordering, etc., as long as the observed behavior does not change:

1.9 / 5: A conforming implementation executing a well-formed program MUST provide the same observable behavior as one of the possible executions of a corresponding abstract machine instance with the same program and the same input. (...)

So, if your member is not volatile

and atomic<>

, the compiler can generate very well

A obj();              // size not touched.  And const member will be initialized if needed
int absoluteVal = 2;  // constant propagation + inlining (the object is not even accessed)

      

Additional Information

Here's an example in which the object cannot be optimized:

A obj(-2);                                 // object is constructed
int absoluteVal = std::abs(obj.constVar);  // will be optimized a way into = 2 
std::cout<<absoluteVal<<std::endl;
size_t lo = sizeof(obj);
std::cout<<lo<<std::endl; 
std::cout.write((char*)&obj, lo);         // obj is written to a stream 
                                          // and output of content at &obj adress is observable behavior

      

You can see the results of the optimizer on the Internet : although the computation is absoluteVal

optimized, the object is created in full length and its constant is initialized :

    ...
    mov     esi, 2                      ; this is absoluteVal calculation
    mov     DWORD PTR [rsp+12], -2      ; the const in [rsp+12] object is nevertheless initialized
    ...
    lea     rsi, [rsp+12]               ; the address of the object 
    mov     edx, 4                      ; and its length 
    ...                                 ; are used to call cout.write()
    call    std::basic_ostream<char, std::char_traits<char> >::write(char const*, long) 

      

This is because the observable behavior of writing this trivially-copied object to a stream requires every byte of the object to meet expectations.

+2


source







All Articles