CIRCT 23.0.0git
Loading...
Searching...
No Matches
FunctionalReduction.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass implements FunctionalReduction (Functionally Reduced And-Inverter
10// Graph) optimization. It identifies and merges functionally equivalent nodes
11// through simulation-based candidate detection followed by SAT-based
12// verification.
13//
14//===----------------------------------------------------------------------===//
15
22#include "mlir/IR/Attributes.h"
23#include "mlir/IR/Builders.h"
24#include "mlir/IR/BuiltinOps.h"
25#include "mlir/IR/PatternMatch.h"
26#include "mlir/Pass/Pass.h"
27#include "mlir/Support/LogicalResult.h"
28#include "llvm/ADT/APInt.h"
29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/ADT/DenseMap.h"
31#include "llvm/ADT/DenseSet.h"
32#include "llvm/ADT/MapVector.h"
33#include "llvm/ADT/STLFunctionalExtras.h"
34#include "llvm/ADT/SmallVector.h"
35#include "llvm/ADT/StringRef.h"
36#include "llvm/ADT/TypeSwitch.h"
37#include "llvm/Support/Debug.h"
38#include <random>
39
40#define DEBUG_TYPE "synth-functional-reduction"
41
42static constexpr llvm::StringLiteral kTestClassAttrName =
43 "synth.test.fc_equiv_class";
44
45namespace circt {
46namespace synth {
47#define GEN_PASS_DEF_FUNCTIONALREDUCTION
48#include "circt/Dialect/Synth/Transforms/SynthPasses.h.inc"
49} // namespace synth
50} // namespace circt
51
52using namespace circt;
53using namespace circt::synth;
54
55namespace {
56enum class EquivResult { Proved, Disproved, Unknown };
57
58std::unique_ptr<IncrementalSATSolver>
59createFunctionalReductionSATSolver(llvm::StringRef backend) {
60 if (backend == "auto") {
61 if (auto solver = createCadicalSATSolver())
62 return solver;
63 return createZ3SATSolver();
64 }
65 if (backend == "cadical")
67 if (backend == "z3")
68 return createZ3SATSolver();
69 return {};
70}
71
72class FunctionalReductionSATBuilder {
73public:
74 FunctionalReductionSATBuilder(IncrementalSATSolver &solver,
75 llvm::DenseMap<Value, int> &satVars,
76 llvm::DenseSet<Value> &encodedValues,
77 int &nextFreshVar);
78
79 EquivResult verify(Value lhs, Value rhs);
80
81private:
82 int getOrCreateVar(Value value);
83 // Create a fresh SAT variable for an intermediate Boolean subexpression that
84 // does not correspond to an MLIR value.
85 int createAuxVar();
86 int getLiteral(Value value, bool inverted = false);
87 void addAndClauses(int outVar, llvm::ArrayRef<int> inputLits);
88 void addOrClauses(int outVar, llvm::ArrayRef<int> inputLits);
89 void addXorClauses(int outVar, int lhsLit, int rhsLit);
90 void addParityClauses(int outVar, llvm::ArrayRef<int> inputLits);
91 void encodeValue(Value value);
92
94 llvm::DenseMap<Value, int> &satVars;
95 llvm::DenseSet<Value> &encodedValues;
96 int &nextFreshVar;
97};
98
99static bool isFunctionalReductionSimulatableOp(Operation *op) {
100 return isa<aig::AndInverterOp, comb::AndOp, comb::OrOp, comb::XorOp>(op);
101}
102
103EquivResult FunctionalReductionSATBuilder::verify(Value lhs, Value rhs) {
104 encodeValue(lhs);
105 encodeValue(rhs);
106
107 int lhsVar = getOrCreateVar(lhs);
108 int rhsVar = getOrCreateVar(rhs);
109
110 // Check the two halves of the XOR miter separately. If either assignment is
111 // satisfiable, the solver found a distinguishing input pattern.
112 solver.assume(lhsVar);
113 solver.assume(-rhsVar);
114 auto result = solver.solve();
115 if (result == IncrementalSATSolver::kSAT)
116 return EquivResult::Disproved;
117 if (result != IncrementalSATSolver::kUNSAT)
118 return EquivResult::Unknown;
119
120 solver.assume(-lhsVar);
121 solver.assume(rhsVar);
122 result = solver.solve();
123 if (result == IncrementalSATSolver::kSAT)
124 return EquivResult::Disproved;
125 if (result != IncrementalSATSolver::kUNSAT)
126 return EquivResult::Unknown;
127
128 return EquivResult::Proved;
129}
130
131int FunctionalReductionSATBuilder::getOrCreateVar(Value value) {
132 auto it = satVars.find(value);
133 assert(it != satVars.end() && "SAT variable must be preallocated");
134 return it->second;
135}
136
137int FunctionalReductionSATBuilder::createAuxVar() {
138 int freshVar = ++nextFreshVar;
139 solver.reserveVars(freshVar);
140 return freshVar;
141}
142
143int FunctionalReductionSATBuilder::getLiteral(Value value, bool inverted) {
144 int lit = getOrCreateVar(value);
145 return inverted ? -lit : lit;
146}
147
148void FunctionalReductionSATBuilder::addAndClauses(
149 int outVar, llvm::ArrayRef<int> inputLits) {
150 // Tseitin encoding (https://en.wikipedia.org/wiki/Tseytin_transformation)
151 // for `outVar <=> and(inputLits)`. This keeps the CNF linear in the gate size
152 // while preserving satisfiability.
153 for (int lit : inputLits)
154 solver.addClause({-outVar, lit});
155
156 SmallVector<int> clause;
157 for (int lit : inputLits)
158 clause.push_back(-lit);
159 clause.push_back(outVar);
160 solver.addClause(clause);
161}
162
163void FunctionalReductionSATBuilder::addOrClauses(
164 int outVar, llvm::ArrayRef<int> inputLits) {
165 // Encode `outVar <=> or(inputLits)`.
166 //
167 // `(-lit v outVar)` for each input enforces `lit -> outVar`, i.e. any true
168 // input forces the OR result high.
169 for (int lit : inputLits)
170 solver.addClause({-lit, outVar});
171
172 SmallVector<int> clause;
173 clause.reserve(inputLits.size() + 1);
174 // `(-outVar v lit0 v lit1 ...)` enforces `outVar -> (lit0 v lit1 ...)`.
175 // Together these clauses make `outVar` exactly the OR of the inputs.
176 clause.push_back(-outVar);
177 clause.append(inputLits.begin(), inputLits.end());
178 solver.addClause(clause);
179}
180
181void FunctionalReductionSATBuilder::addXorClauses(int outVar, int lhsLit,
182 int rhsLit) {
183 // Encode `outVar <=> (lhsLit xor rhsLit)` with the four satisfying rows of
184 // the 2-input XOR truth table. This is the standard definitional CNF for a
185 // binary XOR.
186 solver.addClause({-lhsLit, -rhsLit, -outVar});
187 solver.addClause({lhsLit, rhsLit, -outVar});
188 solver.addClause({lhsLit, -rhsLit, outVar});
189 solver.addClause({-lhsLit, rhsLit, outVar});
190}
191
192void FunctionalReductionSATBuilder::addParityClauses(
193 int outVar, llvm::ArrayRef<int> inputLits) {
194 assert(!inputLits.empty() && "parity requires at least one input");
195 if (inputLits.size() == 1) {
196 solver.addClause({-outVar, inputLits.front()});
197 solver.addClause({outVar, -inputLits.front()});
198 return;
199 }
200
201 int accumulatedLit = inputLits.front();
202 // Variadic XOR does not have a compact direct CNF encoding like AND/OR, so
203 // encode it as a chain of binary XORs and give each intermediate result its
204 // own auxiliary SAT variable.
205 for (auto [index, lit] : llvm::enumerate(inputLits.drop_front())) {
206 bool isLast = index + 2 == inputLits.size();
207 int outLit = isLast ? outVar : createAuxVar();
208 addXorClauses(outLit, accumulatedLit, lit);
209 accumulatedLit = outLit;
210 }
211}
212
213void FunctionalReductionSATBuilder::encodeValue(Value value) {
214 SmallVector<std::pair<Value, bool>> worklist;
215 worklist.push_back({value, false});
216
217 while (!worklist.empty()) {
218 auto [current, readyToEncode] = worklist.pop_back_val();
219 if (encodedValues.contains(current))
220 continue;
221
222 Operation *op = current.getDefiningOp();
223 if (!op) {
224 encodedValues.insert(current);
225 continue;
226 }
227
228 APInt constantValue;
229 if (matchPattern(current, mlir::m_ConstantInt(&constantValue))) {
230 encodedValues.insert(current);
231 solver.addClause({constantValue.isZero() ? -getOrCreateVar(current)
232 : getOrCreateVar(current)});
233 continue;
234 }
235
236 if (!isFunctionalReductionSimulatableOp(op)) {
237 // Unsupported operations remain unconstrained, just like block
238 // arguments. Since we only prove equivalence from UNSAT, omitting these
239 // clauses may miss a proof but cannot create a false proof.
240 encodedValues.insert(current);
241 continue;
242 }
243
244 if (!readyToEncode) {
245 worklist.push_back({current, true});
246 for (auto input : op->getOperands()) {
247 assert(input.getType().isInteger(1) &&
248 "only i1 inputs should be simulated or encoded");
249 if (!encodedValues.contains(input))
250 worklist.push_back({input, false});
251 }
252 continue;
253 }
254
255 encodedValues.insert(current);
256 int outVar = getOrCreateVar(current);
257
258 SmallVector<int> inputLits;
259 inputLits.reserve(op->getNumOperands());
260 TypeSwitch<Operation *>(op)
261 .Case<aig::AndInverterOp>([&](auto andOp) {
262 for (auto [input, inverted] :
263 llvm::zip(andOp.getInputs(), andOp.getInverted()))
264 inputLits.push_back(getLiteral(input, inverted));
265 addAndClauses(outVar, inputLits);
266 })
267 .Case<comb::AndOp>([&](auto andOp) {
268 for (auto input : andOp.getInputs())
269 inputLits.push_back(getLiteral(input));
270 addAndClauses(outVar, inputLits);
271 })
272 .Case<comb::OrOp>([&](auto orOp) {
273 for (auto input : orOp.getInputs())
274 inputLits.push_back(getLiteral(input));
275 addOrClauses(outVar, inputLits);
276 })
277 .Case<comb::XorOp>([&](auto xorOp) {
278 for (auto input : xorOp.getInputs())
279 inputLits.push_back(getLiteral(input));
280 addParityClauses(outVar, inputLits);
281 })
282 .Default(
283 [](Operation *) { llvm_unreachable("unexpected supported op"); });
284 }
285}
286
287//===----------------------------------------------------------------------===//
288// Core Functional Reduction Implementation
289//===----------------------------------------------------------------------===//
290
291class FunctionalReductionSolver {
292public:
293 FunctionalReductionSolver(hw::HWModuleOp module, unsigned numPatterns,
294 unsigned seed, bool testTransformation,
295 std::unique_ptr<IncrementalSATSolver> satSolver)
296 : module(module), numPatterns(numPatterns), seed(seed),
297 testTransformation(testTransformation),
298 satSolver(std::move(satSolver)) {}
299
300 ~FunctionalReductionSolver() = default;
301
302 /// Run the Functional Reduction algorithm and return statistics.
303 struct Stats {
304 unsigned numEquivClasses = 0;
305 unsigned numProvedEquiv = 0;
306 unsigned numDisprovedEquiv = 0;
307 unsigned numUnknown = 0;
308 unsigned numMergedNodes = 0;
309 };
310 mlir::FailureOr<Stats> run();
311
312private:
313 // Phase 1: Collect i1 values and run simulation
314 void collectValues();
315 void runSimulation();
316 llvm::APInt simulateValue(Value v);
317
318 // Phase 2: Build equivalence classes from simulation
319 void buildEquivalenceClasses();
320
321 // Phase 3: SAT-based verification with per-class solver
322 void verifyCandidates();
323 void initializeSATState();
324
325 // Phase 4: Merge equivalent nodes
326 void mergeEquivalentNodes();
327
328 // Test transformation helpers.
329 static Attribute getTestEquivClass(Value value);
330 static bool matchesTestEquivClass(Value lhs, Value rhs);
331 EquivResult verifyEquivalence(Value lhs, Value rhs);
332
333 // Module being processed
334 hw::HWModuleOp module;
335
336 // Configuration
337 unsigned numPatterns;
338 unsigned seed;
339 bool testTransformation;
340
341 // Primary inputs (block arguments or results of unknown operations treated as
342 // inputs)
343 SmallVector<Value> primaryInputs;
344
345 // All i1 values in topological order
346 SmallVector<Value> allValues;
347
348 // Simulation signatures: value -> APInt simulation result
349 llvm::DenseMap<Value, llvm::APInt> simSignatures;
350
351 // Equivalence candidates: groups of values with identical simulation
352 // signatures
353 SmallVector<SmallVector<Value>> equivCandidates;
354
355 // Proven equivalences: representative -> proven equivalent members.
357
358 std::unique_ptr<IncrementalSATSolver> satSolver;
359 std::unique_ptr<FunctionalReductionSATBuilder> satBuilder;
360 llvm::DenseMap<Value, int> satVars;
361 llvm::DenseSet<Value> encodedValues;
362 // Monotonic counter for auxiliary SAT variables introduced by definitional
363 // CNF encodings, currently used for variadic XOR.
364 int nextFreshVar = 0;
365 Stats stats;
366};
367
368FunctionalReductionSATBuilder::FunctionalReductionSATBuilder(
369 IncrementalSATSolver &solver, llvm::DenseMap<Value, int> &satVars,
370 llvm::DenseSet<Value> &encodedValues, int &nextFreshVar)
371 : solver(solver), satVars(satVars), encodedValues(encodedValues),
372 nextFreshVar(nextFreshVar) {}
373
374Attribute FunctionalReductionSolver::getTestEquivClass(Value value) {
375 Operation *op = value.getDefiningOp();
376 if (!op)
377 return {};
378 return op->getAttr(kTestClassAttrName);
379}
380
381bool FunctionalReductionSolver::matchesTestEquivClass(Value lhs, Value rhs) {
382 Attribute lhsClass = getTestEquivClass(lhs);
383 Attribute rhsClass = getTestEquivClass(rhs);
384 return lhsClass && rhsClass && lhsClass == rhsClass;
385}
386
387EquivResult FunctionalReductionSolver::verifyEquivalence(Value lhs, Value rhs) {
388 if (testTransformation) {
389 if (matchesTestEquivClass(lhs, rhs))
390 return EquivResult::Proved;
391 return EquivResult::Unknown;
392 }
393 assert(satBuilder && "SAT builder must be initialized before verification");
394 // SAT-based equivalence checking builds a miter for the two candidate nodes
395 // and proves that no input assignment can make them differ.
396 return satBuilder->verify(lhs, rhs);
397}
398
399void FunctionalReductionSolver::initializeSATState() {
400 assert(satSolver && "SAT solver must be initialized before SAT state setup");
401
402 satVars.clear();
403 encodedValues.clear();
404 satVars.reserve(allValues.size());
405 for (auto [index, value] : llvm::enumerate(allValues))
406 satVars[value] = index + 1;
407 nextFreshVar = allValues.size();
408 satSolver->reserveVars(allValues.size());
409
410 satBuilder = std::make_unique<FunctionalReductionSATBuilder>(
411 *satSolver, satVars, encodedValues, nextFreshVar);
412}
413
414//===----------------------------------------------------------------------===//
415// Phase 1: Collect values and run simulation
416//===----------------------------------------------------------------------===//
417
418void FunctionalReductionSolver::collectValues() {
419 // Collect block arguments (primary inputs) that are i1
420 for (auto arg : module.getBodyBlock()->getArguments()) {
421 if (arg.getType().isInteger(1)) {
422 primaryInputs.push_back(arg);
423 allValues.push_back(arg);
424 }
425 }
426
427 // Walk operations and collect i1 results
428 // - AIG operations: add to allValues for simulation
429 // - Unknown operations: treat as inputs (assign random patterns)
430 module.walk([&](Operation *op) {
431 for (auto result : op->getResults()) {
432 if (!result.getType().isInteger(1))
433 continue;
434
435 allValues.push_back(result);
436 if (!op->hasTrait<OpTrait::ConstantLike>() &&
437 !isFunctionalReductionSimulatableOp(op)) {
438 // Unknown operations - treat as primary inputs
439 primaryInputs.push_back(result);
440 }
441 }
442 });
443
444 LLVM_DEBUG(llvm::dbgs() << "FunctionalReduction: Collected "
445 << primaryInputs.size()
446 << " primary inputs (including unknown ops) and "
447 << allValues.size() << " total i1 values\n");
448}
449
450void FunctionalReductionSolver::runSimulation() {
451 // Calculate number of 64-bit words needed for numPatterns bits
452 unsigned numWords = numPatterns / 64;
453
454 // Create seeded random number generator for deterministic patterns
455 std::mt19937_64 rng(seed);
456
457 for (auto input : primaryInputs) {
458 // Generate random words using seeded RNG
459 SmallVector<uint64_t> words(numWords);
460 for (auto &word : words)
461 word = rng();
462
463 // Construct APInt directly from words
464 llvm::APInt pattern(numPatterns, words);
465 simSignatures[input] = pattern;
466 }
467
468 // Propagate simulation through the circuit in topological order
469 for (auto value : allValues) {
470 if (simSignatures.count(value))
471 continue; // Already computed (primary input)
472
473 simSignatures[value] = simulateValue(value);
474 }
475
476 LLVM_DEBUG({
477 llvm::dbgs() << "FunctionalReduction: Simulation complete with "
478 << numPatterns << " patterns\n";
479 });
480}
481
482llvm::APInt FunctionalReductionSolver::simulateValue(Value v) {
483 Operation *op = v.getDefiningOp();
484 if (!op)
485 return simSignatures.at(v);
486 return llvm::TypeSwitch<Operation *, llvm::APInt>(op)
487 .Case<aig::AndInverterOp>([&](auto op) {
488 SmallVector<llvm::APInt> inputSigs;
489 for (auto input : op.getInputs())
490 inputSigs.push_back(simSignatures.at(input));
491 return op.evaluate(inputSigs);
492 })
493 .Case<comb::AndOp>([&](auto op) {
494 APInt result = APInt::getAllOnes(numPatterns);
495 for (auto input : op.getInputs())
496 result &= simSignatures.at(input);
497 return result;
498 })
499 .Case<comb::OrOp>([&](auto op) {
500 APInt result = APInt::getZero(numPatterns);
501 for (auto input : op.getInputs())
502 result |= simSignatures.at(input);
503 return result;
504 })
505 .Case<comb::XorOp>([&](auto op) {
506 APInt result = APInt::getZero(numPatterns);
507 for (auto input : op.getInputs())
508 result ^= simSignatures.at(input);
509 return result;
510 })
511 .Case([&](hw::ConstantOp op) {
512 return op.getValue().isZero() ? APInt::getZero(numPatterns)
513 : APInt::getAllOnes(numPatterns);
514 })
515 .Default([&](Operation *) {
516 // Unknown operation - treat as input (already assigned a random
517 // pattern)
518 return simSignatures.at(v);
519 });
520}
521
522//===----------------------------------------------------------------------===//
523// Phase 2: Build equivalence classes from simulation
524//===----------------------------------------------------------------------===//
525
526void FunctionalReductionSolver::buildEquivalenceClasses() {
527 // Map from signature to list of values
529
530 for (auto value : allValues)
531 sigGroups[simSignatures.at(value)].push_back(value);
532
533 // Build equivalence candidates for groups with >1 member.
534 for (auto &[hash, members] : sigGroups) {
535 if (members.size() <= 1)
536 continue;
537 equivCandidates.push_back(std::move(members));
538 }
539 stats.numEquivClasses = equivCandidates.size();
540
541 LLVM_DEBUG(llvm::dbgs() << "FunctionalReduction: Built "
542 << equivCandidates.size()
543 << " equivalence candidates\n");
544}
545
546//===----------------------------------------------------------------------===//
547// Phase 3: SAT-based verification with per-class solvers
548//
549// For each equivalence class candidates, verify each member against the
550// representative using a SAT solver.
551//===----------------------------------------------------------------------===//
552
553void FunctionalReductionSolver::verifyCandidates() {
554 LLVM_DEBUG(
555 llvm::dbgs() << "FunctionalReduction: Starting SAT verification with "
556 << equivCandidates.size() << " equivalence classes\n");
557
558 for (auto &members : equivCandidates) {
559 if (members.empty())
560 continue;
561 auto representative = members.front();
562 auto &provenMembers = provenEquivalences[representative];
563 // Representative is the canonical node for this class.
564 for (auto member : llvm::ArrayRef<Value>(members).drop_front()) {
565 EquivResult result = verifyEquivalence(representative, member);
566 if (result == EquivResult::Proved) {
567 stats.numProvedEquiv++;
568 provenMembers.push_back(member);
569 } else if (result == EquivResult::Disproved) {
570 stats.numDisprovedEquiv++;
571 // TODO: Refine equivalence classes based on counterexamples from SAT
572 // solver
573 } else {
574 stats.numUnknown++;
575 }
576 }
577 }
578
579 LLVM_DEBUG(
580 llvm::dbgs() << "FunctionalReduction: SAT verification complete. Proved "
581 << stats.numProvedEquiv << " equivalences\n");
582}
583
584//===----------------------------------------------------------------------===//
585// Phase 4: Merge equivalent nodes
586//===----------------------------------------------------------------------===//
587
588void FunctionalReductionSolver::mergeEquivalentNodes() {
589 if (provenEquivalences.empty())
590 return;
591
592 mlir::OpBuilder builder(module.getContext());
593 for (auto &provenEquivSet : provenEquivalences) {
594 auto &[representative, members] = provenEquivSet;
595 if (members.empty())
596 continue;
597 SmallVector<Value> operands;
598 operands.reserve(members.size() + 1);
599 operands.push_back(representative);
600 operands.append(members);
601 builder.setInsertionPointAfterValue(members.back());
602 auto choice = synth::ChoiceOp::create(builder, representative.getLoc(),
603 representative.getType(), operands);
604 stats.numMergedNodes += members.size() + 1;
605 representative.replaceAllUsesExcept(choice, choice);
606 for (auto value : members)
607 value.replaceAllUsesExcept(choice, choice);
608 }
609
610 LLVM_DEBUG(llvm::dbgs() << "FunctionalReduction: Merged "
611 << stats.numMergedNodes << " nodes\n");
612}
613
614//===----------------------------------------------------------------------===//
615// Main Functional Reduction algorithm
616//===----------------------------------------------------------------------===//
617
618mlir::FailureOr<FunctionalReductionSolver::Stats>
619FunctionalReductionSolver::run() {
620 LLVM_DEBUG(
621 llvm::dbgs() << "FunctionalReduction: Starting functional reduction with "
622 << numPatterns << " simulation patterns\n");
623
624 if (!testTransformation && !satSolver) {
625 module->emitError()
626 << "FunctionalReduction requires a SAT solver, but none is "
627 "available in this build";
628 return failure();
629 }
630
631 // Topologically sort the values
633 module->emitError()
634 << "FunctionalReduction: Failed to topologically sort logic network";
635 return failure();
636 }
637
638 // Phase 1: Collect values and run simulation
639 collectValues();
640 if (allValues.empty()) {
641 LLVM_DEBUG(llvm::dbgs()
642 << "FunctionalReduction: No i1 values to process\n");
643 return stats;
644 }
645
646 runSimulation();
647
648 // Phase 2: Build equivalence classes
649 buildEquivalenceClasses();
650 if (equivCandidates.empty()) {
651 LLVM_DEBUG(llvm::dbgs()
652 << "FunctionalReduction: No equivalence candidates found\n");
653 return stats;
654 }
655
656 // Phase 3: SAT-based verification
657 if (!testTransformation)
658 initializeSATState();
659 verifyCandidates();
660
661 // Phase 4: Merge equivalent nodes
662 mergeEquivalentNodes();
663
664 LLVM_DEBUG(llvm::dbgs() << "FunctionalReduction: Complete. Stats:\n"
665 << " Equivalence classes: " << stats.numEquivClasses
666 << "\n"
667 << " Proved: " << stats.numProvedEquiv << "\n"
668 << " Disproved: " << stats.numDisprovedEquiv << "\n"
669 << " Unknown (limit): " << stats.numUnknown << "\n"
670 << " Merged: " << stats.numMergedNodes << "\n");
671
672 return stats;
673}
674
675//===----------------------------------------------------------------------===//
676// Pass implementation
677//===----------------------------------------------------------------------===//
678
679struct FunctionalReductionPass
680 : public circt::synth::impl::FunctionalReductionBase<
681 FunctionalReductionPass> {
682 using FunctionalReductionBase::FunctionalReductionBase;
683 void updateStats(const FunctionalReductionSolver::Stats &stats) {
684 numEquivClasses += stats.numEquivClasses;
685 numProvedEquiv += stats.numProvedEquiv;
686 numDisprovedEquiv += stats.numDisprovedEquiv;
687 numUnknown += stats.numUnknown;
688 numMergedNodes += stats.numMergedNodes;
689 }
690
691 void runOnOperation() override {
692 auto module = getOperation();
693 LLVM_DEBUG(llvm::dbgs() << "Running FunctionalReduction pass on "
694 << module.getName() << "\n");
695
696 if (numRandomPatterns == 0 || (numRandomPatterns & 63U) != 0) {
697 module.emitError()
698 << "'num-random-patterns' must be a positive multiple of 64";
699 return signalPassFailure();
700 }
701 if (conflictLimit < -1) {
702 module.emitError()
703 << "'conflict-limit' must be greater than or equal to -1";
704 return signalPassFailure();
705 }
706
707 std::unique_ptr<IncrementalSATSolver> satSolver;
708 if (!testTransformation) {
709 satSolver = createFunctionalReductionSATSolver(this->satSolver);
710 if (!satSolver) {
711 module.emitError() << "unsupported or unavailable SAT solver '"
712 << this->satSolver
713 << "' (expected auto, z3, or cadical)";
714 return signalPassFailure();
715 }
716 satSolver->setConflictLimit(static_cast<int>(conflictLimit));
717 }
718
719 FunctionalReductionSolver fcSolver(module, numRandomPatterns, seed,
720 testTransformation,
721 std::move(satSolver));
722 auto stats = fcSolver.run();
723 if (failed(stats))
724 return signalPassFailure();
725 updateStats(*stats);
726 if (stats->numMergedNodes == 0)
727 markAllAnalysesPreserved();
728 }
729};
730
731} // namespace
assert(baseType &&"element must be base type")
static constexpr llvm::StringLiteral kTestClassAttrName
static Block * getBodyBlock(FModuleLike mod)
RewritePatternSet pattern
Abstract interface for incremental SAT solvers with an IPASIR-style API.
Definition SATSolver.h:156
LogicalResult topologicallySortLogicNetwork(mlir::Operation *op)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< IncrementalSATSolver > createZ3SATSolver()
Construct a Z3-backed incremental IPASIR-style SAT solver.
std::unique_ptr< IncrementalSATSolver > createCadicalSATSolver(const CadicalSATSolverOptions &options={})
Construct a CaDiCaL-backed incremental IPASIR-style SAT solver.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:445
Definition synth.py:1