blob: 03f2ec5f8fddcd2d6bc9eed842b1fb91678da4e4 [file] [edit]
//===- DynamicDebugging.cpp - Dynamic Debugging utils --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/DynamicDebugging.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
using namespace llvm;
std::unique_ptr<Module>
llvm::prepareForDynamicDebugging(Module *M, StringRef PromotionSuffix) {
using namespace llvm;
assert(M->getNamedMetadata("llvm.dbg.cu") &&
"Expected module with debug info");
auto ShouldPromoteGlobal = [](const GlobalValue &GV) {
if (!GV.hasLocalLinkage())
return false;
// Local symbols in a comdat shouldn't be promoted either.
// This can happen with (at least) __cxx_global_var_init (which is local
// and may initialize an ODR-weak global variable).
if (GV.hasComdat())
return false;
return true;
};
// Clone functions definitions only - CloneModule will clone data definitions
// as declarations. We rename these and explicitly set their linkage later.
auto ShouldCloneDefinition = [](const GlobalValue *GV) {
return isa<Function>(GV);
};
ValueToValueMapTy VMap;
std::unique_ptr<Module> UnoptM = CloneModule(*M, VMap, ShouldCloneDefinition);
// Insert declarations into Inner that point to Outer, apply attributes to
// Outer functions.
DenseMap<Function *, Function *> OuterDefToInnerDecl;
for (Function &OuterDef : M->functions()) {
if (OuterDef.isDeclaration())
continue;
// Find the Inner version of Outer's function.
Function *InnerDef = cast<Function>(VMap[&OuterDef]);
// Apply some attributes to both Inner and Outer defs.
{
// Unoptimized module wants no inlining at all.
InnerDef->addFnAttr(Attribute::NoInline);
InnerDef->removeFnAttr(Attribute::AlwaysInline);
// Apply optnone, remove clashing attributes.
InnerDef->addFnAttr(Attribute::OptimizeNone);
InnerDef->removeFnAttr(Attribute::OptimizeForSize);
InnerDef->removeFnAttr(Attribute::MinSize);
// Add attributes to the outer-object functions to ensure they're
// always patchable.
//
// The debugger patches outer (optimized) functions to redirect to inner
// (unoptimized) functions. Block interprocedural analysis to ensure
// the two function implementations share the same interface.
OuterDef.addFnAttr(Attribute::NoIPA);
// Outlining creates specialized functions in the outer (optimized)
// module without an inner (unoptimized) equivalent, meaning the debugger
// can't switch to an unoptimized version, so block outlining.
OuterDef.addFnAttr(Attribute::NoOutline);
// TODO: Add patch bytes size/value for other targets.
if (M->getTargetTriple().isX86_64()) {
OuterDef.addFnAttr("tail-pad-to-size", "5");
OuterDef.addFnAttr("tail-pad-value", "144"); // 0x90
}
}
// Apply COMDAT grouping to the clone if OuterDef is in one.
if (OuterDef.hasComdat()) {
std::string NewComdat =
Twine("__dyndbg." + OuterDef.getComdat()->getName()).str();
Comdat *C = M->getOrInsertComdat(NewComdat);
C->setSelectionKind(OuterDef.getComdat()->getSelectionKind());
InnerDef->setComdat(C);
}
// Rename Inner's copy and set appropriate linkage depending on whether
// it'll get promoted in Outer or not.
if (ShouldPromoteGlobal(OuterDef)) {
InnerDef->setName("__dyndbg." + InnerDef->getName() + PromotionSuffix);
InnerDef->setLinkage(GlobalValue::ExternalLinkage);
InnerDef->setVisibility(GlobalValue::HiddenVisibility);
} else {
InnerDef->setName("__dyndbg." + InnerDef->getName());
InnerDef->setLinkage(OuterDef.getLinkage());
InnerDef->setVisibility(OuterDef.getVisibility());
}
// Create Inner's external reference to Outer's version.
Function *InnerDeclOfOuterDef = Function::Create(
cast<FunctionType>(OuterDef.getValueType()), OuterDef.getLinkage(),
OuterDef.getAddressSpace(), OuterDef.getName(), UnoptM.get());
InnerDeclOfOuterDef->copyAttributesFrom(&OuterDef);
// Re-set linkage and visibility after copyAttributesFrom.
InnerDeclOfOuterDef->setLinkage(GlobalValue::ExternalLinkage);
InnerDeclOfOuterDef->setPersonalityFn(nullptr);
// Replace Inner uses of function with that external reference.
InnerDef->replaceAllUsesWith(InnerDeclOfOuterDef);
VMap[&OuterDef] = InnerDeclOfOuterDef;
}
// Add Outer aliases for globals with internal linkage, adding
// ".dyndbg.<TU-unique-hash>" suffix. Update Inner's external references to
// these promoted functions to use their new names.
SmallVector<GlobalValue *> GlobalsToPreserve;
for (GlobalValue &GV : M->global_values()) {
// If the global is used but may be discarded after optimizations
// (e.g. inlining) then ensure it's marked as compiler-used to prevent
// that. It may be referenced from the inner module.
if (GV.isDiscardableIfUnused()) {
if (GV.getNumUses()) {
GlobalsToPreserve.push_back(&GV);
// Name unnamed globals. Compiler-used expects named globals only.
if (GV.getName().empty())
GV.setName("__unnamed");
} else {
// No uses, so the inner module doesn't need a reference nor do we need
// to produce an alias.
// Remove the inner module reference.
auto GVAndUnoptPair = VMap.find(&GV);
assert(GVAndUnoptPair != VMap.end() && "Unmapped global?");
// Delete the external reference - VMap should only contain mappings to
// those declarations now.
assert(cast<GlobalValue>(GVAndUnoptPair->second)->isDeclaration() &&
"expected only declarations in VMap now");
cast<GlobalValue>(GVAndUnoptPair->second)->eraseFromParent();
GVAndUnoptPair->second = nullptr;
// Nothing else to do for this global.
continue;
}
}
if (!ShouldPromoteGlobal(GV))
continue;
// We need external aliases with a mangled name and hidden visibility.
auto *Alias = GlobalAlias::create(GlobalValue::ExternalLinkage,
GV.getName() + PromotionSuffix, &GV);
Alias->setVisibility(GlobalValue::HiddenVisibility);
// Update the Inner external reference that corresponds to the promoted
// Outer global (created just now as an alias in opt) to reference the new
// alias.
GlobalValue *UnoptGV = cast<GlobalValue>(VMap[&GV]);
UnoptGV->setName(Alias->getName());
UnoptGV->setVisibility(GlobalValue::HiddenVisibility);
assert(UnoptGV->getLinkage() == GlobalValue::ExternalLinkage &&
"Expected ExternalLinkage from CloneModule or inserted decl");
}
// Preserve functions that may be discarded after optimizing away call sites
// (e.g. ODR-weak). Another desirable effect of this is that it prevents
// GlobalOpt promoting the alias. If the function-preservation mechanism
// changes in the future GlobalOpt alias promotion must be handled another
// way.
appendToCompilerUsed(*M, GlobalsToPreserve);
return UnoptM;
}