Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AIGOps.cpp
Go to the documentation of this file.
1//===- AIGOps.cpp - AIG Dialect Operations ----------------------*- C++ -*-===//
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 file implement the AIG ops.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/PatternMatch.h"
17
18using namespace mlir;
19using namespace circt;
20using namespace circt::aig;
21
22#define GET_OP_CLASSES
23#include "circt/Dialect/AIG/AIG.cpp.inc"
24
25OpFoldResult AndInverterOp::fold(FoldAdaptor adaptor) {
26 if (getNumOperands() == 1 && !isInverted(0))
27 return getOperand(0);
28 return {};
29}
30
31LogicalResult AndInverterOp::canonicalize(AndInverterOp op,
32 PatternRewriter &rewriter) {
34 SmallVector<Value> uniqueValues;
35 SmallVector<bool> uniqueInverts;
36
37 APInt constValue =
38 APInt::getAllOnes(op.getResult().getType().getIntOrFloatBitWidth());
39
40 bool invertedConstFound = false;
41 bool flippedFound = false;
42
43 for (auto [value, inverted] : llvm::zip(op.getInputs(), op.getInverted())) {
44 bool newInverted = inverted;
45 if (auto constOp = value.getDefiningOp<hw::ConstantOp>()) {
46 if (inverted) {
47 constValue &= ~constOp.getValue();
48 invertedConstFound = true;
49 } else {
50 constValue &= constOp.getValue();
51 }
52 continue;
53 }
54
55 if (auto andInverterOp = value.getDefiningOp<aig::AndInverterOp>()) {
56 if (andInverterOp.getInputs().size() == 1 &&
57 andInverterOp.isInverted(0)) {
58 value = andInverterOp.getOperand(0);
59 newInverted = andInverterOp.isInverted(0) ^ inverted;
60 flippedFound = true;
61 }
62 }
63
64 auto it = seen.find(value);
65 if (it == seen.end()) {
66 seen.insert({value, newInverted});
67 uniqueValues.push_back(value);
68 uniqueInverts.push_back(newInverted);
69 } else if (it->second != newInverted) {
70 // replace with const 0
71 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
72 op, APInt::getZero(value.getType().getIntOrFloatBitWidth()));
73 return success();
74 }
75 }
76
77 // If the constant is zero, we can just replace with zero.
78 if (constValue.isZero()) {
79 rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, constValue);
80 return success();
81 }
82
83 // No change.
84 if ((uniqueValues.size() == op.getInputs().size() && !flippedFound) ||
85 (!constValue.isAllOnes() && !invertedConstFound &&
86 uniqueValues.size() + 1 == op.getInputs().size()))
87 return failure();
88
89 if (!constValue.isAllOnes()) {
90 auto constOp = rewriter.create<hw::ConstantOp>(op.getLoc(), constValue);
91 uniqueInverts.push_back(false);
92 uniqueValues.push_back(constOp);
93 }
94
95 // It means the input is reduced to all ones.
96 if (uniqueValues.empty()) {
97 rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, constValue);
98 return success();
99 }
100
101 // build new op with reduced input values
102 replaceOpWithNewOpAndCopyNamehint<aig::AndInverterOp>(
103 rewriter, op, uniqueValues, uniqueInverts);
104 return success();
105}
106
107ParseResult AndInverterOp::parse(OpAsmParser &parser, OperationState &result) {
108 SmallVector<OpAsmParser::UnresolvedOperand> operands;
109 SmallVector<bool> inverts;
110 auto loc = parser.getCurrentLocation();
111
112 while (true) {
113 inverts.push_back(succeeded(parser.parseOptionalKeyword("not")));
114 operands.push_back(OpAsmParser::UnresolvedOperand());
115
116 if (parser.parseOperand(operands.back()))
117 return failure();
118 if (parser.parseOptionalComma())
119 break;
120 }
121
122 Type type;
123 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
124 parser.parseCustomTypeWithFallback(type))
125 return failure();
126
127 result.addTypes({type});
128 result.addAttribute("inverted",
129 parser.getBuilder().getDenseBoolArrayAttr(inverts));
130 if (parser.resolveOperands(operands, type, loc, result.operands))
131 return failure();
132 return success();
133}
134
135void AndInverterOp::print(OpAsmPrinter &odsPrinter) {
136 odsPrinter << ' ';
137 llvm::interleaveComma(llvm::zip(getInverted(), getInputs()), odsPrinter,
138 [&](auto &&pair) {
139 auto [invert, input] = pair;
140 if (invert) {
141 odsPrinter << "not ";
142 }
143 odsPrinter << input;
144 });
145 odsPrinter.printOptionalAttrDict((*this)->getAttrs(), {"inverted"});
146 odsPrinter << " : " << getResult().getType();
147}
148
149APInt AndInverterOp::evaluate(ArrayRef<APInt> inputs) {
150 assert(inputs.size() == getNumOperands() &&
151 "Expected as many inputs as operands");
152 assert(!inputs.empty() && "Expected non-empty input list");
153 APInt result = APInt::getAllOnes(inputs.front().getBitWidth());
154 for (auto [idx, input] : llvm::enumerate(inputs)) {
155 if (isInverted(idx))
156 result &= ~input;
157 else
158 result &= input;
159 }
160 return result;
161}
162
163LogicalResult CutOp::verify() {
164 auto *block = getBody();
165 // NOTE: Currently input and output types of the block must be exactly the
166 // same. We might want to relax this in the future as a way to represent
167 // "vectorized" cuts. For example in the following cut, the block arguments
168 // types are i1, but the cut is batch-applied over 8-bit lanes.
169 // %0 = aig.cut %a, %b : (i8, i8) -> (i8) {
170 // ^bb0(%arg0: i1, %arg1: i1):
171 // %c = aig.and_inv %arg0, not %arg1 : i1
172 // aig.output %c : i1
173 // }
174
175 if (getInputs().size() != block->getNumArguments())
176 return emitOpError("the number of inputs and the number of block arguments "
177 "do not match. Expected ")
178 << getInputs().size() << " but got " << block->getNumArguments();
179
180 // Check input types.
181 for (auto [input, arg] : llvm::zip(getInputs(), block->getArguments()))
182 if (input.getType() != arg.getType())
183 return emitOpError("input type ")
184 << input.getType() << " does not match "
185 << "block argument type " << arg.getType();
186
187 if (getNumResults() != block->getTerminator()->getNumOperands())
188 return emitOpError("the number of results and the number of terminator "
189 "operands do not match. Expected ")
190 << getNumResults() << " but got "
191 << block->getTerminator()->getNumOperands();
192
193 // Check output types.
194 for (auto [result, arg] :
195 llvm::zip(getResults(), block->getTerminator()->getOperands()))
196 if (result.getType() != arg.getType())
197 return emitOpError("result type ")
198 << result.getType() << " does not match "
199 << "terminator operand type " << arg.getType();
200
201 return success();
202}
assert(baseType &&"element must be base type")
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.