Skip to content

Commit 253025a

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
1 parent 4d81b97 commit 253025a

File tree

6 files changed

+262
-81
lines changed

6 files changed

+262
-81
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#ifndef TOOLS_ARCILATOR_PIPELINES_H
2+
#define TOOLS_ARCILATOR_PIPELINES_H
3+
4+
#include "mlir/Pass/PassManager.h"
5+
#include "mlir/Pass/PassOptions.h"
6+
#include "llvm/Support/CommandLine.h"
7+
8+
namespace circt {
9+
10+
// Pre-process the input such that it no longer contains any SV dialect ops
11+
// and external modules that are relevant to the arc transformation are
12+
// represented as intrinsic ops.
13+
struct ArcPreprocessingOptions
14+
: mlir::PassPipelineOptions<ArcPreprocessingOptions> {
15+
Option<bool> observePorts{*this, "observe-ports",
16+
llvm::cl::desc("Make all ports observable"),
17+
llvm::cl::init(false)};
18+
19+
Option<bool> observeWires{*this, "observe-wires",
20+
llvm::cl::desc("Make all wires observable"),
21+
llvm::cl::init(false)};
22+
23+
Option<bool> observeNamedValues{
24+
*this, "observe-named-values",
25+
llvm::cl::desc("Make values with `sv.namehint` observable"),
26+
llvm::cl::init(false)};
27+
28+
Option<bool> observeMemories{
29+
*this, "observe-memories",
30+
llvm::cl::desc("Make all memory contents observable"),
31+
llvm::cl::init(false)};
32+
};
33+
void arcPreprocessingPipeline(mlir::PassManager &pm,
34+
const ArcPreprocessingOptions &options = {});
35+
36+
// Restructure the input from a `hw.module` hierarchy to a collection of arcs.
37+
struct ArcConversionOptions : mlir::PassPipelineOptions<ArcConversionOptions> {
38+
Option<bool> observeRegisters{*this, "observe-registers",
39+
llvm::cl::desc("Make all registers observable"),
40+
llvm::cl::init(false)};
41+
42+
Option<bool> shouldDedup{*this, "dedup", llvm::cl::desc("Deduplicate arcs"),
43+
llvm::cl::init(true)};
44+
};
45+
void arcConversionPipeline(mlir::PassManager &pm,
46+
const ArcConversionOptions &options = {});
47+
48+
// Perform arc-level optimizations that are not specific to software
49+
// simulation.
50+
struct ArcOptimizationOptions
51+
: mlir::PassPipelineOptions<ArcOptimizationOptions> {
52+
Option<bool> shouldDedup{*this, "dedup", llvm::cl::desc("Deduplicate arcs"),
53+
llvm::cl::init(true)};
54+
55+
Option<bool> shouldDetectEnables{
56+
*this, "detect-enables",
57+
llvm::cl::desc("Infer enable conditions for states to avoid computation"),
58+
llvm::cl::init(true)};
59+
60+
Option<bool> shouldDetectResets{
61+
*this, "detect-resets",
62+
llvm::cl::desc("Infer reset conditions for states to avoid computation"),
63+
llvm::cl::init(false)};
64+
65+
Option<bool> shouldMakeLUTs{
66+
*this, "lookup-tables",
67+
llvm::cl::desc("Optimize arcs into lookup tables"), llvm::cl::init(true)};
68+
};
69+
void arcOptimizationPipeline(mlir::PassManager &pm,
70+
const ArcOptimizationOptions &options = {});
71+
72+
// Lower stateful arcs into explicit state reads and writes.
73+
struct ArcStateLoweringOptions
74+
: mlir::PassPipelineOptions<ArcStateLoweringOptions> {
75+
Option<bool> shouldInline{*this, "inline", llvm::cl::desc("Inline arcs"),
76+
llvm::cl::init(true)};
77+
};
78+
void arcStateLoweringPipeline(mlir::PassManager &pm,
79+
const ArcStateLoweringOptions &options = {});
80+
81+
// Allocate states.
82+
struct ArcStateAllocationOptions
83+
: mlir::PassPipelineOptions<ArcStateAllocationOptions> {
84+
Option<unsigned> splitFuncsThreshold{
85+
*this, "split-funcs-threshold",
86+
llvm::cl::desc("Split large MLIR functions that occur above the given "
87+
"size threshold"),
88+
llvm::cl::ValueOptional};
89+
};
90+
void arcStateAllocationPipeline(mlir::PassManager &pm,
91+
const ArcStateAllocationOptions &options = {});
92+
93+
// Lower the arcs and update functions to LLVM.,
94+
void arcToLLVMPipeline(mlir::PassManager &pm);
95+
96+
} // namespace circt
97+
98+
#endif // TOOLS_ARCILATOR_PIPELINE_H

lib/Tools/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
add_subdirectory(arcilator)
12
add_subdirectory(circt-bmc)
23
add_subdirectory(circt-lec)
34

lib/Tools/arcilator/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
add_circt_library(CIRCTArcilator
2+
pipelines.cpp
3+
4+
ADDITIONAL_HEADER_DIRS
5+
${MLIR_MAIN_INCLUDE_DIR}/circt/Tools/arcilator
6+
)

lib/Tools/arcilator/pipelines.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#include "circt/Tools/arcilator/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+
}

tools/arcilator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(libs
1010
CIRCTArc
1111
CIRCTArcToLLVM
1212
CIRCTArcTransforms
13+
CIRCTArcilator
1314
CIRCTCombToArith
1415
CIRCTConvertToArcs
1516
CIRCTEmitTransforms

tools/arcilator/arcilator.cpp

Lines changed: 26 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "circt/Dialect/Verif/VerifDialect.h"
3737
#include "circt/Support/Passes.h"
3838
#include "circt/Support/Version.h"
39+
#include "circt/Tools/arcilator/pipelines.h"
3940
#include "mlir/Bytecode/BytecodeReader.h"
4041
#include "mlir/Bytecode/BytecodeWriter.h"
4142
#include "mlir/Dialect/Arith/IR/Arith.h"
@@ -250,105 +251,49 @@ static void populateHwModuleToArcPipeline(PassManager &pm) {
250251
// represented as intrinsic ops.
251252
if (untilReached(UntilPreprocessing))
252253
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());
254+
255+
ArcPreprocessingOptions preprocessing_opt;
256+
preprocessing_opt.observePorts = observePorts;
257+
preprocessing_opt.observeWires = observeWires;
258+
preprocessing_opt.observeNamedValues = observeNamedValues;
259+
preprocessing_opt.observeMemories = observeMemories;
260+
arcPreprocessingPipeline(pm, preprocessing_opt);
274261

275262
// Restructure the input from a `hw.module` hierarchy to a collection of arcs.
276263
if (untilReached(UntilArcConversion))
277264
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());
265+
266+
ArcConversionOptions conversion_opt;
267+
conversion_opt.observeRegisters = observeRegisters;
268+
conversion_opt.shouldDedup = shouldDedup;
269+
arcConversionPipeline(pm, conversion_opt);
288270

289271
// Perform arc-level optimizations that are not specific to software
290272
// simulation.
291273
if (untilReached(UntilArcOpt))
292274
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());
309275

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());
276+
ArcOptimizationOptions optimization_opt;
277+
optimization_opt.shouldDetectEnables = shouldDetectEnables;
278+
optimization_opt.shouldDetectResets = shouldDetectResets;
279+
optimization_opt.shouldMakeLUTs = shouldMakeLUTs;
280+
arcOptimizationPipeline(pm, optimization_opt);
321281

322282
// Lower stateful arcs into explicit state reads and writes.
323283
if (untilReached(UntilStateLowering))
324284
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());
285+
286+
ArcStateLoweringOptions lowering_opt;
287+
lowering_opt.shouldInline = shouldInline;
288+
arcStateLoweringPipeline(pm, lowering_opt);
339289

340290
// Allocate states.
341291
if (untilReached(UntilStateAlloc))
342292
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());
293+
294+
ArcStateAllocationOptions allocation_opt;
295+
allocation_opt.splitFuncsThreshold = splitFuncsThreshold;
296+
arcStateAllocationPipeline(pm, allocation_opt);
352297
}
353298

354299
/// Populate a pass manager with the Arc to LLVM pipeline for the given

0 commit comments

Comments
 (0)