Recently I've encountered a problem related to the usage of rvalue std::initializer_list object in the range-based for-loop, specifically on Microsoft Visual C++ 2013 Update 4 in Release x64.
Luckily, it was solved easily by simplifying the code (I cannot express how much I'd love every problem to be solved like that), namely creating a local variable to store std::initializer_list object.
Anyway, I am still curious about the root cause and hopefully you can help me finding it. So if you could explain what goes wrong in the story below or at least give me some hint, you'll make me a happier man ;-)
The symptom
Let's assume that we work with a tree of objects, all of them inherit from the Base class. So Derived1 and Derived2 below both derive from Base.
Derived1* p1, p2;
Derived2* p3, p4, p5, p6;
// code that initializes p1..p6 and ensures they are not nullptr
const std::unique_ptr<Base> pObject = /*some factory function call*/;
for (auto* ptr : std::initializer_list<Base*>{p1, p2, p3, p4, p5, p6})
ptr->setParent(pObject.get()); // crash occurs here
The problem ceases away as I create a local initializer_list variable. All the surrounding code remains the same, only a single line changes.
Derived1* p1, p2;
Derived2* p3, p4, p5, p6;
// code that initializes p1..p6 and ensures they are not nullptr
const std::unique_ptr<Base> pObject = /*some factory function call*/;
const std::initializer_list<Base*> il{p1, p2, p3, p4, p5, p6};
for (auto* ptr : il)
ptr->setParent(pObject.get()); // everything goes fine
Note: I can observe such problem only with temporary std::initializer_list object. If I replace it with e.g. temporary std::vector<Base*> object, the problem ceases. Also, I wasn't able to reproduce the crash on a small toy example. It occurs only in the presence of the surrounding code, in Release x64 with /O2 flag.
Since such a change in a single line produces a difference in behavior, I made a conclusion that this is a problem with Visual C++ compiler.
Assembly output
Of course, I tried to check the generated assembly code. But as I'm not a big expert in that, I was unable to see anything suspicious. Maybe it will give a clue on what was going wrong to those who read this.
I 'anonymized' the accompanying function and variable names, just as in the C++ samples above, but the assembly itself is left intact.
Here is an assembly output from the first program which results in access violation:
const std::unique_ptr<Base> pObject = /*some factory function call*/;
00007FFE25AC21C5 mov esi,6
00007FFE25AC21CA mov rbx,qword ptr [rbp-80h]
00007FFE25AC21CE xchg ax,ax
for (auto* thing : std::initializer_list<Base*>{ p1, p2,
00007FFE25AC21D0 mov rcx,qword ptr [rdi]
p3, p4, p5, p6 })
thing->setParent(pObject.get());
00007FFE25AC21D3 mov rax,qword ptr [rcx]
00007FFE25AC21D6 xor r8d,r8d
00007FFE25AC21D9 mov rdx,rbx
00007FFE25AC21DC call qword ptr [rax+40h]
00007FFE25AC21DF lea rdi,[rdi+8]
for (auto* thing : std::initializer_list<Base*>{ p1, p2,
00007FFE25AC21E3 dec rsi
00007FFE25AC21E6 jne `anonymous namespace'::someUnrelatedFunctionCall+4B0h (07FFE25AC21D0h)
And here's one from the second version which works fine:
const std::unique_ptr<Base> pObject = /*some factory function call*/;
00007FFE25D021C1 lea rcx,[pObject]
00007FFE25D021C6 call Scene_NS::buildSmartPtr<Base> (07FFE25CF55F0h)
00007FFE25D021CB nop
std::initializer_list < Base* > il{ p1, p2, p3, p4, p5, p6 };
00007FFE25D021CC mov qword ptr [rbp+50h],r15
00007FFE25D021D0 mov qword ptr [rbp+58h],r14
00007FFE25D021D4 mov qword ptr [rbp+60h],rdi
00007FFE25D021D8 mov qword ptr [rbp+68h],r12
00007FFE25D021DC mov qword ptr [rbp+70h],r13
00007FFE25D021E0 mov qword ptr [rbp+78h],rbx
for (auto* thing : il)
00007FFE25D021E4 lea rdi,[rbp+50h]
const std::unique_ptr<Base> pObject = /*some factory function call*/;
00007FFE25D021E8 mov esi,6
00007FFE25D021ED mov rbx,qword ptr [pObject]
00007FFE25D021F2 nop dword ptr [rax]
00007FFE25D021F6 nop word ptr [rax+rax]
for (auto* thing : il)
00007FFE25D02200 mov rcx,qword ptr [rdi]
thing->setParent(pObject.get());
00007FFE25D02203 mov rax,qword ptr [rcx]
00007FFE25D02206 xor r8d,r8d
00007FFE25D02209 mov rdx,rbx
00007FFE25D0220C call qword ptr [rax+40h]
00007FFE25D0220F lea rdi,[rdi+8]
for (auto* thing : il)
00007FFE25D02213 dec rsi
00007FFE25D02216 jne `anonymous namespace'::someUnrelatedFunctionCall+4E0h (07FFE25
Aucun commentaire:
Enregistrer un commentaire