[AMDGPU] Fix alias handling in module splitting functionality (#187295)

Summary:
The module splitting used for `-flto-partitions=8` support (which is
passed by default) did not correctly handle aliases. We mainly need to
do two things: keep the aliases in the they are used in and externalize
them. Internalize linkage needs to be handled conservatively.

This is needed because these aliases show up in PGO contexts.

---------

Co-authored-by: Shilei Tian <i@tianshilei.me>
This commit is contained in:
Joseph Huber 2026-03-19 10:51:39 -05:00 committed by GitHub
parent d8a83a1123
commit 923cc2d43b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 8 deletions

View File

@ -43,6 +43,7 @@
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Module.h"
@ -1285,7 +1286,9 @@ namespace {
static bool needsConservativeImport(const GlobalValue *GV) {
if (const auto *Var = dyn_cast<GlobalVariable>(GV))
return Var->hasLocalLinkage();
return isa<GlobalAlias>(GV);
if (const auto *GA = dyn_cast<GlobalAlias>(GV))
return GA->hasLocalLinkage();
return false;
}
/// Prints a summary of the partition \p N, represented by module \p M, to \p
@ -1394,8 +1397,6 @@ static void splitAMDGPUModule(
// visible copy, then internalize all other copies" for some functions?
if (!NoExternalizeOnAddrTaken) {
for (auto &Fn : M) {
// TODO: Should aliases count? Probably not but they're so rare I'm not
// sure it's worth fixing.
if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
LLVM_DEBUG(dbgs() << "[externalize] "; Fn.printAsOperand(dbgs());
dbgs() << " because its address is taken\n");
@ -1414,6 +1415,13 @@ static void splitAMDGPUModule(
}
}
for (auto &GA : M.aliases()) {
if (GA.hasLocalLinkage()) {
LLVM_DEBUG(dbgs() << "[externalize] alias " << GA.getName() << '\n');
externalize(GA);
}
}
// Start by calculating the cost of every function in the module, as well as
// the module's overall cost.
FunctionsCostMap FnCosts;
@ -1510,15 +1518,18 @@ static void splitAMDGPUModule(
return false;
}
// Aliases should not be separated from their underlying object.
if (const auto *GA = dyn_cast<GlobalAlias>(GV)) {
if (const auto *Fn = dyn_cast<Function>(GA->getAliaseeObject()))
return FnsInPart.contains(Fn);
}
// Everything else goes in the first non-empty module we create.
return ImportAllGVs || needsConservativeImport(GV);
}));
ImportAllGVs = false;
// FIXME: Aliases aren't seen often, and their handling isn't perfect so
// bugs are possible.
// Clean-up conservatively imported GVs without any users.
for (auto &GV : make_early_inc_range(MPart->global_values())) {
if (needsConservativeImport(&GV) && GV.use_empty())

View File

@ -4,23 +4,30 @@
; RUN: llvm-dis -o - %t2 | FileCheck --check-prefix=CHECK2 %s
; 3 kernels with each their own dependencies should go into 3
; distinct partitions.
; distinct partitions. Aliases should follow their aliasee's partition
; and not be cleaned up even if unused in that partition.
; CHECK0-NOT: @HelperA_alias = {{.*}}alias
; CHECK0-NOT: define
; CHECK0: define amdgpu_kernel void @C
; CHECK0: define internal void @HelperC
; CHECK0-NOT: @HelperA_alias = {{.*}}alias
; CHECK0-NOT: define
; CHECK1-NOT: @HelperA_alias = {{.*}}alias
; CHECK1-NOT: define
; CHECK1: define amdgpu_kernel void @B
; CHECK1: define internal void @HelperB
; CHECK1-NOT: @HelperA_alias = {{.*}}alias
; CHECK1-NOT: define
; CHECK2: @HelperA_alias = hidden alias void (), ptr @HelperA
; CHECK2-NOT: define
; CHECK2: define amdgpu_kernel void @A
; CHECK2: define internal void @HelperA
; CHECK2: define hidden void @HelperA
; CHECK2-NOT: define
@HelperA_alias = internal alias void (), ptr @HelperA
define amdgpu_kernel void @A() {
call void @HelperA()