Skip to content

Commit 2acbfa1

Browse files
bors[bot]ptersilie
andauthored
57: Enable stackmap support for tracking multiple locations. r=ltratt a=ptersilie Co-authored-by: Lukas Diekmann <lukas.diekmann@gmail.com>
2 parents b2c5bcf + b8e6716 commit 2acbfa1

File tree

7 files changed

+168
-14
lines changed

7 files changed

+168
-14
lines changed

llvm/include/llvm/CodeGen/StackMaps.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class StackMaps {
285285
/// Generate a stackmap record for a stackmap instruction.
286286
///
287287
/// MI must be a raw STACKMAP, not a PATCHPOINT.
288-
void recordStackMap(const MCSymbol &L, const MachineInstr &MI);
288+
void recordStackMap(const MCSymbol &L, const MachineInstr &MI, std::map<Register, int64_t> SpillsOffsets = {});
289289

290290
/// Generate a stackmap record for a patchpoint instruction.
291291
void recordPatchPoint(const MCSymbol &L, const MachineInstr &MI);
@@ -316,7 +316,8 @@ class StackMaps {
316316
MachineInstr::const_mop_iterator
317317
parseOperand(MachineInstr::const_mop_iterator MOI,
318318
MachineInstr::const_mop_iterator MOE, LiveVarsVec &LiveVars,
319-
LiveOutVec &LiveOuts) const;
319+
LiveOutVec &LiveOuts,
320+
std::map<Register, int64_t> SpillOffsets = {}) const;
320321

321322
/// Specialized parser of statepoint operands.
322323
/// They do not directly correspond to StackMap record entries.
@@ -343,6 +344,7 @@ class StackMaps {
343344
void recordStackMapOpers(const MCSymbol &L, const MachineInstr &MI,
344345
uint64_t ID, MachineInstr::const_mop_iterator MOI,
345346
MachineInstr::const_mop_iterator MOE,
347+
std::map<Register, int64_t> SpillOffsets = {},
346348
bool recordResult = false);
347349

348350
/// Emit the stackmap header.

llvm/lib/CodeGen/StackMaps.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/CodeGen/TargetFrameLowering.h"
1919
#include "llvm/CodeGen/TargetOpcodes.h"
2020
#include "llvm/CodeGen/TargetRegisterInfo.h"
21+
#include "llvm/CodeGen/TargetInstrInfo.h"
2122
#include "llvm/CodeGen/TargetSubtargetInfo.h"
2223
#include "llvm/IR/DataLayout.h"
2324
#include "llvm/MC/MCContext.h"
@@ -219,7 +220,8 @@ static unsigned getDwarfRegNum(unsigned Reg, const TargetRegisterInfo *TRI) {
219220
MachineInstr::const_mop_iterator
220221
StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
221222
MachineInstr::const_mop_iterator MOE,
222-
LiveVarsVec &LiveVars, LiveOutVec &LiveOuts) const {
223+
LiveVarsVec &LiveVars, LiveOutVec &LiveOuts,
224+
std::map<Register, int64_t> SpillOffsets) const {
223225
LocationVec &Locs = LiveVars.back();
224226
const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo();
225227
if (MOI->isImm()) {
@@ -282,7 +284,27 @@ StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
282284
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(MOI->getReg());
283285
assert(!MOI->getSubReg() && "Physical subreg still around.");
284286

285-
unsigned Offset = 0;
287+
288+
signed Offset = 0;
289+
// Check if there are any mappings for this register in the spillmap. If so,
290+
// encode the additional location in the offset field of the stackmap record
291+
// (which is unused for register locations). Note that this assumes that
292+
// there can only be one additional location for each value, which may turn
293+
// out to be false.
294+
if (MOI->isReg()) {
295+
Register R = MOI->getReg();
296+
if (SpillOffsets.count(R) > 0) {
297+
Offset = SpillOffsets[R];
298+
assert(SpillOffsets[R] != 0);
299+
if (Offset > 0) {
300+
// If the additional location is another register encode its DWARF id.
301+
// Also temporarily add 1 since 0 is used to mean there is no
302+
// additional location.
303+
Offset = getDwarfRegNum(Offset, TRI) + 1;
304+
}
305+
}
306+
}
307+
286308
unsigned DwarfRegNum = getDwarfRegNum(MOI->getReg(), TRI);
287309
unsigned LLVMRegNum = *TRI->getLLVMRegNum(DwarfRegNum, false);
288310
unsigned SubRegIdx = TRI->getSubRegIndex(LLVMRegNum, MOI->getReg());
@@ -520,6 +542,7 @@ void StackMaps::recordStackMapOpers(const MCSymbol &MILabel,
520542
const MachineInstr &MI, uint64_t ID,
521543
MachineInstr::const_mop_iterator MOI,
522544
MachineInstr::const_mop_iterator MOE,
545+
std::map<Register, int64_t> SpillOffsets,
523546
bool recordResult) {
524547
MCContext &OutContext = AP.OutStreamer->getContext();
525548

@@ -529,7 +552,7 @@ void StackMaps::recordStackMapOpers(const MCSymbol &MILabel,
529552
if (recordResult) {
530553
assert(PatchPointOpers(&MI).hasDef() && "Stackmap has no return value.");
531554
parseOperand(MI.operands_begin(), std::next(MI.operands_begin()), LiveVars,
532-
LiveOuts);
555+
LiveOuts, SpillOffsets);
533556
LiveVars.push_back(LocationVec());
534557
}
535558

@@ -538,7 +561,7 @@ void StackMaps::recordStackMapOpers(const MCSymbol &MILabel,
538561
parseStatepointOpers(MI, MOI, MOE, LiveVars, LiveOuts);
539562
else
540563
while (MOI != MOE)
541-
MOI = parseOperand(MOI, MOE, LiveVars, LiveOuts);
564+
MOI = parseOperand(MOI, MOE, LiveVars, LiveOuts, SpillOffsets);
542565

543566
// Move large constants into the constant pool.
544567
for (auto &Locations : LiveVars) {
@@ -611,14 +634,14 @@ void StackMaps::recordStackMapOpers(const MCSymbol &MILabel,
611634
}
612635
}
613636

614-
void StackMaps::recordStackMap(const MCSymbol &L, const MachineInstr &MI) {
637+
void StackMaps::recordStackMap(const MCSymbol &L, const MachineInstr &MI, std::map<Register, int64_t> SpillOffsets) {
615638
assert(MI.getOpcode() == TargetOpcode::STACKMAP && "expected stackmap");
616639

617640
StackMapOpers opers(&MI);
618641
const int64_t ID = MI.getOperand(PatchPointOpers::IDPos).getImm();
619642
recordStackMapOpers(L, MI, ID,
620643
std::next(MI.operands_begin(), opers.getVarIdx()),
621-
MI.operands_end());
644+
MI.operands_end(), SpillOffsets);
622645
}
623646

624647
void StackMaps::recordPatchPoint(const MCSymbol &L, const MachineInstr &MI) {
@@ -627,7 +650,7 @@ void StackMaps::recordPatchPoint(const MCSymbol &L, const MachineInstr &MI) {
627650
PatchPointOpers opers(&MI);
628651
const int64_t ID = opers.getID();
629652
auto MOI = std::next(MI.operands_begin(), opers.getStackMapStartIdx());
630-
recordStackMapOpers(L, MI, ID, MOI, MI.operands_end(),
653+
recordStackMapOpers(L, MI, ID, MOI, MI.operands_end(), {},
631654
opers.isAnyReg() && opers.hasDef());
632655

633656
#ifndef NDEBUG
@@ -648,7 +671,7 @@ void StackMaps::recordStatepoint(const MCSymbol &L, const MachineInstr &MI) {
648671
StatepointOpers opers(&MI);
649672
const unsigned StartIdx = opers.getVarIdx();
650673
recordStackMapOpers(L, MI, opers.getID(), MI.operands_begin() + StartIdx,
651-
MI.operands_end(), false);
674+
MI.operands_end(), {}, false);
652675
}
653676

654677
/// Emit the stackmap header.

llvm/lib/Support/Yk.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ static cl::opt<bool, true> YkStackMapOffsetFixParser(
2525
cl::desc("Apply a fix to stackmaps that corrects the reported instruction "
2626
"offset in the presence of calls."),
2727
cl::NotHidden, cl::location(YkStackMapOffsetFix));
28+
29+
bool YkStackMapAdditionalLocs;
30+
static cl::opt<bool, true> YkStackMapAdditionalLocsParser(
31+
"yk-stackmap-add-locs",
32+
cl::desc("Encode additional locations for registers into stackmaps."),
33+
cl::NotHidden, cl::location(YkStackMapAdditionalLocs));

llvm/lib/Target/X86/X86AsmPrinter.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "llvm/Target/TargetMachine.h"
4747

4848
using namespace llvm;
49+
extern bool YkStackMapAdditionalLocs;
4950

5051
X86AsmPrinter::X86AsmPrinter(TargetMachine &TM,
5152
std::unique_ptr<MCStreamer> Streamer)
@@ -55,6 +56,116 @@ X86AsmPrinter::X86AsmPrinter(TargetMachine &TM,
5556
// Primitive Helper Functions.
5657
//===----------------------------------------------------------------------===//
5758

59+
const TargetInstrInfo *TII;
60+
61+
/// Clear any mappings that map to the given register.
62+
void clearRhs(Register Reg, std::map<Register, int64_t> &SpillMap) {
63+
auto I = SpillMap.begin();
64+
while (I != SpillMap.end()) {
65+
if (I->second == Reg) {
66+
I = SpillMap.erase(I);
67+
} else {
68+
++I;
69+
}
70+
}
71+
}
72+
73+
/// Given a MachineBasicBlock, analyse its instructions to build a mapping of
74+
/// where duplicate values live. This can be either in another register or on
75+
/// the stack. Since registers are always positive and stack offsets negative,
76+
/// we can store both in the same map while still being able to distinguish
77+
/// them. This allows us to keep the changes in Stackmaps.cpp to a minimum and
78+
/// avoids having to alter the stackmap format.
79+
void processInstructions(
80+
const MachineBasicBlock *MBB,
81+
std::map<Register, int64_t> &SpillMap,
82+
std::map<const MachineInstr *, std::map<Register, int64_t>> &StackmapSpillMaps
83+
) {
84+
for (const MachineInstr &Instr : MBB->instrs()) {
85+
// At each stackmap call, save the current mapping so it can later be
86+
// encoded in the stackmap when it is lowered.
87+
if (Instr.getOpcode() == TargetOpcode::STACKMAP) {
88+
StackmapSpillMaps[&Instr] = SpillMap;
89+
continue;
90+
}
91+
92+
// Copying a value from one register B to another A, creates a mapping from
93+
// A to B. If A is tracked by the stackmap, then B will also be tracked and
94+
// assigned the same value during deoptimisation.
95+
if (Instr.isMoveReg()) {
96+
const MachineOperand Lhs = Instr.getOperand(0);
97+
const MachineOperand Rhs = Instr.getOperand(1);
98+
assert(Lhs.isReg() && "Is register.");
99+
assert(Rhs.isReg() && "Is register.");
100+
SpillMap[Lhs.getReg()] = Rhs.getReg();
101+
// Reassigning a new value to Lhs means any mappings to Lhs are now void
102+
// and need to be removed.
103+
clearRhs(Lhs.getReg(), SpillMap);
104+
continue;
105+
}
106+
107+
int FI;
108+
// A value from a register was spilled to the stack. Create a mapping from
109+
// that register to the stack offset.
110+
if (TII->isStoreToStackSlotPostFE(Instr, FI)) {
111+
const MachineOperand OffsetOp = Instr.getOperand(3);
112+
const MachineOperand MO = Instr.getOperand(5);
113+
assert(MO.isReg() && "Is register.");
114+
const Register Reg = MO.getReg();
115+
if (OffsetOp.isImm()) {
116+
const int64_t Offset = OffsetOp.getImm();
117+
SpillMap[Reg] = Offset;
118+
clearRhs(Reg, SpillMap);
119+
}
120+
continue;
121+
}
122+
123+
// A value from the stack was loaded back into a register. Create a mapping from
124+
// that register to the stack offset.
125+
if (TII->isLoadFromStackSlotPostFE(Instr, FI)) {
126+
const MachineOperand OffsetOp = Instr.getOperand(4);
127+
const MachineOperand Lhs = Instr.getOperand(0);
128+
assert(Lhs.isReg() && "Is register.");
129+
const Register Reg = Lhs.getReg();
130+
if (OffsetOp.isImm()) {
131+
const int64_t Offset = OffsetOp.getImm();
132+
SpillMap[Reg] = Offset;
133+
clearRhs(Reg, SpillMap);
134+
}
135+
continue;
136+
}
137+
138+
// Any other assignments to tracked registers removes their mapping.
139+
for (const MachineOperand MO : Instr.defs()) {
140+
assert(MO.isReg() && "Is register.");
141+
SpillMap.erase(MO.getReg());
142+
clearRhs(MO.getReg(), SpillMap);
143+
}
144+
}
145+
}
146+
147+
/// Walk through the control flow of a function starting at the entry block
148+
/// using depth first search. This makes sure the mappings are are generated in
149+
/// the correct order.
150+
/// YKFIXME: Can be updated to use an iterative approach.
151+
void findSpillLocations(
152+
const MachineBasicBlock *MBB,
153+
std::set<const MachineBasicBlock *> &Seen,
154+
std::map<Register, int64_t> SpillMap,
155+
std::map<const MachineInstr *, std::map<Register, int64_t>> &StackmapSpillMaps
156+
) {
157+
158+
// Block has already been processed.
159+
if (Seen.count(MBB) > 0) {
160+
return;
161+
}
162+
Seen.insert(MBB);
163+
processInstructions(MBB, SpillMap, StackmapSpillMaps);
164+
for (MachineBasicBlock *Succ : MBB->successors()) {
165+
findSpillLocations(Succ, Seen, SpillMap, StackmapSpillMaps);
166+
}
167+
}
168+
58169
/// runOnMachineFunction - Emit the function body.
59170
///
60171
bool X86AsmPrinter::runOnMachineFunction(MachineFunction &MF) {
@@ -82,6 +193,15 @@ bool X86AsmPrinter::runOnMachineFunction(MachineFunction &MF) {
82193
OutStreamer->endCOFFSymbolDef();
83194
}
84195

196+
// Analyse control flow to find out the location of register spills to the
197+
// stack or to other registers, so we can also encode those in stackmaps.
198+
TII = MF.getSubtarget().getInstrInfo();
199+
std::set<const MachineBasicBlock *> S;
200+
std::map<Register, int64_t> SpillMap;
201+
if (YkStackMapAdditionalLocs) {
202+
findSpillLocations(&*MF.begin(), S, SpillMap, StackmapSpillMaps);
203+
}
204+
85205
// Emit the rest of the function body.
86206
emitFunctionBody();
87207

llvm/lib/Target/X86/X86AsmPrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
7171
};
7272

7373
StackMapShadowTracker SMShadowTracker;
74+
// Map holding information about register spills.
75+
std::map<const MachineInstr *, std::map<Register, int64_t>> StackmapSpillMaps;
7476

7577
// All instructions emitted by the X86AsmPrinter should use this helper
7678
// method.

llvm/lib/Target/X86/X86MCInstLower.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1502,7 +1502,7 @@ void X86AsmPrinter::LowerSTACKMAP(const MachineInstr &MI) {
15021502
MILabel = Ctx.createTempSymbol();
15031503
OutStreamer->emitLabel(MILabel);
15041504
}
1505-
SM.recordStackMap(*MILabel, MI);
1505+
SM.recordStackMap(*MILabel, MI, StackmapSpillMaps[&MI]);
15061506
unsigned NumShadowBytes = MI.getOperand(1).getImm();
15071507
SMShadowTracker.reset(NumShadowBytes);
15081508
}

llvm/lib/Transforms/Yk/StackMaps.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ class YkStackmaps : public ModulePass {
5656
CallInst &CI = cast<CallInst>(I);
5757
if (CI.isInlineAsm())
5858
continue;
59-
if (CI.isIndirectCall())
60-
continue;
61-
if (CI.getCalledFunction()->isIntrinsic())
59+
// We don't need to insert stackmaps after intrinsics. But since we
60+
// can't tell if an indirect call is an intrinsic at compile time,
61+
// emit a stackmap in those cases too.
62+
if (!CI.isIndirectCall() && CI.getCalledFunction()->isIntrinsic())
6263
continue;
6364
SMCalls.insert({&I, LA.getLiveVarsBefore(&I)});
6465
} else if ((isa<BranchInst>(I) &&

0 commit comments

Comments
 (0)