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;
74 if (
auto xorOp = dyn_cast<comb::XorOp>(op))
75 return xorOp.isBinaryNot();
81static std::optional<APInt>
getInt(Value value) {
82 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(value.getDefiningOp()))
83 return cst.getValue();
94 auto *srcOp = src.getDefiningOp();
95 auto *dstOp = dst.getDefiningOp();
99 return TypeSwitch<Operation *, bool>(srcOp)
101 auto toField = dyn_cast<sv::StructFieldInOutOp>(dstOp);
104 if (toField.getFieldAttr() != extract.getFieldNameAttr())
106 return isSelfWrite(toField.getInput(), extract.getInput());
108 .Case<hw::ArrayGetOp>([&](
auto get) {
109 auto toGet = dyn_cast<sv::ArrayIndexInOutOp>(dstOp);
110 if (!toGet || toGet.getIndex().getType() != get.getIndex().getType())
112 auto toIdx =
getInt(toGet.getIndex());
113 auto fromIdx =
getInt(get.getIndex());
114 if (!toIdx || !fromIdx || toIdx != fromIdx)
116 return isSelfWrite(toGet.getInput(), get.getInput());
118 .Case<sv::ReadInOutOp>([&](
auto read) {
return dst == read.getInput(); })
119 .Default([&](
auto srcOp) {
return false; });
123bool PrettifyVerilogPass::splitStructAssignment(OpBuilder &builder,
124 hw::StructType ty, Value dst,
128 DenseMap<StringAttr, std::pair<Location, Value>> fields;
129 while (
auto inj = dyn_cast_or_null<hw::StructInjectOp>(src.getDefiningOp())) {
132 auto field = std::make_pair(inj.getLoc(), inj.getNewValue());
133 fields.try_emplace(inj.getFieldNameAttr(), field);
134 src = inj.getInput();
139 if (!
isSelfWrite(dst, src) && ty.getElements().size() != fields.size())
143 for (
auto &field : ty.getElements()) {
144 const auto &name = field.name;
146 auto it = fields.find(name);
147 if (it == fields.end())
150 auto &[loc, value] = it->second;
151 auto ref = sv::StructFieldInOutOp::create(builder, loc, dst, name);
152 if (!splitAssignment(builder, ref, value))
153 sv::PAssignOp::create(builder, loc, ref, value);
159bool PrettifyVerilogPass::splitArrayAssignment(OpBuilder &builder,
160 hw::ArrayType ty, Value dst,
163 if (
auto op = dyn_cast_or_null<hw::ArrayCreateOp>(src.getDefiningOp())) {
166 auto ty = hw::type_cast<hw::ArrayType>(op.getType());
167 if (ty.getNumElements() != 1)
169 APInt zero(std::max(1u, llvm::Log2_64_Ceil(ty.getNumElements())), 0);
171 Value value = op.getInputs()[0];
172 auto loc = op.getLoc();
175 auto field = sv::ArrayIndexInOutOp::create(builder, loc, dst, index);
176 if (!splitAssignment(builder, field, value))
177 sv::PAssignOp::create(builder, loc, field, value);
182 SmallVector<std::tuple<APInt, Location, Value>> fields;
184 dyn_cast_or_null<hw::ArrayConcatOp>(src.getDefiningOp())) {
185 auto loc = concat.getLoc();
190 if (concat.getNumOperands() == 2) {
191 auto c = concat.getInputs();
193 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[1].getDefiningOp());
194 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
195 auto midL = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
196 auto midR = dyn_cast_or_null<hw::ArrayCreateOp>(c[0].getDefiningOp());
199 hw::type_cast<hw::ArrayType>(concat.getType()).getNumElements();
201 auto baseIdx =
getInt(lhs.getLowIndex());
202 if (!baseIdx || *baseIdx != 0 || midR.getInputs().size() != 1)
204 fields.emplace_back(APInt(baseIdx->getBitWidth(), size - 1), loc,
205 midR.getInputs()[0]);
206 src = lhs.getInput();
210 auto baseIdx =
getInt(rhs.getLowIndex());
211 if (!baseIdx || *baseIdx != 1 || midL.getInputs().size() != 1)
213 src = rhs.getInput();
214 fields.emplace_back(APInt(baseIdx->getBitWidth(), 0), loc,
215 midL.getInputs()[0]);
224 if (concat.getNumOperands() == 3) {
225 auto c = concat.getInputs();
226 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
227 auto mid = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
228 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[2].getDefiningOp());
229 if (!lhs || !mid || !rhs || mid.getInputs().size() != 1)
231 auto elem = mid.getInputs()[0];
232 auto arr = lhs.getInput();
233 if (arr != rhs.getInput() || arr.getType() != concat.getType())
237 hw::type_cast<hw::ArrayType>(lhs.getType()).getNumElements();
238 auto lhsIdx =
getInt(lhs.getLowIndex());
239 auto rhsIdx =
getInt(rhs.getLowIndex());
240 if (!lhsIdx || *lhsIdx != 0)
242 if (!rhsIdx || *rhsIdx != lhsSize + 1)
244 fields.emplace_back(*rhsIdx - 1, loc, elem);
255 std::stable_sort(fields.begin(), fields.end(), [](
auto l,
auto r) {
256 return std::get<0>(l).ult(std::get<0>(r));
259 std::optional<APInt> last;
260 for (
auto &[i, loc, value] : fields) {
264 auto field = sv::ArrayIndexInOutOp::create(builder, loc, dst, index);
265 if (!splitAssignment(builder, field, value))
266 sv::PAssignOp::create(builder, loc, field, value);
276bool PrettifyVerilogPass::splitAssignment(OpBuilder &builder, Value dst,
281 if (
auto ty = hw::type_dyn_cast<hw::StructType>(src.getType()))
282 return splitStructAssignment(builder, ty, dst, src);
284 if (
auto ty = hw::type_dyn_cast<hw::ArrayType>(src.getType()))
285 return splitArrayAssignment(builder, ty, dst, src);
293void PrettifyVerilogPass::sinkOrCloneOpToUses(Operation *op) {
294 assert(mlir::isMemoryEffectFree(op) &&
295 "Op with side effects cannot be sunk to its uses.");
296 auto block = op->getBlock();
299 for (
auto &use :
llvm::make_early_inc_range(op->getUses())) {
302 auto localBlock = use.getOwner()->getBlock();
303 if (block == localBlock)
307 auto &localValue = blockLocalValues[localBlock];
310 localValue = OpBuilder::atBlockBegin(localBlock).clone(*op)->getResult(0);
314 anythingChanged =
true;
317 if (op->use_empty()) {
319 anythingChanged =
true;
325bool PrettifyVerilogPass::prettifyUnaryOperator(Operation *op) {
340 if (op->use_empty() || op->hasOneUse())
345 for (
auto *user : op->getUsers()) {
346 if (isa<comb::ExtractOp, hw::ArraySliceOp>(user))
350 if (!options.allowExprInEventControl &&
351 isa<sv::AlwaysFFOp, sv::AlwaysOp, sv::AssertConcurrentOp,
352 sv::AssumeConcurrentOp, sv::CoverConcurrentOp, sv::AssertPropertyOp,
353 sv::AssumePropertyOp, sv::CoverPropertyOp>(user))
359 auto cloneConstantOperandsIfNeeded = [&](Operation *op) {
360 for (
auto &operand : op->getOpOperands()) {
367 if (constant->getBlock() != op->getBlock())
368 operand.set(OpBuilder(op).clone(*constant)->getResult(0));
372 while (!op->hasOneUse()) {
373 OpOperand &use = *op->use_begin();
374 Operation *user = use.getOwner();
377 auto *cloned = OpBuilder(user).clone(*op);
378 cloneConstantOperandsIfNeeded(cloned);
381 use.set(cloned->getResult(0));
385 Operation *user = *op->user_begin();
386 op->moveBefore(user);
387 cloneConstantOperandsIfNeeded(op);
389 anythingChanged =
true;
397 while (block != topBlock) {
398 block = block->getParentOp()->getBlock();
409void PrettifyVerilogPass::sinkExpression(Operation *op) {
414 Block *curOpBlock = op->getBlock();
417 if (op->hasOneUse()) {
418 if (curOpBlock != op->user_begin()->getBlock()) {
421 if (!mlir::isMemoryEffectFree(op))
424 op->moveBefore(*op->user_begin());
425 anythingChanged =
true;
431 auto userIt = op->user_begin();
432 Block *ncaBlock = userIt->getBlock();
434 unsigned ncaBlockDepth =
getBlockDepth(ncaBlock, curOpBlock);
435 if (ncaBlockDepth == 0)
438 for (
auto e = op->user_end(); userIt != e; ++userIt) {
439 auto *userBlock = userIt->getBlock();
440 if (userBlock == curOpBlock)
442 if (userBlock == ncaBlock)
447 unsigned userBlockDepth =
getBlockDepth(userBlock, curOpBlock);
448 while (userBlock != ncaBlock) {
449 if (ncaBlockDepth < userBlockDepth) {
450 userBlock = userBlock->getParentOp()->getBlock();
452 }
else if (userBlockDepth < ncaBlockDepth) {
453 ncaBlock = ncaBlock->getParentOp()->getBlock();
456 userBlock = userBlock->getParentOp()->getBlock();
458 ncaBlock = ncaBlock->getParentOp()->getBlock();
463 if (ncaBlockDepth == 0)
469 if (!mlir::isMemoryEffectFree(op))
474 assert(ncaBlock != curOpBlock &&
"should have bailed out earlier");
475 op->moveBefore(&ncaBlock->front());
476 anythingChanged =
true;
479void PrettifyVerilogPass::processPostOrder(Block &body) {
480 SmallVector<Operation *> instances;
484 llvm::make_early_inc_range(
llvm::reverse(body.getOperations()))) {
485 if (op.getNumRegions()) {
486 for (
auto ®ion : op.getRegions())
487 for (auto ®ionBlock : region.getBlocks())
488 processPostOrder(regionBlock);
492 if (
auto assign = dyn_cast<sv::PAssignOp>(op)) {
493 auto dst = assign.getDest();
494 auto src = assign.getSrc();
496 OpBuilder builder(assign);
497 if (splitAssignment(builder, dst, src)) {
498 anythingChanged =
true;
499 toDelete.insert(src.getDefiningOp());
500 toDelete.insert(dst.getDefiningOp());
514 if (matchPattern(&op, mlir::m_Constant()) ||
516 sv::IndexedPartSelectInOutOp, hw::ParamValueOp>(op)) {
517 sinkOrCloneOpToUses(&op);
526 if (hw::isCombinational(&op) || sv::isExpression(&op)) {
531 if (isa<hw::InstanceOp>(op))
532 instances.push_back(&op);
539 if (!instances.empty()) {
540 for (Operation *instance :
llvm::reverse(instances)) {
541 if (instance != &body.back())
542 instance->moveBefore(&body.back());
547void PrettifyVerilogPass::runOnOperation() {
549 options =
LoweringOptions(thisModule->getParentOfType<mlir::ModuleOp>());
553 anythingChanged =
false;
556 processPostOrder(*thisModule.getBodyBlock());
559 while (!toDelete.empty()) {
560 auto *op = toDelete.pop_back_val();
561 if (!op || !isOpTriviallyDead(op))
564 for (
auto operand : op->getOperands())
565 toDelete.insert(operand.getDefiningOp());
572 if (!anythingChanged)
573 markAllAnalysesPreserved();
assert(baseType &&"element must be base type")
static bool isPrettifiableUnaryOperator(Operation *op)
Return true if this is a unary operator that is cheap enough to sink or duplicate into its users as p...
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)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Options which control the emission from CIRCT to Verilog.