llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.39k stars 11.73k forks source link

c++14 cause worse codegen than c++1z. #33544

Open e0e1e613-dccb-47a5-85db-3dc989429265 opened 7 years ago

e0e1e613-dccb-47a5-85db-3dc989429265 commented 7 years ago
Bugzilla Link 34196
Version trunk
OS All
Attachments code
CC @DougGregor,@zygoloid

Extended Description

Hi.

I was playing with variant implementation and found out that c++1z option produces a better code than with c++14. Might be worth looking into.

Unfortunately, links to goldbolt currently do not work, so I attach code as file.

It's not exactly an easy read so quick idea: for each possible combination of parameters, I instantiate a function. All of this functions are written in one n-dimentional array, where each dimension corresponds to an element. So sum of two variants should be equivalent to:

  int sum(const variant<int, char>& x, const variant<char, int>& y) {
    using f_type = ...
    static const f_type vtable = {
      {
        [](decltype(x) x, decltype(y) y) -> int { 
           return get<0>(x) + get<0>(y); },
        [](decltype(x) x, decltype(y) y) -> int {
           return get<0>(x) + get<1>(y); },
      },
      {
        [](decltype(x) x, decltype(y) y) -> int {
           return get<1>(x) + get<0>(y); },
        [](decltype(x) x, decltype(y) y) -> int {
           return get<1>(x) + get<1>(y); },
      }
    };

    return vtable[x.idx][y.idx](x, y);
  }

Compiling with c++1z generates 2 times less instructions than with c++14. This does not seem right.

Compilation options: -Werror -Wall --std=c++1z -O3 -fno-exceptions -DNDEBUG

e0e1e613-dccb-47a5-85db-3dc989429265 commented 7 years ago

Oh, I see.

So, essentially, in c++17 compiler is mandated to compute everything in compile time. In c++14 however, it's not and the optimizer cannot see through enough layers to optimize it all away.

Would be nice, if the optimizer could see all of that though.

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 7 years ago

Can you elaborate - why non-constexpr lambdas matter in this case? I mean - they are still only called in runtime.

In C++17, the initializer of the 'vtable' variable is a constant expression: both the evaluation of the lambda expression itself (creating an object of the closure type) and the conversion to a function pointer are constexpr operations. So the 'vtable' variable's initializer will be emitted as a compile-time constant.

In C++14, neither the lambda expression itself nor the conversion to a function pointer are permitted in constant expressions (and neither Clang's nor LLVM's optimizations for static initializers are able to completely fold the initializer to a constant). So we emit code that initializes the value of the 'vtable' variable at runtime instead.

e0e1e613-dccb-47a5-85db-3dc989429265 commented 7 years ago

@​Richard Smith

You are right in a sense that if I use constexpr function instead of a lambda function, codegen becomes identical.

Can you elaborate - why non-constexpr lambdas matter in this case? I mean - they are still only called in runtime.

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 7 years ago

In C++17 onwards, lambdas can appear in constant expressions. This change seems like an expected consequence of that to me.