26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "mlir/IR/Matchers.h"
28 #include "llvm/ADT/TypeSwitch.h"
30 using namespace circt;
37 struct PrettifyVerilogPass
38 :
public sv::PrettifyVerilogBase<PrettifyVerilogPass> {
39 void runOnOperation()
override;
42 void processPostOrder(Block &block);
43 bool prettifyUnaryOperator(Operation *op);
44 void sinkOrCloneOpToUses(Operation *op);
45 void sinkExpression(Operation *op);
46 void useNamedOperands(Operation *op, DenseMap<Value, Operation *> &pipeMap);
48 bool splitStructAssignment(OpBuilder &
builder, hw::StructType ty, Value dst,
50 bool splitArrayAssignment(OpBuilder &
builder, hw::ArrayType ty, Value dst,
52 bool splitAssignment(OpBuilder &
builder, Value dst, Value src);
57 DenseSet<Operation *> toDelete;
64 if (isa<comb::ParityOp>(op))
67 if (
auto xorOp = dyn_cast<comb::XorOp>(op))
68 return xorOp.isBinaryNot();
70 if (
auto icmpOp = dyn_cast<comb::ICmpOp>(op))
71 return icmpOp.isEqualAllOnes() || icmpOp.isNotEqualZero();
78 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(
value.getDefiningOp()))
79 return cst.getValue();
90 auto *srcOp = src.getDefiningOp();
91 auto *dstOp = dst.getDefiningOp();
95 return TypeSwitch<Operation *, bool>(srcOp)
97 auto toField = dyn_cast<sv::StructFieldInOutOp>(dstOp);
100 if (toField.getFieldAttr() != extract.getFieldNameAttr())
102 return isSelfWrite(toField.getInput(), extract.getInput());
104 .Case<hw::ArrayGetOp>([&](
auto get) {
105 auto toGet = dyn_cast<sv::ArrayIndexInOutOp>(dstOp);
106 if (!toGet || toGet.getIndex().getType() !=
get.getIndex().getType())
108 auto toIdx =
getInt(toGet.getIndex());
110 if (!toIdx || !fromIdx || toIdx != fromIdx)
114 .Case<sv::ReadInOutOp>([&](
auto read) {
return dst == read.getInput(); })
115 .Default([&](
auto srcOp) {
return false; });
119 bool PrettifyVerilogPass::splitStructAssignment(OpBuilder &
builder,
120 hw::StructType ty, Value dst,
124 DenseMap<StringAttr, std::pair<Location, Value>> fields;
125 while (
auto inj = dyn_cast_or_null<hw::StructInjectOp>(src.getDefiningOp())) {
128 auto field = std::make_pair(inj.getLoc(), inj.getNewValue());
129 fields.try_emplace(inj.getFieldNameAttr(), field);
130 src = inj.getInput();
135 if (!
isSelfWrite(dst, src) && ty.getElements().size() != fields.size())
139 for (
auto &field : ty.getElements()) {
140 const auto &name = field.name;
142 auto it = fields.find(name);
143 if (it == fields.end())
146 auto &[loc,
value] = it->second;
147 auto ref =
builder.create<sv::StructFieldInOutOp>(loc, dst, name);
155 bool PrettifyVerilogPass::splitArrayAssignment(OpBuilder &
builder,
156 hw::ArrayType ty, Value dst,
159 if (
auto op = dyn_cast_or_null<hw::ArrayCreateOp>(src.getDefiningOp())) {
162 auto ty = hw::type_cast<hw::ArrayType>(op.getType());
163 if (ty.getNumElements() != 1)
165 APInt zero(std::max(1u, llvm::Log2_64_Ceil(ty.getNumElements())), 0);
167 Value
value = op.getInputs()[0];
168 auto loc = op.getLoc();
171 auto field =
builder.create<sv::ArrayIndexInOutOp>(loc, dst, index);
178 SmallVector<std::tuple<APInt, Location, Value>> fields;
180 dyn_cast_or_null<hw::ArrayConcatOp>(src.getDefiningOp())) {
181 auto loc =
concat.getLoc();
186 if (
concat.getNumOperands() == 2) {
187 auto c =
concat.getInputs();
189 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[1].getDefiningOp());
190 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
191 auto midL = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
192 auto midR = dyn_cast_or_null<hw::ArrayCreateOp>(c[0].getDefiningOp());
195 hw::type_cast<hw::ArrayType>(
concat.getType()).getNumElements();
197 auto baseIdx =
getInt(lhs.getLowIndex());
198 if (!baseIdx || *baseIdx != 0 || midR.getInputs().size() != 1)
200 fields.emplace_back(APInt(baseIdx->getBitWidth(), size - 1), loc,
201 midR.getInputs()[0]);
202 src = lhs.getInput();
206 auto baseIdx =
getInt(rhs.getLowIndex());
207 if (!baseIdx || *baseIdx != 1 || midL.getInputs().size() != 1)
209 src = rhs.getInput();
210 fields.emplace_back(APInt(baseIdx->getBitWidth(), 0), loc,
211 midL.getInputs()[0]);
220 if (
concat.getNumOperands() == 3) {
221 auto c =
concat.getInputs();
222 auto rhs = dyn_cast_or_null<hw::ArraySliceOp>(c[0].getDefiningOp());
223 auto mid = dyn_cast_or_null<hw::ArrayCreateOp>(c[1].getDefiningOp());
224 auto lhs = dyn_cast_or_null<hw::ArraySliceOp>(c[2].getDefiningOp());
225 if (!lhs || !mid || !rhs || mid.getInputs().size() != 1)
227 auto elem = mid.getInputs()[0];
228 auto arr = lhs.getInput();
229 if (arr != rhs.getInput() || arr.getType() !=
concat.getType())
233 hw::type_cast<hw::ArrayType>(lhs.getType()).getNumElements();
234 auto lhsIdx =
getInt(lhs.getLowIndex());
235 auto rhsIdx =
getInt(rhs.getLowIndex());
236 if (!lhsIdx || *lhsIdx != 0)
238 if (!rhsIdx || *rhsIdx != lhsSize + 1)
240 fields.emplace_back(*rhsIdx - 1, loc, elem);
251 std::stable_sort(fields.begin(), fields.end(), [](
auto l,
auto r) {
252 return std::get<0>(l).ult(std::get<0>(r));
255 std::optional<APInt> last;
256 for (
auto &[i, loc,
value] : fields) {
260 auto field =
builder.create<sv::ArrayIndexInOutOp>(loc, dst, index);
272 bool PrettifyVerilogPass::splitAssignment(OpBuilder &
builder, Value dst,
277 if (
auto ty = hw::type_dyn_cast<hw::StructType>(src.getType()))
278 return splitStructAssignment(
builder, ty, dst, src);
280 if (
auto ty = hw::type_dyn_cast<hw::ArrayType>(src.getType()))
281 return splitArrayAssignment(
builder, ty, dst, src);
289 void PrettifyVerilogPass::sinkOrCloneOpToUses(Operation *op) {
290 assert(mlir::isMemoryEffectFree(op) &&
291 "Op with side effects cannot be sunk to its uses.");
292 auto block = op->getBlock();
295 for (
auto &use : llvm::make_early_inc_range(op->getUses())) {
298 auto localBlock = use.getOwner()->getBlock();
299 if (block == localBlock)
303 auto &localValue = blockLocalValues[localBlock];
306 localValue = OpBuilder::atBlockBegin(localBlock).clone(*op)->getResult(0);
310 anythingChanged =
true;
313 if (op->use_empty()) {
315 anythingChanged =
true;
321 bool PrettifyVerilogPass::prettifyUnaryOperator(Operation *op) {
336 if (op->use_empty() || op->hasOneUse())
341 for (
auto *user : op->getUsers()) {
342 if (isa<comb::ExtractOp, hw::ArraySliceOp>(user))
344 if (!options.allowExprInEventControl &&
345 isa<sv::AlwaysFFOp, sv::AlwaysOp>(user))
351 auto cloneConstantOperandsIfNeeded = [&](Operation *op) {
352 for (
auto &operand : op->getOpOperands()) {
359 if (constant->getBlock() != op->getBlock())
360 operand.set(OpBuilder(op).clone(*constant)->getResult(0));
364 while (!op->hasOneUse()) {
365 OpOperand &use = *op->use_begin();
366 Operation *user = use.getOwner();
369 auto *cloned = OpBuilder(user).clone(*op);
370 cloneConstantOperandsIfNeeded(cloned);
373 use.set(cloned->getResult(0));
377 Operation *user = *op->user_begin();
378 op->moveBefore(user);
379 cloneConstantOperandsIfNeeded(op);
381 anythingChanged =
true;
389 while (block != topBlock) {
390 block = block->getParentOp()->getBlock();
401 void PrettifyVerilogPass::sinkExpression(Operation *op) {
406 Block *curOpBlock = op->getBlock();
409 if (op->hasOneUse()) {
410 if (curOpBlock != op->user_begin()->getBlock()) {
413 if (!mlir::isMemoryEffectFree(op))
416 op->moveBefore(*op->user_begin());
417 anythingChanged =
true;
423 auto userIt = op->user_begin();
424 Block *ncaBlock = userIt->getBlock();
426 unsigned ncaBlockDepth =
getBlockDepth(ncaBlock, curOpBlock);
427 if (ncaBlockDepth == 0)
430 for (
auto e = op->user_end(); userIt != e; ++userIt) {
431 auto *userBlock = userIt->getBlock();
432 if (userBlock == curOpBlock)
434 if (userBlock == ncaBlock)
439 unsigned userBlockDepth =
getBlockDepth(userBlock, curOpBlock);
440 while (userBlock != ncaBlock) {
441 if (ncaBlockDepth < userBlockDepth) {
442 userBlock = userBlock->getParentOp()->getBlock();
444 }
else if (userBlockDepth < ncaBlockDepth) {
445 ncaBlock = ncaBlock->getParentOp()->getBlock();
448 userBlock = userBlock->getParentOp()->getBlock();
450 ncaBlock = ncaBlock->getParentOp()->getBlock();
455 if (ncaBlockDepth == 0)
461 if (!mlir::isMemoryEffectFree(op))
466 assert(ncaBlock != curOpBlock &&
"should have bailed out earlier");
467 op->moveBefore(&ncaBlock->front());
468 anythingChanged =
true;
471 void PrettifyVerilogPass::processPostOrder(Block &body) {
472 SmallVector<Operation *> instances;
476 llvm::make_early_inc_range(llvm::reverse(body.getOperations()))) {
477 if (op.getNumRegions()) {
478 for (
auto ®ion : op.getRegions())
479 for (
auto ®ionBlock : region.getBlocks())
480 processPostOrder(regionBlock);
484 if (
auto assign = dyn_cast<sv::PAssignOp>(op)) {
485 auto dst = assign.getDest();
486 auto src = assign.getSrc();
489 if (splitAssignment(
builder, dst, src)) {
490 anythingChanged =
true;
491 toDelete.insert(src.getDefiningOp());
492 toDelete.insert(dst.getDefiningOp());
506 if (matchPattern(&op, mlir::m_Constant()) ||
508 sv::IndexedPartSelectInOutOp, hw::ParamValueOp>(op)) {
509 sinkOrCloneOpToUses(&op);
523 if (isa<hw::InstanceOp>(op))
524 instances.push_back(&op);
531 if (!instances.empty()) {
532 for (Operation *instance : llvm::reverse(instances)) {
533 if (instance != &body.back())
534 instance->moveBefore(&body.back());
539 void PrettifyVerilogPass::runOnOperation() {
541 options =
LoweringOptions(thisModule->getParentOfType<mlir::ModuleOp>());
545 anythingChanged =
false;
548 processPostOrder(*thisModule.getBodyBlock());
551 while (!toDelete.empty()) {
552 auto it = toDelete.begin();
556 if (!op || !isOpTriviallyDead(op))
559 for (
auto operand : op->getOperands())
560 toDelete.insert(operand.getDefiningOp());
567 if (!anythingChanged)
568 markAllAnalysesPreserved();
572 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.
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()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Options which control the emission from CIRCT to Verilog.