[clang] [clang][Fuchsia] Have global dtors use llvm.global_dtors over atexit (PR #115788)

via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 12 16:36:47 PST 2024


PiJoules wrote:

> I'm not sure we can just unconditionally call the destructor. We need to ensure the constructor runs first. If a constructor throws an exception or calls exit(), not all constructors will run before we start cleaning up. This works out naturally with atexit... but if you use global_dtors, I think you end up calling the destructor on an uninitialized object.

Ah yeah you're right. I didn't consider this. Since we're willing to make ABI changes, my colleague @mysterymath came up with another idea that might help address this:

1. Instrument in a dso_local global counter that indicates how many ctors we successfully called.
2. After each successful ctor call for each global, increment the counter by 1.
3. Inside the function that would be added to `llvm.global_dtors` and would invoke all the dtors, have a global switch that jumps to the appropriate case based on the number of correctly initialized globals. This would be akin to Duff's device.

```
// Given globals:
//
//   A first;
//   A second;
//   A third;

static int SuccessfulInits = 0;

void module_ctor() {
  construct_A(&first);
  SuccessfulInits++;

  construct_A(&second);
  SuccessfulInits++;

  construct_A(&third);
  SuccessfulInits++;
}

void module_dtor() {
  switch (SuccessfulInits) {
    case 3: destruct_A(&third);  // fallthrough
    case 2: destruct_A(&second);  // falthrough
    case 1: destruct_A(&first);  // fallthrough
    case 0: break;  // No destructors invoked
  }
}
```

An alternative approach might be using a lookup table with a decrementing pointer:

```
void __dtor_first() {
  destruct_A(&first);
}

void __dtor_second() {
  destruct_A(&second);
}


void __dtor_third() {
  destruct_A(&third);
}

void (*dtor_list[])() = {__dtor_first, __dtor_second, __dtor_third};
void (*dtor_ptr)() = dtor_list;

void module_ctor() {
  construct_A(&first);
  dtor_ptr++;

  construct_A(&second);
  dtor_ptr++;

  construct_A(&third);
  dtor_ptr++;
}

void module_dtor() {
  while (dtor_ptr != dtor_list) {
    (--dtor_ptr)();  // Invoke one of the functions in dtor_list
  }
}
```

I probably won't implement these here or continue with the patch, but these are some ideas we had.


> Also, I'm not sure objects are destroyed in the correct order.

Do you mean for the test case I added? I think they should be destroyed in reverse order.

https://github.com/llvm/llvm-project/pull/115788


More information about the cfe-commits mailing list