CIRCT 23.0.0git
Loading...
Searching...
No Matches
SVMaskNonSynthesizable.cpp
Go to the documentation of this file.
1//===- SVMaskNonSynthesizable.cpp - Mask non-synthesizable SV ops ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass hides a fixed set of `sv` operations that are not generally
10// synthesizable (concurrent and property assertions) from the SystemVerilog
11// output. It supports two masking modes:
12//
13// * `delete`: erase the matched ops.
14// * `ifdef` : wrap each matched op individually in an
15// `sv.ifdef`/`sv.ifdef.procedural` whose else region holds the
16// moved op, plus a single `sv.macro.decl @SYNTHESIS` at the top
17// of the module if absent.
18//
19//===----------------------------------------------------------------------===//
20
27#include "circt/Support/Utils.h"
28#include "mlir/IR/Builders.h"
29#include "mlir/Pass/Pass.h"
30#include "llvm/ADT/TypeSwitch.h"
31
32namespace circt {
33namespace sv {
34#define GEN_PASS_DEF_SVMASKNONSYNTHESIZABLE
35#include "circt/Dialect/SV/SVPasses.h.inc"
36} // namespace sv
37} // namespace circt
38
39using namespace circt;
40using namespace circt::sv;
41
42//===----------------------------------------------------------------------===//
43// Helpers
44//===----------------------------------------------------------------------===//
45
46/// Returns true if `op` is one of the SV ops we mask.
47static bool isMaskedOp(Operation *op) {
48 return isa<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
49 sv::CoverConcurrentOp, sv::AssertPropertyOp, sv::AssumePropertyOp,
50 sv::CoverPropertyOp>(op);
51}
52
53/// If `op` is an `sv.ifdef`/`sv.ifdef.procedural` whose macro matches
54/// `expectedMacro`, returns its else region; otherwise returns nullptr. Used
55/// to identify the region that need not be re-wrapped in `ifdef` mode.
56static Region *getMatchingIfDefElseRegion(Operation *op,
57 StringRef expectedMacro) {
58 return llvm::TypeSwitch<Operation *, Region *>(op)
59 .Case<sv::IfDefOp, sv::IfDefProceduralOp>([&](auto ifdef) -> Region * {
60 if (ifdef.getCond().getName() != expectedMacro)
61 return nullptr;
62 return &ifdef.getElseRegion();
63 })
64 .Default(static_cast<Region *>(nullptr));
65}
66
67/// Resolve the symbol name to use for the `sv.macro.decl` referenced by
68/// `ifdef` mode's `sv.ifdef` ops. Returns the existing decl's sym_name if a
69/// `sv.macro.decl` whose Verilog identifier matches `verilogName` already
70/// exists at top level; otherwise returns a fresh sym_name that does not
71/// collide with any existing top-level symbol (which may differ from
72/// `verilogName` if a non-`sv.macro.decl` symbol of that name is present).
73/// `created` is set to true iff the returned name belongs to a decl that
74/// the caller still needs to materialize.
75static StringAttr resolveMacroSymName(mlir::ModuleOp moduleOp,
76 StringRef verilogName, bool &created) {
77 for (auto decl : moduleOp.getOps<sv::MacroDeclOp>()) {
78 if (decl.getMacroIdentifier() == verilogName) {
79 created = false;
80 return decl.getSymNameAttr();
81 }
82 }
83 Namespace ns;
84 ns.add(moduleOp);
85 StringRef name = ns.newName(verilogName);
86 created = true;
87 return StringAttr::get(moduleOp.getContext(), name);
88}
89
90/// `delete` mode: walk the block and erase every masked op.
91static void processBlockDelete(Block &block) {
92 block.walk([&](Operation *op) {
93 if (isMaskedOp(op))
94 op->erase();
95 });
96}
97
98/// Wrap a single masked op in its own `sv.ifdef`/`sv.ifdef.procedural`. Picks
99/// the procedural variant based on the enclosing region.
100static void maskOpByIfdef(Operation *op, StringRef macro) {
101 OpBuilder builder(op);
102 Location loc = op->getLoc();
103
104 // Passing a non-null `elseCtor` is what triggers the builder to create an
105 // else block.
106 Block *elseBlock;
107 if (isInProceduralRegion(op))
108 elseBlock =
109 sv::IfDefProceduralOp::create(builder, loc, macro,
110 /*thenCtor=*/{}, /*elseCtor=*/[]() {})
111 .getElseBlock();
112 else
113 elseBlock = sv::IfDefOp::create(builder, loc, macro, /*thenCtor=*/{},
114 /*elseCtor=*/[]() {})
115 .getElseBlock();
116
117 // Move the op into the else block.
118 op->moveBefore(elseBlock, elseBlock->end());
119}
120
121/// `ifdef` mode: wrap each masked op in its own
122/// `sv.ifdef`/`sv.ifdef.procedural`. Recurse into nested regions of non-masked
123/// ops, but skip recursion into the else region of a matching
124/// `sv.ifdef @<macro>` -- those ops are already guarded.
125static bool processBlockIfdef(Block &block, StringRef macro) {
126 bool changed = false;
127 // `make_early_inc_range` lets us move the current op out of `block` inside
128 // the loop body without invalidating the iterator.
129 for (Operation &op : llvm::make_early_inc_range(block)) {
130 if (isMaskedOp(&op)) {
131 maskOpByIfdef(&op, macro);
132 changed = true;
133 continue;
134 }
135 Region *guardedElseRegion = getMatchingIfDefElseRegion(&op, macro);
136 for (Region &region : op.getRegions()) {
137 if (&region == guardedElseRegion)
138 continue;
139 for (Block &nested : region)
140 changed |= processBlockIfdef(nested, macro);
141 }
142 }
143 return changed;
144}
145
146//===----------------------------------------------------------------------===//
147// Pass
148//===----------------------------------------------------------------------===//
149
150namespace {
151struct SVMaskNonSynthesizablePass
152 : public circt::sv::impl::SVMaskNonSynthesizableBase<
153 SVMaskNonSynthesizablePass> {
154 using Base::Base;
155 void runOnOperation() override;
156};
157} // namespace
158
159void SVMaskNonSynthesizablePass::runOnOperation() {
160 mlir::ModuleOp moduleOp = getOperation();
161
162 // For `ifdef` mode, resolve the symbol name to use for the macro decl
163 // up front.
164 StringAttr macroSymName;
165 bool macroDeclNeedsCreation = false;
166 if (mode == MaskNonSynthesizableMode::Ifdef)
167 macroSymName = resolveMacroSymName(moduleOp, macro, macroDeclNeedsCreation);
168
169 StringRef passDownMacro =
170 macroSymName ? macroSymName.getValue() : StringRef();
171 // Each `hw.module`'s body is independent: `processBlock*` only mutates ops
172 // inside the module it's called on.
173 SmallVector<hw::HWModuleOp> hwModules(moduleOp.getOps<hw::HWModuleOp>());
174 unsigned numChanged =
175 transformReduce(&getContext(), hwModules, /*init=*/0u, std::plus<>(),
176 [&](hw::HWModuleOp hwModule) -> unsigned {
177 Block &body = *hwModule.getBodyBlock();
178 switch (mode) {
179 case MaskNonSynthesizableMode::Delete:
180 processBlockDelete(body);
181 return 0;
182 case MaskNonSynthesizableMode::Ifdef:
183 return processBlockIfdef(body, passDownMacro) ? 1 : 0;
184 }
185 llvm_unreachable("all modes handled");
186 });
187
188 if (mode == MaskNonSynthesizableMode::Ifdef && numChanged > 0 &&
189 macroDeclNeedsCreation) {
190 auto builder = OpBuilder::atBlockBegin(moduleOp.getBody());
191 sv::MacroDeclOp::create(builder, moduleOp.getLoc(), macroSymName,
192 /*args=*/ArrayAttr{}, builder.getStringAttr(macro));
193 }
194}
static bool isMaskedOp(Operation *op)
Returns true if op is one of the SV ops we mask.
static void maskOpByIfdef(Operation *op, StringRef macro)
Wrap a single masked op in its own sv.ifdef/sv.ifdef.procedural.
static Region * getMatchingIfDefElseRegion(Operation *op, StringRef expectedMacro)
If op is an sv.ifdef/sv.ifdef.procedural whose macro matches expectedMacro, returns its else region; ...
static bool processBlockIfdef(Block &block, StringRef macro)
ifdef mode: wrap each masked op in its own sv.ifdef/sv.ifdef.procedural.
static StringAttr resolveMacroSymName(mlir::ModuleOp moduleOp, StringRef verilogName, bool &created)
Resolve the symbol name to use for the sv.macro.decl referenced by ifdef mode's sv....
static void processBlockDelete(Block &block)
delete mode: walk the block and erase every masked op.
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
void add(mlir::ModuleOp module)
Definition Namespace.h:48
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition Namespace.h:87
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
Definition Utils.h:81
bool isInProceduralRegion(Operation *op)
Returns true if op has a parent marked as a procedural region that is closer than any parent marked a...
Definition sv.py:1