Skip to content

Commit 3b4f9c5

Browse files
authored
[NFC][KeyInstr] Add Atom Group (re)mapping (#133479)
Add: mapAtomInstance - map the atom group number to a new group. RemapSourceAtom - apply the mapped atom group number to this instruction. Modify: CloneBasicBlock - Call mapAtomInstance on cloned instruction's DebugLocs if MapAtoms is true (default). Setting to false could lead to a degraded debugging experience. See code comment. Optimisations like loop unroll that duplicate instructions need to remap source atom groups so that each duplicated source construct instance is considered distinct when determining is_stmt locations. This commit adds the remapping functionality and a unittest. RFC: https://discourse.llvm.org/t/rfc-improving-is-stmt-placement-for-better-interactive-debugging/82668
1 parent bde39d7 commit 3b4f9c5

File tree

6 files changed

+191
-2
lines changed

6 files changed

+191
-2
lines changed

llvm/include/llvm/IR/ValueMap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class ValueMap {
8787
using ValueMapCVH = ValueMapCallbackVH<KeyT, ValueT, Config>;
8888
using MapT = DenseMap<ValueMapCVH, ValueT, DenseMapInfo<ValueMapCVH>>;
8989
using MDMapT = DenseMap<const Metadata *, TrackingMDRef>;
90+
/// Map {(InlinedAt, old atom number) -> new atom number}.
91+
using DMAtomT = SmallDenseMap<std::pair<Metadata *, uint64_t>, uint64_t>;
9092
using ExtraData = typename Config::ExtraData;
9193

9294
MapT Map;
@@ -117,6 +119,8 @@ class ValueMap {
117119
return *MDMap;
118120
}
119121
std::optional<MDMapT> &getMDMap() { return MDMap; }
122+
/// Map {(InlinedAt, old atom number) -> new atom number}.
123+
DMAtomT AtomMap;
120124

121125
/// Get the mapped metadata, if it's in the map.
122126
std::optional<Metadata *> getMappedMD(const Metadata *MD) const {
@@ -145,6 +149,7 @@ class ValueMap {
145149
void clear() {
146150
Map.clear();
147151
MDMap.reset();
152+
AtomMap.clear();
148153
}
149154

150155
/// Return 1 if the specified key is in the map, 0 otherwise.

llvm/include/llvm/Transforms/Utils/Cloning.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/Analysis/AssumptionCache.h"
2323
#include "llvm/Analysis/InlineCost.h"
2424
#include "llvm/IR/BasicBlock.h"
25+
#include "llvm/IR/DebugLoc.h"
2526
#include "llvm/IR/ValueHandle.h"
2627
#include "llvm/Transforms/Utils/ValueMapper.h"
2728
#include <functional>
@@ -117,9 +118,22 @@ struct ClonedCodeInfo {
117118
/// If you would like to collect additional information about the cloned
118119
/// function, you can specify a ClonedCodeInfo object with the optional fifth
119120
/// parameter.
121+
///
122+
/// \p MapAtoms indicates whether source location atoms should be mapped for
123+
/// later remapping. Must be true when you duplicate a code path and a source
124+
/// location is intended to appear twice in the generated instructions. Can be
125+
/// set to false if you are transplanting code from one place to another.
126+
/// Setting true (default) is always safe (won't produce incorrect debug info)
127+
/// but is sometimes unnecessary, causing extra work that could be avoided by
128+
/// setting the parameter to false.
120129
BasicBlock *CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap,
121130
const Twine &NameSuffix = "", Function *F = nullptr,
122-
ClonedCodeInfo *CodeInfo = nullptr);
131+
ClonedCodeInfo *CodeInfo = nullptr,
132+
bool MapAtoms = true);
133+
134+
/// Mark a cloned instruction as a new instance so that its source loc can
135+
/// be updated when remapped.
136+
void mapAtomInstance(const DebugLoc &DL, ValueToValueMapTy &VMap);
123137

124138
/// Return a copy of the specified function and add it to that
125139
/// function's module. Also, any references specified in the VMap are changed

llvm/include/llvm/Transforms/Utils/ValueMapper.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ enum RemapFlags {
105105
/// Any global values not in value map are mapped to null instead of mapping
106106
/// to self. Illegal if RF_IgnoreMissingLocals is also set.
107107
RF_NullMapMissingGlobalValues = 8,
108+
109+
/// Do not remap source location atoms. Only safe if to do this if the cloned
110+
/// instructions being remapped are inserted into a new function, or an
111+
/// existing function where the inlined-at fields are updated. If in doubt,
112+
/// don't use this flag. It's used when remapping is known to be un-necessary
113+
/// to save some compile-time.
114+
RF_DoNotRemapAtoms = 16,
108115
};
109116

110117
inline RemapFlags operator|(RemapFlags LHS, RemapFlags RHS) {
@@ -284,6 +291,12 @@ inline void RemapInstruction(Instruction *I, ValueToValueMapTy &VM,
284291
.remapInstruction(*I);
285292
}
286293

294+
/// Remap source location atom. Called by RemapInstruction. This updates the
295+
/// instruction's atom group number if it has been mapped (e.g. with
296+
/// llvm::mapAtomInstance), which is necessary to distinguish source code
297+
/// atoms on duplicated code paths.
298+
void RemapSourceAtom(Instruction *I, ValueToValueMapTy &VM);
299+
287300
/// Remap the Values used in the DbgRecord \a DR using the value map \a
288301
/// VM.
289302
inline void RemapDbgRecord(Module *M, DbgRecord *DR, ValueToValueMapTy &VM,

llvm/lib/Transforms/Utils/CloneFunction.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
#include "llvm/ADT/SmallVector.h"
16+
#include "llvm/ADT/Statistic.h"
1617
#include "llvm/Analysis/ConstantFolding.h"
1718
#include "llvm/Analysis/DomTreeUpdater.h"
1819
#include "llvm/Analysis/InstructionSimplify.h"
@@ -40,6 +41,27 @@ using namespace llvm;
4041

4142
#define DEBUG_TYPE "clone-function"
4243

44+
STATISTIC(RemappedAtomMax, "Highest global NextAtomGroup (after mapping)");
45+
46+
void llvm::mapAtomInstance(const DebugLoc &DL, ValueToValueMapTy &VMap) {
47+
auto CurGroup = DL.get()->getAtomGroup();
48+
if (!CurGroup)
49+
return;
50+
51+
// Try inserting a new entry. If there's already a mapping for this atom
52+
// then there's nothing to do.
53+
auto [It, Inserted] = VMap.AtomMap.insert({{DL.getInlinedAt(), CurGroup}, 0});
54+
if (!Inserted)
55+
return;
56+
57+
// Map entry to a new atom group.
58+
uint64_t NewGroup = DL->getContext().incNextDILocationAtomGroup();
59+
assert(NewGroup > CurGroup && "Next should always be greater than current");
60+
It->second = NewGroup;
61+
62+
RemappedAtomMax = std::max<uint64_t>(NewGroup, RemappedAtomMax);
63+
}
64+
4365
namespace {
4466
void collectDebugInfoFromInstructions(const Function &F,
4567
DebugInfoFinder &DIFinder) {
@@ -79,7 +101,7 @@ MetadataPredicate createIdentityMDPredicate(const Function &F,
79101
/// See comments in Cloning.h.
80102
BasicBlock *llvm::CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap,
81103
const Twine &NameSuffix, Function *F,
82-
ClonedCodeInfo *CodeInfo) {
104+
ClonedCodeInfo *CodeInfo, bool MapAtoms) {
83105
BasicBlock *NewBB = BasicBlock::Create(BB->getContext(), "", F);
84106
NewBB->IsNewDbgInfoFormat = BB->IsNewDbgInfoFormat;
85107
if (BB->hasName())
@@ -98,6 +120,11 @@ BasicBlock *llvm::CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap,
98120

99121
VMap[&I] = NewInst; // Add instruction map to value.
100122

123+
if (MapAtoms) {
124+
if (const DebugLoc &DL = NewInst->getDebugLoc())
125+
mapAtomInstance(DL.get(), VMap);
126+
}
127+
101128
if (isa<CallInst>(I) && !I.isDebugOrPseudoInst()) {
102129
hasCalls = true;
103130
hasMemProfMetadata |= I.hasMetadata(LLVMContext::MD_memprof);

llvm/lib/Transforms/Utils/ValueMapper.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "llvm/IR/Type.h"
3838
#include "llvm/IR/Value.h"
3939
#include "llvm/Support/Casting.h"
40+
#include "llvm/Support/CommandLine.h"
4041
#include "llvm/Support/Debug.h"
4142
#include <cassert>
4243
#include <limits>
@@ -1010,6 +1011,10 @@ void Mapper::remapInstruction(Instruction *I) {
10101011
I->setMetadata(MI.first, New);
10111012
}
10121013

1014+
// Remap source location atom instance.
1015+
if (!(Flags & RF_DoNotRemapAtoms))
1016+
RemapSourceAtom(I, getVM());
1017+
10131018
if (!TypeMapper)
10141019
return;
10151020

@@ -1292,3 +1297,24 @@ void ValueMapper::scheduleMapGlobalIFunc(GlobalIFunc &GI, Constant &Resolver,
12921297
void ValueMapper::scheduleRemapFunction(Function &F, unsigned MCID) {
12931298
getAsMapper(pImpl)->scheduleRemapFunction(F, MCID);
12941299
}
1300+
1301+
void llvm::RemapSourceAtom(Instruction *I, ValueToValueMapTy &VM) {
1302+
const DebugLoc &DL = I->getDebugLoc();
1303+
if (!DL)
1304+
return;
1305+
1306+
auto AtomGroup = DL->getAtomGroup();
1307+
if (!AtomGroup)
1308+
return;
1309+
1310+
auto R = VM.AtomMap.find({DL->getInlinedAt(), AtomGroup});
1311+
if (R == VM.AtomMap.end())
1312+
return;
1313+
AtomGroup = R->second;
1314+
1315+
// Remap the atom group and copy all other fields.
1316+
DILocation *New = DILocation::get(
1317+
I->getContext(), DL.getLine(), DL.getCol(), DL.getScope(),
1318+
DL.getInlinedAt(), DL.isImplicitCode(), AtomGroup, DL->getAtomRank());
1319+
I->setDebugLoc(New);
1320+
}

llvm/unittests/Transforms/Utils/CloningTest.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,21 @@
1414
#include "llvm/Analysis/LoopInfo.h"
1515
#include "llvm/AsmParser/Parser.h"
1616
#include "llvm/IR/Argument.h"
17+
#include "llvm/IR/BasicBlock.h"
1718
#include "llvm/IR/Constant.h"
1819
#include "llvm/IR/DIBuilder.h"
1920
#include "llvm/IR/DebugInfo.h"
2021
#include "llvm/IR/Function.h"
2122
#include "llvm/IR/IRBuilder.h"
2223
#include "llvm/IR/InstIterator.h"
24+
#include "llvm/IR/Instruction.h"
2325
#include "llvm/IR/Instructions.h"
2426
#include "llvm/IR/IntrinsicInst.h"
2527
#include "llvm/IR/LLVMContext.h"
2628
#include "llvm/IR/Module.h"
2729
#include "llvm/IR/Verifier.h"
2830
#include "llvm/Support/SourceMgr.h"
31+
#include "llvm/Transforms/Utils/ValueMapper.h"
2932
#include "gtest/gtest.h"
3033

3134
using namespace llvm;
@@ -1162,4 +1165,105 @@ declare i64 @foo(i32 noundef) local_unnamed_addr
11621165
auto NewM = llvm::CloneModule(*M);
11631166
EXPECT_FALSE(verifyModule(*NewM, &errs()));
11641167
}
1168+
1169+
TEST_F(CloneInstruction, cloneKeyInstructions) {
1170+
LLVMContext Context;
1171+
1172+
std::unique_ptr<Module> M = parseIR(Context, R"(
1173+
define void @test(ptr align 4 %dst) !dbg !3 {
1174+
store i64 1, ptr %dst, align 4, !dbg !6
1175+
store i64 2, ptr %dst, align 4, !dbg !7
1176+
store i64 3, ptr %dst, align 4, !dbg !8
1177+
ret void, !dbg !9
1178+
}
1179+
1180+
!llvm.dbg.cu = !{!0}
1181+
!llvm.module.flags = !{!2}
1182+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1)
1183+
!1 = !DIFile(filename: "test.cpp", directory: "")
1184+
!2 = !{i32 1, !"Debug Info Version", i32 3}
1185+
!3 = distinct !DISubprogram(name: "test", scope: !0, unit: !0)
1186+
!4 = distinct !DISubprogram(name: "inlined", scope: !0, unit: !0, retainedNodes: !{!5})
1187+
!5 = !DILocalVariable(name: "awaitables", scope: !4)
1188+
!6 = !DILocation(line: 1, scope: !4, inlinedAt: !8, atomGroup: 1, atomRank: 1)
1189+
!7 = !DILocation(line: 2, scope: !3, atomGroup: 1, atomRank: 1)
1190+
!8 = !DILocation(line: 3, scope: !3, atomGroup: 1, atomRank: 1)
1191+
!9 = !DILocation(line: 4, scope: !3, atomGroup: 2, atomRank: 1)
1192+
)");
1193+
1194+
ASSERT_FALSE(verifyModule(*M, &errs()));
1195+
1196+
#ifdef EXPERIMENTAL_KEY_INSTRUCTIONS
1197+
#define EXPECT_ATOM(Inst, G) \
1198+
EXPECT_TRUE(Inst->getDebugLoc()); \
1199+
EXPECT_EQ(Inst->getDebugLoc()->getAtomGroup(), uint64_t(G));
1200+
#else
1201+
#define EXPECT_ATOM(Inst, G) (void)Inst;
1202+
#endif
1203+
1204+
Function *F = M->getFunction("test");
1205+
BasicBlock *BB = &*F->begin();
1206+
ASSERT_TRUE(F);
1207+
Instruction *Store1 = &*BB->begin();
1208+
Instruction *Store2 = Store1->getNextNode();
1209+
Instruction *Store3 = Store2->getNextNode();
1210+
Instruction *Ret = Store3->getNextNode();
1211+
1212+
// Test the remapping works as expected outside of cloning.
1213+
ValueToValueMapTy VM;
1214+
// Store1 and Store2 have the same atomGroup number, but have different
1215+
// inlining scopes, so only Store1 should change group.
1216+
mapAtomInstance(Store1->getDebugLoc(), VM);
1217+
for (Instruction &I : *BB)
1218+
RemapSourceAtom(&I, VM);
1219+
EXPECT_ATOM(Store1, 3);
1220+
EXPECT_ATOM(Store2, 1);
1221+
EXPECT_ATOM(Store3, 1);
1222+
EXPECT_ATOM(Ret, 2);
1223+
VM.clear();
1224+
1225+
// Store2 and Store3 have the same group number; both should get remapped.
1226+
mapAtomInstance(Store2->getDebugLoc(), VM);
1227+
for (Instruction &I : *BB)
1228+
RemapSourceAtom(&I, VM);
1229+
EXPECT_ATOM(Store1, 3);
1230+
EXPECT_ATOM(Store2, 4);
1231+
EXPECT_ATOM(Store3, 4);
1232+
EXPECT_ATOM(Ret, 2);
1233+
VM.clear();
1234+
1235+
// Cloning BB with MapAtoms=false should clone the atom numbers.
1236+
BasicBlock *BB2 =
1237+
CloneBasicBlock(BB, VM, "", nullptr, nullptr, /*MapAtoms*/ false);
1238+
for (Instruction &I : *BB2)
1239+
RemapSourceAtom(&I, VM);
1240+
Store1 = &*BB2->begin();
1241+
Store2 = Store1->getNextNode();
1242+
Store3 = Store2->getNextNode();
1243+
Ret = Store3->getNextNode();
1244+
EXPECT_ATOM(Store1, 3);
1245+
EXPECT_ATOM(Store2, 4);
1246+
EXPECT_ATOM(Store3, 4);
1247+
EXPECT_ATOM(Ret, 2);
1248+
VM.clear();
1249+
delete BB2;
1250+
1251+
// Cloning BB with MapAtoms=true should map the atom numbers.
1252+
BasicBlock *BB3 =
1253+
CloneBasicBlock(BB, VM, "", nullptr, nullptr, /*MapAtoms*/ true);
1254+
for (Instruction &I : *BB3)
1255+
RemapSourceAtom(&I, VM);
1256+
Store1 = &*BB3->begin();
1257+
Store2 = Store1->getNextNode();
1258+
Store3 = Store2->getNextNode();
1259+
Ret = Store3->getNextNode();
1260+
EXPECT_ATOM(Store1, 5);
1261+
EXPECT_ATOM(Store2, 6);
1262+
EXPECT_ATOM(Store3, 6);
1263+
EXPECT_ATOM(Ret, 7);
1264+
VM.clear();
1265+
delete BB3;
1266+
#undef EXPECT_ATOM
1267+
}
1268+
11651269
} // namespace

0 commit comments

Comments
 (0)