Skip to content

LTO: Redesign the CFI !aliases metadata. #150690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,12 @@ class LTO {
// resolutions used by a single input module. Functions return ranges refering
// to the resolutions for the remaining modules in the InputFile.
Expected<ArrayRef<SymbolResolution>>
addModule(InputFile &Input, unsigned ModI, ArrayRef<SymbolResolution> Res);
addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
unsigned ModI, ArrayRef<SymbolResolution> Res);

Expected<std::pair<RegularLTOState::AddedModule, ArrayRef<SymbolResolution>>>
addRegularLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
addRegularLTO(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
ArrayRef<SymbolResolution> Res);
Error linkRegularLTO(RegularLTOState::AddedModule Mod,
bool LivenessFromIndex);
Expand Down
43 changes: 33 additions & 10 deletions llvm/lib/LTO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,9 @@ Error LTO::add(std::unique_ptr<InputFile> Input,
Conf.VisibilityScheme = Config::ELF;
}

ArrayRef<SymbolResolution> InputRes = Res;
for (unsigned I = 0; I != Input->Mods.size(); ++I) {
if (auto Err = addModule(*Input, I, Res).moveInto(Res))
if (auto Err = addModule(*Input, InputRes, I, Res).moveInto(Res))
return Err;
}

Expand All @@ -753,8 +754,8 @@ Error LTO::add(std::unique_ptr<InputFile> Input,
}

Expected<ArrayRef<SymbolResolution>>
LTO::addModule(InputFile &Input, unsigned ModI,
ArrayRef<SymbolResolution> Res) {
LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
unsigned ModI, ArrayRef<SymbolResolution> Res) {
Expected<BitcodeLTOInfo> LTOInfo = Input.Mods[ModI].getLTOInfo();
if (!LTOInfo)
return LTOInfo.takeError();
Expand Down Expand Up @@ -791,7 +792,7 @@ LTO::addModule(InputFile &Input, unsigned ModI,
return addThinLTO(BM, ModSyms, Res);

RegularLTO.EmptyCombinedModule = false;
auto ModOrErr = addRegularLTO(BM, ModSyms, Res);
auto ModOrErr = addRegularLTO(Input, InputRes, BM, ModSyms, Res);
if (!ModOrErr)
return ModOrErr.takeError();
Res = ModOrErr->second;
Expand Down Expand Up @@ -846,7 +847,8 @@ handleNonPrevailingComdat(GlobalValue &GV,
// linkRegularLTO.
Expected<
std::pair<LTO::RegularLTOState::AddedModule, ArrayRef<SymbolResolution>>>
LTO::addRegularLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
LTO::addRegularLTO(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
ArrayRef<SymbolResolution> Res) {
RegularLTOState::AddedModule Mod;
Expected<std::unique_ptr<Module>> MOrErr =
Expand All @@ -860,13 +862,34 @@ LTO::addRegularLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
if (Error Err = M.materializeMetadata())
return std::move(Err);

// If cfi.functions is present and we are in regular LTO mode, LowerTypeTests
// will rename local functions in the merged module as "<function name>.1".
// This causes linking errors, since other parts of the module expect the
// original function name.
if (LTOMode == LTOK_UnifiedRegular)
if (LTOMode == LTOK_UnifiedRegular) {
// cfi.functions metadata is intended to be used with ThinLTO and may
// trigger invalid IR transformations if they are present when doing regular
// LTO, so delete it.
if (NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"))
M.eraseNamedMetadata(CfiFunctionsMD);
} else if (NamedMDNode *AliasesMD = M.getNamedMetadata("aliases")) {
// Delete aliases entries for non-prevailing symbols on the ThinLTO side of
// this input file.
DenseSet<StringRef> Prevailing;
for (auto [I, R] : zip(Input.symbols(), Res))
if (R.Prevailing && !I.getIRName().empty())
Prevailing.insert(I.getIRName());
std::vector<MDNode *> AliasGroups;
for (MDNode *AliasGroup : AliasesMD->operands()) {
std::vector<Metadata *> Aliases;
for (Metadata *Alias : AliasGroup->operands()) {
if (isa<MDString>(Alias) &&
Prevailing.count(cast<MDString>(Alias)->getString()))
Aliases.push_back(Alias);
}
if (Aliases.size() > 1)
AliasGroups.push_back(MDTuple::get(RegularLTO.Ctx, Aliases));
}
AliasesMD->clearOperands();
for (MDNode *G : AliasGroups)
AliasesMD->addOperand(G);
}

UpgradeDebugInfo(M);

Expand Down
127 changes: 70 additions & 57 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,7 @@ class LowerTypeTestsModule {
uint8_t *exportTypeId(StringRef TypeId, const TypeIdLowering &TIL);
TypeIdLowering importTypeId(StringRef TypeId);
void importTypeTest(CallInst *CI);
void importFunction(Function *F, bool isJumpTableCanonical,
std::vector<GlobalAlias *> &AliasesToErase);
void importFunction(Function *F, bool isJumpTableCanonical);

BitSetInfo
buildBitSet(Metadata *TypeId,
Expand Down Expand Up @@ -1103,9 +1102,8 @@ void LowerTypeTestsModule::maybeReplaceComdat(Function *F,

// ThinLTO backend: the function F has a jump table entry; update this module
// accordingly. isJumpTableCanonical describes the type of the jump table entry.
void LowerTypeTestsModule::importFunction(
Function *F, bool isJumpTableCanonical,
std::vector<GlobalAlias *> &AliasesToErase) {
void LowerTypeTestsModule::importFunction(Function *F,
bool isJumpTableCanonical) {
assert(F->getType()->getAddressSpace() == 0);

GlobalValue::VisibilityTypes Visibility = F->getVisibility();
Expand Down Expand Up @@ -1135,23 +1133,23 @@ void LowerTypeTestsModule::importFunction(
} else {
F->setName(Name + ".cfi");
maybeReplaceComdat(F, Name);
F->setLinkage(GlobalValue::ExternalLinkage);
FDecl = Function::Create(F->getFunctionType(), GlobalValue::ExternalLinkage,
F->getAddressSpace(), Name, &M);
FDecl->setVisibility(Visibility);
Visibility = GlobalValue::HiddenVisibility;

// Delete aliases pointing to this function, they'll be re-created in the
// merged output. Don't do it yet though because ScopedSaveAliaseesAndUsed
// will want to reset the aliasees first.
// Update aliases pointing to this function to also include the ".cfi" suffix,
// We expect the jump table entry to either point to the real function or an
// alias. Redirect all other users to the jump table entry.
for (auto &U : F->uses()) {
if (auto *A = dyn_cast<GlobalAlias>(U.getUser())) {
std::string AliasName = A->getName().str() + ".cfi";
Function *AliasDecl = Function::Create(
F->getFunctionType(), GlobalValue::ExternalLinkage,
F->getAddressSpace(), "", &M);
AliasDecl->takeName(A);
A->replaceAllUsesWith(AliasDecl);
AliasesToErase.push_back(A);
A->setName(AliasName);
}
}
}
Expand Down Expand Up @@ -2077,16 +2075,13 @@ bool LowerTypeTestsModule::lower() {
Decls.push_back(&F);
}

std::vector<GlobalAlias *> AliasesToErase;
{
ScopedSaveAliaseesAndUsed S(M);
for (auto *F : Defs)
importFunction(F, /*isJumpTableCanonical*/ true, AliasesToErase);
importFunction(F, /*isJumpTableCanonical*/ true);
for (auto *F : Decls)
importFunction(F, /*isJumpTableCanonical*/ false, AliasesToErase);
importFunction(F, /*isJumpTableCanonical*/ false);
}
for (GlobalAlias *GA : AliasesToErase)
GA->eraseFromParent();

return true;
}
Expand Down Expand Up @@ -2137,6 +2132,18 @@ bool LowerTypeTestsModule::lower() {
if (auto Alias = dyn_cast<AliasSummary>(RefGVS.get()))
AddressTaken.insert(Alias->getAliaseeGUID());
}
auto IsAddressTaken = [&](GlobalValue::GUID GUID) {
if (AddressTaken.count(GUID))
return true;
auto VI = ExportSummary->getValueInfo(GUID);
if (!VI)
return false;
for (auto &I : VI.getSummaryList())
if (auto Alias = dyn_cast<AliasSummary>(I.get()))
if (AddressTaken.count(Alias->getAliaseeGUID()))
return true;
return false;
};
for (auto *FuncMD : CfiFunctionsMD->operands()) {
assert(FuncMD->getNumOperands() >= 2);
StringRef FunctionName =
Expand All @@ -2153,7 +2160,7 @@ bool LowerTypeTestsModule::lower() {
// have no live references (and are not exported with cross-DSO CFI.)
if (!ExportSummary->isGUIDLive(GUID))
continue;
if (!AddressTaken.count(GUID)) {
if (!IsAddressTaken(GUID)) {
if (!CrossDsoCfi || Linkage != CFL_Definition)
continue;

Expand Down Expand Up @@ -2227,6 +2234,43 @@ bool LowerTypeTestsModule::lower() {
}
}

struct AliasToCreate {
Function *Alias;
std::string TargetName;
};
std::vector<AliasToCreate> AliasesToCreate;

// Parse alias data to replace stand-in function declarations for aliases
// with an alias to the intended target.
if (ExportSummary) {
if (NamedMDNode *AliasesMD = M.getNamedMetadata("aliases")) {
for (auto *AliasMD : AliasesMD->operands()) {
SmallVector<Function *> Aliases;
for (Metadata *MD : AliasMD->operands()) {
auto *MDS = dyn_cast<MDString>(MD);
if (!MDS)
continue;
StringRef AliasName = MDS->getString();
if (!ExportedFunctions.count(AliasName))
continue;
auto *AliasF = M.getFunction(AliasName);
if (AliasF)
Aliases.push_back(AliasF);
}

if (Aliases.empty())
continue;

for (unsigned I = 1; I != Aliases.size(); ++I) {
auto *AliasF = Aliases[I];
ExportedFunctions.erase(AliasF->getName());
AliasesToCreate.push_back(
{AliasF, std::string(Aliases[0]->getName())});
}
}
}
}

DenseMap<GlobalObject *, GlobalTypeMember *> GlobalTypeMembers;
for (GlobalObject &GO : M.global_objects()) {
if (isa<GlobalVariable>(GO) && GO.isDeclarationForLinker())
Expand Down Expand Up @@ -2414,47 +2458,16 @@ bool LowerTypeTestsModule::lower() {

allocateByteArrays();

// Parse alias data to replace stand-in function declarations for aliases
// with an alias to the intended target.
if (ExportSummary) {
if (NamedMDNode *AliasesMD = M.getNamedMetadata("aliases")) {
for (auto *AliasMD : AliasesMD->operands()) {
assert(AliasMD->getNumOperands() >= 4);
StringRef AliasName =
cast<MDString>(AliasMD->getOperand(0))->getString();
StringRef Aliasee = cast<MDString>(AliasMD->getOperand(1))->getString();

if (auto It = ExportedFunctions.find(Aliasee);
It == ExportedFunctions.end() ||
It->second.Linkage != CFL_Definition || !M.getNamedAlias(Aliasee))
continue;

GlobalValue::VisibilityTypes Visibility =
static_cast<GlobalValue::VisibilityTypes>(
cast<ConstantAsMetadata>(AliasMD->getOperand(2))
->getValue()
->getUniqueInteger()
.getZExtValue());
bool Weak =
static_cast<bool>(cast<ConstantAsMetadata>(AliasMD->getOperand(3))
->getValue()
->getUniqueInteger()
.getZExtValue());

auto *Alias = GlobalAlias::create("", M.getNamedAlias(Aliasee));
Alias->setVisibility(Visibility);
if (Weak)
Alias->setLinkage(GlobalValue::WeakAnyLinkage);

if (auto *F = M.getFunction(AliasName)) {
Alias->takeName(F);
F->replaceAllUsesWith(Alias);
F->eraseFromParent();
} else {
Alias->setName(AliasName);
}
}
}
for (auto A : AliasesToCreate) {
auto *Target = M.getNamedValue(A.TargetName);
if (!isa<GlobalAlias>(Target))
continue;
auto *AliasGA = GlobalAlias::create("", Target);
AliasGA->setVisibility(A.Alias->getVisibility());
AliasGA->setLinkage(A.Alias->getLinkage());
AliasGA->takeName(A.Alias);
A.Alias->replaceAllUsesWith(AliasGA);
A.Alias->eraseFromParent();
}

// Emit .symver directives for exported functions, if they exist.
Expand Down
31 changes: 15 additions & 16 deletions llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ void splitAndWriteThinLTOBitcode(
for (auto &F : M)
if ((!F.hasLocalLinkage() || F.hasAddressTaken()) && HasTypeMetadata(&F))
CfiFunctions.insert(&F);
for (auto &A : M.aliases())
if (auto *F = dyn_cast<Function>(A.getAliasee()))
if (HasTypeMetadata(F))
CfiFunctions.insert(&A);

// Remove all globals with type metadata, globals with comdats that live in
// MergedM, and aliases pointing to such globals from the thin LTO module.
Expand All @@ -403,12 +407,12 @@ void splitAndWriteThinLTOBitcode(
auto &Ctx = MergedM->getContext();
SmallVector<MDNode *, 8> CfiFunctionMDs;
for (auto *V : CfiFunctions) {
Function &F = *cast<Function>(V);
Function &F = *cast<Function>(V->getAliaseeObject());
SmallVector<MDNode *, 2> Types;
F.getMetadata(LLVMContext::MD_type, Types);

SmallVector<Metadata *, 4> Elts;
Elts.push_back(MDString::get(Ctx, F.getName()));
Elts.push_back(MDString::get(Ctx, V->getName()));
CfiFunctionLinkage Linkage;
if (lowertypetests::isJumpTableCanonical(&F))
Linkage = CFL_Definition;
Expand All @@ -428,29 +432,24 @@ void splitAndWriteThinLTOBitcode(
NMD->addOperand(MD);
}

SmallVector<MDNode *, 8> FunctionAliases;
MapVector<Function *, std::vector<GlobalAlias *>> FunctionAliases;
for (auto &A : M.aliases()) {
if (!isa<Function>(A.getAliasee()))
continue;

auto *F = cast<Function>(A.getAliasee());

Metadata *Elts[] = {
MDString::get(Ctx, A.getName()),
MDString::get(Ctx, F->getName()),
ConstantAsMetadata::get(
ConstantInt::get(Type::getInt8Ty(Ctx), A.getVisibility())),
ConstantAsMetadata::get(
ConstantInt::get(Type::getInt8Ty(Ctx), A.isWeakForLinker())),
};

FunctionAliases.push_back(MDTuple::get(Ctx, Elts));
FunctionAliases[F].push_back(&A);
}

if (!FunctionAliases.empty()) {
NamedMDNode *NMD = MergedM->getOrInsertNamedMetadata("aliases");
for (auto *MD : FunctionAliases)
NMD->addOperand(MD);
for (auto &Alias : FunctionAliases) {
SmallVector<Metadata *> Elts;
Elts.push_back(MDString::get(Ctx, Alias.first->getName()));
for (auto *A : Alias.second)
Elts.push_back(MDString::get(Ctx, A->getName()));
NMD->addOperand(MDTuple::get(Ctx, Elts));
}
}

SmallVector<MDNode *, 8> Symvers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@ GlobalValueMap:
15859245615183425489: # guid("internal")
- Linkage: 7 # internal
Live: true
1062103744896965210: # guid("alias1")
- Linkage: 4 # weak
Live: true
Aliasee: 16594175687743574550 # guid("external_addrtaken")
2510616090736846890: # guid("alias2")
- Linkage: 0 # weak
Live: true
Aliasee: 16594175687743574550 # guid("external_addrtaken")
...
Loading
Loading