Why this dead store of unique_ptr cannot be eliminated?

为君一笑 提交于 2019-12-06 07:48:35

问题


#include <memory>
#include <vector>
using namespace std;

vector<unique_ptr<int>> e;

void f(unique_ptr<int> u) {
    e.emplace_back(move(u));
}

For both Clang and GCC, the above code snippet generates something like:

f(std::unique_ptr<int, std::default_delete<int> >):
        mov     rsi, QWORD PTR e[rip+8] # rsi: vector.end_ptr
        cmp     rsi, QWORD PTR e[rip+16] # [e + rip + 16]: vector.storage_end_ptr
        je      .L52 # Slow path, need to reallocate
        mov     rax, QWORD PTR [rdi] # rax: unique_ptr<int> u
        add     rsi, 8               # end_ptr += 8
        mov     QWORD PTR [rdi], 0   # <==== Do we need to set the argument u to null here? 
        mov     QWORD PTR [rsi-8], rax # *(end_ptr - 8) = u 
        mov     QWORD PTR e[rip+8], rsi # update end_ptr
        ret
.L52:   # omitted

I was wondering why does the compiler generate mov QWORD PTR[rdi], 0 in this function? Is there any convention that requires compiler to do so?

Moreover, for simpler case like this:

void f(unique_ptr<int> u);

void h(int x) {
    auto p = make_unique<int>(x);
    f(move(p));
}

Why does the compiler generate:

call    operator delete(void*, unsigned long)

at the end of h(), given that p is always nullptr after the invocation of f?


回答1:


In both of these cases, the answer is: because the object you moved from will still be destroyed.

If you look at the code generated for a call to

void f(unique_ptr<int> u);

you will notice that the caller creates the object for parameter u and calls its destructor afterwards as mandated by the calling convention. In case the call to f() is inlined, the compiler will most likely be able to optimize this away. But the code generated for f() has no control over the destructor of u and, thus, has to set the internal pointer of u to zero assuming that the destructor of u will run after the function returns.

In your second example, we have sort of the inverse situation:

void h(int x) {
    auto p = make_unique<int>(x);
    f(move(p));
}

Contrary to what the name may suggest, std::move() does not actually move an object. All it does is cast to an rvalue reference which allows the recipient of that reference to move from the object referred to—if he so choses. The actual move only happens, e.g., when another object is constructed from the given argument via a move constructor. Since the compiler does not know anything about what happens inside f() at the point of definition of h(), it can't assume that f() will always move from the given object. For example, f() could simply return or move only in some cases and not in others. Therefore, the compiler has to assume that the function might return without moving from the object and has to emit the delete for the destructor. The function could also perform a move assignment instead of a move construction, in which case the outer destructor would still be needed to release ownership of the object previously held by whatever was assigned ownership of the new object…



来源:https://stackoverflow.com/questions/53330428/why-this-dead-store-of-unique-ptr-cannot-be-eliminated

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