CIRCT 23.0.0git
Loading...
Searching...
No Matches
SynthOps.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
17#include "mlir/Analysis/TopologicalSortUtils.h"
18#include "mlir/IR/BuiltinAttributes.h"
19#include "mlir/IR/Matchers.h"
20#include "mlir/IR/OpDefinition.h"
21#include "mlir/IR/PatternMatch.h"
22#include "mlir/IR/Value.h"
23#include "mlir/Interfaces/CallInterfaces.h"
24#include "mlir/Interfaces/FunctionImplementation.h"
25#include "llvm/ADT/APInt.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/Support/Casting.h"
29#include "llvm/Support/LogicalResult.h"
30
31using namespace mlir;
32using namespace circt;
33using namespace circt::synth;
34using namespace circt::synth::aig;
35using namespace circt::comb;
36using namespace matchers;
37
38#define GET_OP_CLASSES
39#include "circt/Dialect/Synth/Synth.cpp.inc"
40
41namespace {
42
43inline llvm::KnownBits applyInversion(llvm::KnownBits value, bool inverted) {
44 if (inverted)
45 std::swap(value.Zero, value.One);
46 return value;
47}
48
49template <typename SubType>
50struct ComplementMatcher {
51 SubType lhs;
52 ComplementMatcher(SubType lhs) : lhs(std::move(lhs)) {}
53 bool match(Operation *op) {
54 auto boolOp = dyn_cast<BooleanLogicOpInterface>(op);
55 return boolOp && boolOp.getInputs().size() == 1 && boolOp.isInverted(0) &&
56 lhs.match(op->getOperand(0));
57 }
58};
59
60template <typename SubType>
61static inline ComplementMatcher<SubType> m_Complement(const SubType &subExpr) {
62 return ComplementMatcher<SubType>(subExpr);
63}
64
65} // namespace
66
67LogicalResult ChoiceOp::verify() {
68 if (getNumOperands() < 1)
69 return emitOpError("requires at least one operand");
70 return success();
71}
72
73OpFoldResult ChoiceOp::fold(FoldAdaptor adaptor) {
74 if (adaptor.getInputs().size() == 1)
75 return getOperand(0);
76 return {};
77}
78
79// Canonicalize a network of synth.choice operations by computing their
80// transitive closure and flattening them into a single choice operation.
81// This merges nested choices and deduplicates shared operands.
82// Pattern matched:
83// %0 = synth.choice %x, %y, %z
84// %1 = synth.choice %0, %u
85// %2 = synth.choice %z, %v
86// =>
87// %merged = synth.choice %x, %y, %z, %u, %v
88LogicalResult ChoiceOp::canonicalize(ChoiceOp op, PatternRewriter &rewriter) {
89 llvm::SetVector<Value> worklist;
91
92 auto addToWorklist = [&](ChoiceOp choice) -> bool {
93 if (choice->getBlock() == op->getBlock() && visitedChoices.insert(choice)) {
94 worklist.insert(choice.getInputs().begin(), choice.getInputs().end());
95 return true;
96 }
97 return false;
98 };
99
100 addToWorklist(op);
101
102 bool mergedOtherChoices = false;
103
104 // Look up and down at definitions and users.
105 for (unsigned i = 0; i < worklist.size(); ++i) {
106 Value val = worklist[i];
107 if (auto defOp = val.getDefiningOp<synth::ChoiceOp>()) {
108
109 if (addToWorklist(defOp))
110 mergedOtherChoices = true;
111 }
112
113 for (Operation *user : val.getUsers()) {
114 if (auto userChoice = llvm::dyn_cast<synth::ChoiceOp>(user)) {
115 if (addToWorklist(userChoice)) {
116 mergedOtherChoices = true;
117 }
118 }
119 }
120 }
121
122 llvm::SmallVector<mlir::Value> finalOperands;
123 for (Value v : worklist) {
124 if (!visitedChoices.contains(v.getDefiningOp())) {
125 finalOperands.push_back(v);
126 }
127 }
128
129 if (!mergedOtherChoices && finalOperands.size() == op.getInputs().size())
130 return llvm::failure();
131
132 auto newChoice = synth::ChoiceOp::create(rewriter, op->getLoc(), op.getType(),
133 finalOperands);
134 for (Operation *visited : visitedChoices.takeVector())
135 rewriter.replaceOp(visited, newChoice);
136
137 for (auto value : newChoice.getInputs())
138 rewriter.replaceAllUsesExcept(value, newChoice.getResult(), newChoice);
139
140 return success();
141}
142
143//===----------------------------------------------------------------------===//
144// AndInverterOp
145//===----------------------------------------------------------------------===//
146
147bool AndInverterOp::areInputsPermutationInvariant() { return true; }
148
149OpFoldResult AndInverterOp::fold(FoldAdaptor adaptor) {
150 if (getNumOperands() == 1 && !isInverted(0))
151 return getOperand(0);
152
153 auto inputs = adaptor.getInputs();
154 if (inputs.size() == 2)
155 if (auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1])) {
156 auto value = intAttr.getValue();
157 if (isInverted(1))
158 value = ~value;
159 if (value.isZero())
160 return IntegerAttr::get(
161 IntegerType::get(getContext(), value.getBitWidth()), value);
162 if (value.isAllOnes()) {
163 if (isInverted(0))
164 return {};
165
166 return getOperand(0);
167 }
168 }
169 return {};
170}
171
172LogicalResult AndInverterOp::canonicalize(AndInverterOp op,
173 PatternRewriter &rewriter) {
175 SmallVector<Value> uniqueValues;
176 SmallVector<bool> uniqueInverts;
177
178 APInt constValue =
179 APInt::getAllOnes(op.getResult().getType().getIntOrFloatBitWidth());
180
181 bool invertedConstFound = false;
182 bool flippedFound = false;
183
184 for (auto [value, inverted] : llvm::zip(op.getInputs(), op.getInverted())) {
185 bool newInverted = inverted;
186 if (auto constOp = value.getDefiningOp<hw::ConstantOp>()) {
187 if (inverted) {
188 constValue &= ~constOp.getValue();
189 invertedConstFound = true;
190 } else {
191 constValue &= constOp.getValue();
192 }
193 continue;
194 }
195
196 if (auto andInverterOp = value.getDefiningOp<synth::aig::AndInverterOp>()) {
197 if (andInverterOp.getInputs().size() == 1 &&
198 andInverterOp.isInverted(0)) {
199 value = andInverterOp.getOperand(0);
200 newInverted = andInverterOp.isInverted(0) ^ inverted;
201 flippedFound = true;
202 }
203 }
204
205 auto it = seen.find(value);
206 if (it == seen.end()) {
207 seen.insert({value, newInverted});
208 uniqueValues.push_back(value);
209 uniqueInverts.push_back(newInverted);
210 } else if (it->second != newInverted) {
211 // replace with const 0
212 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
213 op, APInt::getZero(value.getType().getIntOrFloatBitWidth()));
214 return success();
215 }
216 }
217
218 // If the constant is zero, we can just replace with zero.
219 if (constValue.isZero()) {
220 rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, constValue);
221 return success();
222 }
223
224 // No change.
225 if ((uniqueValues.size() == op.getInputs().size() && !flippedFound) ||
226 (!constValue.isAllOnes() && !invertedConstFound &&
227 uniqueValues.size() + 1 == op.getInputs().size()))
228 return failure();
229
230 if (!constValue.isAllOnes()) {
231 auto constOp = hw::ConstantOp::create(rewriter, op.getLoc(), constValue);
232 uniqueInverts.push_back(false);
233 uniqueValues.push_back(constOp);
234 }
235
236 // It means the input is reduced to all ones.
237 if (uniqueValues.empty()) {
238 rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, constValue);
239 return success();
240 }
241
242 // build new op with reduced input values
243 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(
244 rewriter, op, uniqueValues, uniqueInverts);
245 return success();
246}
247
248APInt AndInverterOp::evaluateBooleanLogicWithoutInversion(
249 llvm::ArrayRef<APInt> inputs) {
250 assert(!inputs.empty() && "expected non-empty input list");
251 APInt result = APInt::getAllOnes(inputs.front().getBitWidth());
252 for (const APInt &input : inputs)
253 result &= input;
254 return result;
255}
256
257bool AndInverterOp::supportsNumInputs(unsigned numInputs) {
258 return numInputs >= 1;
259}
260
261llvm::KnownBits AndInverterOp::computeKnownBits(
262 llvm::function_ref<const llvm::KnownBits &(unsigned)> getInputKnownBits) {
263 assert(getNumOperands() > 0 && "Expected non-empty input list");
264
265 auto width = getInputKnownBits(0).getBitWidth();
266 llvm::KnownBits result(width);
267 result.One = APInt::getAllOnes(width);
268 result.Zero = APInt::getZero(width);
269
270 for (auto [i, inverted] : llvm::enumerate(getInverted()))
271 result &= applyInversion(getInputKnownBits(i), inverted);
272
273 return result;
274}
275
276int64_t AndInverterOp::getLogicDepthCost() {
277 return llvm::Log2_64_Ceil(getNumOperands());
278}
279
280std::optional<uint64_t> AndInverterOp::getLogicAreaCost() {
281 int64_t bitWidth = hw::getBitWidth(getType());
282 if (bitWidth < 0)
283 return std::nullopt;
284 return static_cast<uint64_t>(getNumOperands() - 1) * bitWidth;
285}
286
287void AndInverterOp::emitCNFWithoutInversion(
288 int outVar, llvm::ArrayRef<int> inputVars,
289 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
290 llvm::function_ref<int()> newVar) {
291 (void)newVar;
292 circt::addAndClauses(outVar, inputVars, addClause);
293}
294
295//===----------------------------------------------------------------------===//
296// XorInverterOp
297//===----------------------------------------------------------------------===//
298
299bool XorInverterOp::areInputsPermutationInvariant() { return true; }
300
301OpFoldResult XorInverterOp::fold(FoldAdaptor adaptor) {
302 // xor_inv(a) -> a
303 if (getNumOperands() == 1 && !isInverted(0))
304 return getOperand(0);
305
306 auto inputs = adaptor.getInputs();
307 if (inputs.size() == 2)
308 if (auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1])) {
309 auto value = intAttr.getValue();
310 if (isInverted(1))
311 value = ~value;
312 // xor_inv(a, 0000000) -> a
313 if (value.isZero())
314 return getOperand(0);
315 }
316 return {};
317}
318
319LogicalResult XorInverterOp::canonicalize(XorInverterOp op,
320 PatternRewriter &rewriter) {
321
322 // Map to store active (non-canceled) operands and their inversion state
323 SmallMapVector<Value, bool, 4> activeOperands;
324
325 // XOR identity is zero; accumulate all constant operands here.
326 APInt constValue =
327 APInt::getZero(op.getResult().getType().getIntOrFloatBitWidth());
328
329 bool constFound = false;
330 bool changed = false;
331
332 for (auto [value, inverted] : llvm::zip(op.getInputs(), op.getInverted())) {
333 Value currentValue = value;
334 bool newInverted = inverted;
335
336 // xor_inv(a, c0, c1) -> xor_inv(a, c0 ^ c1)
337 // xor_inv(a, not c0) -> xor_inv(a, ~c0)
338 if (auto constOp = currentValue.getDefiningOp<hw::ConstantOp>()) {
339 APInt val = constOp.getValue();
340 if (newInverted)
341 val = ~val;
342 constValue ^= val;
343 constFound = true;
344 continue;
345 }
346
347 // xor_inv(a, not (xor_inv/aig_inv not b)) -> xor_inv(a, b)
348 Value matchedVal;
349 if (newInverted &&
350 matchPattern(currentValue, m_Complement(m_Any(&matchedVal)))) {
351 currentValue = matchedVal;
352 newInverted = false; // double inversion cancels out
353 changed = true;
354 }
355
356 // xor_inv (a, a, b) -> b
357 // xor_inv (a, not a, b) -> ~b
358 if (activeOperands.count(currentValue)) {
359 // If we see the value again, they cancel out.
360 // If one was inverted and the other wasn't (x ^ ~x), it results in a '1'.
361 if (activeOperands[currentValue] != newInverted)
362 constValue.flipAllBits();
363 activeOperands.erase(currentValue);
364 changed = true;
365 } else {
366 activeOperands[currentValue] = newInverted;
367 }
368 }
369
370 // No constants were folded and no operands cancelled out. There is nothing to
371 // do.
372 if (!changed && !constFound && activeOperands.size() == op.getInputs().size())
373 return failure();
374
375 // xor_inv(a, 1111111) -> xor_inv(not a)
376 // xor_inv(a, c0, c1) -> xor_inv(a, c0^c1)
377 if (!constValue.isZero()) {
378 if (constValue.isAllOnes() && !activeOperands.empty()) {
379 // Propagate ones as an inversion on the last operand.
380 activeOperands.back().second = !activeOperands.back().second;
381 } else {
382 if (op.getInputs().size() == 2 && !op.getInverted()[1] &&
383 activeOperands.size() == 1)
384 return failure();
385 auto constOp = hw::ConstantOp::create(rewriter, op.getLoc(), constValue);
386 activeOperands.insert({constOp, false});
387 }
388 }
389
390 if (activeOperands.empty()) {
391 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
392 op, APInt::getZero(op.getResult().getType().getIntOrFloatBitWidth()));
393 return success();
394 }
395
396 replaceOpAndCopyNamehint(rewriter, op,
397 XorInverterOp::create(rewriter, op.getLoc(),
398 activeOperands.getArrayRef()));
399 return success();
400}
401
402APInt XorInverterOp::evaluateBooleanLogicWithoutInversion(
403 llvm::ArrayRef<APInt> inputs) {
404 assert(!inputs.empty() && "expected non-empty input list");
405 APInt result = APInt::getZero(inputs.front().getBitWidth());
406 for (const APInt &input : inputs)
407 result ^= input;
408 return result;
409}
410
411bool XorInverterOp::supportsNumInputs(unsigned numInputs) {
412 return numInputs >= 1;
413}
414
415llvm::KnownBits XorInverterOp::computeKnownBits(
416 llvm::function_ref<const llvm::KnownBits &(unsigned)> getInputKnownBits) {
417 assert(getNumOperands() > 0 && "Expected non-empty input list");
418
419 llvm::KnownBits result(getInputKnownBits(0).getBitWidth());
420 for (auto [i, inverted] : llvm::enumerate(getInverted()))
421 result ^= applyInversion(getInputKnownBits(i), inverted);
422 return result;
423}
424
425int64_t XorInverterOp::getLogicDepthCost() {
426 return llvm::Log2_64_Ceil(getNumOperands());
427}
428
429std::optional<uint64_t> XorInverterOp::getLogicAreaCost() {
430 int64_t bitWidth = hw::getBitWidth(getType());
431 if (bitWidth < 0)
432 return std::nullopt;
433 return static_cast<uint64_t>(getNumOperands() - 1) * bitWidth;
434}
435
436void XorInverterOp::emitCNFWithoutInversion(
437 int outVar, llvm::ArrayRef<int> inputVars,
438 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
439 llvm::function_ref<int()> newVar) {
440 circt::addParityClauses(outVar, inputVars, addClause, newVar);
441}
442
443//===----------------------------------------------------------------------===//
444// DotOp
445//===----------------------------------------------------------------------===//
446
447void DotOp::emitCNFWithoutInversion(
448 int outVar, llvm::ArrayRef<int> inputVars,
449 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
450 llvm::function_ref<int()> newVar) {
451 assert(inputVars.size() == 3 && "expected one SAT variable per operand");
452 int andVar = newVar();
453 int orVar = newVar();
454 // andVar = x and y
455 circt::addAndClauses(andVar, {inputVars[0], inputVars[1]}, addClause);
456 // orVar = z or andVar
457 circt::addOrClauses(orVar, {inputVars[2], andVar}, addClause);
458 // outVar = x xor orVar
459 circt::addXorClauses(outVar, inputVars[0], orVar, addClause);
460}
461
462//===----------------------------------------------------------------------===//
463// MajorityOp
464//===----------------------------------------------------------------------===//
465
466void MajorityOp::emitCNFWithoutInversion(
467 int outVar, llvm::ArrayRef<int> inputVars,
468 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
469 llvm::function_ref<int()> newVar) {
470 assert(inputVars.size() == 3 && "expected exactly three inputs");
471 int ab = newVar();
472 int ac = newVar();
473 int bc = newVar();
474 // ab = a & b
475 circt::addAndClauses(ab, {inputVars[0], inputVars[1]}, addClause);
476 // ac = a & c
477 circt::addAndClauses(ac, {inputVars[0], inputVars[2]}, addClause);
478 // bc = b & c
479 circt::addAndClauses(bc, {inputVars[1], inputVars[2]}, addClause);
480 // out = ab | ac | bc
481 circt::addOrClauses(outVar, {ab, ac, bc}, addClause);
482}
483
485 Location loc, ValueRange operands, ArrayRef<bool> inverts,
486 PatternRewriter &rewriter,
487 llvm::function_ref<Value(Value, bool)> createUnary,
488 llvm::function_ref<Value(Value, Value, bool, bool)> createBinary) {
489 switch (operands.size()) {
490 case 0:
491 assert(0 && "cannot be called with empty operand range");
492 break;
493 case 1:
494 return inverts[0] ? createUnary(operands[0], true) : operands[0];
495 case 2:
496 return createBinary(operands[0], operands[1], inverts[0], inverts[1]);
497 default:
498 auto firstHalf = operands.size() / 2;
499 auto lhs = lowerVariadicInvertibleOp(loc, operands.take_front(firstHalf),
500 inverts.take_front(firstHalf),
501 rewriter, createUnary, createBinary);
502 auto rhs = lowerVariadicInvertibleOp(loc, operands.drop_front(firstHalf),
503 inverts.drop_front(firstHalf),
504 rewriter, createUnary, createBinary);
505 return createBinary(lhs, rhs, false, false);
506 }
507 return Value();
508}
509
510template <typename OpTy>
512 PatternRewriter &rewriter) {
513 if (op.getInputs().size() <= 2)
514 return failure();
515 auto result = lowerVariadicInvertibleOp(
516 op.getLoc(), op.getOperands(), op.getInverted(), rewriter,
517 [&](Value input, bool invert) {
518 return OpTy::create(rewriter, op.getLoc(), input, invert);
519 },
520 [&](Value lhs, Value rhs, bool invertLhs, bool invertRhs) {
521 return OpTy::create(rewriter, op.getLoc(), lhs, rhs, invertLhs,
522 invertRhs);
523 });
524 replaceOpAndCopyNamehint(rewriter, op, result);
525 return success();
526}
527
529 RewritePatternSet &patterns) {
530 patterns.add(lowerVariadicAndInverterOpConversion<aig::AndInverterOp>);
531}
532
534 RewritePatternSet &patterns) {
535 patterns.add(lowerVariadicAndInverterOpConversion<XorInverterOp>);
536}
537
538bool circt::synth::isLogicNetworkOp(Operation *op) {
539 return isa<synth::BooleanLogicOpInterface, synth::ChoiceOp, comb::ExtractOp,
540 comb::ReplicateOp, comb::ConcatOp>(op);
541}
542
544 mlir::Operation *op,
545 llvm::function_ref<bool(mlir::Value, mlir::Operation *)> isOperandReady) {
546 // Sort the operations topologically
547 auto walkResult = op->walk([&](Region *region) {
548 auto regionKindOp =
549 dyn_cast<mlir::RegionKindInterface>(region->getParentOp());
550 if (!regionKindOp ||
551 regionKindOp.hasSSADominance(region->getRegionNumber()))
552 return WalkResult::advance();
553
554 // Graph region.
555 for (auto &block : *region) {
556 if (!mlir::sortTopologically(&block, isOperandReady))
557 return WalkResult::interrupt();
558 }
559 return WalkResult::advance();
560 });
561
562 return success(!walkResult.wasInterrupted());
563}
564
565//===----------------------------------------------------------------------===//
566// OneHotOp
567//===----------------------------------------------------------------------===//
568
569void OneHotOp::emitCNFWithoutInversion(
570 int outVar, llvm::ArrayRef<int> inputVars,
571 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
572 llvm::function_ref<int()> newVar) {
573 assert(inputVars.size() == 3 && "expected exactly three inputs");
574
575 // parity = a ^ b ^ c.
576 int parity = newVar();
577 circt::addParityClauses(parity, inputVars, addClause, newVar);
578
579 // allSet = a & b & c.
580 int allSet = newVar();
581 circt::addAndClauses(allSet, inputVars, addClause);
582
583 // out = (a ^ b ^ c) & ~(a & b & c).
584 circt::addAndClauses(outVar, {parity, -allSet}, addClause);
585}
586
587//===----------------------------------------------------------------------===//
588// MuxInverterOp
589//===----------------------------------------------------------------------===//
590
591void MuxInverterOp::emitCNFWithoutInversion(
592 int outVar, llvm::ArrayRef<int> inputVars,
593 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
594 llvm::function_ref<int()> newVar) {
595 assert(inputVars.size() == 3 && "expected exactly three inputs");
596
597 int cond = inputVars[0];
598 int trueValue = inputVars[1];
599 int falseValue = inputVars[2];
600
601 int lhs = newVar();
602 int rhs = newVar();
603
604 // lhs = cond & trueValue
605 circt::addAndClauses(lhs, {cond, trueValue}, addClause);
606 // rhs = ~cond & falseValue
607 circt::addAndClauses(rhs, {-cond, falseValue}, addClause);
608 // out = lhs | rhs
609 circt::addOrClauses(outVar, {lhs, rhs}, addClause);
610}
611
612//===----------------------------------------------------------------------===//
613// GambleOp
614//===----------------------------------------------------------------------===//
615
616void GambleOp::emitCNFWithoutInversion(
617 int outVar, llvm::ArrayRef<int> inputVars,
618 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
619 llvm::function_ref<int()> newVar) {
620 assert(inputVars.size() == 3 && "expected exactly three inputs");
621
622 // allSet = a & b & c
623 int allSet = newVar();
624 circt::addAndClauses(allSet, inputVars, addClause);
625
626 // orSet = a | b | c
627 int orSet = newVar();
628 circt::addOrClauses(orSet, inputVars, addClause);
629
630 // out = allSet | ~orSet
631 circt::addOrClauses(outVar, {allSet, -orSet}, addClause);
632}
633
634//===----------------------------------------------------------------------===//
635// CutRewritePatternOp
636//===----------------------------------------------------------------------===//
637
638ParseResult CutRewritePatternOp::parse(OpAsmParser &parser,
639 OperationState &result) {
640
641 SmallVector<OpAsmParser::Argument> entryArgs;
642 SmallVector<Type> resultTypes;
643 SmallVector<DictionaryAttr> resultAttrs;
644 bool isVariadic = false;
645
646 if (function_interface_impl::parseFunctionSignatureWithArguments(
647 parser, /*allowVariadic=*/false, entryArgs, isVariadic, resultTypes,
648 resultAttrs))
649 return failure();
650
651 auto inputTypes = llvm::map_to_vector(
652 entryArgs, [](auto &arg) -> Type { return arg.type; });
653 auto functionType =
654 parser.getBuilder().getFunctionType(inputTypes, resultTypes);
655
656 result.addAttribute(getFunctionTypeAttrName(result.name),
657 TypeAttr::get(functionType));
658 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
659 return failure();
660
661 return parser.parseRegion(*result.addRegion(), entryArgs,
662 /*enableNameShadowing=*/false);
663}
664
665void CutRewritePatternOp::print(OpAsmPrinter &p) {
666 auto functionType = getFunctionType();
667 call_interface_impl::printFunctionSignature(
668 p, functionType.getInputs(), /*argAttrs=*/{}, /*isVariadic=*/false,
669 functionType.getResults(), /*resultAttrs=*/{}, &getBody(),
670 /*printEmptyResult=*/false);
671
672 p.printOptionalAttrDictWithKeyword((*this)->getAttrs(),
673 {getFunctionTypeAttrName()});
674
675 p << ' ';
676 p.printRegion(getBody(), /*printEntryBlockArgs=*/false,
677 /*printBlockTerminators=*/true);
678}
679
680LogicalResult CutRewritePatternOp::verify() {
681 auto functionType = getFunctionType();
682
683 if (functionType.getNumResults() != 1)
684 return emitError() << "requires exactly one result";
685
686 for (auto type : functionType.getInputs())
687 if (!type.isInteger(1))
688 return emitError() << "argument type must be i1, but got " << type;
689
690 for (auto type : functionType.getResults())
691 if (!type.isInteger(1))
692 return emitError() << "result type must be i1, but got " << type;
693
694 // Check outputs.
695 auto *terminator = this->getBody().front().getTerminator();
696 if (terminator->getOperands().size() != functionType.getNumResults())
697 return emitError() << "result type doesn't match with the terminator";
698
699 for (auto [lhs, rhs] : llvm::zip(terminator->getOperands().getTypes(),
700 functionType.getResults()))
701 if (rhs != lhs)
702 return emitError() << rhs << " is expected but got " << lhs;
703
704 auto blockArgs = this->getBody().front().getArguments();
705 if (blockArgs.size() != functionType.getNumInputs())
706 return emitError() << "operand type doesn't match with the block arg";
707
708 for (auto [blockArg, inputType] :
709 llvm::zip(blockArgs, functionType.getInputs()))
710 if (blockArg.getType() != inputType)
711 return emitError() << inputType << " is expected but got "
712 << blockArg.getType();
713
714 auto cost = getCost();
715
716 auto arcs = cost.getArcs();
717 if (arcs.size() != functionType.getNumResults() * functionType.getNumInputs())
718 return emitError() << "mapping cost arcs must match the number of results "
719 "times arguments";
720
721 if (auto inputCaps = cost.getInputCaps())
722 if (inputCaps.size() != functionType.getNumInputs())
723 return emitError()
724 << "input_caps size must match the number of arguments";
725
726 return success();
727}
assert(baseType &&"element must be base type")
static ComplementMatcher< SubType > m_Complement(const SubType &subExpr)
Definition CombFolds.cpp:85
LogicalResult lowerVariadicAndInverterOpConversion(OpTy op, PatternRewriter &rewriter)
Definition SynthOps.cpp:511
static Value lowerVariadicInvertibleOp(Location loc, ValueRange operands, ArrayRef< bool > inverts, PatternRewriter &rewriter, llvm::function_ref< Value(Value, bool)> createUnary, llvm::function_ref< Value(Value, Value, bool, bool)> createBinary)
Definition SynthOps.cpp:484
create(data_type, value)
Definition hw.py:433
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:110
void populateVariadicXorInverterLoweringPatterns(mlir::RewritePatternSet &patterns)
LogicalResult topologicallySortGraphRegionBlocks(mlir::Operation *op, llvm::function_ref< bool(mlir::Value, mlir::Operation *)> isOperandReady)
This function performs a topological sort on the operations within each block of graph regions in the...
Definition SynthOps.cpp:543
bool isLogicNetworkOp(mlir::Operation *op)
void populateVariadicAndInverterLoweringPatterns(mlir::RewritePatternSet &patterns)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void addAndClauses(int outVar, llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause)
Emit clauses encoding outVar <=> and(inputLits).
void addXorClauses(int outVar, int lhsLit, int rhsLit, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause)
Emit clauses encoding outVar <=> (lhsLit xor rhsLit).
void replaceOpAndCopyNamehint(PatternRewriter &rewriter, Operation *op, Value newValue)
A wrapper of PatternRewriter::replaceOp to propagate "sv.namehint" attribute.
Definition Naming.cpp:73
void addOrClauses(int outVar, llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause)
Emit clauses encoding outVar <=> or(inputLits).
void addParityClauses(int outVar, llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause, llvm::function_ref< int()> newVar)
Emit clauses encoding outVar <=> parity(inputLits).