Skip to content
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

[Coro] Amortize debug info processing cost in CoroSplit #109032

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
50 changes: 50 additions & 0 deletions llvm/include/llvm/Analysis/DebugInfoCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===- llvm/Analysis/DebugInfoCache.h - debug info cache --------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains an analysis that builds a cache of debug info for each
// DICompileUnit in a module.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_ANALYSIS_DEBUGINFOCACHE_H
#define LLVM_ANALYSIS_DEBUGINFOCACHE_H

#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/PassManager.h"

namespace llvm {

/// Processes and caches debug info for each DICompileUnit in a module.
///
/// The result of the analysis is a set of DebugInfoFinders primed on their
/// respective DICompileUnit. Such DebugInfoFinders can be used to speed up
/// function cloning which otherwise requires an expensive traversal of
/// DICompileUnit-level debug info. See an example usage in CoroSplit.
class DebugInfoCache {
artempyanykh marked this conversation as resolved.
Show resolved Hide resolved
public:
using DIFinderCache = SmallDenseMap<const DICompileUnit *, DebugInfoFinder>;
DIFinderCache Result;

DebugInfoCache(const Module &M);

bool invalidate(Module &, const PreservedAnalyses &,
ModuleAnalysisManager::Invalidator &);
};

class DebugInfoCacheAnalysis
: public AnalysisInfoMixin<DebugInfoCacheAnalysis> {
friend AnalysisInfoMixin<DebugInfoCacheAnalysis>;
static AnalysisKey Key;

public:
using Result = DebugInfoCache;
Result run(Module &M, ModuleAnalysisManager &);
};
} // namespace llvm

#endif
4 changes: 3 additions & 1 deletion llvm/include/llvm/IR/DebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,13 @@ class DebugInfoFinder {
/// Process subprogram.
void processSubprogram(DISubprogram *SP);

/// Process a compile unit.
void processCompileUnit(DICompileUnit *CU);

/// Clear all lists.
void reset();

private:
void processCompileUnit(DICompileUnit *CU);
void processScope(DIScope *Scope);
void processType(DIType *DT);
bool addCompileUnit(DICompileUnit *CU);
Expand Down
4 changes: 3 additions & 1 deletion llvm/include/llvm/IR/ValueMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ struct ValueMapConfig {
static mutex_type *getMutex(const ExtraDataT &/*Data*/) { return nullptr; }
};

/// This type stores Metadata. Used in ValueMap.
using MDMapT = DenseMap<const Metadata *, TrackingMDRef>;

/// See the file comment.
template<typename KeyT, typename ValueT, typename Config =ValueMapConfig<KeyT>>
class ValueMap {
friend class ValueMapCallbackVH<KeyT, ValueT, Config>;

using ValueMapCVH = ValueMapCallbackVH<KeyT, ValueT, Config>;
using MapT = DenseMap<ValueMapCVH, ValueT, DenseMapInfo<ValueMapCVH>>;
using MDMapT = DenseMap<const Metadata *, TrackingMDRef>;
using ExtraData = typename Config::ExtraData;

MapT Map;
Expand Down
4 changes: 1 addition & 3 deletions llvm/include/llvm/Linker/IRMover.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/IR/ValueMap.h"
#include <functional>

namespace llvm {
Expand Down Expand Up @@ -41,9 +42,6 @@ class IRMover {
static bool isEqual(const StructType *LHS, const StructType *RHS);
};

/// Type of the Metadata map in \a ValueToValueMapTy.
typedef DenseMap<const Metadata *, TrackingMDRef> MDMapT;

public:
class IdentifiedStructTypeSet {
// The set of opaque types is the composite module.
Expand Down
44 changes: 42 additions & 2 deletions llvm/include/llvm/Transforms/Utils/Cloning.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ struct ClonedCodeInfo {
/// parameter.
BasicBlock *CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap,
const Twine &NameSuffix = "", Function *F = nullptr,
ClonedCodeInfo *CodeInfo = nullptr,
DebugInfoFinder *DIFinder = nullptr);
ClonedCodeInfo *CodeInfo = nullptr);

/// Return a copy of the specified function and add it to that
/// function's module. Also, any references specified in the VMap are changed
Expand Down Expand Up @@ -175,6 +174,35 @@ void CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr);

void CloneFunctionAttributesInto(Function *NewFunc, const Function *OldFunc,
ValueToValueMapTy &VMap,
bool ModuleLevelChanges,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr);

/// Clone OldFunc's metadata into NewFunc.
///
/// The caller is expected to populate \p VMap beforehand and set an appropriate
/// \p RemapFlag.
///
/// NOTE: This function doesn't clone !llvm.dbg.cu when cloning into a different
/// module. Use CloneFunctionInto for that behavior.
void CloneFunctionMetadataInto(Function *NewFunc, const Function *OldFunc,
ValueToValueMapTy &VMap, RemapFlags RemapFlag,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr);

/// Clone OldFunc's body NewFunct.
void CloneFunctionBodyInto(Function *NewFunc, const Function *OldFunc,
ValueToValueMapTy &VMap, RemapFlags RemapFlag,
SmallVectorImpl<ReturnInst *> &Returns,
const char *NameSuffix = "",
ClonedCodeInfo *CodeInfo = nullptr,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr);

void CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
const Instruction *StartingInst,
ValueToValueMapTy &VMap, bool ModuleLevelChanges,
Expand All @@ -199,6 +227,18 @@ void CloneAndPruneFunctionInto(Function *NewFunc, const Function *OldFunc,
const char *NameSuffix = "",
ClonedCodeInfo *CodeInfo = nullptr);

/// Process debug information from function's subprogram attachment.
DISubprogram *ProcessSubprogramAttachment(const Function &F,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on "Process"? This is more about collecting debug info, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right. This function hydrates the passed DebugInfoFinder by visiting relevant components of the function's subprogram attachment. I'll update the name/comment when we get to extracting that commit in a separate PR.

Copy link
Contributor

@felipepiovezan felipepiovezan Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, now that the other two PRs are open & approved, let's proceed with this commit!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, yes! @felipepiovezan a silly question -- now that those PRs are approved, how/when do they get merged? Stacking unmerged PRs on top of each other doesn't have the best UX in Github.

Copy link
Contributor

@felipepiovezan felipepiovezan Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think the best option here is to merge the two PRs that are approved (#112976 and #112948) . Both of those stand on their own and don't depend on each other, so it's fine to merge in w/e order, right?

And then you can grab the third commit (or more, if the subsequent commits are also independent of each other) of this PR and open a new PR (or multiple PRs, if you did the () before), so it will look fine on top of main. My understanding is that the fourth commit depends on the third though.

Regarding what to do about this umbrella PR in the mean time, you have two options: leave this as is (with the understanding that it is not meant to be merged, merely serve as a reference for all the work in the proposal), or rebase on top of main, which will get rid of the two-already merged commits and force push to your branch artempyanykh:fast-coro-upstream, which will mean this PR serves a reference of the work that's left to be merged, also with the understand that we're not merging it.

With all that stack, we'd never have to stack unmerged PRs on top of each other, does that make sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, do you have commit access?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think the best option here is to merge the two PRs that are approved (#112976 and #112948) . Both of those stand on their own and don't depend on each other, so it's fine to merge in w/e order, right?

That's right, yes.

And then you can grab the third commit (or more, if the subsequent commits are also independent of each other) of this PR and open a new PR (or multiple PRs, if you did the () before), so it will look fine on top of main. My understanding is that the fourth commit depends on the third though.

After the first 2 are merged, extracting the third one in a separate PR is straightforward. With the rest of the stack there's quite a bit of sequencing, so we may need to rinse and repeat.

rebase on top of main, which will get rid of the two-already merged commits and force push to your branch artempyanykh:fast-coro-upstream, which will mean this PR serves a reference of the work that's left to be merged, also with the understand that we're not merging it

+1 to this. I was planning to keep rebasing this stack anyway to make sure things keep working end-to-end while the chunks are being extracted and merged.

By the way, do you have commit access?

No, not sure what the process for this is. Why?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay, the LLVM dev conference happened last week and we were mostly out.

No, not sure what the process for this is. Why?

Just wondering if you needed help merging the open PRs. I've merged the first two for you.
The process for getting commit access is described here: https://llvm.org/docs/DeveloperPolicy.html#obtaining-commit-access
If you don't want to do that, you can always let the reviewers know you need someone to press the merge button for you

CloneFunctionChangeType Changes,
DebugInfoFinder &DIFinder);

/// Based on \p Changes and \p DIFinder populate \p MD with debug info that
/// needs to be identity mapped during Metadata cloning.
void FindDebugInfoToIdentityMap(MetadataSetTy &MD,
CloneFunctionChangeType Changes,
DebugInfoFinder &DIFinder,
DISubprogram *SPClonedWithinModule);

/// This class captures the data input to the InlineFunction call, and records
/// the auxiliary results produced by it.
class InlineFunctionInfo {
Expand Down
67 changes: 49 additions & 18 deletions llvm/include/llvm/Transforms/Utils/ValueMapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define LLVM_TRANSFORMS_UTILS_VALUEMAPPER_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/simple_ilist.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/IR/ValueMap.h"
Expand All @@ -35,6 +36,7 @@ class Value;

using ValueToValueMapTy = ValueMap<const Value *, WeakTrackingVH>;
using DbgRecordIterator = simple_ilist<DbgRecord>::iterator;
using MetadataSetTy = SmallPtrSet<const Metadata *, 16>;

/// This is a class that can be implemented by clients to remap types when
/// cloning constants and instructions.
Expand Down Expand Up @@ -136,6 +138,18 @@ inline RemapFlags operator|(RemapFlags LHS, RemapFlags RHS) {
/// alternate \a ValueToValueMapTy and \a ValueMaterializer and returns a ID to
/// pass into the schedule*() functions.
///
/// NOTE: \c IdentityMD is used by CloneFunction* to directly specify metadata
/// that should be identity mapped (and hence not cloned). The metadata will be
/// identity mapped in \c VM on first use. There are several reasons for doing
/// it this way rather than eagerly identity mapping metadata nodes in \c VM:
/// 1. Mapping metadata is not cheap, particularly because of tracking.
/// 2. When cloning a Function we identity map lots of global module-level
/// metadata to avoid cloning it, while only a fraction of it is actually
/// used by the function. Mapping on first use is a lot faster for modules
/// with meaningful amount of debug info.
/// 3. Eagerly identity mapping metadata makes it harder to cache module-level
/// data (e.g. a set of metadata nodes in a \a DICompileUnit).
///
/// TODO: lib/Linker really doesn't need the \a ValueHandle in the \a
/// ValueToValueMapTy. We should template \a ValueMapper (and its
/// implementation classes), and explicitly instantiate on two concrete
Expand All @@ -152,7 +166,8 @@ class ValueMapper {
public:
ValueMapper(ValueToValueMapTy &VM, RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr);
ValueMapper(ValueMapper &&) = delete;
ValueMapper(const ValueMapper &) = delete;
ValueMapper &operator=(ValueMapper &&) = delete;
Expand Down Expand Up @@ -218,8 +233,10 @@ class ValueMapper {
inline Value *MapValue(const Value *V, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer).mapValue(*V);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.mapValue(*V);
}

/// Lookup or compute a mapping for a piece of metadata.
Expand All @@ -231,7 +248,9 @@ inline Value *MapValue(const Value *V, ValueToValueMapTy &VM,
/// \c MD.
/// 3. Else if \c MD is a \a ConstantAsMetadata, call \a MapValue() and
/// re-wrap its return (returning nullptr on nullptr).
/// 4. Else, \c MD is an \a MDNode. These are remapped, along with their
/// 4. Else if \c MD is in \c IdentityMD then add an identity mapping for it
/// and return it.
/// 5. Else, \c MD is an \a MDNode. These are remapped, along with their
/// transitive operands. Distinct nodes are duplicated or moved depending
/// on \a RF_MoveDistinctNodes. Uniqued nodes are remapped like constants.
///
Expand All @@ -240,16 +259,20 @@ inline Value *MapValue(const Value *V, ValueToValueMapTy &VM,
inline Metadata *MapMetadata(const Metadata *MD, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer).mapMetadata(*MD);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.mapMetadata(*MD);
}

/// Version of MapMetadata with type safety for MDNode.
inline MDNode *MapMetadata(const MDNode *MD, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer).mapMDNode(*MD);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.mapMDNode(*MD);
}

/// Convert the instruction operands from referencing the current values into
Expand All @@ -263,17 +286,21 @@ inline MDNode *MapMetadata(const MDNode *MD, ValueToValueMapTy &VM,
inline void RemapInstruction(Instruction *I, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer).remapInstruction(*I);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.remapInstruction(*I);
}

/// Remap the Values used in the DbgRecord \a DR using the value map \a
/// VM.
inline void RemapDbgRecord(Module *M, DbgRecord *DR, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer).remapDbgRecord(M, *DR);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.remapDbgRecord(M, *DR);
}

/// Remap the Values used in the DbgRecords \a Range using the value map \a
Expand All @@ -283,8 +310,9 @@ inline void RemapDbgRecordRange(Module *M,
ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer)
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.remapDbgRecordRange(M, Range);
}

Expand All @@ -297,16 +325,19 @@ inline void RemapDbgRecordRange(Module *M,
inline void RemapFunction(Function &F, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer).remapFunction(F);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD).remapFunction(F);
}

/// Version of MapValue with type safety for Constant.
inline Constant *MapValue(const Constant *V, ValueToValueMapTy &VM,
RemapFlags Flags = RF_None,
ValueMapTypeRemapper *TypeMapper = nullptr,
ValueMaterializer *Materializer = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer).mapConstant(*V);
ValueMaterializer *Materializer = nullptr,
const MetadataSetTy *IdentityMD = nullptr) {
return ValueMapper(VM, Flags, TypeMapper, Materializer, IdentityMD)
.mapConstant(*V);
}

} // end namespace llvm
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/Analysis/CGSCCPassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Analysis/DebugInfoCache.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/InstIterator.h"
Expand Down Expand Up @@ -141,6 +142,11 @@ ModuleToPostOrderCGSCCPassAdaptor::run(Module &M, ModuleAnalysisManager &AM) {
// Get the call graph for this module.
LazyCallGraph &CG = AM.getResult<LazyCallGraphAnalysis>(M);

// Prime DebugInfoCache.
// TODO: Currently, the only user is CoroSplitPass. Consider running
// conditionally.
AM.getResult<DebugInfoCacheAnalysis>(M);

// Get Function analysis manager from its proxy.
FunctionAnalysisManager &FAM =
AM.getCachedResult<FunctionAnalysisManagerModuleProxy>(M)->getManager();
Expand Down Expand Up @@ -352,6 +358,7 @@ ModuleToPostOrderCGSCCPassAdaptor::run(Module &M, ModuleAnalysisManager &AM) {
// analysis proxies by handling them above and in any nested pass managers.
PA.preserveSet<AllAnalysesOn<LazyCallGraph::SCC>>();
PA.preserve<LazyCallGraphAnalysis>();
PA.preserve<DebugInfoCacheAnalysis>();
PA.preserve<CGSCCAnalysisManagerModuleProxy>();
PA.preserve<FunctionAnalysisManagerModuleProxy>();
return PA;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ add_llvm_component_library(LLVMAnalysis
DDGPrinter.cpp
ConstraintSystem.cpp
Delinearization.cpp
DebugInfoCache.cpp
DemandedBits.cpp
DependenceAnalysis.cpp
DependenceGraphBuilder.cpp
Expand Down
Loading