Skip to content

Commit 07f9c1f

Browse files
committed
[NFC][KeyInstr] Add Atom Group (re)mapping
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.
1 parent d12e993 commit 07f9c1f

File tree

6 files changed

+187
-2
lines changed

6 files changed

+187
-2
lines changed

llvm/include/llvm/IR/ValueMap.h

+5
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 = DenseMap<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

+14-1
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,21 @@ 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+
/// Set \p MapAtoms to false to skip mapping source atoms for later remapping.
123+
/// Incorrectly setting false may harm the debugging experience. It's safe to
124+
/// set false if the cloned basic block is destined for a different function or
125+
/// if the original block is deleted. Setting true (default) is always safe
126+
/// (correct) but sometimes unecessary; this option reduces the compile-time
127+
/// impact of Key Instruction in such cases.
120128
BasicBlock *CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap,
121129
const Twine &NameSuffix = "", Function *F = nullptr,
122-
ClonedCodeInfo *CodeInfo = nullptr);
130+
ClonedCodeInfo *CodeInfo = nullptr,
131+
bool MapAtoms = true);
132+
133+
/// Mark a cloned instruction as a new instance so that its source loc can
134+
/// be updated when remapped.
135+
void mapAtomInstance(const DebugLoc &DL, ValueToValueMapTy &VMap);
123136

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

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

+10
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 atom instances. 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 for compiler performance reasons rather
113+
/// than correctness.
114+
RF_DoNotRemapAtoms = 16,
108115
};
109116

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

294+
/// Remap source atom. Called by RemapInstruction.
295+
void RemapSourceAtom(Instruction *I, ValueToValueMapTy &VM);
296+
287297
/// Remap the Values used in the DbgRecord \a DR using the value map \a
288298
/// VM.
289299
inline void RemapDbgRecord(Module *M, DbgRecord *DR, ValueToValueMapTy &VM,

llvm/lib/Transforms/Utils/CloneFunction.cpp

+28-1
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().incNextAtomGroup();
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

+26
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

+104
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)