llvm / llvm-project

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

[C++] NRVO doesn't elide move when return type is a placeholder in a function template #95280

Open MitalAshok opened 4 months ago

MitalAshok commented 4 months ago

Looking at the assembly output for this https://godbolt.org/z/6eG7TWvvd:

struct test {
    test();
    test(test&&);
};

auto f1() {
    test t;
    return t;
}

template<typename = void>
auto f2() {
    test t;
    return t;
}

template<typename = void>
test f3() {
    test t;
    return t;
}

template auto f2();
template test f3();

Shows that the move is elided in f1 and f3, but not in f2. This also happens if the placeholder is decltype(auto)

MitalAshok commented 4 months ago

Apparently this is just unimplemented

https://github.com/llvm/llvm-project/blob/abedb3b8356d5d56f1c575c4f7682fba2cb19787/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp#L1254-L1256

One fix I can think of is to "speculatively infer" what an auto type will be. When isNRVOVariable(), we know that it is of the form return x; or return (x);. With a plain (possibly cv-qualified) auto placeholder, this will be deduced to the correct type and be eligible for NRVO. With decltype(auto), only return x; would be (and return (x); would be a reference type, so not eligible). Any other form (auto*, auto&) would not be a class type.

Would that approach work?

Another fix could be to keep a list of possible NRVO variables re-check them after the placeholder type is deduced. This list can not be larger than the number of return statements I think

llvmbot commented 4 months ago

@llvm/issue-subscribers-clang-frontend

Author: Mital Ashok (MitalAshok)

Looking at the assembly output for this <https://godbolt.org/z/6eG7TWvvd>: ```c++ struct test { test(); test(test&&); }; auto f1() { test t; return t; } template<typename = void> auto f2() { test t; return t; } template<typename = void> test f3() { test t; return t; } template auto f2(); template test f3(); ``` Shows that the move is elided in `f1` and `f3`, but not in `f2`. This also happens if the placeholder is `decltype(auto)`
llvmbot commented 4 months ago

@llvm/issue-subscribers-clang-codegen

Author: Mital Ashok (MitalAshok)

Looking at the assembly output for this <https://godbolt.org/z/6eG7TWvvd>: ```c++ struct test { test(); test(test&&); }; auto f1() { test t; return t; } template<typename = void> auto f2() { test t; return t; } template<typename = void> test f3() { test t; return t; } template auto f2(); template test f3(); ``` Shows that the move is elided in `f1` and `f3`, but not in `f2`. This also happens if the placeholder is `decltype(auto)`
mizvekov commented 4 months ago

I think the best here would be to change NRVO from a Scope based approach, into an AST-based approach.