This is an archive of the discontinued LLVM Phabricator instance.

Always traverse GlobalVariable initializer when computing the export list
ClosedPublic

Authored by mehdi_amini on Apr 14 2016, 2:04 AM.

Details

Summary

We are always importing the initializer for a GlobalVariable.
So if a GlobalVariable is in the export-list, we pull in any
refs as well.

Diff Detail

Event Timeline

mehdi_amini retitled this revision from to Always traverse GlobalVariable initializer when computing the export list.
mehdi_amini updated this object.
mehdi_amini added a reviewer: tejohnson.
mehdi_amini added a subscriber: llvm-commits.
tejohnson edited edge metadata.Apr 14 2016, 11:38 AM

We are always importing the initializer for a GlobalVariable.

I don't think this is true anymore, or at least I can't find where we are doing this. We used to always import variable defs in the Linker, but now that is being passed a list of globals to import.

Now we are skipping variables with a comment:

auto *Summary = dyn_cast_or_null<FunctionSummary>(GlobInfo->summary());
if (!Summary)
  /// Ignore global variable, focus on functions
  continue;

and computeImportForFunction does not walk refs() looking for possible imports. Am I missing something?

I think if we want to add handling like this for the future when variable defs are imported, a better place would be in computeImportForFunction where we would presumably walk the refs() and make those decisions (similar to how the ExportList is updated for callees in the loop over the call edges in that function.

We are always importing the initializer for a GlobalVariable.

I don't think this is true anymore, or at least I can't find where we are doing this. We used to always import variable defs in the Linker, but now that is being passed a list of globals to import.

We do it for every global that is referenced from the IR we import, this is done in IRLinker::linkGlobalInit(GlobalVariable &Dst, GlobalVariable &Src).
(I couldn't find a reliable way to avoid this, too many corner cases).

I think if we want to add handling like this for the future when variable defs are imported, a better place would be in computeImportForFunction where we would presumably walk the refs() and make those decisions (similar to how the ExportList is updated for callees in the loop over the call edges in that function.

That's was the plan when we moved to the "reference graph", however it seems to me that we're blocked on IRLinker::linkGlobalInit(GlobalVariable &Dst, GlobalVariable &Src) right now.

I'll update the comment mentionning that it is a current limitation of the IRMover.

mehdi_amini edited edge metadata.

Update comment to reflect that it is a workaround for an IRMover limitation

In D19102#401562, @joker.eph wrote:

We are always importing the initializer for a GlobalVariable.

I don't think this is true anymore, or at least I can't find where we are doing this. We used to always import variable defs in the Linker, but now that is being passed a list of globals to import.

We do it for every global that is referenced from the IR we import, this is done in IRLinker::linkGlobalInit(GlobalVariable &Dst, GlobalVariable &Src).
(I couldn't find a reliable way to avoid this, too many corner cases).

I don't believe that is true that we always link in inits for global variables that are referenced. Yes linkGlobalInit is called for variables unconditionally from linkGlobalValueBody, but then, so is linkFunctionBody for functions from the same place. We invoke linkGlobalValueBody from materializeInitFor, which I believe is unconditionally invoked on references from the ValueMapper. However, materializeInitFor only invokes linkGlobalValueBody only when "ForAlias || shouldLink(New, *Old)".

Looking at when those conditions are true it seems like there are therefore a few conditions under which we would map in a global variable init (although I don't see anything specific to variables here):

  1. ForAlias==true: This happens when this value was encountered when mapping an aliasee, which could be a variable
  2. New is in ValuesToLink: Not currently true for global variables
  3. New hasLocalLinkage(): We should have promoted any locals referenced by an import before this, so shouldn't be true here
  4. AddLazyFor returns true: Aha - you are probably catching some variables here due to the issue with addLazyFor currently force importing linkonce!

So I think the issue is not with linkGlobalInit, but rather the current Linker behavior around aliases and linkonce (and I don't think this is specific to variables). Again, correct me if I am missing something though.

In D19102#401562, @joker.eph wrote:

We are always importing the initializer for a GlobalVariable.

I don't think this is true anymore, or at least I can't find where we are doing this. We used to always import variable defs in the Linker, but now that is being passed a list of globals to import.

We do it for every global that is referenced from the IR we import, this is done in IRLinker::linkGlobalInit(GlobalVariable &Dst, GlobalVariable &Src).
(I couldn't find a reliable way to avoid this, too many corner cases).

I don't believe that is true that we always link in inits for global variables that are referenced. Yes linkGlobalInit is called for variables unconditionally from linkGlobalValueBody, but then, so is linkFunctionBody for functions from the same place. We invoke linkGlobalValueBody from materializeInitFor, which I believe is unconditionally invoked on references from the ValueMapper. However, materializeInitFor only invokes linkGlobalValueBody only when "ForAlias || shouldLink(New, *Old)".

Looking at when those conditions are true it seems like there are therefore a few conditions under which we would map in a global variable init (although I don't see anything specific to variables here):

  1. ForAlias==true: This happens when this value was encountered when mapping an aliasee, which could be a variable
  2. New is in ValuesToLink: Not currently true for global variables
  3. New hasLocalLinkage(): We should have promoted any locals referenced by an import before this, so shouldn't be true here
  4. AddLazyFor returns true: Aha - you are probably catching some variables here due to the issue with addLazyFor currently force importing linkonce!

So I think the issue is not with linkGlobalInit, but rather the current Linker behavior around aliases and linkonce (and I don't think this is specific to variables). Again, correct me if I am missing something though.

Right, so I didn't point to the right API, I have *another* patch (D19096) for the issue with linkGlobalInit (that comes from addLazyFor indeed).

For this patch it was maybe rather for *constant* that we will always import? (but we don't know in the summary if a GlobalVariable is constant right?)

mehdi_amini abandoned this revision.Apr 20 2016, 11:22 PM

I think this is obsolete now, it was indeed global linkonce odr that were pulled in (like Vtable for instance) as well as the linkonce_odr member function referenced by it.

mehdi_amini reclaimed this revision.Apr 21 2016, 2:18 AM

I can't bootstrap clang locally without this patch, so I need to extract a reduced test case to motivate it

Add a test that illustrates the bug fixed here.

tejohnson accepted this revision.Apr 22 2016, 2:32 PM
tejohnson edited edge metadata.

Just needs comment update now that we finally know why gvar init is being imported in this case.

lib/Transforms/IPO/FunctionImport.cpp
173

Can you update the comment which is inaccurate since we don't always import initializers? Oh, I just figured out why this is getting imported. It is related to #3 from my earlier list of when materializeInitFor will decide to linkGlobalValueBody():

  1. New hasLocalLinkage(): We should have promoted any locals referenced by an import before this, so shouldn't be true here

But I was wrong! The one situation where we will not promote a local is a global variable that is constant with unnamed addr:

bool FunctionImportGlobalProcessing::doPromoteLocalToGlobal(
...

auto *GVar = dyn_cast<GlobalVariable>(SGV);
if (GVar && GVar->isConstant() && GVar->hasUnnamedAddr())
  return false;

which is exactly what happens in your test case. Can you update the comment accordingly?

Also, maybe add a FIXME that if we had a flag in summary indicating that GVar was constant that we could refine this?

This revision is now accepted and ready to land.Apr 22 2016, 2:32 PM
mehdi_amini added inline comments.Apr 22 2016, 2:50 PM
lib/Transforms/IPO/FunctionImport.cpp
173

I'll update.
(added a note in our TODO shared doc as well)

mehdi_amini marked 2 inline comments as done.
mehdi_amini edited edge metadata.

Update before commit