This is an archive of the discontinued LLVM Phabricator instance.

[CSPGO] Set module flag for CSPGO to avoid IRPGO value loss in LTO
AbandonedPublic

Authored by YolandaCY on Jul 28 2021, 10:03 PM.

Details

Reviewers
xur
rnk
MaskRay
Summary

The IRPGOFlag symbol (__llvm_profile_raw_version) will be dropped when identified
as non-prevailing for either regular or thin LTO during the mixed-LTO mode compilation.
This caused an unresolved symbol linker error when enable CSPGO over ThinLTO
after https://reviews.llvm.org/D103717.

Try set a CSIRPGO flag for CSPGO to avoid this symbol resolution caused value loss.

Diff Detail

Event Timeline

YolandaCY created this revision.Jul 28 2021, 10:03 PM
YolandaCY edited the summary of this revision. (Show Details)Jul 29 2021, 3:51 AM
YolandaCY updated this revision to Diff 363002.Jul 30 2021, 2:33 AM

Use a new flag to avoid test failures

YolandaCY updated this revision to Diff 363666.Aug 3 2021, 2:44 AM

Fix for CSPGO only

YolandaCY retitled this revision from [LTO] Enable ValueProfiling for Regular LTO in mixed LTO mode to [CSPGO] Reserve IRPGO flag for CSPGO when running with mixed-LTO mode.Aug 3 2021, 3:05 AM
YolandaCY edited the summary of this revision. (Show Details)
YolandaCY published this revision for review.Aug 3 2021, 8:02 PM
YolandaCY added reviewers: xur, rnk.

Could you help take a look? This is trying to address a linker error for CSPGO with ThinLTO on Windows after a recent CL to keep __profd_ variable private.

Herald added a project: Restricted Project. · View Herald TranscriptAug 3 2021, 8:02 PM
rnk added a comment.Aug 4 2021, 11:04 AM

Should we set the "EnableValueProfiling" module flag as an alternative way to express that value profiling is enabled? That flag wouldn't be subject to this kind of symbol resolution.

YolandaCY added a comment.EditedAug 10 2021, 8:04 PM

Thank you Reid for the comment. (Sorry I missed your comment last week.)

Yes, setting "EnableValueProfiling" flag can be an option. Just I encountered a test failure at "llvm/test/Instrumentation/InstrProfiling/comdat.ll" that will change the generated "__prof*" variable name with a trailing 0. The test may need be revised. I'm not quite sure if "EnableValueProfiling" flag can be equal to the IRFlag value, if there's no objection, I can try this method.

YolandaCY updated this revision to Diff 365955.Aug 12 2021, 3:46 AM
YolandaCY retitled this revision from [CSPGO] Reserve IRPGO flag for CSPGO when running with mixed-LTO mode to [LTO][CSPGO] Set EnableValueProfiling flag for CSPGO when running with mixed-LTO mode.
YolandaCY edited the summary of this revision. (Show Details)

Change to set EnableValueProfiling module flag

YolandaCY updated this revision to Diff 365957.Aug 12 2021, 4:03 AM

cleanup unrelated changes

rnk added inline comments.
llvm/lib/LTO/LTO.cpp
1236 ↗(On Diff #365957)

My only concern is that we have to do this in two places. Is there a better place we can do this? Maybe the frontend, or wherever we enable IR PGO? These feel like the wrong places.

YolandaCY updated this revision to Diff 366226.Aug 13 2021, 2:38 AM
YolandaCY retitled this revision from [LTO][CSPGO] Set EnableValueProfiling flag for CSPGO when running with mixed-LTO mode to [CSPGO] Set EnableValueProfiling flag for CSPGO to avoid IRPGO value loss in LTO.
YolandaCY edited the summary of this revision. (Show Details)

Set the flag earlier when generate the IRPGO var

YolandaCY marked an inline comment as done.Aug 13 2021, 2:48 AM
YolandaCY added inline comments.
llvm/lib/LTO/LTO.cpp
1236 ↗(On Diff #365957)

Thank you Reid for the suggestion. Agree that setting this ealier when we enable the IR PGO is better. Revised the patch accordingly.

rnk accepted this revision.Aug 13 2021, 10:11 AM

lgtm

@MaskRay, please take a look.

This revision is now accepted and ready to land.Aug 13 2021, 10:11 AM
MaskRay added a comment.EditedAug 13 2021, 1:46 PM

The IRPGOFlag symbol (__llvm_profile_raw_version) will be dropped when identified as non-prevailing for either regular or thin LTO during the mixed-LTO mode compilation. This caused an unresolved symbol linker error when enable CSPGO over ThinLTO after https://reviews.llvm.org/D103717.

I am concerned that EnableValueProfiling might not be added to the correct place. (Why specifically to CSPGO?) Can you give a concrete example for the mixed LTO scenario?
What's the "duplicate symbol" error?

llvm/test/Transforms/PGOProfile/lto_cspgo_gen.ll
13

This test is unclear why EnableValueProfiling is set for CSPGO.

MaskRay added inline comments.Aug 13 2021, 1:48 PM
llvm/lib/ProfileData/InstrProf.cpp
1148

The way comdat any is used, it is unsupported if you mix non-CS and CS PGO object files. The first copy is picked by the linker.

This does not seem a proper fix to me. If you can give a concrete example, if may help me to understand.

YolandaCY marked 2 inline comments as done.Aug 15 2021, 5:12 AM

The IRPGOFlag symbol (__llvm_profile_raw_version) will be dropped when identified as non-prevailing for either regular or thin LTO during the mixed-LTO mode compilation. This caused an unresolved symbol linker error when enable CSPGO over ThinLTO after https://reviews.llvm.org/D103717.

I am concerned that EnableValueProfiling might not be added to the correct place. (Why specifically to CSPGO?) Can you give a concrete example for the mixed LTO scenario?
What's the "duplicate symbol" error?

The background is when enable CSPGO over ThinLTO, it requires a seperate pass to create IRPGO flag in prelink, hence the var will later be subject to symbol resolution during LTO. This is not the case for PGO though. See references at:
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp#L1614
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Passes/PassBuilder.cpp#L1183

For mixed LTO, I think it should relate to flag "-fsplit-lto-unit" (also subject to the whole program devirtualization optimization pass). I included a test case for hybrid lto in a previous revision, see: https://reviews.llvm.org/D107034?vs=on&id=363666#toc
You may find a "...!"ThinLTO", i32 0}" mudule flag which indicate this file will be added to regular LTO, while the other without it will be added for ThinLTO.

Here are some more references on why mixed LTO is needed and how:
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp#L34
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp#L269

The duplicate symbol error if when I simply try to reserve the IRPGO flag in regular LTO when it is identified as non-prevailing, I will get such error for ELF, but not COFF.

Hope this helps.

llvm/lib/ProfileData/InstrProf.cpp
1148

Yes, I think mixing non-CS and CS PGO seems not a normal practice. As CSPGO is trying collect profile after inlining by applying profile created by PGO.
The user guide for CSPGO is run after PGO as described here:
https://clang.llvm.org/docs/UsersManual.html#cmdoption-fcs-profile-generate

Hope this makes sense now.

Can you give concrete instructions to reproduce the linker error?

YolandaCY marked an inline comment as done.Aug 18 2021, 7:19 AM

Can you give concrete instructions to reproduce the linker error?

I tried to create a small reproducer but still encountered several issues. The linker error may be explained with below example:

class Str {
  int size;

public:
  char *p;

  Str(int size) {
    p = new char[size];
  }

  ~Str() {
    delete[] p;
  }

  void assign_mem(char *a, char *b, int size) {
    memcpy(a, b, size);
  }

  int assign(Str &b) {
    if (b.size < size) return -1;
    assign_mem(p, b.p, size);
    return 1;
  }
};

If function assign inlines assign_mem, it does contain mem op and should require a value profile var, but not vice versa.
The case encounterd the linker error is that a function similar to assign (e.g. basic_string::operator=) does not inline the callee assign_mem in the splitted regular LTO link, thus it will generate a __profd_assign* with private linkage, while in a later ThinLTO module, the assign inlined the callee containing mem op, thus it may generate a __profd_assign with non-private linkage (due to NS!=0). During the final link together with both regular and thin LTO modules for COFF, symbols from regular LTO are inserted earlier as prevailing, and the private __profd_assign* is not inserted. Whereas the symbol from thin module has external attribute, and is associated to __profc_assign* who is non-prevailing and should not be inserted. While there is a prevailing __profc_assign* existing in symbol table, there is no __profd_assign*, thus reported as unresolved symbol.

I think by correcting the profile var name with a hash suffix when IRPGO enabled should avoid such inconsistency in different translation units. And the linker error helped exposing the issue.

I'm implementing a CL to build Chromium that depends on this. The steps described in the CL should help reproduce the error if you are interested @MaskRay. The original error looks like following:

lld-link: error: undefined symbol: __profd_??4?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@QEAAAEAV012@AEBV012@@Z
>>> referenced by .\..\..\buildtools\third_party\libc++\trunk\include\string:2401
>>>               ./chrome.dll.lto.147.obj:(public: class std::__1::basic_string<char, struct std::__1::char_traits<char>, class std::__1::allocator<char>> & __cdecl std::td::__1::allocator<char>>::operator=(class std::__1::basic_string<char, struct std::__1::char_traits<char>, class std::__1::allocator<char>> const &))
>>> referenced by .\..\..\buildtools\third_party\libc++\trunk\include\string:2401
>>>               ./chrome.dll.lto.147.obj:(public: class std::__1::basic_string<char, struct std::__1::char_traits<char>, class std::__1::allocator<char>> & __cdecl std::td::__1::allocator<char>>::operator=(class std::__1::basic_string<char, struct std::__1::char_traits<char>, class std::__1::allocator<char>> const &))

Please let me know if more information is needed.

MaskRay added a comment.EditedAug 18 2021, 5:27 PM

So, this appears to be a PE/COFF specific problem, due to our wrestling with link.exe's comdat limitation....

I am still unclear yet whether this is only specific to post-inlining instrumentation. If yes, this is a CSPGO specific problem. Perhaps we can think about dropping link.exe compatibility if this turns out to be too much of a hassle...


I do not understand this part:

Whereas the symbol from thin module has external attribute, and is associated to profc_assign* who is non-prevailing and should not be inserted. While there is a prevailing profc_assign* existing in symbol table, there is no __profd_assign*, thus reported as unresolved symbol.

Summary of the issue:

  • The final (after implicit or distributed ThinLTO generated object files) linker command line looks like lld-link ... regular.o thin.o.
  • regular.o (regular LTO) emits __prof[cd]_assign. __profc_assign as the comdat leader is prevailing. profc is linkonce_odr while profd is private.
  • thin.o (Thin LTO) emits __prof[cd]_assign. __profc_assign as the comdat leader is non-prevailing. Both profc and profd are linkonce_odr.

Does your change make regular.o __profc_assign linkonce_odr?
Which variable in a non-prevailing comdat is referenced, causing an undefined symbol error?

I think by correcting the profile var name with a hash suffix when IRPGO enabled should avoid such inconsistency in different translation units. And the linker error helped exposing the issue.

Any idea why a hash suffix isn't used?
See canRenameComdatFunc, is the address of assign taken?

MaskRay added inline comments.Aug 18 2021, 5:28 PM
llvm/lib/ProfileData/InstrProf.cpp
1149

According to your description, various profc could cause linker errors. So the comment "The IRPGO flag var (__llvm_profile_raw_version) has external linkage," is incomplete/incorrect.

I edited my comment a bit so your email notification may not catch it.
This looks to me a PE/COFF specific issue. I wonder whether we can drop link.exe support for PGO / CSPGO.
link.exe has the limitation "non-leader members of a comdat must be private".
I recall that PE/COFF can entirely use the ELF scheme if we can lift the limitation.
(https://lists.llvm.org/pipermail/llvm-dev/2021-May/150758.html)

So, this appears to be a PE/COFF specific problem, due to our wrestling with link.exe's comdat limitation....

I am still unclear yet whether this is only specific to post-inlining instrumentation. If yes, this is a CSPGO specific problem. Perhaps we can think about dropping link.exe compatibility if this turns out to be too much of a hassle...


I do not understand this part:

Whereas the symbol from thin module has external attribute, and is associated to profc_assign* who is non-prevailing and should not be inserted. While there is a prevailing profc_assign* existing in symbol table, there is no __profd_assign*, thus reported as unresolved symbol.

Summary of the issue:

  • The final (after implicit or distributed ThinLTO generated object files) linker command line looks like lld-link ... regular.o thin.o.
  • regular.o (regular LTO) emits __prof[cd]_assign. __profc_assign as the comdat leader is prevailing. profc is linkonce_odr while profd is private.
  • thin.o (Thin LTO) emits __prof[cd]_assign. __profc_assign as the comdat leader is non-prevailing. Both profc and profd are linkonce_odr.

In thin.o, both profc and profd are weak_odr linkage.

Does your change make regular.o __profc_assign linkonce_odr?

Yes. Just make sure the ValueProfiling flag available and will keep it as it is for COFF.

Which variable in a non-prevailing comdat is referenced, causing an undefined symbol error?

It's profd_assign. As its associate parent (profc_assign) is non-prevailing, it will get discarded as the parent. See https://github.com/llvm/llvm-project/blob/main/lld/COFF/InputFiles.cpp#L349

I think by correcting the profile var name with a hash suffix when IRPGO enabled should avoid such inconsistency in different translation units. And the linker error helped exposing the issue.

Any idea why a hash suffix isn't used?
See canRenameComdatFunc, is the address of assign taken?

The canRenameComdatFunc is turned off by default by flag DoComdatRenaming. I can have a try later.
The getVarName for profile variables will add FunHash as suffix in this change.

I edited my comment a bit so your email notification may not catch it.
This looks to me a PE/COFF specific issue. I wonder whether we can drop link.exe support for PGO / CSPGO.
link.exe has the limitation "non-leader members of a comdat must be private".
I recall that PE/COFF can entirely use the ELF scheme if we can lift the limitation.
(https://lists.llvm.org/pipermail/llvm-dev/2021-May/150758.html)

I think I used lld-link.exe, not MSVC's link.exe. As mentioned in the link,

lld-link doesn't have this limitation.

I hope my last reply will help resolve some confusion.

MaskRay added a comment.EditedAug 19 2021, 5:19 PM

So, this appears to be a PE/COFF specific problem, due to our wrestling with link.exe's comdat limitation....

I am still unclear yet whether this is only specific to post-inlining instrumentation. If yes, this is a CSPGO specific problem. Perhaps we can think about dropping link.exe compatibility if this turns out to be too much of a hassle...


I do not understand this part:

Whereas the symbol from thin module has external attribute, and is associated to profc_assign* who is non-prevailing and should not be inserted. While there is a prevailing profc_assign* existing in symbol table, there is no __profd_assign*, thus reported as unresolved symbol.

Summary of the issue:

  • The final (after implicit or distributed ThinLTO generated object files) linker command line looks like lld-link ... regular.o thin.o.
  • regular.o (regular LTO) emits __prof[cd]_assign. __profc_assign as the comdat leader is prevailing. profc is linkonce_odr while profd is private.
  • thin.o (Thin LTO) emits __prof[cd]_assign. __profc_assign as the comdat leader is non-prevailing. Both profc and profd are linkonce_odr.

In thin.o, both profc and profd are weak_odr linkage.

OK, clang emitted weak_odr directly, perhaps due to explicit instantiation of a template (GVA_StrongODR).
Since comdat renaming is disabled in this case, profd should not be private.

Does your change make regular.o __profc_assign linkonce_odr?

Yes. Just make sure the ValueProfiling flag available and will keep it as it is for COFF.

Which variable in a non-prevailing comdat is referenced, causing an undefined symbol error?

It's profd_assign. As its associate parent (profc_assign) is non-prevailing, it will get discarded as the parent. See https://github.com/llvm/llvm-project/blob/main/lld/COFF/InputFiles.cpp#L349

Is __profd_assign referenced by a non-assign function? The __profd_assign reference is a result of inlining.

I think by correcting the profile var name with a hash suffix when IRPGO enabled should avoid such inconsistency in different translation units. And the linker error helped exposing the issue.

Any idea why a hash suffix isn't used?
See canRenameComdatFunc, is the address of assign taken?

The canRenameComdatFunc is turned off by default by flag DoComdatRenaming. I can have a try later.
The getVarName for profile variables will add FunHash as suffix in this change.

DoComdatRenaming is PGOInstrumentation.cpp's comdat renaming.
canRenameComdatFunc is InstrProfiling.cpp's comdat renaming. It is not disabled.

linkonce_odr functions are generally renamed.
The linkage in question is weak_odr, so the renaming is disabled for canRenameComdatFunc.

So now this change looks strange to me:

if (!DoHashBasedCounterSplit || !enablesValueProfiling(*M) ||
    !canRenameComdatFunc(*F))
  return (Prefix + Name).str();

canRenameComdatFunc(*F) is false.

Perhaps the right fix is D108432. I need to update its description.

Perhaps the right fix is D108432. I need to update its description.

Thank you for the patch. I will have a try with this fix and get back to you.

There might be multiple solutions to resolve the issues. Regardingless the linker error, the issue with IRPGO flag missing should be addressed I think. Does that sound good to you from CSPGO point of view?

About this patch M.addModuleFlag(llvm::Module::Warning, "EnableValueProfiling", 1);: I am not so sure we should do it.

Note that we only set "EnableValueProfiling" to 0 for the clang -fprofile-instr-generate case. The value is not set to 1 (without poking into the internal -enable-value-profiling option).

clang -fprofile-generate with value profiling doesn't set this module flag, so -fcs-profile-generate doesn't need to set it, either.

lld/COFF/LTO.cpp
226 ↗(On Diff #366226)

drop unrelated change

MaskRay requested changes to this revision.Aug 19 2021, 11:09 PM
This revision now requires changes to proceed.Aug 19 2021, 11:09 PM

About this patch M.addModuleFlag(llvm::Module::Warning, "EnableValueProfiling", 1);: I am not so sure we should do it.

From enablesValueProfiling both IRPGOFlag and EnableValueProfiling mudule flag are examined as indicators. My understanding IRPGOFlag should imply "EnableValueProfiling" to 1.

This alternative solution was recommended by Reid, and no objection from Xu. Could you help clarify with Fangrui? @rnk @xur

Note that we only set "EnableValueProfiling" to 0 for the clang -fprofile-instr-generate case. The value is not set to 1 (without poking into the internal -enable-value-profiling option).

clang -fprofile-generate with value profiling doesn't set this module flag, so -fcs-profile-generate doesn't need to set it, either.

I think I have clarified why this change is unnecessary to PGO in previous reply.

I can drop the unrelated change regarding /lldsavetemps for sure. That should be an easy fix.

About this patch M.addModuleFlag(llvm::Module::Warning, "EnableValueProfiling", 1);: I am not so sure we should do it.

From enablesValueProfiling both IRPGOFlag and EnableValueProfiling mudule flag are examined as indicators. My understanding IRPGOFlag should imply "EnableValueProfiling" to 1.

This alternative solution was recommended by Reid, and no objection from Xu. Could you help clarify with Fangrui? @rnk @xur

"EnableValueProfiling" is set to 0 by -fprofile-instr-generate to enable a size saving optimization (D102818).

IR profiling doesn't set 1. It is inconsistent for CSPGO to set "EnableValueProfiling" to 1, while -fprofile-generate keeps omitting the module flag.
-disable-vp can disable value profiling. This patch doesn't respect that option.

In addition, I think changing the condition in getVarName is incorrect. Coverage and PGO have different use cases while share some infrastructure (InstrProfiling.cpp).
Coverage suppresses renaming to have better coverage results. The decision is independent of whether value profiling is enabled or not.
On the other hand, PGO's preferring renaming behavior is also independent of value profiling.

Some confusion here is that -fprofile-instr-generate was used to do PGO (perhaps all groups except Apple have migrated away).
When "EnableValueProfiling" was introduced, I think the intention was to prevent breakage to them.
Otherwise, PGO use cases could just ignore "EnableValueProfiling" everywhere.

Actually, I am thinking of dropping "EnableValueProfiling". It needs some COFF side work (D44543) (or we just unsupport that use case).

Note that we only set "EnableValueProfiling" to 0 for the clang -fprofile-instr-generate case. The value is not set to 1 (without poking into the internal -enable-value-profiling option).

clang -fprofile-generate with value profiling doesn't set this module flag, so -fcs-profile-generate doesn't need to set it, either.

I think I have clarified why this change is unnecessary to PGO in previous reply.

I can drop the unrelated change regarding /lldsavetemps for sure. That should be an easy fix.

As previously explained, I think the patch is not correct. So keeping the "Request Changes" state.

xur added a comment.EditedAug 20 2021, 5:47 PM

Sorry that I did not follow this patch closely. But after reading this I don't think this is a proper fix.

Are you sure IRPGOFlag is dropped? CSPGO won't work properly if this happens. You might work-around the linker issue (unresolved symbols), but the resulted profile will not be marked properly.
But I really doubt IRPGOFlag is indeed dropped. From the explanation of the example test case, it seems you just see the unsats from value profiling variables. Do you have other evidence that IRPGOFlag variable is dropped?

A test case is really helping here (or the build log).

Did you try MasRay's patch
https://reviews.llvm.org/D108432
to see if that fixes the problem?

Sorry that I did not follow this patch closely. But after reading this I don't think this is a proper fix.

Are you sure IRPGOFlag is dropped? CSPGO won't work properly if this happens. You might work-around the linker issue (unresolved symbols), but the resulted profile will not be marked properly.
But I really doubt IRPGOFlag is indeed dropped. From the explanation of the example test case, it seems you just see the unsats from value profiling variables. Do you have other evidence that IRPGOFlag variable is dropped?

This variable is not dropped for the final binary, it’s just dropped for each module (if non-prevailing) during LTO/ThinLTO as there’s no uses or references. In the final instrumented binary, the variable is still kept. The example in discussion was trying to explain why it will introduce a unresolved symbol error, can be a seperate issue.

A test case is really helping here (or the build log).

For the loss of IRPGOFlag in LTO should be easy to reproduce. (Just hard for the linker issue)
You may have a try to the test I added lto_cspgo_gen.ll, if exaimine output of the last command "llvm-dis %t.0.0.preopt.bc -o - ", you will find the variable "__llvm_profile_raw_version" is not emitted.

Did you try MasRay's patch
https://reviews.llvm.org/D108432
to see if that fixes the problem?

MaskRay's patch may help avoid the linker error, but it wont fix the IRPGOFlag missing issue.

I actually have an alternative patch in a previous diff, if we have concern on using EnableValueProfiling flag.
Not sure if this looks better?

MaskRay added a comment.EditedAug 20 2021, 9:55 PM

Sorry that I did not follow this patch closely. But after reading this I don't think this is a proper fix.

Are you sure IRPGOFlag is dropped? CSPGO won't work properly if this happens. You might work-around the linker issue (unresolved symbols), but the resulted profile will not be marked properly.
But I really doubt IRPGOFlag is indeed dropped. From the explanation of the example test case, it seems you just see the unsats from value profiling variables. Do you have other evidence that IRPGOFlag variable is dropped?

This variable is not dropped for the final binary, it’s just dropped for each module (if non-prevailing) during LTO/ThinLTO as there’s no uses or references. In the final instrumented binary, the variable is still kept. The example in discussion was trying to explain why it will introduce a unresolved symbol error, can be a seperate issue.

A test case is really helping here (or the build log).

For the loss of IRPGOFlag in LTO should be easy to reproduce. (Just hard for the linker issue)
You may have a try to the test I added lto_cspgo_gen.ll, if exaimine output of the last command "llvm-dis %t.0.0.preopt.bc -o - ", you will find the variable "__llvm_profile_raw_version" is not emitted.

Did you try MasRay's patch
https://reviews.llvm.org/D108432
to see if that fixes the problem?

MaskRay's patch may help avoid the linker error, but it wont fix the IRPGOFlag missing issue.

I actually have an alternative patch in a previous diff, if we have concern on using EnableValueProfiling flag.
Not sure if this looks better?

I am afraid https://reviews.llvm.org/D107034?vs=on&id=363777#toc is incorrect, either.

My understanding below. @xur is the expert and may correct me:)

ThinLTO+CSPGO instrumentation is:

# After regular PGO instrumentation, profile collection, llvm-profdata merge
...
clang -flto=thin -fprofile-use=... -fcs-profile-generate=... a.c b.c # ThinLTO prelink phase
clang -flto=thin -fcs-profile-generate=... a.o b.o # ThinLTO postlink (backend compile) phase

The ThinLTO bitcode files need the -fcs-profile-generate= option to set __llvm_profile_raw_version in the PGOInstrumentationGenCreateVar pass.

ld.lld does comdat selection but the prevailing/non-prevailing decisions do not drop non-prevailing __llvm_profile_raw_version variables from bitcode files.

During ThinLTO postlink (backend compile), InstrProfiling.cpp doesn't need to add __llvm_profile_raw_version, because the variable is already there.


I think we need better documentation for CSPGO.

@xur This is also reproducible using the existing test case PGOProfile/thinlto_cspgo_gen.ll. If evaluating the generated "%t.2.4.opt.bc" containing non-prevailing IRPGO flag, you will find the value is missed. (The value is still reserved in "%t.2.3.import.bc".)

In the LTO case, the symbol is dropped at *.preopt phase.

In summary after tracing:

  1. In LTO, the value is dropped when addRegularLTO as it has external linkage and non-prevailing.
  2. In ThinLTO, the value is first changed to available_external linkage by thinLTOResolvePrevailingInModule and thinLTOResolvePrevailingGUID. It is dropped later in the backend GlobleOpt pass by deleteIfDead.

Hence to keep the value we may need to identify the IRPGO variable in all above places, make sure it is reserved, and then drop it after instrumentation to avoid duplicate symbols in later linkage. My feeling is that using a module flag may avoid too much hassle across multiple places and more complexity to the symbol resolution stuff. (as suggested by Reid)
Agree that mixing EnableValueProfiling with IRPGO flag is improper as it seems Clang also may support value profiling (from comment of shouldRecordFunctionAddr ).
Maybe we can rename the module flag to "CSIRPGO"? We can also remove the flag after instrumentation as no need for later passes.

YolandaCY updated this revision to Diff 368086.Aug 23 2021, 6:17 AM
YolandaCY retitled this revision from [CSPGO] Set EnableValueProfiling flag for CSPGO to avoid IRPGO value loss in LTO to [CSPGO] Set module flag for CSPGO to avoid IRPGO value loss in LTO.
YolandaCY edited the summary of this revision. (Show Details)

change flag name and remove unrelated fix

xur added a comment.Aug 23 2021, 12:29 PM

@xur This is also reproducible using the existing test case PGOProfile/thinlto_cspgo_gen.ll. If evaluating the generated "%t.2.4.opt.bc" containing non-prevailing IRPGO flag, you will find the value is missed. (The value is still reserved in "%t.2.3.import.bc".)

In the LTO case, the symbol is dropped at *.preopt phase.

In summary after tracing:

  1. In LTO, the value is dropped when addRegularLTO as it has external linkage and non-prevailing.
  2. In ThinLTO, the value is first changed to available_external linkage by thinLTOResolvePrevailingInModule and thinLTOResolvePrevailingGUID. It is dropped later in the backend GlobleOpt pass by deleteIfDead.

Hence to keep the value we may need to identify the IRPGO variable in all above places, make sure it is reserved, and then drop it after instrumentation to avoid duplicate symbols in later linkage. My feeling is that using a module flag may avoid too much hassle across multiple places and more complexity to the symbol resolution stuff. (as suggested by Reid)
Agree that mixing EnableValueProfiling with IRPGO flag is improper as it seems Clang also may support value profiling (from comment of shouldRecordFunctionAddr ).
Maybe we can rename the module flag to "CSIRPGO"? We can also remove the flag after instrumentation as no need for later passes.

OK. I now see the problem.

I added IRPGOFlag was to only used to mark the profile as IR instrumentation (rather clang instrumentation).
But it was later used to coordinate instrumentation pass and lowering pass. This breaks in thin-lto and lto.

IRPGOFlag now not only is used in enabling value profiling, but also in comdat renaming.

I still don't want to use module flag -- using IRPGOFlag still preferred.

I will fix this.

YolandaCY abandoned this revision.Aug 24 2021, 6:59 PM

Issue is fixed at D108581.