| Issue |
183621
|
| Summary |
[clang] Clang 22 incorrect code for `delete[]` with MS ABI
|
| Labels |
clang
|
| Assignees |
|
| Reporter |
tycho
|
Godbolt showing problem (they lack a Clang 22 release build yet, so I'm using "trunk" on there for this):
https://gcc.godbolt.org/z/YPY6T7bdT
I tried to minify this manually and it's not quite representative of my code, but it still illustrates something going wrong with `delete[]` on the MS ABI.
Code sample:
```
template <class T>
__attribute__((always_inline)) inline void safe_delete_array(T& p) {
delete[] p;
p = nullptr;
}
// Non-trivial, non-virtual dtor (forces cookie for arrays).
struct A {
~A();
};
// Non-trivial *virtual* dtor (polymorphic).
struct B {
virtual ~B();
virtual int f() { return 1; }
};
struct D : B { ~D() override {} };
struct C {
A* a;
B* b;
unsigned int n;
C(unsigned int n_);
~C();
};
C::C(unsigned int n_) : a(nullptr), b(nullptr), n(n_) {
a = new A[n];
b = new B[n];
}
C::~C() {
safe_delete_array(a);
safe_delete_array(b);
}
```
Clang 21 destructor for struct `C` (looks okay):
```
"??1C@@QEAA@XZ":
push r15
push r14
push rsi
push rdi
push rbx
sub rsp, 32
mov rsi, rcx
mov r14, qword ptr [rcx]
test r14, r14
je .LBB2_5
lea rdi, [r14 - 8]
mov rbx, qword ptr [r14 - 8]
test rbx, rbx
je .LBB2_4
dec r14
mov r15, rbx
.LBB2_3:
lea rcx, [r14 + r15]
call "??1A@@QEAA@XZ"
dec r15
jne .LBB2_3
.LBB2_4:
add rbx, 8
mov rcx, rdi
mov rdx, rbx
call "??_V@YAXPEAX_K@Z"
.LBB2_5:
mov qword ptr [rsi], 0
mov rdi, qword ptr [rsi + 8]
test rdi, rdi
je .LBB2_10
mov rax, qword ptr [rdi - 8]
add rdi, -8
lea rbx, [8*rax]
test rax, rax
je .LBB2_9
mov r14, rbx
.LBB2_8:
lea rcx, [rdi + r14]
call "??1B@@UEAA@XZ"
add r14, -8
jne .LBB2_8
.LBB2_9:
add rbx, 8
mov rcx, rdi
mov rdx, rbx
call "??_V@YAXPEAX_K@Z"
.LBB2_10:
mov qword ptr [rsi + 8], 0
add rsp, 32
pop rbx
pop rdi
pop rsi
pop r14
pop r15
ret
```
Clang 22 destructor for struct `C`:
```
"??1C@@QEAA@XZ":
push r15
push r14
push rsi
push rdi
push rbx
sub rsp, 32
mov rsi, rcx
mov r14, qword ptr [rcx]
test r14, r14
je .LBB2_5
lea rdi, [r14 - 8]
mov rbx, qword ptr [r14 - 8]
test rbx, rbx
je .LBB2_4
dec r14
mov r15, rbx
.LBB2_3:
lea rcx, [r14 + r15]
call "??1A@@QEAA@XZ"
dec r15
jne .LBB2_3
.LBB2_4:
add rbx, 8
mov rcx, rdi
mov rdx, rbx
call "??_V@YAXPEAX_K@Z"
.LBB2_5:
mov qword ptr [rsi], 0
mov rcx, qword ptr [rsi + 8]
test rcx, rcx
je .LBB2_9
cmp qword ptr [rcx - 8], 0
je .LBB2_7
mov rax, qword ptr [rcx]
mov edx, 3
call qword ptr [rax]
jmp .LBB2_9
.LBB2_7:
add rcx, -8
mov edx, 8
call "??_V@YAXPEAX_K@Z"
nop
.LBB2_9:
add rsp, 32
pop rbx
pop rdi
pop rsi
pop r14
pop r15
ret
```
The second `delete[]` in there is not right. What I observed in my own code (not this repro) was that it was not passing the original raw pointer returned from `new[]` to the `delete[]` operator, it was passing the address of the first element instead -- and this caused a crash.
But let's break down the above a bit. Here's the 2nd `delete[]` only:
```
.LBB2_5:
mov qword ptr [rsi], 0 # null out first member (a = nullptr) after its delete[] completed
mov rcx, qword ptr [rsi + 8] # rcx = b (element pointer returned by new B[n])
test rcx, rcx # b == nullptr ?
je .LBB2_9 # if null, skip deletion (ok)
cmp qword ptr [rcx - 8], 0 # load array cookie (n) from *(b - 8); compare n == 0
je .LBB2_7 # if n == 0, go free the allocation (cookie-only) path (ok)
mov rax, qword ptr [rcx] # rax = vptr of *b (first element)
mov edx, 3 # MSVC deleting-dtor selector (scalar delete form; NOT array delete)
call qword ptr [rax] # call virtual "deleting destructor" for ONE element only (wrong for delete[])
jmp .LBB2_9 # then skip any array deallocation (leaks / misses freeing whole block) (wrong)
.LBB2_7:
add rcx, -8 # rcx = cookie/raw pointer (base of allocation, not element pointer)
mov edx, 8 # size = 8 bytes (cookie only; corresponds to n==0)
call "??_V@YAXPEAX_K@Z" # call sized operator delete[](void*, size_t) with (base, 8)
nop
.LBB2_9:
...
ret
```
And here's what my production code has -- where we call the sized `delete[]` with the wrong address:
```
xom::safe_delete_array(m_vbo);
00007FF7C82CF728 add rax,58h
00007FF7C82CF72C mov qword ptr [rsp+80h],rax
00007FF7C82CF734 mov rax,qword ptr [rsp+80h]
00007FF7C82CF73C mov rax,qword ptr [rax]
00007FF7C82CF73F mov qword ptr [rsp+38h],rax
00007FF7C82CF744 cmp rax,0
00007FF7C82CF748 je GLImmediate::~GLImmediate+12Ch (07FF7C82CF79Ch)
00007FF7C82CF74A mov rax,qword ptr [rsp+38h]
00007FF7C82CF74F mov rcx,rax # rcx = element pointer (m_vbo), i.e. first element, not cookie/base
00007FF7C82CF752 add rcx,0FFFFFFFFFFFFFFF8h # rcx = (m_vbo - 8) => cookie/base pointer (raw allocation)
00007FF7C82CF756 mov qword ptr [rsp+28h],rcx
00007FF7C82CF75B mov rax,qword ptr [rax-8] # rax = array cookie (n)
00007FF7C82CF75F mov qword ptr [rsp+30h],rax
00007FF7C82CF764 cmp rax,0
00007FF7C82CF768 jne GLImmediate::~GLImmediate+113h (07FF7C82CF783h)
00007FF7C82CF76A mov rcx,qword ptr [rsp+28h] # (n == 0) rcx = cookie/base pointer
00007FF7C82CF76F mov rax,qword ptr [rsp+30h]
00007FF7C82CF774 imul rdx,rax,68h
00007FF7C82CF778 add rdx,8
00007FF7C82CF77C call operator delete[] (07FF7C8269920h) # (n == 0) delete[] called with cookie/base pointer (looks consistent)
00007FF7C82CF781 jmp GLImmediate::~GLImmediate+12Ch (07FF7C82CF79Ch)
00007FF7C82CF783 mov rcx,qword ptr [rsp+38h] # (n != 0) rcx = element pointer (m_vbo) again
00007FF7C82CF788 call VertexBuffer::~VertexBuffer (07FF7C7FD22F0h) # (n != 0) destroys exactly ONE element (should be a loop for delete[])
00007FF7C82CF78D mov rcx,qword ptr [rsp+38h] # (n != 0) rcx still = element pointer, NOT cookie/base pointer
00007FF7C82CF792 mov edx,68h # passes sizeof(VertexBuffer) as "size" (scalar-sized-delete shape)
00007FF7C82CF797 call operator delete[] (07FF7C8269920h) # BUG: delete[] called with element pointer (m_vbo), not cookie/base (m_vbo-8)
00007FF7C82CF79C mov rcx,qword ptr [rsp+60h]
00007FF7C82CF7A1 mov rax,qword ptr [rsp+80h]
00007FF7C82CF7A9 mov qword ptr [rax],0
}
00007FF7C82CF7B0 call std::vector<GLImmediateVertex,std::allocator<GLImmediateVertex> >::~vector (07FF7C82D0D60h)
00007FF7C82CF7B5 nop
00007FF7C82CF7B6 add rsp,88h
00007FF7C82CF7BD ret
```
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs