This is an archive of the discontinued LLVM Phabricator instance.

[LTO] Make local linkage GlobalValue in non-prevailing COMDAT available_externally
ClosedPublic

Authored by MaskRay on Oct 7 2022, 12:31 AM.

Details

Summary

For a local linkage GlobalObject in a non-prevailing COMDAT, it remains defined while its
leader has been made available_externally. This violates the COMDAT rule that
its members must be retained or discarded as a unit.

To fix this, update the regular LTO change D34803 to track local linkage
GlobalValues, and port the code to ThinLTO (GlobalAliases are not handled.)

This fixes two problems.

(a) __cxx_global_var_init in a non-prevailing COMDAT group used to
linger around (unreferenced, hence benign), and is now correctly discarded.

int foo();
inline int v = foo();

(b) Fix https://github.com/llvm/llvm-project/issues/58215:
as a size optimization, we place private __profd_ in a COMDAT with a
__profc_ key. When FuncImport.cpp makes __profc_ available_externally due to
a non-prevailing COMDAT, __profd_ incorrectly remains private. This change
makes the __profd_ available_externally.

cat > c.h <<'eof'
extern void bar();
inline __attribute__((noinline)) void foo() {}
eof
cat > m1.cc <<'eof'
#include "c.h"
int main() {
  bar();
  foo();
}
eof
cat > m2.cc <<'eof'
#include "c.h"
__attribute__((noinline)) void bar() {
  foo();
}
eof

clang -O2 -fprofile-generate=./t m1.cc m2.cc -flto -fuse-ld=lld -o t_gen
rm -fr t && ./t_gen && llvm-profdata show -function=foo t/default_*.profraw

clang -O2 -fprofile-generate=./t m1.cc m2.cc -flto=thin -fuse-ld=lld -o t_gen
rm -fr t && ./t_gen && llvm-profdata show -function=foo t/default_*.profraw

If a GlobalAlias references a GlobalValue which is just changed to
available_externally, change the GlobalAlias as well (e.g. C5/D5 comdats due to
cc1 -mconstructor-aliases). The GlobalAlias may be referenced by other
available_externally functions, so it cannot easily be removed.

Depends on D137441: we use available_externally to mark a GlobalAlias in a
non-prevailing COMDAT, similar to how we handle GlobalVariable/Function.
GlobalAlias may refer to a ConstantExpr, not changing GlobalAlias to
GlobalVariable gives flexibility for future extensions (the use case is niche.
For simplicity we don't handle it yet). In addition, available_externally
GlobalAlias is the most straightforward implementation and retains the aliasee
information to help optimizers.

See windows-vftable.ll: Windows vftable uses an alias pointing to a
private constant where the alias is the COMDAT leader. The COMDAT use case
is skeptical and ThinLTO does not discard the alias in the non-prevailing COMDAT.
This patch retains the behavior.

Diff Detail

Unit TestsFailed

Event Timeline

MaskRay created this revision.Oct 7 2022, 12:31 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 7 2022, 12:31 AM
MaskRay requested review of this revision.Oct 7 2022, 12:31 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 7 2022, 12:31 AM
MaskRay edited the summary of this revision. (Show Details)Oct 7 2022, 12:40 AM
MaskRay edited the summary of this revision. (Show Details)Oct 7 2022, 12:47 AM

Regular LTO is not fixed yet.

Looks like handleNonPrevailingComdat in LTO.cpp would need a similar fix.

When FuncImport.cpp ...

Note that while this functionality currently lives in FunctionImport.cpp it isn't really related to function importing, rather to ThinLTO symbol resolution. So mentioning the file name might be a little misleading (but if you do, correct it to FunctionImport.cpp).

@xur can you confirm this fixes the original problem?

llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
22

I assume the rest of the cases above (i.e. besides g_internal) already worked before this patch, including g_private since it is not local (see question about this below), is that correct?

42

Was this meant to have private linkage?

xur added a comment.Oct 7 2022, 12:43 PM

Regular LTO is not fixed yet.

Looks like handleNonPrevailingComdat in LTO.cpp would need a similar fix.

When FuncImport.cpp ...

Note that while this functionality currently lives in FunctionImport.cpp it isn't really related to function importing, rather to ThinLTO symbol resolution. So mentioning the file name might be a little misleading (but if you do, correct it to FunctionImport.cpp).

@xur can you confirm this fixes the original problem?

Yes. This fix also works. This basically retains the COMDAT group for the profile variables in LTO builds -- the same as non-LTO builds.

rnk added a comment.Oct 7 2022, 12:46 PM

This makes sense. I think there may be an analogous situation for ASan and inline globals with dynamic initialization on COFF, see the comdat group usage here:
https://gcc.godbolt.org/z/3rMo3M6cv

If one were to run ThinLTO on that IR, you'd end up with double-registered ASan globals.

MaskRay updated this revision to Diff 466199.Oct 7 2022, 3:19 PM
MaskRay retitled this revision from [ThinLTO] Make local linkage GlobalValue in non-prevailing comdat available_externally to [LTO] Make local linkage GlobalValue in non-prevailing COMDAT available_externally.
MaskRay edited the summary of this revision. (Show Details)

Fix regular LTO as well

Thanks for fixing regular LTO as well. Question on test change for that, and I had a couple questions on the ThinLTO test.

llvm/lib/LTO/LTO.cpp
699 ↗(On Diff #466199)

Update comment.

llvm/test/LTO/Resolution/X86/comdat-mixed-lto.ll
19 ↗(On Diff #466199)

Any idea where this is getting dropped to a declaration before optimization?

MaskRay updated this revision to Diff 466228.Oct 7 2022, 5:16 PM
MaskRay marked 2 inline comments as done.
MaskRay edited the summary of this revision. (Show Details)

update comment

MaskRay marked 2 inline comments as done.Oct 7 2022, 5:16 PM
MaskRay added inline comments.
llvm/test/LTO/Resolution/X86/comdat-mixed-lto.ll
19 ↗(On Diff #466199)

I am very familiar with IRMover but the logic is in

LTO.cpp RegularLTO.Mover->move
...
IRMover.cpp:594 Expected<Constant *> NewProto = linkGlobalValueProto(SGV, ForIndirectSymbol);
IRMover.cpp:1038 NewGV = copyGlobalValueProto(SGV, ShouldLink || ForIndirectSymbol);

shouldLink returns false for the available_externally GlobalObject.

Then D28737 ensures the global_ctors entry is dropped (since the comdat object is available_externally).

llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
22

Objects except g_internal/g_private work before this patch. g_internal/g_private work with this patch.

42

g (comdat key) is meant to have private linkage. A comdat with a local linkage key should not be used for portability.

MaskRay edited the summary of this revision. (Show Details)Oct 7 2022, 5:25 PM
tejohnson added inline comments.Oct 7 2022, 9:54 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1148

maybe add assert below that anything we find has local linkage.

llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
42

Sorry, not following. How/when does g_private get private linkage? Because it is external here afaict.

MaskRay updated this revision to Diff 466256.Oct 7 2022, 10:38 PM
MaskRay marked 3 inline comments as done.

Make g_private in tests private.
Add an assert

llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
42

Sorry. Missed private for g_private (the suffix is to indicate the original linkage). Added

This revision is now accepted and ready to land.Oct 7 2022, 11:06 PM
MaskRay added inline comments.Oct 8 2022, 10:24 AM
llvm/test/LTO/Resolution/X86/comdat-mixed-lto.ll
11 ↗(On Diff #466256)

The test has an issue. If C is prevailing in %t1.o, its member testglobfunc must be prevailing as well.

I'll swap the two -r options for testglobfunc: -r=%t1.o,testglobfunc,lxp -r=%t2.o,testglobfunc,lx

This revision was landed with ongoing or failed builds.Oct 8 2022, 11:09 AM
This revision was automatically updated to reflect the committed changes.
tejohnson added inline comments.Oct 8 2022, 1:15 PM
llvm/test/LTO/Resolution/X86/comdat-mixed-lto.ll
11 ↗(On Diff #466256)

This was by design. It was trying to simulate an issue that occurred with mixed Thin and regular LTO modules (per some discussion on original patch and bug). I'm not sure if we need to keep it that way, but I noticed you removed the check of testglobfunc being made available_externally - what happened to that check?

MaskRay added inline comments.Oct 8 2022, 1:27 PM
llvm/test/LTO/Resolution/X86/comdat-mixed-lto.ll
11 ↗(On Diff #466256)

assert(GO.hasLocalLinkage() && "GO's comdat should have been dropped"); fails with -r=%t2.o,testglobfunc,lxp -r=%t1.o,testglobfunc,lx.

Restore the original RUN line and remove the assert then? Note that the assert can fire in other erroneous cases where a user uses an external linkage definition to prevail a linkonce_odr definition in a prevailing comdat.

tejohnson added inline comments.Oct 11 2022, 8:55 AM
llvm/test/LTO/Resolution/X86/comdat-mixed-lto.ll
11 ↗(On Diff #466256)

Note that the assert can fire in other erroneous cases where a user uses an external linkage definition to prevail a linkonce_odr definition in a prevailing comdat.

Is this a case we want to detect and assert on in this code? Should it be detected and flagged elsewhere if it is erroneous?

MaskRay reopened this revision.Oct 11 2022, 3:06 PM
This revision is now accepted and ready to land.Oct 11 2022, 3:06 PM
MaskRay updated this revision to Diff 466938.Oct 11 2022, 3:08 PM
MaskRay edited the summary of this revision. (Show Details)

Rebase after addrsig fix
Update description that this fixes a __cxx_global_var_init problem

This revision was landed with ongoing or failed builds.Oct 11 2022, 3:30 PM
This revision was automatically updated to reflect the committed changes.
MaskRay added a comment.EditedOct 19 2022, 11:16 AM

I put this in the description

To fix this, update the regular LTO change D34803 to track local linkage GlobalValues, and port the code to ThinLTO (GlobalAliases are not handled.)

It turns out that we need to handle GlobalAlias in this patch (as opposed to in a follow-up), as otherwise it may break clang codegen of C5/D5 constructor/destructor

% cat a.ll
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

$_ZN1AD5Ev = comdat any
$_ZTV1A = comdat any

@_ZN1AD1Ev = weak_odr unnamed_addr alias void (ptr), ptr @_ZN1AD2Ev
; @_ZTV1A = weak_odr unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1AD1Ev, ptr @_ZN1AD0Ev] }, comdat, align 8
@_ZTV1A = weak_odr unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr @_ZN1AD1Ev, ptr @_ZN1AD0Ev] }, comdat, align 8

define weak_odr void @_ZN1AD2Ev(ptr noundef nonnull align 8 dereferenceable(464) %0) unnamed_addr #9 comdat($_ZN1AD5Ev) align 32 {
  ret void
}

define weak_odr void @_ZN1AD0Ev(ptr noundef nonnull align 8 dereferenceable(464) %0) unnamed_addr #9 comdat($_ZN1AD5Ev) align 32 {
  tail call void @_ZN1AD1Ev(ptr noundef nonnull align 8 dereferenceable(464) %0)
  ret void
}

% opt -module-sumary a.ll -o a.bc && cp a.bc b.bc
% ld.lld a.bc b.bc
...verifier error

The IR above is reduced from C++ examples:

// a.cc
template <typename T>
struct A {
  A() {}
  virtual ~A() {}
};

void *fa() { return new A<void>; }

// b.cc
template <typename T>
struct A {
  A() {}
  virtual ~A() {}
};

template struct A<void>;

void *fb() { return new A<void>; }
% clang -flto=thin -c a.cc b.cc -fpic
% ld.lld a.o b.o -shared   # if GlobalAlias (e.g. ` @_ZN1AIvED1Ev = weak_odr unnamed_addr alias void (ptr), ptr @_ZN1AIvED2Ev`) is not handled
Alias must point to a definition                                                                                                                                                                                                               
ptr @_ZN1AIvED1Ev                                                                                                                                                                                                                              
LLVM ERROR: Broken module found, compilation aborted! 
...
MaskRay reopened this revision.Oct 20 2022, 8:06 PM
This revision is now accepted and ready to land.Oct 20 2022, 8:06 PM
MaskRay updated this revision to Diff 469457.Oct 20 2022, 8:06 PM
MaskRay edited the summary of this revision. (Show Details)

handle GlobalAlias

I put this in the description

To fix this, update the regular LTO change D34803 to track local linkage GlobalValues, and port the code to ThinLTO (GlobalAliases are not handled.)

Is the part about GlobalAliases not handled stale now with the most recent update?

llvm/lib/IR/Verifier.cpp
849 ↗(On Diff #469457)

Would probably need to update https://llvm.org/docs/LangRef.html#aliases too.
Should we be checking that if alias is available_externally, then its aliasee is as well?

llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Would an alternative be converting the alias to a declaration? Slight pessimization in optimization ability but the verifier and other documentation doesn't need to be change.

But what if this was the prevailing copy of the alias? Could that happen?

1162

Should that be asserted?

MaskRay marked an inline comment as done.Oct 24 2022, 10:41 PM
MaskRay added inline comments.
llvm/lib/IR/Verifier.cpp
849 ↗(On Diff #469457)

LLParser.cpp still rejects available_externally aliases. so available_externally alias is currently for LTO internal use, not exposed in .ll files.

llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Currently a GlobalAlias cannot alias a declaration or an available_externally. We need to make an extension and I feel that allowing available_externally is a better choice as it enables optimizations.

But what if this was the prevailing copy of the alias? Could that happen?

If a bitcode does something unsupported thing like making one comdat prevailing while another comdat non-prevailing and creating references between the two, such an unsupported case can happen. There is probably not much we can do and I don't think we need extra efforts to detect the case.

1162

The case technically can happen and will cause a Verifier failure, so we don't necessarily assert it here...

MaskRay marked an inline comment as done.Oct 24 2022, 10:41 PM
tejohnson added inline comments.Oct 25 2022, 7:46 AM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Currently a GlobalAlias cannot alias a declaration or an available_externally. We need to make an extension and I feel that allowing available_externally is a better choice as it enables optimizations.

I meant make the alias itself a declaration, not its aliasee. Since we are making it available_externally it must have a def elsewhere. Making it a declaration instead would avoid having to allow GlobalAlias to be available_externally in the verifier.

But what if this was the prevailing copy of the alias? Could that happen?

If a bitcode does something unsupported thing like making one comdat prevailing while another comdat non-prevailing and creating references between the two, such an unsupported case can happen. There is probably not much we can do and I don't think we need extra efforts to detect the case.

I think it would be the linker not the bitcode that would cause this situation to happen if at all? Not sure if we have a solid way to detect here what is prevailing (although if we have bumped the linkage from linkonce_odr to weak_odr then it was the prevailing copy). In the C5/D5 constructor alias case that failed, what was the linkage type before and after FinalizeInModule? The added test case has multiple weak_odr, but I would imagine that normally these would start being linkonce_odr?

1162

Ok as long as it fails obviously somewhere. Wondering if we need a TODO here to handle that case in the future?

MaskRay marked an inline comment as done.Oct 25 2022, 10:14 AM
MaskRay added inline comments.
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

I meant make the alias itself a declaration, not its aliasee. Since we are making it available_externally it must have a def elsewhere. Making it a declaration instead would avoid having to allow GlobalAlias to be available_externally in the verifier.

An alias cannot be a declaration.

If we use a function or variable, we need to figure out the aliasee of the alias to decide whether to use GlobalVariable or a Function. This is more work than using an available_externally alias.

If a C5/D5 constructor is non-prevailing, we expect that everything in the non-prevailing $_ZN1AD2Ev and the associated $_ZN1AD5Ev to be non-prevailing. The current code satisfies this property.

1162

Aliasing a ConstantExpr is a less-used extension, so I think a TODO is not necessary (the comment is still clear, just without the TODO keyword).

tejohnson added inline comments.Oct 25 2022, 10:59 AM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

An alias cannot be a declaration.

If we use a function or variable, we need to figure out the aliasee of the alias to decide whether to use GlobalVariable or a Function. This is more work than using an available_externally alias.

Right, I meant drop to a variable or function declaration. We already have a helper to do so, defined in this file (see convertToDeclaration), that handles the alias case. Actually, the comments at the callsite (in this function) points out a case where we may need to do this for correctness (original linkage type is interposable) - not sure if that can ever happen here but would be good to handle correctly or assert.

If a C5/D5 constructor is non-prevailing

But we don't know for sure here whether it is prevailing or not, I think? In the C5/D5 case that was failing, what were the linkage types before and after FinalizeInModule above?

MaskRay marked an inline comment as done.Oct 25 2022, 11:14 AM
MaskRay added inline comments.
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Unfortunately, convertToDeclaration is insufficient (this fact motivated me to use available_externally alias). It is for GlobalValues which will not call auto GS = DefinedGlobals.find(GV.getGUID()); in thinLTOInternalizeModule (as an empty name triggers a fault in getGUID). Non-prevailing GlobalAlias can exercise the fault code path.

If we really want to use a declaration, we need to make distinction of GlobalVariable/Function and remove the old GlobalAlias in TheModule.getAliasList() and add a new one to TheModule.getGlobalList() (or the Function counterpart). This is lots of complexity.

If a C5/D5 constructor is non-prevailing

But we don't know for sure here whether it is prevailing or not, I think? In the C5/D5 case that was failing, what were the linkage types before and after FinalizeInModule above?

We know for sure. C2/C5 must either be all prevailing or all non-prevailing. If we the available_externally code path is triggered for one alias in C2, then we know everything in C2/C5 should be available_externally as well. I verified this with some internal targets which motivated me to revert the previous patch.

rnk added inline comments.Oct 25 2022, 1:03 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

If we really want to use a declaration, we need to make distinction of GlobalVariable/Function and remove the old GlobalAlias in TheModule.getAliasList() and add a new one to TheModule.getGlobalList() (or the Function counterpart). This is lots of complexity.

It doesn't sound so bad to me, but maybe I don't have the right visibility. As it stands, I'd rather have complex code than a less accurate IR model.

tejohnson added inline comments.Oct 25 2022, 2:11 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Unfortunately, convertToDeclaration is insufficient (this fact motivated me to use available_externally alias). It is for GlobalValues which will not call auto GS = DefinedGlobals.find(GV.getGUID()); in thinLTOInternalizeModule (as an empty name triggers a fault in getGUID). Non-prevailing GlobalAlias can exercise the fault code path.

Sorry, you lost me. What does DefinedGlobals have to do with calling convertToDeclaration, which takes a GV? The GlobalAlias below is a GV, why can't we just call convertToDeclaration on it instead of making it available_externally?

If we really want to use a declaration, we need to make distinction of GlobalVariable/Function and remove the old GlobalAlias in TheModule.getAliasList() and add a new one to TheModule.getGlobalList() (or the Function counterpart). This is lots of complexity.

convertToDeclaration handles converting alias to either a variable or function, and the underlying methods used to get one of those does add it to the appropriate list on the module. Not sure about removing from the alias list, but it does do the RAUW.

We know for sure. C2/C5 must either be all prevailing or all non-prevailing. If we the available_externally code path is triggered for one alias in C2, then we know everything in C2/C5 should be available_externally as well. I verified this with some internal targets which motivated me to revert the previous patch.

I agree they should all get the same prevailingness, if things are working as expected, but it sounds like they are in different comdat groups. I'm trying to understand what the linkage type was because if it started out as linkonce_odr the thin link should convert to either available_externally if not prevailing or weak_odr if prevailing.

MaskRay added inline comments.Oct 25 2022, 2:12 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

An available_externally alias preserve the most information: it is still an alias and the aliasee unknown. Changing GlobalAlias to a GlobalVariable/Function declaration changes the shape drops the information. To me an available_externally GlobalAlias is a better choice here.

MaskRay added inline comments.Oct 25 2022, 2:37 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Sorry, you lost me. What does DefinedGlobals have to do with calling convertToDeclaration, which takes a GV? The GlobalAlias below is a GV, why can't we just call convertToDeclaration on it instead of making it available_externally?

convertToDeclaration handles converting alias to either a variable or function, and the underlying methods used to get one of those does add it to the appropriate list on the module. Not sure about removing from the alias list, but it does do the RAUW.

Sorry, it's not DefinedGlobals.

thinLTOInternalizeModule is a pass after thinLTOFinalizeInModule.
convertToDeclaration does RAUW, but does not remove the GlobalAlias from the module getAliasList.
An unnamed GlobalAlias (due to NewGV->takeName(&GV); in convertToDeclaration) causes a failure in thinLTOInternalizeModule=>internalizeModule=>Internalize.cpp:266=>Internalize.cpp:163 (if (shouldPreserveGV(GV))`).
The function tries to acceoss the first byte of the name while the name is empty.

I think this can be fixed by replacing a GlobalAlias with a GlobalAlias/Function declaration. However, the GlobalAlias/Function approach does not scale when we ever need to support a GlobalAlias which references a ConstantExpr (instead of a GlobalValue).
An available_externally GlobalAlias can be extended to such a use case. Though I cannot think of a case Clang does such codegen.

I agree they should all get the same prevailingness, if things are working as expected, but it sounds like they are in different comdat groups. I'm trying to understand what the linkage type was because if it started out as linkonce_odr the thin link should convert to either available_externally if not prevailing or weak_odr if prevailing.

The new test file constructor-alias.ll describes the case. It is derived from this C++ source file https://reviews.llvm.org/D135427#3869003

tejohnson added inline comments.Oct 27 2022, 8:14 AM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

I see. Ok, I spent some time looking at the other callers of convertToDeclaration, since it seems as though they should have the same problem. The callsite in this file has a big FIXME (line 1107) that explains why we don't hit this - we currently explicitly avoid modifying GlobalAlias in thinLTOResolvePrevailingGUID, so that we don't end up with any that are even transiently available_externally, because the compiler doesn't handle that for aliases as we know also from your patch, so we never end up calling convertToDeclaration at that callsite for GlobalAlias.

The 2 other callsites are:

  • dropDeadSymbols: also called from the ThinLTO backend just before this method, follows up with the deletion of affected symbols from the module (for aliases since convertToDeclaration does a RAUW the old GV should never have any uses left, so it should always get erased from module there).
  • filterModule: called when writing the bitcode at the very end of the pre-LTO link compile. It erases the original GV from the module if convertToDeclaration returns false, which it does in the case of an alias where the symbol was replaced. So aliases are handled correctly there.

Given the FIXME in the earlier callsite, I think it would be good to go ahead and just add support for deleting the alias when it has been converted to a decl. This avoids having to loosen up the constraints in the verifier. In theory there could be some lost opportunities e.g. for inlining, but I would guess that in the comdat case we would have been able to capitalize on most available inlining opportunities in the pre-LTO compile's inlining step and not need something like function importing first anyway, so dropping to a decl here and removing the old alias seems safest.

Ideally given the issue you hit it would be safest to delete the original alias in convertToDeclaration after the RAUW. Then dropDeadSymbols just needs to be changed somewhat since it is adding the symbol to a set before calling convertToDeclaration - it can just guard adding the symbol to the set based on the return value of convertToDeclaration. And the callsite in filterModule should be changed to skip the erasure and just ignore the return value. This will be a nice cleanup anyway.

The new test file constructor-alias.ll describes the case. It is derived from this C++ source file https://reviews.llvm.org/D135427#3869003

Ok. I couldn't get it to generate the C5/D5 alias for me, even with cc1 -mconstructor-aliases, but in any case from the test case IR here it looks like they are getting generated as weak_odr. But also after looking at the FIXME from line 1107 I realized that because these are aliases we wouldn't have already modified their linkage type anyway.

MaskRay added inline comments.Oct 27 2022, 1:37 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1160

Ideally given the issue you hit it would be safest to delete the original alias in convertToDeclaration after the RAUW. Then dropDeadSymbols just needs to be changed somewhat since it is adding the symbol to a set before calling convertToDeclaration - it can just guard adding the symbol to the set based on the return value of convertToDeclaration. And the callsite in filterModule should be changed to skip the erasure and just ignore the return value. This will be a nice cleanup anyway.

A GlobalAlias can alias a GlobalAlias, ultimately (via the alias chain) it aliases to a GlobalVariable or Function or GlobalIFunc or ConstantExpr. I see that convertToDeclaration can make GlobalAlias work for GlobalVariable/Function, and with some code GlobalIFunc can be supported, but how to support ConstantExpr?

available_externally GlobalAlias seems a natural extension and supports ConstantExpr well (if we find a need for it).

The new test file constructor-alias.ll describes the case. It is derived from this C++ source file https://reviews.llvm.org/D135427#3869003

Ok. I couldn't get it to generate the C5/D5 alias for me, even with cc1 -mconstructor-aliases, but in any case from the test case IR here it looks like they are getting generated as weak_odr. But also after looking at the FIXME from line 1107 I realized that because these are aliases we wouldn't have already modified their linkage type anyway.

Sorry, updated https://reviews.llvm.org/D135427#3869003 to include a correct C++ example using C5/D5.

MaskRay updated this revision to Diff 473083.Nov 3 2022, 4:48 PM
MaskRay edited the summary of this revision. (Show Details)

Update LangRef.rst and LLParser for available_externally GlobalAlias

nikic added a subscriber: nikic.Nov 4 2022, 1:17 AM

Update LangRef.rst and LLParser for available_externally GlobalAlias

This should go into a separate patch, which is not mixed up with LTO changes.

MaskRay updated this revision to Diff 473271.Nov 4 2022, 9:51 AM
MaskRay edited the summary of this revision. (Show Details)

Depend on D137441

tejohnson added inline comments.Nov 4 2022, 4:32 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1162

Thinking more about this, if we know that a constant expr aliasee will lead to a verifier failure, I think it is best to assert here so that it is easier to debug when and if that happens.

MaskRay updated this revision to Diff 473384.Nov 4 2022, 6:08 PM
MaskRay marked an inline comment as done.

rebase
add an assert (reachable in a corner case, but ok to miss - Verifier.cpp will catch it anyway. assert just improves diagnostics).

"can happen and will cause a Verifier failure" - could you clarify further what that means? A pass, given verified IR, shouldn't ever produce verifier-failing IR, right?

"can happen and will cause a Verifier failure" - could you clarify further what that means? A pass, given verified IR, shouldn't ever produce verifier-failing IR, right?

GlobalAlias in a non-prevailing COMDAT is not correctly handled today (may crash or spuriously succeed). This patch makes it correct for likely all cases Clang CodeGen produces (when C5/D5 constructor/destructor aliases are used).
But I do not plan to handle ConstantExpr aliasee (an extension espindola added in ~2015/2016 which allows .set test5, test2-bar) in a non-prevailing COMDAT - which to the best of my knowledge not used today.
That code still crashes, even not caught by the assert in FuncImport.cpp; I added a check in Verifier.cpp to catch to avoid a crash further down in the compilation pipeline.

"can happen and will cause a Verifier failure" - could you clarify further what that means? A pass, given verified IR, shouldn't ever produce verifier-failing IR, right?

GlobalAlias in a non-prevailing COMDAT is not correctly handled today (may crash or spuriously succeed). This patch makes it correct for likely all cases Clang CodeGen produces (when C5/D5 constructor/destructor aliases are used).

Ah, OK, sorry - incremental improvement then, fair enough. Thanks for the context!

aeubanks added inline comments.
llvm/lib/Transforms/IPO/FunctionImport.cpp
1166

we're hitting this assert in a bootstrap build of clang with ThinLTO on windows: https://crbug.com/1382839

MaskRay added inline comments.Nov 10 2022, 1:58 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1166

How is the non-GlobalValue currently used? This feature in a COMDAT was not properly supported...

ayzhao added a subscriber: ayzhao.Nov 10 2022, 4:47 PM
ayzhao added inline comments.
llvm/lib/Transforms/IPO/FunctionImport.cpp
1166

Not sure if this is helpful, but I loaded lldb-link.exe into a debugger, and calling GA.dump() when I hit this assertion resulted in the following:

@"??_7bad_array_new_length@stdext@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [4 x ptr] }, ptr @anon.2dbfeeb171d3ed35dcf0e9128a5a5941.7, i32 0, i32 0, i32 1)

FYI repro instructions are here: https://crbug.com/1382839#c6

ayzhao added inline comments.Nov 10 2022, 5:28 PM
llvm/lib/Transforms/IPO/FunctionImport.cpp
1166

This seems to be blowing up in the gmock module:

Debug.Print TheModule.getModuleIdentifier().c_str()
0x00000227ea758630 "lib/llvm_gtest.libgmock-all.cc.obj1929440"

IR from building gmock-all.cc on windows with ThinLTO

(https://bugs.chromium.org/p/chromium/issues/detail?id=1382839#c6 is probably useful for chromium developers, but I do not know how to build chrome with the instruction...)

MaskRay reopened this revision.Nov 10 2022, 8:53 PM
This revision is now accepted and ready to land.Nov 10 2022, 8:53 PM
MaskRay updated this revision to Diff 474654.EditedNov 10 2022, 8:53 PM
MaskRay edited the summary of this revision. (Show Details)

Switch to GA.getAliaseeObject() to work around a Windows vftable hack (see updated summary): ThinLTO does not handle the alias correctly, the comdat is discarded while the global isn't marked as available_externally. It appears to be benign and the latest diff uses a way to retain the behavior.

(https://bugs.chromium.org/p/chromium/issues/detail?id=1382839#c6 is probably useful for chromium developers, but I do not know how to build chrome with the instruction...)

it wasn't clear, but that was just a typical bootstrap build of clang on windows with ThinLTO, nothing to do with chrome itself

anyway, verified that this latest patch fixes the issue

This revision was landed with ongoing or failed builds.Nov 10 2022, 9:55 PM
This revision was automatically updated to reflect the committed changes.