Skip to content

Commit a7cbe2b

Browse files
committed
Move Arcilator pipeline into separate library.
Allow re-use of the arcilator library. Split up the large pipeline into finer grained stages and move into its own library. populateHwModuleToArcPipeline is split into: - arcPreprocessingPipeline - arcConversionPipeline - arcOptimizationPipeline - arcStateLoweringPipeline and - arcStateAllocationPipeline Flags to customize passes are moved from the binary into the corresponding pipeline options.
1 parent 4d81b97 commit a7cbe2b

File tree

4 files changed

+233
-148
lines changed

4 files changed

+233
-148
lines changed

tools/arcilator/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ set(libs
3737
MLIRTargetLLVMIRExport
3838
)
3939

40-
add_circt_tool(arcilator arcilator.cpp DEPENDS ${libs})
40+
add_circt_tool(arcilator arcilator.cpp pipelines.h pipelines.cpp DEPENDS ${libs})
4141
target_link_libraries(arcilator
4242
PRIVATE
4343
${libs}
4444
${ARCILATOR_JIT_DEPS}
4545
)
46+
target_include_directories(arcilator PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
4647

4748
llvm_update_compile_flags(arcilator)
4849
mlir_check_all_link_libraries(arcilator)

tools/arcilator/arcilator.cpp

Lines changed: 8 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14-
#include "circt/Conversion/ArcToLLVM.h"
15-
#include "circt/Conversion/CombToArith.h"
16-
#include "circt/Conversion/ConvertToArcs.h"
1714
#include "circt/Conversion/Passes.h"
18-
#include "circt/Conversion/SeqToSV.h"
1915
#include "circt/Dialect/Arc/ArcDialect.h"
2016
#include "circt/Dialect/Arc/ArcInterfaces.h"
2117
#include "circt/Dialect/Arc/ArcOps.h"
@@ -24,15 +20,10 @@
2420
#include "circt/Dialect/Arc/ModelInfoExport.h"
2521
#include "circt/Dialect/Comb/CombDialect.h"
2622
#include "circt/Dialect/Emit/EmitDialect.h"
27-
#include "circt/Dialect/Emit/EmitPasses.h"
28-
#include "circt/Dialect/HW/HWPasses.h"
2923
#include "circt/Dialect/LLHD/IR/LLHDDialect.h"
3024
#include "circt/Dialect/OM/OMDialect.h"
31-
#include "circt/Dialect/OM/OMPasses.h"
3225
#include "circt/Dialect/SV/SVDialect.h"
33-
#include "circt/Dialect/Seq/SeqPasses.h"
3426
#include "circt/Dialect/Sim/SimDialect.h"
35-
#include "circt/Dialect/Sim/SimPasses.h"
3627
#include "circt/Dialect/Verif/VerifDialect.h"
3728
#include "circt/Support/Passes.h"
3829
#include "circt/Support/Version.h"
@@ -64,6 +55,7 @@
6455
#include "mlir/Target/LLVMIR/Export.h"
6556
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
6657
#include "mlir/Transforms/Passes.h"
58+
#include "pipelines.h"
6759
#include "llvm/IR/LLVMContext.h"
6860
#include "llvm/IR/Module.h"
6961
#include "llvm/Support/CommandLine.h"
@@ -96,59 +88,12 @@ static llvm::cl::opt<std::string>
9688
llvm::cl::value_desc("filename"), llvm::cl::init("-"),
9789
llvm::cl::cat(mainCategory));
9890

99-
static llvm::cl::opt<bool>
100-
observePorts("observe-ports", llvm::cl::desc("Make all ports observable"),
101-
llvm::cl::init(false), llvm::cl::cat(mainCategory));
102-
103-
static llvm::cl::opt<bool>
104-
observeWires("observe-wires", llvm::cl::desc("Make all wires observable"),
105-
llvm::cl::init(false), llvm::cl::cat(mainCategory));
106-
107-
static llvm::cl::opt<bool> observeNamedValues(
108-
"observe-named-values",
109-
llvm::cl::desc("Make values with `sv.namehint` observable"),
110-
llvm::cl::init(false), llvm::cl::cat(mainCategory));
111-
112-
static llvm::cl::opt<bool>
113-
observeRegisters("observe-registers",
114-
llvm::cl::desc("Make all registers observable"),
115-
llvm::cl::init(false), llvm::cl::cat(mainCategory));
116-
117-
static llvm::cl::opt<bool>
118-
observeMemories("observe-memories",
119-
llvm::cl::desc("Make all memory contents observable"),
120-
llvm::cl::init(false), llvm::cl::cat(mainCategory));
121-
12291
static llvm::cl::opt<std::string> stateFile("state-file",
12392
llvm::cl::desc("State file"),
12493
llvm::cl::value_desc("filename"),
12594
llvm::cl::init(""),
12695
llvm::cl::cat(mainCategory));
12796

128-
static llvm::cl::opt<bool> shouldInline("inline", llvm::cl::desc("Inline arcs"),
129-
llvm::cl::init(true),
130-
llvm::cl::cat(mainCategory));
131-
132-
static llvm::cl::opt<bool> shouldDedup("dedup",
133-
llvm::cl::desc("Deduplicate arcs"),
134-
llvm::cl::init(true),
135-
llvm::cl::cat(mainCategory));
136-
137-
static llvm::cl::opt<bool> shouldDetectEnables(
138-
"detect-enables",
139-
llvm::cl::desc("Infer enable conditions for states to avoid computation"),
140-
llvm::cl::init(true), llvm::cl::cat(mainCategory));
141-
142-
static llvm::cl::opt<bool> shouldDetectResets(
143-
"detect-resets",
144-
llvm::cl::desc("Infer reset conditions for states to avoid computation"),
145-
llvm::cl::init(false), llvm::cl::cat(mainCategory));
146-
147-
static llvm::cl::opt<bool>
148-
shouldMakeLUTs("lookup-tables",
149-
llvm::cl::desc("Optimize arcs into lookup tables"),
150-
llvm::cl::init(true), llvm::cl::cat(mainCategory));
151-
15297
static llvm::cl::opt<bool>
15398
printDebugInfo("print-debug-info",
15499
llvm::cl::desc("Print debug information"),
@@ -176,12 +121,6 @@ static llvm::cl::opt<bool> splitInputFile(
176121
"chunk independently"),
177122
llvm::cl::init(false), llvm::cl::Hidden, llvm::cl::cat(mainCategory));
178123

179-
static llvm::cl::opt<unsigned> splitFuncsThreshold(
180-
"split-funcs-threshold",
181-
llvm::cl::desc(
182-
"Split large MLIR functions that occur above the given size threshold"),
183-
llvm::cl::ValueOptional, llvm::cl::cat(mainCategory));
184-
185124
// Options to control early-out from pipeline.
186125
enum Until {
187126
UntilPreprocessing,
@@ -250,105 +189,28 @@ static void populateHwModuleToArcPipeline(PassManager &pm) {
250189
// represented as intrinsic ops.
251190
if (untilReached(UntilPreprocessing))
252191
return;
253-
pm.addPass(om::createStripOMPass());
254-
pm.addPass(emit::createStripEmitPass());
255-
pm.addPass(createLowerFirMemPass());
256-
pm.addPass(createLowerVerifSimulationsPass());
257-
{
258-
arc::AddTapsOptions opts;
259-
opts.tapPorts = observePorts;
260-
opts.tapWires = observeWires;
261-
opts.tapNamedValues = observeNamedValues;
262-
pm.addPass(arc::createAddTapsPass(opts));
263-
}
264-
pm.addPass(arc::createStripSVPass());
265-
{
266-
arc::InferMemoriesOptions opts;
267-
opts.tapPorts = observePorts;
268-
opts.tapMemories = observeMemories;
269-
pm.addPass(arc::createInferMemoriesPass(opts));
270-
}
271-
pm.addPass(sim::createLowerDPIFunc());
272-
pm.addPass(createCSEPass());
273-
pm.addPass(arc::createArcCanonicalizerPass());
192+
arcPreprocessingPipeline(pm);
274193

275194
// Restructure the input from a `hw.module` hierarchy to a collection of arcs.
276195
if (untilReached(UntilArcConversion))
277196
return;
278-
{
279-
ConvertToArcsPassOptions opts;
280-
opts.tapRegisters = observeRegisters;
281-
pm.addPass(createConvertToArcsPass(opts));
282-
}
283-
if (shouldDedup)
284-
pm.addPass(arc::createDedupPass());
285-
pm.addPass(hw::createFlattenModules());
286-
pm.addPass(createCSEPass());
287-
pm.addPass(arc::createArcCanonicalizerPass());
197+
arcConversionPipeline(pm);
288198

289199
// Perform arc-level optimizations that are not specific to software
290200
// simulation.
291201
if (untilReached(UntilArcOpt))
292202
return;
293-
pm.addPass(arc::createSplitLoopsPass());
294-
if (shouldDedup)
295-
pm.addPass(arc::createDedupPass());
296-
{
297-
arc::InferStatePropertiesOptions opts;
298-
opts.detectEnables = shouldDetectEnables;
299-
opts.detectResets = shouldDetectResets;
300-
pm.addPass(arc::createInferStateProperties(opts));
301-
}
302-
pm.addPass(createCSEPass());
303-
pm.addPass(arc::createArcCanonicalizerPass());
304-
pm.addNestedPass<hw::HWModuleOp>(arc::createMergeTaps());
305-
if (shouldMakeLUTs)
306-
pm.addPass(arc::createMakeTablesPass());
307-
pm.addPass(createCSEPass());
308-
pm.addPass(arc::createArcCanonicalizerPass());
309-
310-
// Now some arguments may be unused because reset conditions are not passed as
311-
// inputs anymore pm.addPass(arc::createRemoveUnusedArcArgumentsPass());
312-
// Because we replace a lot of StateOp inputs with constants in the enable
313-
// patterns we may be able to sink a lot of them
314-
// TODO: maybe merge RemoveUnusedArcArguments with SinkInputs?
315-
// pm.addPass(arc::createSinkInputsPass());
316-
// pm.addPass(createCSEPass());
317-
// pm.addPass(createSimpleCanonicalizerPass());
318-
// Removing some muxes etc. may lead to additional dedup opportunities
319-
// if (shouldDedup)
320-
// pm.addPass(arc::createDedupPass());
203+
arcOptimizationPipeline(pm);
321204

322205
// Lower stateful arcs into explicit state reads and writes.
323206
if (untilReached(UntilStateLowering))
324207
return;
325-
pm.addPass(arc::createLowerStatePass());
326-
327-
// TODO: LowerClocksToFuncsPass might not properly consider scf.if operations
328-
// (or nested regions in general) and thus errors out when muxes are also
329-
// converted in the hw.module or arc.model
330-
// TODO: InlineArcs seems to not properly handle scf.if operations, thus the
331-
// following is commented out
332-
// pm.addPass(arc::createMuxToControlFlowPass());
333-
if (shouldInline)
334-
pm.addPass(arc::createInlineArcsPass());
335-
336-
pm.addPass(arc::createMergeIfsPass());
337-
pm.addPass(createCSEPass());
338-
pm.addPass(arc::createArcCanonicalizerPass());
208+
arcStateLoweringPipeline(pm);
339209

340210
// Allocate states.
341211
if (untilReached(UntilStateAlloc))
342212
return;
343-
pm.addPass(arc::createLowerArcsToFuncsPass());
344-
pm.nest<arc::ModelOp>().addPass(arc::createAllocateStatePass());
345-
pm.addPass(arc::createLowerClocksToFuncsPass()); // no CSE between state alloc
346-
// and clock func lowering
347-
if (splitFuncsThreshold.getNumOccurrences()) {
348-
pm.addPass(arc::createSplitFuncs({splitFuncsThreshold}));
349-
}
350-
pm.addPass(createCSEPass());
351-
pm.addPass(arc::createArcCanonicalizerPass());
213+
arcStateAllocationPipeline(pm);
352214
}
353215

354216
/// Populate a pass manager with the Arc to LLVM pipeline for the given
@@ -357,9 +219,7 @@ static void populateArcToLLVMPipeline(PassManager &pm) {
357219
// Lower the arcs and update functions to LLVM.
358220
if (untilReached(UntilLLVMLowering))
359221
return;
360-
pm.addPass(createLowerArcToLLVMPass());
361-
pm.addPass(createCSEPass());
362-
pm.addPass(arc::createArcCanonicalizerPass());
222+
arcToLLVMPipeline(pm);
363223
}
364224

365225
static LogicalResult processBuffer(
@@ -379,6 +239,7 @@ static LogicalResult processBuffer(
379239
pmArc.enableTiming(ts);
380240
if (failed(applyPassManagerCLOptions(pmArc)))
381241
return failure();
242+
382243
populateHwModuleToArcPipeline(pmArc);
383244

384245
if (failed(pmArc.run(module.get())))

tools/arcilator/pipelines.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#include "pipelines.h"
2+
3+
#include "circt/Conversion/ArcToLLVM.h"
4+
#include "circt/Conversion/CombToArith.h"
5+
#include "circt/Conversion/ConvertToArcs.h"
6+
#include "circt/Conversion/Passes.h"
7+
#include "circt/Conversion/SeqToSV.h"
8+
#include "circt/Dialect/Arc/ArcOps.h"
9+
#include "circt/Dialect/Arc/ArcPasses.h"
10+
#include "circt/Dialect/Emit/EmitPasses.h"
11+
#include "circt/Dialect/HW/HWPasses.h"
12+
#include "circt/Dialect/OM/OMPasses.h"
13+
#include "circt/Dialect/Seq/SeqPasses.h"
14+
#include "circt/Dialect/Sim/SimPasses.h"
15+
#include "circt/Support/Passes.h"
16+
#include "mlir/Transforms/Passes.h"
17+
18+
using namespace mlir;
19+
using namespace circt;
20+
using namespace arc;
21+
22+
void circt::arcPreprocessingPipeline(PassManager &pm,
23+
const ArcPreprocessingOptions &options) {
24+
pm.addPass(om::createStripOMPass());
25+
pm.addPass(emit::createStripEmitPass());
26+
pm.addPass(createLowerFirMemPass());
27+
pm.addPass(createLowerVerifSimulationsPass());
28+
{
29+
arc::AddTapsOptions opts;
30+
opts.tapPorts = options.observePorts;
31+
opts.tapWires = options.observeWires;
32+
opts.tapNamedValues = options.observeNamedValues;
33+
pm.addPass(arc::createAddTapsPass(opts));
34+
}
35+
pm.addPass(arc::createStripSVPass());
36+
{
37+
arc::InferMemoriesOptions opts;
38+
opts.tapPorts = options.observePorts;
39+
opts.tapMemories = options.observeMemories;
40+
pm.addPass(arc::createInferMemoriesPass(opts));
41+
}
42+
pm.addPass(sim::createLowerDPIFunc());
43+
pm.addPass(createCSEPass());
44+
pm.addPass(arc::createArcCanonicalizerPass());
45+
}
46+
47+
void circt::arcConversionPipeline(PassManager &pm,
48+
const ArcConversionOptions &options) {
49+
{
50+
ConvertToArcsPassOptions opts;
51+
opts.tapRegisters = options.observeRegisters;
52+
pm.addPass(createConvertToArcsPass(opts));
53+
}
54+
if (options.shouldDedup)
55+
pm.addPass(arc::createDedupPass());
56+
pm.addPass(hw::createFlattenModules());
57+
pm.addPass(createCSEPass());
58+
pm.addPass(arc::createArcCanonicalizerPass());
59+
}
60+
61+
void circt::arcOptimizationPipeline(PassManager &pm,
62+
const ArcOptimizationOptions &options) {
63+
// Perform arc-level optimizations that are not specific to software
64+
// simulation.
65+
pm.addPass(arc::createSplitLoopsPass());
66+
if (options.shouldDedup)
67+
pm.addPass(arc::createDedupPass());
68+
{
69+
arc::InferStatePropertiesOptions opts;
70+
opts.detectEnables = options.shouldDetectEnables;
71+
opts.detectResets = options.shouldDetectResets;
72+
pm.addPass(arc::createInferStateProperties(opts));
73+
}
74+
pm.addPass(createCSEPass());
75+
pm.addPass(arc::createArcCanonicalizerPass());
76+
pm.addNestedPass<hw::HWModuleOp>(arc::createMergeTaps());
77+
if (options.shouldMakeLUTs)
78+
pm.addPass(arc::createMakeTablesPass());
79+
pm.addPass(createCSEPass());
80+
pm.addPass(arc::createArcCanonicalizerPass());
81+
82+
// Now some arguments may be unused because reset conditions are not passed as
83+
// inputs anymore pm.addPass(arc::createRemoveUnusedArcArgumentsPass());
84+
// Because we replace a lot of StateOp inputs with constants in the enable
85+
// patterns we may be able to sink a lot of them
86+
// TODO: maybe merge RemoveUnusedArcArguments with SinkInputs?
87+
// pm.addPass(arc::createSinkInputsPass());
88+
// pm.addPass(createCSEPass());
89+
// pm.addPass(createSimpleCanonicalizerPass());
90+
// Removing some muxes etc. may lead to additional dedup opportunities
91+
// if (options.shouldDedup)
92+
// pm.addPass(arc::createDedupPass());
93+
}
94+
95+
void circt::arcStateLoweringPipeline(PassManager &pm,
96+
const ArcStateLoweringOptions &options) {
97+
pm.addPass(arc::createLowerStatePass());
98+
99+
// TODO: LowerClocksToFuncsPass might not properly consider scf.if operations
100+
// (or nested regions in general) and thus errors out when muxes are also
101+
// converted in the hw.module or arc.model
102+
// TODO: InlineArcs seems to not properly handle scf.if operations, thus the
103+
// following is commented out
104+
// pm.addPass(arc::createMuxToControlFlowPass());
105+
if (options.shouldInline)
106+
pm.addPass(arc::createInlineArcsPass());
107+
108+
pm.addPass(arc::createMergeIfsPass());
109+
pm.addPass(createCSEPass());
110+
pm.addPass(arc::createArcCanonicalizerPass());
111+
}
112+
113+
void circt::arcStateAllocationPipeline(
114+
PassManager &pm, const ArcStateAllocationOptions &options) {
115+
pm.addPass(arc::createLowerArcsToFuncsPass());
116+
pm.nest<arc::ModelOp>().addPass(arc::createAllocateStatePass());
117+
pm.addPass(arc::createLowerClocksToFuncsPass()); // no CSE between state alloc
118+
// and clock func lowering
119+
if (options.splitFuncsThreshold.getNumOccurrences()) {
120+
pm.addPass(arc::createSplitFuncs({options.splitFuncsThreshold}));
121+
}
122+
pm.addPass(createCSEPass());
123+
pm.addPass(arc::createArcCanonicalizerPass());
124+
}
125+
126+
void circt::arcToLLVMPipeline(PassManager &pm) {
127+
pm.addPass(createLowerArcToLLVMPass());
128+
pm.addPass(createCSEPass());
129+
pm.addPass(arc::createArcCanonicalizerPass());
130+
}

0 commit comments

Comments
 (0)