[PATCH] D11297: PR17829: Functions declared extern "C" with a name matching a mangled C++ function are allowed

John McCall via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 13 18:51:14 PDT 2015

rjmccall added a comment.

In http://reviews.llvm.org/D11297#223622, @andreybokhanko wrote:

> John,
> Thank you for the quick reply!
> Let me make sure I understand what you said, using my test as an example (BTW, sorry if this is a dumb question -- I asked our local Clang experts, but no-one seems to be 100% sure what to do):
>   1: struct T {
>   2:   ~T() {}
>   3: };
>   4: 
>   5: extern "C" void _ZN1TD1Ev();
>   6: 
>   7: int main() {
>   8:   _ZN1TD1Ev();
>   9:   T t;
>   10: }
> When we deal with the call at line N8, there are no Functions created yet, so nothing to bitcast. Thus, we create a Function and the following call:
>   call void @_ZN1TD1Ev()


> When we deal with implicit destructor call at line N10, there is already a Function with "_ZN1TD1Ev" mangled name exists. Thus, we create a bitcast and the following call:


>   call void bitcast (void ()* @_ZN1TD1Ev to void (%struct.T*)*)(%struct.T* %t)


> At the end of IRGen we should replace all references of old _ZN1TD1Ev (one with zero arguments) with new _ZN1TD1Ev (one with a single T* argument) -- *including* adding a new bitcast (as we replace a Function with different type) in all places in IR where we do the replacement.

This is only necessary if you try to emit the definition at some point.  In this case, you will, because emitting the reference to the destructor as part of the second call will require the destructor to be emitted, because it's inline.

Let me try to spell out the sequence more precisely.

1. IRGen starts out with no Function.
2. IRGen sees the declaration of the extern "C" function.  It's just a declaration, not a definition, so IRGen doesn't need to do anything.
3. IRGen sees the declaration of the destructor.  It's a definition, but it's a deferrable definition, so IRGen doesn't need to do anything except record that it's got a deferred definition in DeferredDecls.
4. IRGen emits a reference to the extern "C" function.  This is a reference, not a definition, so it's fine for getOrCreateLLVMFunction to return an arbitrary Constant; it doesn't have to return a Function.  But we don't have a Function yet, so we create one with the expected type for the extern "C" function.  We also notice that we've got a deferred definition for this name, so we move that to the deferred-decls-to-emit queue.
5. IRGen emits a reference to the destructor.  This is a reference, not a definition, so any kind of Constant is fine.  Now, we've got a Function, but it's got the wrong type, so we need to bitcast it.  We've already enqueued the deferred definition, so that's fine.
6. IRGen emits the deferred definition.  We tell getOrCreateLLVMFunction that we're going to define the function, so getOrCreate has to return a Function with the right type; it's got an existing llvm::Function, but it's the wrong type, so it has to make a new llvm::Function.  It creates a new Function with no name, takes the name of the existing Function (with takeName), replaces existing references with a bitcast to the new name, and queues up something for the end of emission to remove the replaced Function.
7. IRGen reaches the end of emission and sees that it's got a Function to replace.  It replaces the Function with a bitcast of the new function again (if there are any remaining uses) and then deletes it.

Make sense?


More information about the cfe-commits mailing list