comment on r 153990

// comment on r 153990
// /lib/CodeGen/MicrosoftCXXABI.cpp
// Hopefully the following can help clear up unknowns.

// The following is true for 32-bit. While expectation is that 64-bit
// would use 8 bytes for count, I have not verified it.
//
// MSVC doesn't use "cookies" (an array count at p-4) when the type T
// is trivial (f.ex. POD) - the overhead isn't needed.
//
// If T has has a destructor, the actual size of memory allocated is
// n_bytes+4, as follows:
// int : no_of_array_elements.
// n_bytes : the memory returned to the caller
//
// When time comes to delete[], if T is trivial it simply deallocates
// using operator[] (f.ex. T::operator[](void*,size_t) if declared).
// This is comparable to plain malloc/free of e.g. an array of char's.
//
// If non-trivial (i.e. a user-supplied or compiler generated d'tor
// exists) it instead does the following to destruct and delete:
// p->T::vector_deleting_destructor(1|2); // 1 = dealloc, 2 = vector
//
// T::vector_deleting_destructor is a compiler-generated member
// function of the form:
// void /* __thiscall */ T::vector_deleting_destructor(int flags) {
// if (flags & 2) {
// vector_destructor_iterator(this, sizeof T,
// ((int*)this)[-1], &T::~T);
// if (flags & 1)
// delete[] ((T**)this)[-1]; // note [1]
// return ((void**)this)[-1];
// } else {
// p->~T();
// if (flags & 1)
// delete this; // NOTE: Not array version.
// return (void*)this;
// }
// }
//
// [1] If no T::operator delete[] function exists, this becomes a call
// to operator delete(void*).
// If (as in the following example) it exists, it's called as:
// T::operator delete[](p, sizeof T);
//
// Worth noting is:
// - The vector_deleting_destructor returns a pointer to the _actual_
// memory. It is not known under what circumstances it could be
// called without bit 0 set for its flags argument.
// - The signature of the vector_destructor_iterator, where
// elemcount is the most curious (it's signed!).
// void __stdcall vector_destructor_iterator(
// void* p,
// size_t elemsize,
// int elemcount,
// void (__thiscall*)(void*));

// The result using a Microsoft 32-bit compiler able to compile the
// following is expected to be:
// A: 100, 1
// B: 104, 1
// SEGV: obvious. :slight_smile:

#include <stdio.h>
#include <stdlib.h>

struct A {
   char x;
   void *operator new[](size_t blocksize) {
     printf("new: %u\n", blocksize);
     return malloc(blocksize);
   }
   void operator delete(void *p) { }
   void operator delete[](void *p, size_t elementsize) {
     printf("del: %u\n", elementsize);
     free(p);
   }
};
struct B : public A { ~B() { /*printf("B d'tor\n");*/ } };
void test_A() { A *p = new A[100]; delete[] p; }
void test_B() { B *p = new B[100]; delete[] p; }
void test_SEGV() { A *p = new B[100]; delete[] p; }
int main() {
   test_A();
   test_B();
   printf("The app will now crash in free() due to being given a ptr\n"
          "4 bytes into the actually allocated block of memory...\n");
   test_SEGV();
   return 0;
}

I don't think it's worth checking this in without a corresponding
implementation, but thank you for the investigation.

John.