Skip to content

Commit 9354995

Browse files
author
James Molloy
committedSep 3, 2019
[MachinePipeliner] Add a way to unit-test the schedule emitter
Emitting a schedule is really hard. There are lots of corner cases to take care of; in fact, of the 60+ SWP-specific testcases in the Hexagon backend most of those are testing codegen rather than the schedule creation itself. One issue is that to test an emission corner case we must craft an input such that the generated schedule uses that corner case; sometimes this is very hard and convolutes testcases. Other times it is impossible but we want to test it anyway. This patch adds a simple test pass that will consume a module containing a loop and generate pipelined code from it. We use post-instr-symbols as a way to annotate instructions with the stage and cycle that we want to schedule them at. We also provide a flag that causes the MachinePipeliner to generate these annotations instead of actually emitting code; this allows us to generate an input testcase with: llc < %s -stop-after=pipeliner -pipeliner-annotate-for-testing -o test.mir And run the emission in isolation with: llc < test.mir -run-pass=modulo-schedule-test llvm-svn: 370705
1 parent 8b2df85 commit 9354995

File tree

6 files changed

+298
-0
lines changed

6 files changed

+298
-0
lines changed
 

‎llvm/include/llvm/CodeGen/ModuloSchedule.h

+18
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,24 @@ class ModuloScheduleExpander {
254254
void expand();
255255
};
256256

257+
/// Expander that simply annotates each scheduled instruction with a post-instr
258+
/// symbol that can be consumed by the ModuloScheduleTest pass.
259+
///
260+
/// The post-instr symbol is a way of annotating an instruction that can be
261+
/// roundtripped in MIR. The syntax is:
262+
/// MYINST %0, post-instr-symbol <mcsymbol Stage-1_Cycle-5>
263+
class ModuloScheduleTestAnnotater {
264+
MachineFunction &MF;
265+
ModuloSchedule &S;
266+
267+
public:
268+
ModuloScheduleTestAnnotater(MachineFunction &MF, ModuloSchedule &S)
269+
: MF(MF), S(S) {}
270+
271+
/// Performs the annotation.
272+
void annotate();
273+
};
274+
257275
} // end namespace llvm
258276

259277
#endif // LLVM_LIB_CODEGEN_MODULOSCHEDULE_H

‎llvm/include/llvm/InitializePasses.h

+1
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ void initializeMergedLoadStoreMotionLegacyPassPass(PassRegistry&);
288288
void initializeMetaRenamerPass(PassRegistry&);
289289
void initializeModuleDebugInfoPrinterPass(PassRegistry&);
290290
void initializeModuleSummaryIndexWrapperPassPass(PassRegistry&);
291+
void initializeModuloScheduleTestPass(PassRegistry&);
291292
void initializeMustExecutePrinterPass(PassRegistry&);
292293
void initializeMustBeExecutedContextPrinterPass(PassRegistry&);
293294
void initializeNameAnonGlobalLegacyPassPass(PassRegistry&);

‎llvm/lib/CodeGen/CodeGen.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
6868
initializeMachineOptimizationRemarkEmitterPassPass(Registry);
6969
initializeMachineOutlinerPass(Registry);
7070
initializeMachinePipelinerPass(Registry);
71+
initializeModuloScheduleTestPass(Registry);
7172
initializeMachinePostDominatorTreePass(Registry);
7273
initializeMachineRegionInfoPassPass(Registry);
7374
initializeMachineSchedulerPass(Registry);

‎llvm/lib/CodeGen/MachinePipeliner.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ static cl::opt<bool> SwpShowResMask("pipeliner-show-mask", cl::Hidden,
154154
static cl::opt<bool> SwpDebugResource("pipeliner-dbg-res", cl::Hidden,
155155
cl::init(false));
156156

157+
static cl::opt<bool> EmitTestAnnotations(
158+
"pipeliner-annotate-for-testing", cl::Hidden, cl::init(false),
159+
cl::desc("Instead of emitting the pipelined code, annotate instructions "
160+
"with the generated schedule for feeding into the "
161+
"-modulo-schedule-test pass"));
162+
157163
namespace llvm {
158164

159165
// A command line option to enable the CopyToPhi DAG mutation.
@@ -536,6 +542,13 @@ void SwingSchedulerDAG::schedule() {
536542

537543
ModuloSchedule MS(MF, &Loop, std::move(OrderedInsts), std::move(Cycles),
538544
std::move(Stages));
545+
if (EmitTestAnnotations) {
546+
assert(NewInstrChanges.empty() &&
547+
"Cannot serialize a schedule with InstrChanges!");
548+
ModuloScheduleTestAnnotater MSTI(MF, MS);
549+
MSTI.annotate();
550+
return;
551+
}
539552
ModuloScheduleExpander MSE(MF, MS, LIS, std::move(NewInstrChanges));
540553
MSE.expand();
541554
++NumPipelined;

‎llvm/lib/CodeGen/ModuloSchedule.cpp

+114
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,21 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/CodeGen/ModuloSchedule.h"
10+
#include "llvm/Support/raw_ostream.h"
11+
#include "llvm/ADT/StringExtras.h"
1012
#include "llvm/CodeGen/LiveIntervals.h"
1113
#include "llvm/CodeGen/MachineInstrBuilder.h"
1214
#include "llvm/CodeGen/TargetInstrInfo.h"
15+
#include "llvm/MC/MCContext.h"
1316
#include "llvm/Support/Debug.h"
1417

1518
#define DEBUG_TYPE "pipeliner"
1619
using namespace llvm;
1720

21+
//===----------------------------------------------------------------------===//
22+
// ModuloScheduleExpander implementation
23+
//===----------------------------------------------------------------------===//
24+
1825
/// Return the register values for the operands of a Phi instruction.
1926
/// This function assume the instruction is a Phi.
2027
static void getPhiRegs(MachineInstr &Phi, MachineBasicBlock *Loop,
@@ -1188,3 +1195,110 @@ bool ModuloScheduleExpander::isLoopCarried(MachineInstr &Phi) {
11881195
int LoopStage = Schedule.getStage(Use);
11891196
return (LoopCycle > DefCycle) || (LoopStage <= DefStage);
11901197
}
1198+
1199+
//===----------------------------------------------------------------------===//
1200+
// ModuloScheduleTestPass implementation
1201+
//===----------------------------------------------------------------------===//
1202+
// This pass constructs a ModuloSchedule from its module and runs
1203+
// ModuloScheduleExpander.
1204+
//
1205+
// The module is expected to contain a single-block analyzable loop.
1206+
// The total order of instructions is taken from the loop as-is.
1207+
// Instructions are expected to be annotated with a PostInstrSymbol.
1208+
// This PostInstrSymbol must have the following format:
1209+
// "Stage=%d Cycle=%d".
1210+
//===----------------------------------------------------------------------===//
1211+
1212+
class ModuloScheduleTest : public MachineFunctionPass {
1213+
public:
1214+
static char ID;
1215+
1216+
ModuloScheduleTest() : MachineFunctionPass(ID) {
1217+
initializeModuloScheduleTestPass(*PassRegistry::getPassRegistry());
1218+
}
1219+
1220+
bool runOnMachineFunction(MachineFunction &MF) override;
1221+
void runOnLoop(MachineFunction &MF, MachineLoop &L);
1222+
1223+
void getAnalysisUsage(AnalysisUsage &AU) const override {
1224+
AU.addRequired<MachineLoopInfo>();
1225+
AU.addRequired<LiveIntervals>();
1226+
MachineFunctionPass::getAnalysisUsage(AU);
1227+
}
1228+
};
1229+
1230+
char ModuloScheduleTest::ID = 0;
1231+
1232+
INITIALIZE_PASS_BEGIN(ModuloScheduleTest, "modulo-schedule-test",
1233+
"Modulo Schedule test pass", false, false)
1234+
INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
1235+
INITIALIZE_PASS_DEPENDENCY(LiveIntervals)
1236+
INITIALIZE_PASS_END(ModuloScheduleTest, "modulo-schedule-test",
1237+
"Modulo Schedule test pass", false, false)
1238+
1239+
bool ModuloScheduleTest::runOnMachineFunction(MachineFunction &MF) {
1240+
MachineLoopInfo &MLI = getAnalysis<MachineLoopInfo>();
1241+
for (auto *L : MLI) {
1242+
if (L->getTopBlock() != L->getBottomBlock())
1243+
continue;
1244+
runOnLoop(MF, *L);
1245+
return false;
1246+
}
1247+
return false;
1248+
}
1249+
1250+
static void parseSymbolString(StringRef S, int &Cycle, int &Stage) {
1251+
std::pair<StringRef, StringRef> StageAndCycle = getToken(S, "_");
1252+
std::pair<StringRef, StringRef> StageTokenAndValue =
1253+
getToken(StageAndCycle.first, "-");
1254+
std::pair<StringRef, StringRef> CycleTokenAndValue =
1255+
getToken(StageAndCycle.second, "-");
1256+
if (StageTokenAndValue.first != "Stage" ||
1257+
CycleTokenAndValue.first != "_Cycle") {
1258+
llvm_unreachable(
1259+
"Bad post-instr symbol syntax: see comment in ModuloScheduleTest");
1260+
return;
1261+
}
1262+
1263+
StageTokenAndValue.second.drop_front().getAsInteger(10, Stage);
1264+
CycleTokenAndValue.second.drop_front().getAsInteger(10, Cycle);
1265+
1266+
dbgs() << " Stage=" << Stage << ", Cycle=" << Cycle << "\n";
1267+
}
1268+
1269+
void ModuloScheduleTest::runOnLoop(MachineFunction &MF, MachineLoop &L) {
1270+
LiveIntervals &LIS = getAnalysis<LiveIntervals>();
1271+
MachineBasicBlock *BB = L.getTopBlock();
1272+
dbgs() << "--- ModuloScheduleTest running on BB#" << BB->getNumber() << "\n";
1273+
1274+
DenseMap<MachineInstr *, int> Cycle, Stage;
1275+
std::vector<MachineInstr *> Instrs;
1276+
for (MachineInstr &MI : *BB) {
1277+
if (MI.isTerminator())
1278+
continue;
1279+
Instrs.push_back(&MI);
1280+
if (MCSymbol *Sym = MI.getPostInstrSymbol()) {
1281+
dbgs() << "Parsing post-instr symbol for " << MI;
1282+
parseSymbolString(Sym->getName(), Cycle[&MI], Stage[&MI]);
1283+
}
1284+
}
1285+
1286+
ModuloSchedule MS(MF, &L, std::move(Instrs), std::move(Cycle), std::move(Stage));
1287+
ModuloScheduleExpander MSE(
1288+
MF, MS, LIS, /*InstrChanges=*/ModuloScheduleExpander::InstrChangesTy());
1289+
MSE.expand();
1290+
}
1291+
1292+
//===----------------------------------------------------------------------===//
1293+
// ModuloScheduleTestAnnotater implementation
1294+
//===----------------------------------------------------------------------===//
1295+
1296+
void ModuloScheduleTestAnnotater::annotate() {
1297+
for (MachineInstr *MI : S.getInstructions()) {
1298+
SmallVector<char, 16> SV;
1299+
raw_svector_ostream OS(SV);
1300+
OS << "Stage-" << S.getStage(MI) << "_Cycle-" << S.getCycle(MI);
1301+
MCSymbol *Sym = MF.getContext().getOrCreateSymbol(OS.str());
1302+
MI->setPostInstrSymbol(MF, Sym);
1303+
}
1304+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# RUN: llc < %s -x mir -march=hexagon -run-pass=modulo-schedule-test | FileCheck %s
2+
3+
# Simple check for this sanity test; ensure all instructions are in stage 0 in
4+
# the prolog and stage 3 in the epilog.
5+
6+
# CHECK-NOT: Stage-3
7+
# CHECK: J2_loop0r
8+
# CHECK: intregs = S2_addasl_rrri %{{[0-9]+}}, %{{[0-9]+}}, 1, post-instr-symbol <mcsymbol Stage-0_Cycle-0>
9+
# CHECK: intregs = L2_loadruh_io %{{[0-9]+}}, -4, post-instr-symbol <mcsymbol Stage-3_Cycle-0> :: (load 2 from %ir.cgep2, !tbaa !0)
10+
# CHECK: intregs = S2_storerh_pi %{{[0-9]+}}, -2, %{{[0-9]+}}, post-instr-symbol <mcsymbol Stage-3_Cycle-0> :: (store 2 into %ir.lsr.iv, !tbaa !0)
11+
# CHECK: intregs = nsw A2_addi %{{[0-9]+}}, -1, post-instr-symbol <mcsymbol Stage-0_Cycle-0>
12+
# CHECK: ENDLOOP0 %bb.{{[0-9]+}}, implicit-def $pc, implicit-def $lc0, implicit $sa0, implicit $lc0
13+
# CHECK-NOT: Stage-0
14+
15+
--- |
16+
; ModuleID = '/google/src/cloud/jmolloy/tc/google3/third_party/llvm/llvm/test/CodeGen/Hexagon/swp-phi-start.ll'
17+
source_filename = "/google/src/cloud/jmolloy/tc/google3/third_party/llvm/llvm/test/CodeGen/Hexagon/swp-phi-start.ll"
18+
target datalayout = "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048"
19+
20+
; Function Attrs: nounwind
21+
define void @f0(i32 %a0, i16* nocapture %a1) #0 {
22+
b0:
23+
br i1 undef, label %b1, label %b2.preheader
24+
25+
b1: ; preds = %b0
26+
br i1 undef, label %b3, label %b2.preheader
27+
28+
b2.preheader: ; preds = %b0, %b1
29+
%cgep = getelementptr i16, i16* %a1, i32 undef
30+
br label %b2
31+
32+
b2: ; preds = %b2.preheader, %b2
33+
%lsr.iv = phi i16* [ %cgep, %b2.preheader ], [ %cgep3, %b2 ]
34+
%v1 = phi i32 [ %v7, %b2 ], [ undef, %b2.preheader ]
35+
%v2 = phi i32 [ %v1, %b2 ], [ %a0, %b2.preheader ]
36+
%v3 = add nsw i32 %v2, -2
37+
%cgep2 = getelementptr inbounds i16, i16* %a1, i32 %v3
38+
%v5 = load i16, i16* %cgep2, align 2, !tbaa !0
39+
store i16 %v5, i16* %lsr.iv, align 2, !tbaa !0
40+
%v7 = add nsw i32 %v1, -1
41+
%v8 = icmp sgt i32 %v7, 0
42+
%cgep3 = getelementptr i16, i16* %lsr.iv, i32 -1
43+
br i1 %v8, label %b2, label %b3
44+
45+
b3: ; preds = %b2, %b1
46+
ret void
47+
}
48+
49+
attributes #0 = { nounwind "target-cpu"="hexagonv55" }
50+
51+
!0 = !{!1, !1, i64 0}
52+
!1 = !{!"short", !2, i64 0}
53+
!2 = !{!"omnipotent char", !3, i64 0}
54+
!3 = !{!"Simple C/C++ TBAA"}
55+
56+
...
57+
---
58+
name: f0
59+
alignment: 4
60+
exposesReturnsTwice: false
61+
legalized: false
62+
regBankSelected: false
63+
selected: false
64+
failedISel: false
65+
tracksRegLiveness: true
66+
hasWinCFI: false
67+
registers:
68+
- { id: 0, class: intregs, preferred-register: '' }
69+
- { id: 1, class: intregs, preferred-register: '' }
70+
- { id: 2, class: intregs, preferred-register: '' }
71+
- { id: 3, class: intregs, preferred-register: '' }
72+
- { id: 4, class: intregs, preferred-register: '' }
73+
- { id: 5, class: intregs, preferred-register: '' }
74+
- { id: 6, class: intregs, preferred-register: '' }
75+
- { id: 7, class: intregs, preferred-register: '' }
76+
- { id: 8, class: predregs, preferred-register: '' }
77+
- { id: 9, class: predregs, preferred-register: '' }
78+
- { id: 10, class: intregs, preferred-register: '' }
79+
- { id: 11, class: intregs, preferred-register: '' }
80+
- { id: 12, class: intregs, preferred-register: '' }
81+
- { id: 13, class: predregs, preferred-register: '' }
82+
- { id: 14, class: intregs, preferred-register: '' }
83+
liveins:
84+
- { reg: '$r0', virtual-reg: '%6' }
85+
- { reg: '$r1', virtual-reg: '%7' }
86+
frameInfo:
87+
isFrameAddressTaken: false
88+
isReturnAddressTaken: false
89+
hasStackMap: false
90+
hasPatchPoint: false
91+
stackSize: 0
92+
offsetAdjustment: 0
93+
maxAlignment: 1
94+
adjustsStack: false
95+
hasCalls: false
96+
stackProtector: ''
97+
maxCallFrameSize: 4294967295
98+
cvBytesOfCalleeSavedRegisters: 0
99+
hasOpaqueSPAdjustment: false
100+
hasVAStart: false
101+
hasMustTailInVarArgFunc: false
102+
localFrameSize: 0
103+
savePoint: ''
104+
restorePoint: ''
105+
fixedStack: []
106+
stack: []
107+
callSites: []
108+
constants: []
109+
machineFunctionInfo: {}
110+
body: |
111+
bb.0.b0:
112+
successors: %bb.1(0x40000000), %bb.2(0x40000000)
113+
liveins: $r0, $r1
114+
115+
%7:intregs = COPY $r1
116+
%6:intregs = COPY $r0
117+
%8:predregs = IMPLICIT_DEF
118+
J2_jumpt %8, %bb.2, implicit-def dead $pc
119+
J2_jump %bb.1, implicit-def dead $pc
120+
121+
bb.1.b1:
122+
successors: %bb.4(0x40000000), %bb.2(0x40000000)
123+
124+
%9:predregs = IMPLICIT_DEF
125+
J2_jumpt %9, %bb.4, implicit-def dead $pc
126+
J2_jump %bb.2, implicit-def dead $pc
127+
128+
bb.2.b2.preheader:
129+
successors: %bb.3(0x80000000)
130+
131+
%10:intregs = IMPLICIT_DEF
132+
%14:intregs = COPY %10
133+
J2_loop0r %bb.3, %14, implicit-def $lc0, implicit-def $sa0, implicit-def $usr
134+
135+
bb.3.b2 (address-taken):
136+
successors: %bb.3(0x7c000000), %bb.4(0x04000000)
137+
138+
%1:intregs = PHI %7, %bb.2, %5, %bb.3, post-instr-symbol <mcsymbol Stage-3_Cycle-0>
139+
%2:intregs = PHI %10, %bb.2, %4, %bb.3, post-instr-symbol <mcsymbol Stage-0_Cycle-0>
140+
%3:intregs = PHI %6, %bb.2, %2, %bb.3, post-instr-symbol <mcsymbol Stage-0_Cycle-0>
141+
%11:intregs = S2_addasl_rrri %7, %3, 1, post-instr-symbol <mcsymbol Stage-0_Cycle-0>
142+
%12:intregs = L2_loadruh_io %11, -4, post-instr-symbol <mcsymbol Stage-3_Cycle-0> :: (load 2 from %ir.cgep2, !tbaa !0)
143+
%5:intregs = S2_storerh_pi %1, -2, %12, post-instr-symbol <mcsymbol Stage-3_Cycle-0> :: (store 2 into %ir.lsr.iv, !tbaa !0)
144+
%4:intregs = nsw A2_addi %2, -1, post-instr-symbol <mcsymbol Stage-0_Cycle-0>
145+
ENDLOOP0 %bb.3, implicit-def $pc, implicit-def $lc0, implicit $sa0, implicit $lc0
146+
J2_jump %bb.4, implicit-def dead $pc
147+
148+
bb.4.b3:
149+
PS_jmpret $r31, implicit-def dead $pc
150+
151+
...

0 commit comments

Comments
 (0)
Please sign in to comment.