Skip to content

Commit deb3936

Browse files
bors[bot]ptersilie
andauthored
52: Fix stackmaps. r=ltratt a=ptersilie Co-authored-by: Lukas Diekmann <lukas.diekmann@gmail.com>
2 parents fb45fc6 + 79cbb8e commit deb3936

File tree

8 files changed

+110
-16
lines changed

8 files changed

+110
-16
lines changed

Diff for: llvm/include/llvm/CodeGen/AsmPrinter.h

+6
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ class AsmPrinter : public MachineFunctionPass {
329329
/// definition in the same module.
330330
MCSymbol *getSymbolPreferLocal(const GlobalValue &GV) const;
331331

332+
/// Maps call instructions to the label emitted just after the call. The label
333+
/// is later used to calculate the correct offset of the call instruction
334+
/// without interference from caller-saved registers which are sometimes
335+
/// emitted inbetween call and stackmap.
336+
MCSymbol *YkLastCallLabel = nullptr;
337+
332338
//===------------------------------------------------------------------===//
333339
// XRay instrumentation implementation.
334340
//===------------------------------------------------------------------===//

Diff for: llvm/include/llvm/Transforms/Yk/LivenessAnalysis.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef __YK_LIVENESS_H
22
#define __YK_LIVENESS_H
33

4+
#include "llvm/IR/Dominators.h"
45
#include "llvm/IR/Instructions.h"
56

67
#include <map>
@@ -22,15 +23,18 @@ class LivenessAnalysis {
2223
/// Find the successor instructions of the specified instruction.
2324
std::set<Instruction *> getSuccessorInstructions(Instruction *I);
2425

26+
// A domniator tree used to sort the live variables.
27+
DominatorTree DT;
28+
2529
/// Replaces the set `S` with the set `R`, returning if the set changed.
2630
bool updateValueSet(std::set<Value *> &S, const std::set<Value *> &R);
2731

2832
public:
2933
LivenessAnalysis(Function *Func);
3034

31-
/// Returns the set of live variables immediately before the specified
32-
/// instruction.
33-
std::set<Value *> getLiveVarsBefore(Instruction *I);
35+
/// Returns the vector of live variables immediately before the specified
36+
/// instruction (sorted in order of appearance).
37+
std::vector<Value *> getLiveVarsBefore(Instruction *I);
3438
};
3539

3640
} // namespace llvm

Diff for: llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ static cl::opt<bool>
135135

136136
extern bool YkAllocLLVMBBAddrMapSection;
137137
extern bool YkExtendedLLVMBBAddrMapSection;
138+
extern bool YkStackMapOffsetFix;
138139

139140
const char DWARFGroupName[] = "dwarf";
140141
const char DWARFGroupDescription[] = "DWARF Emission";
@@ -1699,6 +1700,9 @@ void AsmPrinter::emitFunctionBody() {
16991700
bool HasAnyRealCode = false;
17001701
int NumInstsInFunction = 0;
17011702

1703+
// Reset YkLastCallLabel so we don't use it across different functions.
1704+
YkLastCallLabel = nullptr;
1705+
17021706
bool CanDoExtraAnalysis = ORE->allowExtraAnalysis(DEBUG_TYPE);
17031707
for (auto &MBB : *MF) {
17041708
// Print a label for the basic block.
@@ -1800,6 +1804,31 @@ void AsmPrinter::emitFunctionBody() {
18001804
}
18011805

18021806
emitInstruction(&MI);
1807+
// Generate labels for function calls so we can record the correct
1808+
// instruction offset. The conditions for generating the label must be
1809+
// the same as the ones for generating the stackmap call in
1810+
// `Transforms/Yk/Stackmaps.cpp`, as otherwise we could end up with
1811+
// wrong offsets (e.g. we create a label here but the corresponding
1812+
// stackmap call was ommitted, and this label is then used for the
1813+
// following stackmap call).
1814+
1815+
// Convoluted way of finding our whether this function call is an
1816+
// intrinsic.
1817+
bool IsIntrinsic = false;
1818+
if (MI.getNumExplicitDefs() < MI.getNumOperands()) {
1819+
IsIntrinsic = MI.getOperand(MI.getNumExplicitDefs()).isIntrinsicID();
1820+
}
1821+
if (YkStackMapOffsetFix && MI.isCall() && !MI.isInlineAsm() &&
1822+
(MI.getOpcode() != TargetOpcode::STACKMAP) &&
1823+
(MI.getOpcode() != TargetOpcode::PATCHPOINT) && !IsIntrinsic) {
1824+
// YKFIXME: We don't need to emit labels (and stackmap calls) for
1825+
// functions that cannot guard fail, e.g. inlined functions, or
1826+
// functions we don't have IR for.
1827+
MCSymbol *MILabel =
1828+
OutStreamer->getContext().createTempSymbol("ykstackcall", true);
1829+
OutStreamer->emitLabel(MILabel);
1830+
YkLastCallLabel = MILabel;
1831+
}
18031832
if (CanDoExtraAnalysis) {
18041833
MCInst MCI;
18051834
MCI.setOpcode(MI.getOpcode());

Diff for: llvm/lib/Support/Yk.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@ static cl::opt<bool, true> YkExtendedLLVMBBAddrMapSectionParser(
1818
"yk-extended-llvmbbaddrmap-section",
1919
cl::desc("Use the extended Yk `.llvmbbaddrmap` section format"),
2020
cl::NotHidden, cl::location(YkExtendedLLVMBBAddrMapSection));
21+
22+
bool YkStackMapOffsetFix;
23+
static cl::opt<bool, true> YkStackMapOffsetFixParser(
24+
"yk-stackmap-offset-fix",
25+
cl::desc("Apply a fix to stackmaps that corrects the reported instruction "
26+
"offset in the presence of calls."),
27+
cl::NotHidden, cl::location(YkStackMapOffsetFix));

Diff for: llvm/lib/Target/X86/X86MCInstLower.cpp

+19-4
Original file line numberDiff line numberDiff line change
@@ -1422,10 +1422,25 @@ void X86AsmPrinter::LowerPATCHABLE_OP(const MachineInstr &MI,
14221422
void X86AsmPrinter::LowerSTACKMAP(const MachineInstr &MI) {
14231423
SMShadowTracker.emitShadowPadding(*OutStreamer, getSubtargetInfo());
14241424

1425-
auto &Ctx = OutStreamer->getContext();
1426-
MCSymbol *MILabel = Ctx.createTempSymbol();
1427-
OutStreamer->emitLabel(MILabel);
1428-
1425+
// When the stackmap is attached to a function call, caller-saved register
1426+
// loads may be emitted in-between call and stackmap call, thus skewing the
1427+
// reported offset.
1428+
// This makes it impossible to reliably continue at the correct location after
1429+
// deoptimisation has occured. In order to fix this, we insert labels after
1430+
// every call that could lead to a guard failure, and store the last such
1431+
// label inside `YkLastCallLabel`. If `YkLastCallLabel` is set (i.e. the
1432+
// stackmap was inserted after a call) use it to calculate the stackmap
1433+
// offset, otherwise (i.e. the stackmap was inserted before a branch) generate
1434+
// a new label as per ususual.
1435+
MCSymbol *MILabel;
1436+
if (YkLastCallLabel != nullptr) {
1437+
MILabel = YkLastCallLabel;
1438+
YkLastCallLabel = nullptr;
1439+
} else {
1440+
auto &Ctx = OutStreamer->getContext();
1441+
MILabel = Ctx.createTempSymbol();
1442+
OutStreamer->emitLabel(MILabel);
1443+
}
14291444
SM.recordStackMap(*MILabel, MI);
14301445
unsigned NumShadowBytes = MI.getOperand(1).getImm();
14311446
SMShadowTracker.reset(NumShadowBytes);

Diff for: llvm/lib/Transforms/Yk/ControlPoint.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ class YkControlPoint : public ModulePass {
130130

131131
// Find all live variables just before the call to the control point.
132132
LivenessAnalysis LA(OldCtrlPointCall->getFunction());
133-
const std::set<Value *> LiveVals = LA.getLiveVarsBefore(OldCtrlPointCall);
133+
const std::vector<Value *> LiveVals =
134+
LA.getLiveVarsBefore(OldCtrlPointCall);
134135
if (LiveVals.size() == 0) {
135136
Context.emitError(
136137
"The interpreter loop has no live variables!\n"

Diff for: llvm/lib/Transforms/Yk/LivenessAnalysis.cpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ LivenessAnalysis::LivenessAnalysis(Function *Func) {
6969
// Compute defs and uses for each instruction.
7070
std::map<Instruction *, std::set<Value *>> Defs;
7171
std::map<Instruction *, std::set<Value *>> Uses;
72+
73+
// Create a domniator tree so we can later sort the live variables.
74+
DT = DominatorTree(*Func);
75+
7276
for (BasicBlock &BB : *Func) {
7377
for (Instruction &I : BB) {
7478
// Record what this instruction defines.
@@ -172,8 +176,19 @@ LivenessAnalysis::LivenessAnalysis(Function *Func) {
172176
} while (Changed); // Until a fixed-point.
173177
}
174178

175-
std::set<Value *> LivenessAnalysis::getLiveVarsBefore(Instruction *I) {
176-
return In[I];
179+
std::vector<Value *> LivenessAnalysis::getLiveVarsBefore(Instruction *I) {
180+
std::set<Value *> Res = In[I];
181+
// Sort the live variables by order of appearance using a dominator tree. The
182+
// order is important for frame construction during deoptimisation: since live
183+
// variables may reference other live variables they need to be proceesed in
184+
// the order they appear in the module.
185+
std::vector<Value *> Sorted(Res.begin(), Res.end());
186+
std::sort(Sorted.begin(), Sorted.end(), [this](Value *A, Value *B) {
187+
if (isa<Instruction>(B))
188+
return DT.dominates(A, cast<Instruction>(B));
189+
return false;
190+
});
191+
return Sorted;
177192
}
178193

179194
} // namespace llvm

Diff for: llvm/lib/Transforms/Yk/StackMaps.cpp

+23-6
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,28 @@ class YkStackmaps : public ModulePass {
4545
return false;
4646
}
4747

48-
std::map<Instruction *, std::set<Value *>> SMCalls;
48+
std::map<Instruction *, std::vector<Value *>> SMCalls;
4949
for (Function &F : M) {
5050
if (F.empty()) // skip declarations.
5151
continue;
5252
LivenessAnalysis LA(&F);
5353
for (BasicBlock &BB : F)
54-
for (Instruction &I : BB)
55-
if ((isa<CallInst>(I)) ||
56-
((isa<BranchInst>(I)) && (cast<BranchInst>(I).isConditional())) ||
57-
isa<SwitchInst>(I))
54+
for (Instruction &I : BB) {
55+
if (isa<CallInst>(I)) {
56+
CallInst &CI = cast<CallInst>(I);
57+
if (CI.isInlineAsm())
58+
continue;
59+
if (CI.isIndirectCall())
60+
continue;
61+
if (CI.getCalledFunction()->isIntrinsic())
62+
continue;
5863
SMCalls.insert({&I, LA.getLiveVarsBefore(&I)});
64+
} else if ((isa<BranchInst>(I) &&
65+
cast<BranchInst>(I).isConditional()) ||
66+
isa<SwitchInst>(I)) {
67+
SMCalls.insert({&I, LA.getLiveVarsBefore(&I)});
68+
}
69+
}
5970
}
6071

6172
Function *SMFunc = Intrinsic::getDeclaration(&M, SMFuncID);
@@ -65,14 +76,20 @@ class YkStackmaps : public ModulePass {
6576
Value *Shadow = ConstantInt::get(Type::getInt32Ty(Context), 0);
6677
for (auto It : SMCalls) {
6778
Instruction *I = cast<Instruction>(It.first);
68-
const std::set<Value *> L = It.second;
79+
const std::vector<Value *> L = It.second;
6980

7081
IRBuilder<> Bldr(I);
7182
Value *SMID = ConstantInt::get(Type::getInt64Ty(Context), Count);
7283
std::vector<Value *> Args = {SMID, Shadow};
7384
for (Value *A : L)
7485
Args.push_back(A);
7586

87+
if (isa<CallInst>(I)) {
88+
// Insert the stackmap call after (not before) the call instruction, so
89+
// the offset of the stackmap entry will record the instruction after
90+
// the call, which is where we want to continue after deoptimisation.
91+
Bldr.SetInsertPoint(I->getNextNonDebugInstruction());
92+
}
7693
Bldr.CreateCall(SMFunc->getFunctionType(), SMFunc,
7794
ArrayRef<Value *>(Args));
7895
Count++;

0 commit comments

Comments
 (0)