14#include "mlir/Analysis/Liveness.h"
15#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16#include "mlir/Dialect/SCF/IR/SCF.h"
17#include "mlir/IR/Dominance.h"
18#include "mlir/IR/Matchers.h"
19#include "mlir/Transforms/RegionUtils.h"
20#include "llvm/ADT/ScopeExit.h"
21#include "llvm/Support/Debug.h"
22#include "llvm/Support/GenericIteratedDominanceFrontier.h"
24#define DEBUG_TYPE "llhd-deseq"
28#define GEN_PASS_DEF_DESEQPASS
29#include "circt/Dialect/LLHD/Transforms/LLHDPasses.h.inc"
36using llvm::SmallMapVector;
37using llvm::SmallSetVector;
41static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
const DNF &dnf) {
41static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
const DNF &dnf) {
…}
60 int compare(
const ValueEntry &other)
const {
61 auto order = condition.
compare(other.condition);
64 if (value.getAsOpaquePointer() < other.value.getAsOpaquePointer())
66 if (value.getAsOpaquePointer() > other.value.getAsOpaquePointer())
71 bool operator<(
const ValueEntry &other)
const {
return compare(other) < 0; }
81 SmallVector<ValueEntry, 1> entries;
86 explicit ValueTable(Value value) : ValueTable(
DNF(true), value) {}
88 ValueTable(
DNF condition, Value value) {
90 entries.push_back(ValueEntry{condition, value});
94 bool isEmpty()
const {
return entries.empty(); }
97 int compare(
const ValueTable &other)
const;
98 bool operator==(
const ValueTable &other)
const {
return compare(other) == 0; }
99 bool operator!=(
const ValueTable &other)
const {
return compare(other) != 0; }
100 bool operator<(
const ValueTable &other)
const {
return compare(other) < 0; }
101 bool operator>(
const ValueTable &other)
const {
return compare(other) > 0; }
102 bool operator>=(
const ValueTable &other)
const {
return compare(other) >= 0; }
103 bool operator<=(
const ValueTable &other)
const {
return compare(other) <= 0; }
106 void merge(ValueTable &&other);
109 void addCondition(
const DNF &condition);
112 ValueTable filtered(
const DNF &condition);
117int ValueTable::compare(
const ValueTable &other)
const {
118 if (entries.size() < other.entries.size())
120 if (entries.size() > other.entries.size())
122 for (
auto [thisEntry, otherEntry] :
llvm::zip(entries, other.entries)) {
123 auto order = thisEntry.compare(otherEntry);
130void ValueTable::merge(ValueTable &&other) {
132 for (
auto [index, entry] :
llvm::enumerate(entries))
133 seenValues[entry.value] = index;
134 for (
auto &&entry : other.entries) {
135 if (
auto it = seenValues.find(entry.value); it != seenValues.end()) {
136 entries[it->second].condition |= entry.condition;
138 seenValues.insert({entry.value, entries.size()});
139 entries.push_back(entry);
145void ValueTable::addCondition(
const DNF &condition) {
146 llvm::erase_if(entries, [&](
auto &entry) {
147 entry.condition &= condition;
148 return entry.condition.
isFalse();
152ValueTable ValueTable::filtered(
const DNF &condition) {
154 for (
auto entry : entries) {
155 entry.condition &= condition;
156 if (entry.condition.isFalse())
160 for (
auto &orTerm : entry.condition.orTerms)
161 llvm::erase_if(orTerm.andTerms, [&](auto andTerm) {
162 return llvm::any_of(condition.
orTerms, [&](
auto &conditionOrTerm) {
163 return llvm::is_contained(conditionOrTerm.andTerms, andTerm);
167 result.entries.push_back(std::move(entry));
173 const ValueTable &table) {
174 SmallMapVector<Value, unsigned, 8> valueIds;
175 auto dumpValue = [&](Value value) {
176 auto id = valueIds.insert({value, valueIds.size()}).first->second;
182 llvm::interleaveComma(table.entries, os, [&](
auto &entry) {
183 entry.condition.print(os, dumpValue);
186 if (auto *defOp = entry.value.getDefiningOp();
187 defOp && defOp->template hasTrait<OpTrait::ConstantLike>() &&
188 m_Constant(&attr).match(defOp))
191 dumpValue(entry.value);
195 if (!valueIds.empty()) {
197 llvm::interleaveComma(valueIds, os, [&](
auto valueAndId) {
198 os <<
"v" << valueAndId.second <<
" = ";
199 valueAndId.first.printAsOperand(os, OpPrintingFlags());
218struct SuccessorValue {
223 SmallVector<DNF, 1> booleanArgs;
225 SmallVector<ValueTable, 1> valueArgs;
227 bool operator==(
const SuccessorValue &other)
const {
228 return condition == other.condition && booleanArgs == other.booleanArgs &&
229 valueArgs == other.valueArgs;
232 bool operator!=(
const SuccessorValue &other)
const {
233 return !(*
this == other);
240using SuccessorValues = SmallVector<SuccessorValue, 2>;
261 operator bool()
const {
return bool(reset); }
265enum class Edge {
None = 0, Pos, Neg };
278 ValueTable valueTable;
281 operator bool()
const {
return bool(clock); }
300 explicit DriveInfo(DrvOp op) : op(op) {}
322 bool operator==(
const FixedValue &other)
const {
323 return value == other.value && past == other.past &&
324 present == other.present;
327 operator bool()
const {
return bool(value); }
333using FixedValues = SmallVector<FixedValue, 2>;
335llvm::hash_code
hash_value(
const FixedValue &arg) {
336 return llvm::hash_combine(arg.value, arg.past, arg.present);
339llvm::hash_code
hash_value(
const FixedValues &arg) {
340 return llvm::hash_combine_range(arg.begin(), arg.end());
358 static bool isEqual(
const FixedValue &a,
const FixedValue &b) {
358 static bool isEqual(
const FixedValue &a,
const FixedValue &b) {
…}
373 static bool isEqual(
const FixedValues &a,
const FixedValues &b) {
373 static bool isEqual(
const FixedValues &a,
const FixedValues &b) {
…}
386 Deseq(ProcessOp process) : process(process) {}
389 bool analyzeProcess();
390 bool analyzeTriggers();
392 bool matchDrive(DriveInfo &drive);
393 bool isValidResetValue(Value value);
394 void implementRegisters();
395 void implementRegister(DriveInfo &drive);
396 Value specializeValue(Value value, FixedValues fixedValues);
397 ValueRange specializeProcess(FixedValues fixedValues);
399 void updateBoolean(Value value,
DNF dnf);
400 void updateValue(Value value, ValueTable table);
401 void updateSuccessors(Block *block, SuccessorValues values);
402 void updateCondition(Block *block,
DNF condition);
404 void markDirty(Operation *op);
405 void markDirty(Block *block);
408 void propagate(Block *block);
409 void propagate(Operation *op);
410 void propagate(cf::BranchOp op);
411 void propagate(cf::CondBranchOp op);
412 void propagate(WaitOp op);
423 SmallSetVector<Value, 2> observedBooleans;
425 SmallVector<DriveInfo> driveInfos;
427 SmallSetVector<Value, 2> triggers;
431 SmallVector<Value, 2> pastValues;
435 ConstantTimeOp epsilonDelay;
437 DenseMap<Operation *, bool> staticOps;
440 SmallSetVector<PointerUnion<Operation *, Block *>, 4> dirtyNodes;
442 DenseMap<Value, DNF> booleanLattice;
445 DenseMap<Value, ValueTable> valueLattice;
449 DenseMap<Block *, DNF> blockConditionLattice;
451 DenseMap<Block *, SuccessorValues> successorLattice;
460 if (!analyzeProcess())
463 llvm::dbgs() <<
"Desequentializing " << process.getLoc() <<
"\n";
464 llvm::dbgs() <<
"- Feeds " << driveInfos.size() <<
" conditional drives\n";
472 if (!analyzeTriggers())
475 llvm::dbgs() <<
"- " << triggers.size() <<
" potential triggers:\n";
476 for (
auto trigger : triggers)
477 llvm::dbgs() <<
" - " << trigger <<
"\n";
488 implementRegisters();
501bool Deseq::analyzeProcess() {
504 for (
auto &block : process.getBody()) {
505 for (
auto &op : block) {
506 if (isa<WaitOp, HaltOp>(op))
508 if (!isMemoryEffectFree(&op)) {
510 llvm::dbgs() <<
"Skipping " << process.getLoc()
511 <<
": contains side-effecting op ";
512 op.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
513 llvm::dbgs() <<
"\n";
521 for (
auto &block : process.getBody()) {
522 if (
auto candidate = dyn_cast<WaitOp>(block.getTerminator())) {
524 LLVM_DEBUG(llvm::dbgs() <<
"Skipping " << process.getLoc()
525 <<
": has multiple waits\n");
532 LLVM_DEBUG(llvm::dbgs()
533 <<
"Skipping " << process.getLoc() <<
": has no wait\n");
536 for (
auto value : wait.getObserved())
537 if (value.getType().isSignlessInteger(1))
538 observedBooleans.insert(value);
541 SmallPtrSet<Operation *, 8> seenDrives;
542 for (
auto &use : process->getUses()) {
543 auto driveOp = dyn_cast<DrvOp>(use.getOwner());
545 LLVM_DEBUG(llvm::dbgs()
546 <<
"Skipping " << process.getLoc() <<
": feeds non-drive "
547 << use.getOwner()->getLoc() <<
"\n");
550 if (!seenDrives.insert(driveOp).second)
554 if (!driveOp.getEnable()) {
555 LLVM_DEBUG(llvm::dbgs()
556 <<
"Skipping " << process.getLoc()
557 <<
": feeds unconditional drive " << driveOp <<
"\n");
563 if (use.getOperandNumber() != 1 && use.getOperandNumber() != 2) {
564 LLVM_DEBUG(llvm::dbgs()
565 <<
"Skipping " << process.getLoc()
566 <<
": feeds drive operand that is neither value nor enable: "
571 driveInfos.push_back(DriveInfo(driveOp));
582void Deseq::markDirty(Block *block) { dirtyNodes.insert(block); }
585void Deseq::markDirty(Operation *op) {
586 if (op->getParentRegion()->isAncestor(&process.getBody()) ||
587 process.getBody().isAncestor(op->getParentRegion()))
588 dirtyNodes.insert(op);
593void Deseq::updateBoolean(Value value,
DNF dnf) {
594 assert(value.getType().isSignlessInteger(1) &&
"can only trace i1 DNFs");
595 auto &slot = booleanLattice[value];
598 llvm::dbgs() <<
"- Update ";
599 value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
600 llvm::dbgs() <<
" = " << dnf <<
"\n";
603 for (
auto *user : value.getUsers())
610void Deseq::updateValue(Value value, ValueTable table) {
611 auto &slot = valueLattice[value];
614 llvm::dbgs() <<
"- Update ";
615 value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
616 llvm::dbgs() <<
" = " << table <<
"\n";
619 for (
auto *user : value.getUsers())
626void Deseq::updateSuccessors(Block *block, SuccessorValues values) {
627 auto &slot = successorLattice[block];
628 for (
auto [index, successor] :
llvm::enumerate(block->getSuccessors())) {
629 if (!slot.empty() && slot[index] == values[index])
631 markDirty(successor);
638void Deseq::updateCondition(Block *block,
DNF condition) {
639 auto &slot = blockConditionLattice[block];
640 if (slot != condition) {
642 llvm::dbgs() <<
"- Update ";
643 block->printAsOperand(llvm::dbgs());
644 llvm::dbgs() <<
" condition = " << condition <<
"\n";
647 markDirty(block->getTerminator());
658void Deseq::propagate() {
660 SmallPtrSet<Operation *, 8> opsAboveSeen;
661 mlir::visitUsedValuesDefinedAbove(
662 process->getRegions(), [&](
auto *opOperand) {
663 auto value = opOperand->get();
664 if (auto *defOp = value.getDefiningOp();
665 defOp && !observedBooleans.contains(value)) {
666 if (opsAboveSeen.insert(defOp).second)
669 if (value.getType().isSignlessInteger(1))
670 updateBoolean(value, DNF(value));
671 updateValue(value, ValueTable(value));
676 for (
auto &block : process.getBody()) {
678 for (
auto &op : block)
683 while (!dirtyNodes.empty()) {
684 auto node = dirtyNodes.pop_back_val();
685 if (
auto *op = dyn_cast<Operation *>(node))
688 propagate(cast<Block *>(node));
690 LLVM_DEBUG(llvm::dbgs() <<
"- Finished propagation\n");
696void Deseq::propagate(Block *block) {
698 const unsigned numArgs = block->getNumArguments();
699 auto condition =
DNF(
false);
700 SmallVector<DNF> booleanArgs(numArgs,
DNF(
false));
701 SmallVector<ValueTable> valueArgs(numArgs, ValueTable());
703 SmallPtrSet<Block *, 4> seen;
704 for (
auto *predecessor : block->getPredecessors()) {
705 if (!seen.insert(predecessor).second)
707 auto &successorValues = successorLattice[predecessor];
708 if (successorValues.empty())
710 auto *terminator = predecessor->getTerminator();
711 for (
auto &blockOperand : terminator->getBlockOperands()) {
712 if (blockOperand.get() != block)
714 auto &successorValue = successorValues[blockOperand.getOperandNumber()];
715 condition |= successorValue.condition;
716 for (
unsigned argIdx = 0; argIdx < numArgs; ++argIdx) {
717 booleanArgs[argIdx] |=
718 successorValue.booleanArgs[argIdx] & successorValue.condition;
719 auto valueTable = successorValue.valueArgs[argIdx];
720 valueTable.addCondition(successorValue.condition);
721 valueArgs[argIdx].merge(std::move(valueTable));
727 updateCondition(block, condition);
730 for (
auto [arg, booleanArg, valueArg] :
731 llvm::zip(block->getArguments(), booleanArgs, valueArgs)) {
732 if (arg.getType().isSignlessInteger(1))
733 updateBoolean(arg, booleanArg);
734 updateValue(arg, valueArg);
739void Deseq::propagate(Operation *op) {
741 if (op->hasTrait<OpTrait::ConstantLike>() && op->getNumResults() == 1) {
743 if (op->getResult(0).getType().isSignlessInteger(1) &&
744 m_ConstantInt(&intValue).match(op)) {
745 assert(intValue.getBitWidth() == 1);
746 updateBoolean(op->getResult(0),
DNF(intValue.isOne()));
748 updateValue(op->getResult(0), ValueTable(op->getResult(0)));
753 TypeSwitch<Operation *>(op)
761 for (
auto result : op->getResults()) {
762 if (result.getType().isSignlessInteger(1))
763 updateBoolean(result,
DNF(result));
764 updateValue(result, ValueTable(result));
770void Deseq::propagate(cf::BranchOp op) {
771 SuccessorValue latticeValue;
772 latticeValue.condition = blockConditionLattice[op->getBlock()];
773 latticeValue.booleanArgs.reserve(op.getDestOperands().size());
774 latticeValue.valueArgs.reserve(op.getDestOperands().size());
775 for (
auto operand : op.getDestOperands()) {
776 latticeValue.booleanArgs.push_back(booleanLattice.lookup(operand));
777 latticeValue.valueArgs.push_back(valueLattice.lookup(operand));
779 updateSuccessors(op->getBlock(), {latticeValue});
786void Deseq::propagate(cf::CondBranchOp op) {
787 SuccessorValues latticeValues(2, SuccessorValue());
788 auto blockCondition = blockConditionLattice[op->getBlock()];
789 auto branchCondition = booleanLattice.lookup(op.getCondition());
792 auto &trueValue = latticeValues[0];
793 trueValue.condition = blockCondition & branchCondition;
794 trueValue.booleanArgs.reserve(op.getTrueDestOperands().size());
795 trueValue.valueArgs.reserve(op.getTrueDestOperands().size());
796 for (
auto operand : op.getTrueDestOperands()) {
797 trueValue.booleanArgs.push_back(booleanLattice.lookup(operand));
798 trueValue.valueArgs.push_back(valueLattice.lookup(operand));
802 auto &falseValue = latticeValues[1];
803 falseValue.condition = blockCondition & ~branchCondition;
804 falseValue.booleanArgs.reserve(op.getFalseDestOperands().size());
805 falseValue.valueArgs.reserve(op.getFalseDestOperands().size());
806 for (
auto operand : op.getFalseDestOperands()) {
807 falseValue.booleanArgs.push_back(booleanLattice.lookup(operand));
808 falseValue.valueArgs.push_back(valueLattice.lookup(operand));
811 updateSuccessors(op->getBlock(), latticeValues);
819void Deseq::propagate(WaitOp op) {
820 SuccessorValue latticeValue;
821 latticeValue.condition =
DNF(
true);
822 latticeValue.booleanArgs.reserve(op.getDestOperands().size());
823 latticeValue.valueArgs.reserve(op.getDestOperands().size());
824 for (
auto operand : op.getDestOperands()) {
827 auto value = booleanLattice.lookup(operand);
829 bool nonePastAlready = llvm::all_of(value.orTerms, [](
auto &orTerm) {
830 return llvm::all_of(orTerm.andTerms, [](auto &andTerm) {
831 if (andTerm.hasUse(AndTerm::Past) || andTerm.hasUse(AndTerm::NotPast))
837 if (!nonePastAlready)
840 latticeValue.booleanArgs.push_back(value);
841 latticeValue.valueArgs.push_back(ValueTable());
843 updateSuccessors(op->getBlock(), {latticeValue});
849 for (
auto [result, operand] :
850 llvm::zip(process->getResults(), op.getYieldOperands())) {
851 if (operand.getType().isSignlessInteger(1))
852 updateBoolean(result, booleanLattice.lookup(operand));
853 updateValue(result, valueLattice.lookup(operand));
859 if (op.getType().isSignlessInteger(1)) {
860 auto result =
DNF(
false);
861 for (
auto operand : op.getInputs()) {
862 result |= booleanLattice.lookup(operand);
866 updateBoolean(op, result);
868 updateValue(op, ValueTable(op.getResult()));
873 if (op.getType().isSignlessInteger(1)) {
874 auto result =
DNF(
true);
875 for (
auto operand : op.getInputs()) {
876 result &= booleanLattice.lookup(operand);
877 if (result.isFalse())
880 updateBoolean(op, result);
882 updateValue(op, ValueTable(op.getResult()));
887 if (op.getType().isSignlessInteger(1)) {
888 auto result =
DNF(
false);
889 for (
auto operand : op.getInputs())
890 result ^= booleanLattice.lookup(operand);
891 updateBoolean(op, result);
893 updateValue(op, ValueTable(op.getResult()));
898 auto condition = booleanLattice.lookup(op.getCond());
899 if (op.getType().isSignlessInteger(1)) {
900 auto trueValue = booleanLattice.lookup(op.getTrueValue());
901 auto falseValue = booleanLattice.lookup(op.getFalseValue());
902 updateBoolean(op, condition & trueValue | ~condition & falseValue);
904 auto trueValue = valueLattice.lookup(op.getTrueValue());
905 auto falseValue = valueLattice.lookup(op.getFalseValue());
906 trueValue.addCondition(condition);
907 falseValue.addCondition(~condition);
908 trueValue.merge(std::move(falseValue));
909 updateValue(op, trueValue);
918bool Deseq::analyzeTriggers() {
919 for (
auto operand : wait.getDestOperands()) {
921 if (!operand.getType().isSignlessInteger(1)) {
923 llvm::dbgs() <<
"- Aborting: " << operand.getType() <<
" trigger ";
924 operand.printAsOperand(llvm::dbgs(), OpPrintingFlags());
925 llvm::dbgs() <<
"\n";
932 auto dnf = booleanLattice.lookup(operand);
936 std::tie(value, use) = *single;
939 llvm::dbgs() <<
"- Aborting: trigger ";
940 operand.printAsOperand(llvm::dbgs(), OpPrintingFlags());
941 llvm::dbgs() <<
" with multiple values " << dnf <<
"\n";
947 llvm::dbgs() <<
"- Aborting: trigger ";
948 operand.printAsOperand(llvm::dbgs(), OpPrintingFlags());
949 llvm::dbgs() <<
" is inverted: " << dnf <<
"\n";
955 if (process.getBody().isAncestor(value.getParentRegion())) {
957 llvm::dbgs() <<
"- Aborting: trigger ";
958 operand.printAsOperand(llvm::dbgs(), OpPrintingFlags());
959 llvm::dbgs() <<
" defined inside the process\n";
966 if (!observedBooleans.contains(value)) {
968 llvm::dbgs() <<
"- Aborting: unobserved trigger ";
969 value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
970 llvm::dbgs() <<
"\n";
975 triggers.insert(value);
976 pastValues.push_back(value);
979 if (triggers.empty()) {
980 LLVM_DEBUG(llvm::dbgs() <<
"- Aborting: no triggers\n");
993bool Deseq::matchDrives() {
994 for (
auto &drive : driveInfos)
995 if (!matchDrive(drive))
1009bool Deseq::matchDrive(DriveInfo &drive) {
1010 LLVM_DEBUG(llvm::dbgs() <<
"- Analyzing " << drive.op <<
"\n");
1013 auto condition = booleanLattice.lookup(drive.op.getEnable());
1014 if (condition.
isNull()) {
1015 LLVM_DEBUG(llvm::dbgs()
1016 <<
"- Aborting: null condition on " << drive.op <<
"\n");
1021 auto valueTable = valueLattice.lookup(drive.op.getValue());
1022 valueTable.addCondition(condition);
1024 llvm::dbgs() <<
" - Condition: " << condition <<
"\n";
1025 llvm::dbgs() <<
" - Value: " << valueTable <<
"\n";
1030 SmallMapVector<Value, Edge, 2> edges;
1031 for (
auto &entry : valueTable.entries) {
1032 for (
auto &orTerm : entry.condition.orTerms) {
1033 bool edgeSeen =
false;
1034 for (
auto &andTerm : orTerm.andTerms) {
1040 if (!triggers.contains(andTerm.value)) {
1042 llvm::dbgs() <<
"- Aborting: past sample of ";
1043 andTerm.value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1044 llvm::dbgs() <<
" which is not a trigger\n";
1050 auto edge = Edge::None;
1057 llvm::dbgs() <<
"- Aborting: sampling of ";
1058 andTerm.value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1059 llvm::dbgs() <<
" is neither not a pos/neg edge\n";
1067 LLVM_DEBUG(llvm::dbgs() <<
"- Aborting: AND of multiple edges\n");
1073 auto &existingEdge = edges[andTerm.value];
1074 if (existingEdge != Edge::None && existingEdge != edge) {
1076 llvm::dbgs() <<
"- Aborting: ";
1077 andTerm.value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1078 llvm::dbgs() <<
" used both as pos and neg edge trigger\n";
1082 existingEdge = edge;
1086 LLVM_DEBUG(llvm::dbgs() <<
"- Aborting: triggered by non-edge\n");
1092 for (
auto [value, edge] : edges) {
1093 llvm::dbgs() <<
" - Triggered by ";
1094 value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1095 llvm::dbgs() << (edge == Edge::Pos ?
" posedge\n" :
" negedge\n");
1103 SmallVector<AndTerm, 2> resetEntries;
1104 SmallPtrSet<Value, 2> resetTriggers;
1105 SmallVector<ResetInfo, 2> resetInfos;
1107 if (edges.size() > 1) {
1108 for (
auto [value, edge] : edges) {
1117 unsigned singleResetEntry = -1;
1118 for (
auto [index, entry] :
llvm::enumerate(valueTable.entries)) {
1119 if (!entry.condition.contains(
OrTerm(resetEdge)))
1122 if (singleResetEntry !=
unsigned(-1)) {
1123 singleResetEntry = -1;
1126 singleResetEntry = index;
1128 if (singleResetEntry ==
unsigned(-1))
1141 auto resetValue = valueTable.entries[singleResetEntry].
value;
1142 if (!isValidResetValue(resetValue))
1147 auto allGated = llvm::all_of(
1148 llvm::enumerate(valueTable.entries), [&](
auto indexAndEntry) {
1150 if (indexAndEntry.index() == singleResetEntry)
1153 return llvm::all_of(
1154 indexAndEntry.value().condition.orTerms,
1155 [&](auto &orTerm) { return orTerm.contains(resetInactive); });
1161 resetEntries.push_back(resetEdge);
1162 resetEntries.push_back(resetActive);
1163 resetInfos.push_back({value, resetValue, edge == Edge::Pos});
1164 resetTriggers.insert(value);
1169 for (
auto &resetInfo : resetInfos)
1170 edges.erase(resetInfo.reset);
1174 llvm::erase_if(valueTable.entries, [&](
auto &entry) {
1175 llvm::erase_if(entry.condition.orTerms, [&](auto &orTerm) {
1176 for (auto &andTerm : orTerm.andTerms)
1177 if (llvm::is_contained(resetEntries, andTerm))
1181 return entry.condition.isFalse();
1186 for (
auto &entry : valueTable.entries) {
1187 for (
auto &orTerm : entry.condition.orTerms) {
1188 llvm::erase_if(orTerm.andTerms, [&](
auto &andTerm) {
1189 return resetTriggers.contains(andTerm.value);
1195 SmallVector<ClockInfo, 2> clockInfos;
1197 for (
auto [clock, edge] : edges) {
1200 ValueTable clockValueTable;
1202 for (
auto &entry : valueTable.entries) {
1203 auto condition = entry.condition;
1209 llvm::erase_if(condition.
orTerms, [&](
auto &orTerm) {
1210 bool clockEdgeFound = false;
1211 llvm::erase_if(orTerm.andTerms, [&](auto &andTerm) {
1212 if (andTerm == clockEdge) {
1213 clockEdgeFound = true;
1218 return !clockEdgeFound;
1222 if (llvm::any_of(condition.
orTerms,
1223 [](
auto &orTerm) { return orTerm.isTrue(); }))
1224 condition =
DNF(
true);
1228 clockValueTable.entries.push_back({condition, entry.value});
1232 clockInfos.push_back({clock, edge, std::move(clockValueTable)});
1239 if (resetInfos.size() == 2 && clockInfos.empty()) {
1243 unsigned clockIdx = 0;
1244 if (resetInfos[0].activeHigh && !resetInfos[1].activeHigh)
1246 else if (!resetInfos[0].activeHigh && resetInfos[1].activeHigh)
1248 else if (matchPattern(resetInfos[1].value, m_Zero()))
1250 else if (matchPattern(resetInfos[0].value, m_Zero()))
1252 else if (resetInfos[0].reset == triggers[0])
1254 else if (resetInfos[1].reset == triggers[0])
1258 auto &
info = resetInfos[clockIdx];
1260 llvm::dbgs() <<
" - Two resets, no clock: promoting ";
1261 info.reset.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1262 llvm::dbgs() <<
" to clock\n";
1264 clockInfos.push_back({
info.reset,
info.activeHigh ? Edge::Pos : Edge::Neg,
1265 ValueTable(
info.value)});
1266 resetInfos.erase(resetInfos.begin() + clockIdx);
1271 for (
auto &resetInfo : resetInfos) {
1272 llvm::dbgs() <<
" - Reset ";
1273 resetInfo.reset.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1274 llvm::dbgs() <<
" (active " << (resetInfo.activeHigh ?
"high" :
"low")
1276 resetInfo.value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1277 llvm::dbgs() <<
"\n";
1279 for (
auto &clockInfo : clockInfos) {
1280 llvm::dbgs() <<
" - Clock ";
1281 clockInfo.clock.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1282 llvm::dbgs() <<
" ("
1283 << (clockInfo.edge == Edge::Pos ?
"posedge" :
"negedge")
1284 <<
"): " << clockInfo.valueTable <<
"\n";
1289 for (
auto &clockInfo : clockInfos) {
1290 if (clockInfo.clock.getParentRegion()->isProperAncestor(&process.getBody()))
1293 llvm::dbgs() <<
"- Aborting: clock ";
1294 clockInfo.clock.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1295 llvm::dbgs() <<
" not available outside the process\n";
1299 for (
auto &resetInfo : resetInfos) {
1300 if (resetInfo.reset.getParentRegion()->isProperAncestor(&process.getBody()))
1303 llvm::dbgs() <<
"- Aborting: reset ";
1304 resetInfo.reset.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1305 llvm::dbgs() <<
" not available outside the process\n";
1312 if (clockInfos.size() != 1) {
1313 LLVM_DEBUG(llvm::dbgs()
1314 <<
"- Aborting: " << clockInfos.size() <<
" clocks\n");
1317 if (resetInfos.size() > 1) {
1318 LLVM_DEBUG(llvm::dbgs()
1319 <<
"- Aborting: " << resetInfos.size() <<
" resets\n");
1322 drive.clock = clockInfos[0];
1323 drive.reset = resetInfos.empty() ? ResetInfo{} : resetInfos[0];
1331bool Deseq::isValidResetValue(Value value) {
1332 auto result = dyn_cast<OpResult>(value);
1335 if (
auto it = staticOps.find(result.getOwner()); it != staticOps.end())
1338 struct WorklistItem {
1340 OperandRange::iterator it;
1342 SmallVector<WorklistItem> worklist;
1343 worklist.push_back({result.getOwner(), result.getOwner()->operand_begin()});
1345 while (!worklist.empty()) {
1346 auto item = worklist.pop_back_val();
1347 auto &isStatic = staticOps[item.op];
1348 if (item.it == item.op->operand_begin()) {
1349 if (item.op->hasTrait<OpTrait::ConstantLike>()) {
1353 if (!isMemoryEffectFree(item.op))
1356 if (item.it == item.op->operand_end()) {
1360 auto result = dyn_cast<OpResult>(*item.it);
1363 auto it = staticOps.find(result.getOwner());
1364 if (it == staticOps.end()) {
1365 worklist.push_back(item);
1367 {result.getOwner(), result.getOwner()->operand_begin()});
1368 }
else if (it->second) {
1370 worklist.push_back(item);
1375 return staticOps.lookup(result.getOwner());
1384void Deseq::implementRegisters() {
1385 for (
auto &drive : driveInfos)
1386 implementRegister(drive);
1396void Deseq::implementRegister(DriveInfo &drive) {
1397 OpBuilder builder(drive.op);
1398 auto loc = drive.op.getLoc();
1402 Value clock = builder.create<seq::ToClockOp>(loc, drive.clock.clock);
1403 if (drive.clock.edge == Edge::Neg)
1404 clock = builder.create<seq::ClockInverterOp>(loc, clock);
1411 reset = drive.reset.reset;
1412 resetValue = drive.reset.value;
1416 if (!drive.reset.activeHigh) {
1417 auto one = builder.create<
hw::ConstantOp>(loc, builder.getI1Type(), 1);
1424 if (!resetValue.getParentRegion()->isProperAncestor(&process.getBody())) {
1425 if (
auto *defOp = resetValue.getDefiningOp();
1426 defOp && defOp->hasTrait<OpTrait::ConstantLike>()) {
1427 defOp->moveBefore(process);
1429 resetValue = specializeValue(
1430 drive.op.getValue(),
1431 FixedValues{{drive.clock.clock, drive.clock.edge == Edge::Neg,
1432 drive.clock.edge == Edge::Neg},
1433 {drive.reset.reset, !drive.reset.activeHigh,
1434 drive.reset.activeHigh}});
1442 Value enable = drive.op.getEnable();
1443 if (drive.clock.valueTable.entries.size() == 1) {
1444 if (drive.clock.valueTable.entries[0].condition.isTrue())
1446 else if (
auto singleTerm =
1447 drive.clock.valueTable.entries[0].condition.getSingleTerm();
1448 singleTerm && singleTerm->second ==
AndTerm::Id &&
1449 singleTerm->first.getParentRegion()->isProperAncestor(
1450 &process.getBody())) {
1451 enable = singleTerm->first;
1458 Value value = drive.op.getValue();
1459 if (drive.clock.valueTable.entries.size() == 1) {
1460 auto tryValue = drive.clock.valueTable.entries[0].value;
1461 if (tryValue.getParentRegion()->isProperAncestor(&process.getBody())) {
1463 }
else if (
auto *defOp = tryValue.getDefiningOp();
1464 defOp && defOp->hasTrait<OpTrait::ConstantLike>()) {
1465 defOp->moveBefore(process);
1472 FixedValues fixedValues;
1473 fixedValues.push_back({drive.clock.clock, drive.clock.edge == Edge::Neg,
1474 drive.clock.edge == Edge::Pos});
1476 fixedValues.push_back(
1477 {drive.reset.reset, !drive.reset.activeHigh, !drive.reset.activeHigh});
1479 value = specializeValue(value, fixedValues);
1481 enable = specializeValue(enable, fixedValues);
1487 loc, value, clock, enable, StringAttr{}, reset, resetValue, Value{},
1488 hw::InnerSymAttr{});
1491 builder.create<
seq::CompRegOp>(loc, value, clock, StringAttr{}, reset,
1492 resetValue, Value{}, hw::InnerSymAttr{});
1495 drive.op.getValueMutable().assign(reg);
1496 drive.op.getEnableMutable().clear();
1501 if (matchPattern(drive.op.getTime(), m_Constant(&attr)) &&
1502 attr.getTime() == 0 && attr.getDelta() == 1 && attr.getEpsilon() == 0) {
1505 builder.create<ConstantTimeOp>(process.getLoc(), 0,
"ns", 0, 1);
1506 drive.op.getTimeMutable().assign(epsilonDelay);
1519Value Deseq::specializeValue(Value value, FixedValues fixedValues) {
1520 auto result = dyn_cast<OpResult>(value);
1521 if (!result || result.getOwner() != process)
1523 return specializeProcess(fixedValues)[result.getResultNumber()];
1534ValueRange Deseq::specializeProcess(FixedValues fixedValues) {
1535 if (
auto it = specializedProcesses.find(fixedValues);
1536 it != specializedProcesses.end())
1540 llvm::dbgs() <<
"- Specializing process for:\n";
1541 for (
auto fixedValue : fixedValues) {
1542 llvm::dbgs() <<
" - ";
1543 fixedValue.value.printAsOperand(llvm::dbgs(), OpPrintingFlags());
1544 llvm::dbgs() <<
": " << fixedValue.past <<
" -> " << fixedValue.present
1553 OpBuilder builder(process);
1554 auto executeOp = builder.create<scf::ExecuteRegionOp>(
1555 process.getLoc(), process.getResultTypes());
1558 SmallVector<std::pair<Block *, Block *>> worklist;
1560 auto scheduleBlock = [&](
Block *block) {
1561 if (
auto *newBlock = mapping.lookupOrNull(block))
1563 auto *newBlock = &executeOp.getRegion().emplaceBlock();
1564 for (
auto arg : block->getArguments()) {
1565 auto newArg = newBlock->addArgument(arg.getType(), arg.getLoc());
1566 mapping.map(arg, newArg);
1568 mapping.map(block, newBlock);
1569 worklist.push_back({block, newBlock});
1574 auto &entryBlock = executeOp.getRegion().emplaceBlock();
1575 builder.setInsertionPointToStart(&entryBlock);
1576 auto i1 = builder.getI1Type();
1577 auto trueValue = builder.create<
hw::ConstantOp>(process.getLoc(), i1, 1);
1581 for (
auto fixedValue : fixedValues) {
1582 auto present = fixedValue.present ? trueValue : falseValue;
1583 auto past = fixedValue.past ? trueValue : falseValue;
1584 materializedFixedValues.insert({fixedValue.value, {past, present}});
1585 mapping.map(fixedValue.value, present);
1588 auto evaluateTerm = [&](Value value,
bool past) -> std::optional<bool> {
1589 for (
auto fixedValue : fixedValues)
1590 if (fixedValue.value == value)
1591 return past ? fixedValue.past : fixedValue.present;
1596 auto cloneBlocks = [&](
bool stopAtWait) {
1597 SmallVector<Value> foldedResults;
1598 while (!worklist.empty()) {
1599 auto [oldBlock, newBlock] = worklist.pop_back_val();
1600 builder.setInsertionPointToEnd(newBlock);
1601 for (
auto &oldOp : *oldBlock) {
1603 if (
auto waitOp = dyn_cast<WaitOp>(oldOp)) {
1606 SmallVector<Value> operands;
1607 for (
auto operand : waitOp.getYieldOperands())
1608 operands.push_back(mapping.lookupOrDefault(operand));
1609 builder.
create<scf::YieldOp>(waitOp.getLoc(), operands);
1614 if (
auto condBranchOp = dyn_cast<cf::CondBranchOp>(oldOp)) {
1615 SmallVector<Value> operands;
1616 auto condition = mapping.lookupOrDefault(condBranchOp.getCondition());
1617 if (matchPattern(condition, m_NonZero())) {
1618 for (
auto operand : condBranchOp.getTrueDestOperands())
1619 operands.push_back(mapping.lookupOrDefault(operand));
1620 builder.create<cf::BranchOp>(
1621 condBranchOp.getLoc(),
1622 scheduleBlock(condBranchOp.getTrueDest()), operands);
1625 if (matchPattern(condition, m_Zero())) {
1626 for (
auto operand : condBranchOp.getFalseOperands())
1627 operands.push_back(mapping.lookupOrDefault(operand));
1628 builder.create<cf::BranchOp>(
1629 condBranchOp.getLoc(),
1630 scheduleBlock(condBranchOp.getFalseDest()), operands);
1638 if (oldOp.getNumResults() == 1 &&
1639 oldOp.getResult(0).getType().isSignlessInteger(1)) {
1640 if (
auto dnf = booleanLattice.lookup(oldOp.getResult(0))) {
1641 if (
auto result = dnf.
evaluate(evaluateTerm)) {
1642 mapping.map(oldOp.getResult(0), *result ? trueValue : falseValue);
1649 for (
auto &blockOperand : oldOp.getBlockOperands())
1650 scheduleBlock(blockOperand.
get());
1651 auto *clonedOp = builder.clone(oldOp, mapping);
1655 if (succeeded(builder.tryFold(clonedOp, foldedResults)) &&
1656 !foldedResults.empty()) {
1657 for (
auto [oldResult, foldedResult] :
1658 llvm::zip(oldOp.getResults(), foldedResults))
1659 mapping.map(oldResult, foldedResult);
1662 foldedResults.clear();
1669 worklist.push_back({&process.getBody().front(), &entryBlock});
1671 builder.setInsertionPointToEnd(mapping.lookup(wait->getBlock()));
1676 for (
auto &block : process.getBody())
1677 mapping.erase(&block);
1683 if (wait.getDest()->hasOneUse()) {
1686 for (
auto [arg, pastValue] :
1687 llvm::zip(wait.getDest()->getArguments(), pastValues))
1688 mapping.map(arg, materializedFixedValues.lookup(pastValue).first);
1691 mapping.map(wait.getDest(), builder.getBlock());
1692 worklist.push_back({wait.getDest(), builder.getBlock()});
1695 auto *dest = scheduleBlock(wait.getDest());
1699 SmallVector<Value> destOperands;
1700 assert(pastValues.size() == wait.getDestOperands().size());
1701 for (
auto pastValue : pastValues)
1702 destOperands.push_back(materializedFixedValues.lookup(pastValue).first);
1703 builder.create<cf::BranchOp>(wait.getLoc(), dest, destOperands);
1710 if (isOpTriviallyDead(trueValue))
1712 if (isOpTriviallyDead(falseValue))
1715 specializedProcesses.insert({fixedValues, executeOp.getResults()});
1716 return executeOp.getResults();
1724struct DeseqPass :
public llhd::impl::DeseqPassBase<DeseqPass> {
1725 void runOnOperation()
override;
1729void DeseqPass::runOnOperation() {
1730 SmallVector<ProcessOp> processes(getOperation().getOps<ProcessOp>());
1731 for (
auto process : processes)
1732 Deseq(process).deseq();
assert(baseType &&"element must be base type")
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
@ None
Don't preserve aggregate at all.
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
static bool operator==(const ModulePort &a, const ModulePort &b)
static llvm::hash_code hash_value(const ModulePort &port)
static llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const DNF &dnf)
bool operator<(const DictEntry &entry, const DictEntry &other)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool operator!=(uint64_t a, const FVInt &b)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
An individual term of an AND expression in a DNF.
static constexpr Uses PastUses
The set of past uses.
Use
An integer indicating one use of the value in this term.
@ NotId
The inverted value !x.
static constexpr Uses PosEdgeUses
The set of uses that indicates a posedge.
static AndTerm posEdge(Value value)
Create a term representing a posedge on a value.
static constexpr Uses NegEdgeUses
The set of uses that indicates a negedge.
std::optional< bool > evaluate(llvm::function_ref< std::optional< bool >(Value, bool)> evaluateTerm)
Try to evaluate this DNF to a constant true or false value.
int compare(const DNF &other) const
Compare against another DNF.
void printWithValues(llvm::raw_ostream &os) const
Print this DNF to os, followed by a list of the concrete values used.
std::optional< std::pair< Value, AndTerm::Use > > getSingleTerm() const
If this DNF consists of a single term, return it.
bool isNull() const
Check whether this DNF is null.
bool isFalse() const
Check whether this DNF is trivially false.
An individual term of an OR expression in a DNF.
static FixedValue getTombstoneKey()
static unsigned getHashValue(const FixedValue &key)
static FixedValue getEmptyKey()
static bool isEqual(const FixedValue &a, const FixedValue &b)
static FixedValues getEmptyKey()
static unsigned getHashValue(const FixedValues &key)
static FixedValues getTombstoneKey()
static bool isEqual(const FixedValues &a, const FixedValues &b)