26#include "mlir/IR/ImplicitLocOpBuilder.h"
27#include "mlir/IR/Matchers.h"
28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/SetVector.h"
30#include "llvm/ADT/TypeSwitch.h"
34#define GEN_PASS_DEF_PRETTIFYVERILOG
35#include "circt/Dialect/SV/SVPasses.h.inc"
45struct PrettifyVerilogPass
46 :
public circt::sv::impl::PrettifyVerilogBase<PrettifyVerilogPass> {
47 void runOnOperation()
override;
50 void processPostOrder(
Block &block);
51 bool prettifyUnaryOperator(Operation *op);
52 void sinkOrCloneOpToUses(Operation *op);
53 void sinkExpression(Operation *op);
54 void useNamedOperands(Operation *op, DenseMap<Value, Operation *> &pipeMap);
56 bool splitStructAssignment(OpBuilder &builder, hw::StructType ty, Value dst,
58 bool splitArrayAssignment(OpBuilder &builder, hw::ArrayType ty, Value dst,
60 bool splitAssignment(OpBuilder &builder, Value dst, Value src);
65 llvm::SetVector<Operation *> toDelete;
72 if (isa<comb::ParityOp>(op))
75 if (
auto xorOp = dyn_cast<comb::XorOp>(op))
76 return xorOp.isBinaryNot();
78 if (
auto icmpOp = dyn_cast<comb::ICmpOp>(op))
79 return icmpOp.isEqualAllOnes() || icmpOp.isNotEqualZero();
85static std::optional<APInt>
getInt(Value value) {
86 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(value.getDefiningOp()))
87 return cst.getValue();
98 auto *srcOp = src.getDefiningOp();
99 auto *dstOp = dst.getDefiningOp();
100 if (!srcOp || !dstOp)
103 return TypeSwitch<Operation *, bool>(srcOp)
105 auto toField = dyn_cast<sv::StructFieldInOutOp>(dstOp);
108 if (toField.getFieldAttr() != extract.getFieldNameAttr())
110 return isSelfWrite(toField.getInput(), extract.getInput());
112 .Case<hw::ArrayGetOp>([&](
auto get) {
113 auto toGet = dyn_cast<sv::ArrayIndexInOutOp>(dstOp);
114 if (!toGet || toGet.getIndex().getType() != get.getIndex().getType())
116 auto toIdx =
getInt(toGet.getIndex());
117 auto fromIdx =
getInt(get.getIndex());
118 if (!toIdx || !fromIdx || toIdx != fromIdx)
120 return isSelfWrite(toGet.getInput(), get.getInput());
122 .Case<sv::ReadInOutOp>([&](
auto read) {
return dst == read.getInput(); })
123 .Default([&](
auto srcOp) {
return false; });
127bool PrettifyVerilogPass::splitStructAssignment(OpBuilder &builder,
128 hw::StructType ty, Value dst,
132 DenseMap<StringAttr, std::pair<Location, Value>> fields;
133 while (
auto inj = dyn_cast_or_null<hw::StructInjectOp>(src.getDefiningOp())) {
136 auto field = std::make_pair(inj.getLoc(), inj.getNewValue());
137 fields.try_emplace(inj.getFieldNameAttr(), field);
138 src = inj.getInput();
143 if (!
isSelfWrite(dst, src) && ty.getElements().size() != fields.size())
147 for (
auto &field : ty.getElements()) {
148 const auto &name = field.name;
150 auto it = fields.find(name);
151 if (it == fields.end())
154 auto &[loc, value] = it->second;
155 auto ref = sv::StructFieldInOutOp::create(builder, loc, dst, name);
156 if (!splitAssignment(builder, ref, value))
157 sv::PAssignOp::create(builder, loc, ref, value);
163bool PrettifyVerilogPass::splitArrayAssignment(OpBuilder &builder,
164 hw::ArrayType ty, Value dst,
167 if (
auto op = dyn_cast_or_null<hw::ArrayCreateOp>(src.getDefiningOp())) {
170 auto ty = hw::type_cast<hw::ArrayType>(op.getType());
171 if (ty.getNumElements() != 1)
173 APInt zero(std::max(1u, llvm::Log2_64_Ceil(ty.getNumElements())), 0);
175 Value value = op.getInputs()[0];
176 auto loc = op.getLoc();
179 auto field = sv::ArrayIndexInOutOp::create(builder, loc, dst, index);
180 if (!splitAssignment(builder, field, value))
181 sv::PAssignOp::create(builder, loc, field, value);
186 SmallVector<std::tuple<APInt, Location, Value>> fields;
188 dyn_cast_or_null<hw::ArrayConcatOp>(src.getDefiningOp())) {
189 auto loc = concat.getLoc();
194 if (concat.getNumOperands() == 2) {
195 auto c = concat.getInputs();
197 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[1].getDefiningOp());
198 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
199 auto midL = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
200 auto midR = dyn_cast_or_null<hw::ArrayCreateOp>(c[0].getDefiningOp());
203 hw::type_cast<hw::ArrayType>(concat.getType()).getNumElements();
205 auto baseIdx =
getInt(lhs.getLowIndex());
206 if (!baseIdx || *baseIdx != 0 || midR.getInputs().size() != 1)
208 fields.emplace_back(APInt(baseIdx->getBitWidth(), size - 1), loc,
209 midR.getInputs()[0]);
210 src = lhs.getInput();
214 auto baseIdx =
getInt(rhs.getLowIndex());
215 if (!baseIdx || *baseIdx != 1 || midL.getInputs().size() != 1)
217 src = rhs.getInput();
218 fields.emplace_back(APInt(baseIdx->getBitWidth(), 0), loc,
219 midL.getInputs()[0]);
228 if (concat.getNumOperands() == 3) {
229 auto c = concat.getInputs();
230 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
231 auto mid = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
232 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[2].getDefiningOp());
233 if (!lhs || !mid || !rhs || mid.getInputs().size() != 1)
235 auto elem = mid.getInputs()[0];
236 auto arr = lhs.getInput();
237 if (arr != rhs.getInput() ||
arr.getType() != concat.getType())
241 hw::type_cast<hw::ArrayType>(lhs.getType()).getNumElements();
242 auto lhsIdx =
getInt(lhs.getLowIndex());
243 auto rhsIdx =
getInt(rhs.getLowIndex());
244 if (!lhsIdx || *lhsIdx != 0)
246 if (!rhsIdx || *rhsIdx != lhsSize + 1)
248 fields.emplace_back(*rhsIdx - 1, loc, elem);
259 std::stable_sort(fields.begin(), fields.end(), [](
auto l,
auto r) {
260 return std::get<0>(l).ult(std::get<0>(r));
263 std::optional<APInt> last;
264 for (
auto &[i, loc, value] : fields) {
268 auto field = sv::ArrayIndexInOutOp::create(builder, loc, dst, index);
269 if (!splitAssignment(builder, field, value))
270 sv::PAssignOp::create(builder, loc, field, value);
280bool PrettifyVerilogPass::splitAssignment(OpBuilder &builder, Value dst,
285 if (
auto ty = hw::type_dyn_cast<hw::StructType>(src.getType()))
286 return splitStructAssignment(builder, ty, dst, src);
288 if (
auto ty = hw::type_dyn_cast<hw::ArrayType>(src.getType()))
289 return splitArrayAssignment(builder, ty, dst, src);
297void PrettifyVerilogPass::sinkOrCloneOpToUses(Operation *op) {
298 assert(mlir::isMemoryEffectFree(op) &&
299 "Op with side effects cannot be sunk to its uses.");
300 auto block = op->getBlock();
303 for (
auto &use :
llvm::make_early_inc_range(op->getUses())) {
306 auto localBlock = use.getOwner()->getBlock();
307 if (block == localBlock)
311 auto &localValue = blockLocalValues[localBlock];
314 localValue = OpBuilder::atBlockBegin(localBlock).clone(*op)->getResult(0);
318 anythingChanged =
true;
321 if (op->use_empty()) {
323 anythingChanged =
true;
329bool PrettifyVerilogPass::prettifyUnaryOperator(Operation *op) {
344 if (op->use_empty() || op->hasOneUse())
349 for (
auto *user : op->getUsers()) {
350 if (isa<comb::ExtractOp, hw::ArraySliceOp>(user))
354 if (!options.allowExprInEventControl &&
355 isa<sv::AlwaysFFOp, sv::AlwaysOp, sv::AssertConcurrentOp,
356 sv::AssumeConcurrentOp, sv::CoverConcurrentOp, sv::AssertPropertyOp,
357 sv::AssumePropertyOp, sv::CoverPropertyOp>(user))
363 auto cloneConstantOperandsIfNeeded = [&](Operation *op) {
364 for (
auto &operand : op->getOpOperands()) {
371 if (constant->getBlock() != op->getBlock())
372 operand.set(OpBuilder(op).clone(*constant)->getResult(0));
376 while (!op->hasOneUse()) {
377 OpOperand &use = *op->use_begin();
378 Operation *user = use.getOwner();
381 auto *cloned = OpBuilder(user).clone(*op);
382 cloneConstantOperandsIfNeeded(cloned);
385 use.set(cloned->getResult(0));
389 Operation *user = *op->user_begin();
390 op->moveBefore(user);
391 cloneConstantOperandsIfNeeded(op);
393 anythingChanged =
true;
401 while (block != topBlock) {
402 block = block->getParentOp()->getBlock();
413void PrettifyVerilogPass::sinkExpression(Operation *op) {
418 Block *curOpBlock = op->getBlock();
421 if (op->hasOneUse()) {
422 if (curOpBlock != op->user_begin()->getBlock()) {
425 if (!mlir::isMemoryEffectFree(op))
428 op->moveBefore(*op->user_begin());
429 anythingChanged =
true;
435 auto userIt = op->user_begin();
436 Block *ncaBlock = userIt->getBlock();
438 unsigned ncaBlockDepth =
getBlockDepth(ncaBlock, curOpBlock);
439 if (ncaBlockDepth == 0)
442 for (
auto e = op->user_end(); userIt != e; ++userIt) {
443 auto *userBlock = userIt->getBlock();
444 if (userBlock == curOpBlock)
446 if (userBlock == ncaBlock)
451 unsigned userBlockDepth =
getBlockDepth(userBlock, curOpBlock);
452 while (userBlock != ncaBlock) {
453 if (ncaBlockDepth < userBlockDepth) {
454 userBlock = userBlock->getParentOp()->getBlock();
456 }
else if (userBlockDepth < ncaBlockDepth) {
457 ncaBlock = ncaBlock->getParentOp()->getBlock();
460 userBlock = userBlock->getParentOp()->getBlock();
462 ncaBlock = ncaBlock->getParentOp()->getBlock();
467 if (ncaBlockDepth == 0)
473 if (!mlir::isMemoryEffectFree(op))
478 assert(ncaBlock != curOpBlock &&
"should have bailed out earlier");
479 op->moveBefore(&ncaBlock->front());
480 anythingChanged =
true;
483void PrettifyVerilogPass::processPostOrder(Block &body) {
484 SmallVector<Operation *> instances;
488 llvm::make_early_inc_range(
llvm::reverse(body.getOperations()))) {
489 if (op.getNumRegions()) {
490 for (
auto ®ion : op.getRegions())
491 for (auto ®ionBlock : region.getBlocks())
492 processPostOrder(regionBlock);
496 if (
auto assign = dyn_cast<sv::PAssignOp>(op)) {
497 auto dst = assign.getDest();
498 auto src = assign.getSrc();
500 OpBuilder builder(assign);
501 if (splitAssignment(builder, dst, src)) {
502 anythingChanged =
true;
503 toDelete.insert(src.getDefiningOp());
504 toDelete.insert(dst.getDefiningOp());
518 if (matchPattern(&op, mlir::m_Constant()) ||
520 sv::IndexedPartSelectInOutOp, hw::ParamValueOp>(op)) {
521 sinkOrCloneOpToUses(&op);
530 if (hw::isCombinational(&op) || sv::isExpression(&op)) {
535 if (isa<hw::InstanceOp>(op))
536 instances.push_back(&op);
543 if (!instances.empty()) {
544 for (Operation *instance :
llvm::reverse(instances)) {
545 if (instance != &body.back())
546 instance->moveBefore(&body.back());
551void PrettifyVerilogPass::runOnOperation() {
553 options =
LoweringOptions(thisModule->getParentOfType<mlir::ModuleOp>());
557 anythingChanged =
false;
560 processPostOrder(*thisModule.getBodyBlock());
563 while (!toDelete.empty()) {
564 auto *op = toDelete.pop_back_val();
565 if (!op || !isOpTriviallyDead(op))
568 for (
auto operand : op->getOperands())
569 toDelete.insert(operand.getDefiningOp());
576 if (!anythingChanged)
577 markAllAnalysesPreserved();
assert(baseType &&"element must be base type")
static bool isSelfWrite(Value dst, Value src)
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static unsigned getBlockDepth(Block *block, Block *topBlock)
static bool isVerilogUnaryOperator(Operation *op)
Return true if this is something that will get printed as a unary operator by the Verilog printer.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Options which control the emission from CIRCT to Verilog.