26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "mlir/IR/Matchers.h"
28 #include "mlir/Pass/Pass.h"
29 #include "llvm/ADT/TypeSwitch.h"
33 #define GEN_PASS_DEF_PRETTIFYVERILOG
34 #include "circt/Dialect/SV/SVPasses.h.inc"
38 using namespace circt;
44 struct PrettifyVerilogPass
45 :
public circt::sv::impl::PrettifyVerilogBase<PrettifyVerilogPass> {
46 void runOnOperation()
override;
49 void processPostOrder(
Block &block);
50 bool prettifyUnaryOperator(Operation *op);
51 void sinkOrCloneOpToUses(Operation *op);
52 void sinkExpression(Operation *op);
53 void useNamedOperands(Operation *op, DenseMap<Value, Operation *> &pipeMap);
55 bool splitStructAssignment(OpBuilder &builder, hw::StructType ty, Value dst,
57 bool splitArrayAssignment(OpBuilder &builder, hw::ArrayType ty, Value dst,
59 bool splitAssignment(OpBuilder &builder, Value dst, Value src);
64 DenseSet<Operation *> toDelete;
71 if (isa<comb::ParityOp>(op))
74 if (
auto xorOp = dyn_cast<comb::XorOp>(op))
75 return xorOp.isBinaryNot();
77 if (
auto icmpOp = dyn_cast<comb::ICmpOp>(op))
78 return icmpOp.isEqualAllOnes() || icmpOp.isNotEqualZero();
84 static std::optional<APInt>
getInt(Value value) {
85 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(value.getDefiningOp()))
86 return cst.getValue();
97 auto *srcOp = src.getDefiningOp();
98 auto *dstOp = dst.getDefiningOp();
102 return TypeSwitch<Operation *, bool>(srcOp)
104 auto toField = dyn_cast<sv::StructFieldInOutOp>(dstOp);
107 if (toField.getFieldAttr() != extract.getFieldNameAttr())
109 return isSelfWrite(toField.getInput(), extract.getInput());
111 .Case<hw::ArrayGetOp>([&](
auto get) {
112 auto toGet = dyn_cast<sv::ArrayIndexInOutOp>(dstOp);
113 if (!toGet || toGet.getIndex().getType() !=
get.getIndex().getType())
115 auto toIdx =
getInt(toGet.getIndex());
117 if (!toIdx || !fromIdx || toIdx != fromIdx)
121 .Case<sv::ReadInOutOp>([&](
auto read) {
return dst == read.getInput(); })
122 .Default([&](
auto srcOp) {
return false; });
126 bool PrettifyVerilogPass::splitStructAssignment(OpBuilder &builder,
127 hw::StructType ty, Value dst,
131 DenseMap<StringAttr, std::pair<Location, Value>> fields;
132 while (
auto inj = dyn_cast_or_null<hw::StructInjectOp>(src.getDefiningOp())) {
135 auto field = std::make_pair(inj.getLoc(), inj.getNewValue());
136 fields.try_emplace(inj.getFieldNameAttr(), field);
137 src = inj.getInput();
142 if (!
isSelfWrite(dst, src) && ty.getElements().size() != fields.size())
146 for (
auto &field : ty.getElements()) {
147 const auto &name = field.name;
149 auto it = fields.find(name);
150 if (it == fields.end())
153 auto &[loc, value] = it->second;
154 auto ref = builder.create<sv::StructFieldInOutOp>(loc, dst, name);
155 if (!splitAssignment(builder, ref, value))
156 builder.create<sv::PAssignOp>(loc, ref, value);
162 bool PrettifyVerilogPass::splitArrayAssignment(OpBuilder &builder,
163 hw::ArrayType ty, Value dst,
166 if (
auto op = dyn_cast_or_null<hw::ArrayCreateOp>(src.getDefiningOp())) {
169 auto ty = hw::type_cast<hw::ArrayType>(op.getType());
170 if (ty.getNumElements() != 1)
172 APInt zero(std::max(1u, llvm::Log2_64_Ceil(ty.getNumElements())), 0);
174 Value value = op.getInputs()[0];
175 auto loc = op.getLoc();
178 auto field = builder.
create<sv::ArrayIndexInOutOp>(loc, dst, index);
179 if (!splitAssignment(builder, field, value))
180 builder.create<sv::PAssignOp>(loc, field, value);
185 SmallVector<std::tuple<APInt, Location, Value>> fields;
187 dyn_cast_or_null<hw::ArrayConcatOp>(src.getDefiningOp())) {
188 auto loc =
concat.getLoc();
193 if (
concat.getNumOperands() == 2) {
194 auto c =
concat.getInputs();
196 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[1].getDefiningOp());
197 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
198 auto midL = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
199 auto midR = dyn_cast_or_null<hw::ArrayCreateOp>(c[0].getDefiningOp());
202 hw::type_cast<hw::ArrayType>(
concat.getType()).getNumElements();
204 auto baseIdx =
getInt(lhs.getLowIndex());
205 if (!baseIdx || *baseIdx != 0 || midR.getInputs().size() != 1)
207 fields.emplace_back(APInt(baseIdx->getBitWidth(), size - 1), loc,
208 midR.getInputs()[0]);
209 src = lhs.getInput();
213 auto baseIdx =
getInt(rhs.getLowIndex());
214 if (!baseIdx || *baseIdx != 1 || midL.getInputs().size() != 1)
216 src = rhs.getInput();
217 fields.emplace_back(APInt(baseIdx->getBitWidth(), 0), loc,
218 midL.getInputs()[0]);
227 if (
concat.getNumOperands() == 3) {
228 auto c =
concat.getInputs();
229 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
230 auto mid = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
231 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[2].getDefiningOp());
232 if (!lhs || !mid || !rhs || mid.getInputs().size() != 1)
234 auto elem = mid.getInputs()[0];
235 auto arr = lhs.getInput();
236 if (arr != rhs.getInput() || arr.getType() !=
concat.getType())
240 hw::type_cast<hw::ArrayType>(lhs.getType()).getNumElements();
241 auto lhsIdx =
getInt(lhs.getLowIndex());
242 auto rhsIdx =
getInt(rhs.getLowIndex());
243 if (!lhsIdx || *lhsIdx != 0)
245 if (!rhsIdx || *rhsIdx != lhsSize + 1)
247 fields.emplace_back(*rhsIdx - 1, loc, elem);
258 std::stable_sort(fields.begin(), fields.end(), [](
auto l,
auto r) {
259 return std::get<0>(l).ult(std::get<0>(r));
262 std::optional<APInt> last;
263 for (
auto &[i, loc, value] : fields) {
267 auto field = builder.
create<sv::ArrayIndexInOutOp>(loc, dst, index);
268 if (!splitAssignment(builder, field, value))
269 builder.create<sv::PAssignOp>(loc, field, value);
279 bool PrettifyVerilogPass::splitAssignment(OpBuilder &builder, Value dst,
284 if (
auto ty = hw::type_dyn_cast<hw::StructType>(src.getType()))
285 return splitStructAssignment(builder, ty, dst, src);
287 if (
auto ty = hw::type_dyn_cast<hw::ArrayType>(src.getType()))
288 return splitArrayAssignment(builder, ty, dst, src);
296 void PrettifyVerilogPass::sinkOrCloneOpToUses(Operation *op) {
297 assert(mlir::isMemoryEffectFree(op) &&
298 "Op with side effects cannot be sunk to its uses.");
299 auto block = op->getBlock();
302 for (
auto &use : llvm::make_early_inc_range(op->getUses())) {
305 auto localBlock = use.getOwner()->getBlock();
306 if (block == localBlock)
310 auto &localValue = blockLocalValues[localBlock];
313 localValue = OpBuilder::atBlockBegin(localBlock).clone(*op)->getResult(0);
317 anythingChanged =
true;
320 if (op->use_empty()) {
322 anythingChanged =
true;
328 bool PrettifyVerilogPass::prettifyUnaryOperator(Operation *op) {
343 if (op->use_empty() || op->hasOneUse())
348 for (
auto *user : op->getUsers()) {
349 if (isa<comb::ExtractOp, hw::ArraySliceOp>(user))
353 if (!options.allowExprInEventControl &&
354 isa<sv::AlwaysFFOp, sv::AlwaysOp, sv::AssertConcurrentOp,
355 sv::AssumeConcurrentOp, sv::CoverConcurrentOp, sv::AssertPropertyOp,
356 sv::AssumePropertyOp, sv::CoverPropertyOp>(user))
362 auto cloneConstantOperandsIfNeeded = [&](Operation *op) {
363 for (
auto &operand : op->getOpOperands()) {
370 if (constant->getBlock() != op->getBlock())
371 operand.set(OpBuilder(op).clone(*constant)->getResult(0));
375 while (!op->hasOneUse()) {
376 OpOperand &use = *op->use_begin();
377 Operation *user = use.getOwner();
380 auto *cloned = OpBuilder(user).clone(*op);
381 cloneConstantOperandsIfNeeded(cloned);
384 use.set(cloned->getResult(0));
388 Operation *user = *op->user_begin();
389 op->moveBefore(user);
390 cloneConstantOperandsIfNeeded(op);
392 anythingChanged =
true;
400 while (block != topBlock) {
401 block = block->getParentOp()->getBlock();
412 void PrettifyVerilogPass::sinkExpression(Operation *op) {
417 Block *curOpBlock = op->getBlock();
420 if (op->hasOneUse()) {
421 if (curOpBlock != op->user_begin()->getBlock()) {
424 if (!mlir::isMemoryEffectFree(op))
427 op->moveBefore(*op->user_begin());
428 anythingChanged =
true;
434 auto userIt = op->user_begin();
435 Block *ncaBlock = userIt->getBlock();
437 unsigned ncaBlockDepth =
getBlockDepth(ncaBlock, curOpBlock);
438 if (ncaBlockDepth == 0)
441 for (
auto e = op->user_end(); userIt != e; ++userIt) {
442 auto *userBlock = userIt->getBlock();
443 if (userBlock == curOpBlock)
445 if (userBlock == ncaBlock)
450 unsigned userBlockDepth =
getBlockDepth(userBlock, curOpBlock);
451 while (userBlock != ncaBlock) {
452 if (ncaBlockDepth < userBlockDepth) {
453 userBlock = userBlock->getParentOp()->getBlock();
455 }
else if (userBlockDepth < ncaBlockDepth) {
456 ncaBlock = ncaBlock->getParentOp()->getBlock();
459 userBlock = userBlock->getParentOp()->getBlock();
461 ncaBlock = ncaBlock->getParentOp()->getBlock();
466 if (ncaBlockDepth == 0)
472 if (!mlir::isMemoryEffectFree(op))
477 assert(ncaBlock != curOpBlock &&
"should have bailed out earlier");
478 op->moveBefore(&ncaBlock->front());
479 anythingChanged =
true;
482 void PrettifyVerilogPass::processPostOrder(Block &body) {
483 SmallVector<Operation *> instances;
487 llvm::make_early_inc_range(llvm::reverse(body.getOperations()))) {
488 if (op.getNumRegions()) {
489 for (
auto ®ion : op.getRegions())
490 for (
auto ®ionBlock : region.getBlocks())
491 processPostOrder(regionBlock);
495 if (
auto assign = dyn_cast<sv::PAssignOp>(op)) {
496 auto dst = assign.getDest();
497 auto src = assign.getSrc();
499 OpBuilder builder(assign);
500 if (splitAssignment(builder, dst, src)) {
501 anythingChanged =
true;
502 toDelete.insert(src.getDefiningOp());
503 toDelete.insert(dst.getDefiningOp());
517 if (matchPattern(&op, mlir::m_Constant()) ||
519 sv::IndexedPartSelectInOutOp, hw::ParamValueOp>(op)) {
520 sinkOrCloneOpToUses(&op);
534 if (isa<hw::InstanceOp>(op))
535 instances.push_back(&op);
542 if (!instances.empty()) {
543 for (Operation *instance : llvm::reverse(instances)) {
544 if (instance != &body.back())
545 instance->moveBefore(&body.back());
550 void PrettifyVerilogPass::runOnOperation() {
552 options =
LoweringOptions(thisModule->getParentOfType<mlir::ModuleOp>());
556 anythingChanged =
false;
559 processPostOrder(*thisModule.getBodyBlock());
562 while (!toDelete.empty()) {
563 auto it = toDelete.begin();
567 if (!op || !isOpTriviallyDead(op))
570 for (
auto operand : op->getOperands())
571 toDelete.insert(operand.getDefiningOp());
578 if (!anythingChanged)
579 markAllAnalysesPreserved();
583 return std::make_unique<PrettifyVerilogPass>();
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static bool isSelfWrite(Value dst, Value src)
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.
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
bool isExpression(Operation *op)
Return true if the specified operation is an expression.
std::unique_ptr< mlir::Pass > createPrettifyVerilogPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Options which control the emission from CIRCT to Verilog.