/
Combinational.td
318 lines (257 loc) · 11.6 KB
/
Combinational.td
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
//===- Combinational.td - combinational logic ops ----------*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This describes the MLIR ops for combinational logic.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Arithmetic and Logical Operations
//===----------------------------------------------------------------------===//
#ifndef HEIR_INCLUDE_DIALECT_COMB_COMBINATIONAL_TD
#define HEIR_INCLUDE_DIALECT_COMB_COMBINATIONAL_TD
include "include/Dialect/HEIRInterfaces.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/IR/BuiltinAttributes.td"
include "mlir/IR/EnumAttr.td"
def HWIntegerType : Type<
CPred<"$_self.isSignlessInteger()">, "signless integer",
"::mlir::IntegerType">;
// Base class for binary operators.
class BinOp<string mnemonic, list<Trait> traits = []> :
CombOp<mnemonic, traits # [Pure]> {
let arguments = (ins HWIntegerType:$lhs, HWIntegerType:$rhs, UnitAttr:$twoState);
let results = (outs HWIntegerType:$result);
let assemblyFormat =
"$lhs `,` $rhs (`bin` $twoState^)? attr-dict `:` functional-type($args, $results)";
}
// Binary operator with uniform input/result types.
class UTBinOp<string mnemonic, list<Trait> traits = []> :
BinOp<mnemonic,
traits # [SameTypeOperands, SameOperandsAndResultType]> {
let assemblyFormat = "(`bin` $twoState^)? $lhs `,` $rhs attr-dict `:` qualified(type($result))";
}
// Base class for variadic operators.
class VariadicOp<string mnemonic, list<Trait> traits = []> :
CombOp<mnemonic, traits # [Pure]> {
let arguments = (ins Variadic<HWIntegerType>:$inputs, UnitAttr:$twoState);
let results = (outs HWIntegerType:$result);
}
class UTVariadicOp<string mnemonic, list<Trait> traits = []> :
VariadicOp<mnemonic,
traits # [SameTypeOperands, SameOperandsAndResultType]> {
let hasVerifier = 1;
let assemblyFormat = "(`bin` $twoState^)? $inputs attr-dict `:` qualified(type($result))";
let builders = [
OpBuilder<(ins "Value":$lhs, "Value":$rhs, CArg<"bool", "false">:$twoState), [{
return build($_builder, $_state, lhs.getType(),
ValueRange{lhs, rhs}, twoState);
}]>
];
}
// Arithmetic and Logical Operations.
def AddOp : UTVariadicOp<"add", [Commutative]>;
def MulOp : UTVariadicOp<"mul", [Commutative]>;
def AndOp : UTVariadicOp<"and", [Commutative]>;
def OrOp : UTVariadicOp<"or", [Commutative]>;
def XorOp : UTVariadicOp<"xor", [Commutative]> {
let extraClassDeclaration = [{
/// Return true if this is a two operand xor with an all ones constant as
/// its RHS operand.
bool isBinaryNot();
}];
}
def XNorOp : UTVariadicOp<"xnor">;
def NandOp : UTVariadicOp<"nand">;
def NorOp : UTVariadicOp<"nor">;
//===----------------------------------------------------------------------===//
// Comparisons
//===----------------------------------------------------------------------===//
def ICmpPredicateEQ : I64EnumAttrCase<"eq", 0>;
def ICmpPredicateNE : I64EnumAttrCase<"ne", 1>;
def ICmpPredicateSLT : I64EnumAttrCase<"slt", 2>;
def ICmpPredicateSLE : I64EnumAttrCase<"sle", 3>;
def ICmpPredicateSGT : I64EnumAttrCase<"sgt", 4>;
def ICmpPredicateSGE : I64EnumAttrCase<"sge", 5>;
def ICmpPredicateULT : I64EnumAttrCase<"ult", 6>;
def ICmpPredicateULE : I64EnumAttrCase<"ule", 7>;
def ICmpPredicateUGT : I64EnumAttrCase<"ugt", 8>;
def ICmpPredicateUGE : I64EnumAttrCase<"uge", 9>;
// SV case equality
def ICmpPredicateCEQ : I64EnumAttrCase<"ceq", 10>;
def ICmpPredicateCNE : I64EnumAttrCase<"cne", 11>;
// SV wild card equality
def ICmpPredicateWEQ : I64EnumAttrCase<"weq", 12>;
def ICmpPredicateWNE : I64EnumAttrCase<"wne", 13>;
let cppNamespace = "::mlir::heir::comb" in
def ICmpPredicate : I64EnumAttr<
"ICmpPredicate",
"hw.icmp comparison predicate",
[ICmpPredicateEQ, ICmpPredicateNE, ICmpPredicateSLT, ICmpPredicateSLE,
ICmpPredicateSGT, ICmpPredicateSGE, ICmpPredicateULT, ICmpPredicateULE,
ICmpPredicateUGT, ICmpPredicateUGE, ICmpPredicateCEQ, ICmpPredicateCNE,
ICmpPredicateWEQ, ICmpPredicateWNE]>;
def ICmpOp : CombOp<"icmp", [Pure, SameTypeOperands]> {
let summary = "Compare two integer values";
let description = [{
This operation compares two integers using a predicate. If the predicate is
true, returns 1, otherwise returns 0. This operation always returns a one
bit wide result.
```
%r = comb.icmp eq %a, %b : i4
```
}];
let arguments = (ins ICmpPredicate:$predicate,
HWIntegerType:$lhs, HWIntegerType:$rhs, UnitAttr:$twoState);
let results = (outs I1:$result);
let assemblyFormat = "(`bin` $twoState^)? $predicate $lhs `,` $rhs attr-dict `:` qualified(type($lhs))";
let extraClassDeclaration = [{
/// Returns the flipped predicate, reversing the LHS and RHS operands. The
/// lhs and rhs operands should be flipped to match the new predicate.
static ICmpPredicate getFlippedPredicate(ICmpPredicate predicate);
/// Returns true if the predicate is signed.
static bool isPredicateSigned(ICmpPredicate predicate);
/// Returns the predicate for a logically negated comparison, e.g. mapping
/// EQ => NE and SLE => SGT.
static ICmpPredicate getNegatedPredicate(ICmpPredicate predicate);
/// Return true if this is an equality test with -1, which is a "reduction
/// and" operation in Verilog.
bool isEqualAllOnes();
/// Return true if this is a not equal test with 0, which is a "reduction
/// or" operation in Verilog.
bool isNotEqualZero();
}];
}
//===----------------------------------------------------------------------===//
// Unary Operations
//===----------------------------------------------------------------------===//
class UnaryOp<string mnemonic, list<Trait> traits = []> :
CombOp<mnemonic, traits # [Pure, SameOperandsAndResultType]> {
let arguments = (ins HWIntegerType:$input, UnitAttr:$twoState);
let results = (outs HWIntegerType:$result);
let assemblyFormat = "(`bin` $twoState^)? $input attr-dict `:` qualified(type($input))";
}
def InvOp : UnaryOp<"inv">;
// Base class for unary reduction operations that produce an i1.
class UnaryI1ReductionOp<string mnemonic, list<Trait> traits = []> :
CombOp<mnemonic, traits # [Pure]> {
let arguments = (ins HWIntegerType:$input, UnitAttr:$twoState);
let results = (outs I1:$result);
let assemblyFormat = "(`bin` $twoState^)? $input attr-dict `:` qualified(type($input))";
}
def ParityOp : UnaryI1ReductionOp<"parity">;
//===----------------------------------------------------------------------===//
// Integer width modifying operations.
//===----------------------------------------------------------------------===//
// Extract a range of bits from the specified input.
def ExtractOp : CombOp<"extract", [Pure]> {
let summary = "Extract a range of bits into a smaller value, lowBit "
"specifies the lowest bit included.";
let arguments = (ins HWIntegerType:$input, I32Attr:$lowBit);
let results = (outs HWIntegerType:$result);
let assemblyFormat =
"$input `from` $lowBit attr-dict `:` functional-type($input, $result)";
let hasVerifier = 1;
let builders = [
OpBuilder<(ins "Value":$lhs, "int32_t":$lowBit, "int32_t":$bitWidth), [{
auto resultType = $_builder.getIntegerType(bitWidth);
return build($_builder, $_state, resultType, lhs, lowBit);
}]>
];
}
//===----------------------------------------------------------------------===//
// Other Operations
//===----------------------------------------------------------------------===//
def ConcatOp : CombOp<"concat", [InferTypeOpInterface, Pure]> {
let summary = "Concatenate a variadic list of operands together.";
let description = [{
See the comb rationale document for details on operand ordering.
}];
let arguments = (ins Variadic<HWIntegerType>:$inputs);
let results = (outs HWIntegerType:$result);
let hasVerifier = 1;
let assemblyFormat = "$inputs attr-dict `:` qualified(type($inputs))";
let builders = [
OpBuilder<(ins "Value":$lhs, "Value":$rhs), [{
return build($_builder, $_state, ValueRange{lhs, rhs});
}]>,
OpBuilder<(ins "Value":$hd, "ValueRange":$tl)>,
];
let extraClassDeclaration = [{
/// Infer the return types of this operation.
static LogicalResult inferReturnTypes(MLIRContext *context,
std::optional<Location> loc,
ValueRange operands,
DictionaryAttr attrs,
mlir::OpaqueProperties properties,
mlir::RegionRange regions,
SmallVectorImpl<Type> &results);
}];
}
def ReplicateOp : CombOp<"replicate", [Pure]> {
let summary = "Concatenate the operand a constant number of times";
let arguments = (ins HWIntegerType:$input);
let results = (outs HWIntegerType:$result);
let assemblyFormat =
"$input attr-dict `:` functional-type($input, $result)";
let hasVerifier = 1;
let builders = [
OpBuilder<(ins "Value":$operand, "int32_t":$multiple), [{
auto bitWidth = operand.getType().cast<IntegerType>().getWidth();
auto resultType = $_builder.getIntegerType(bitWidth*multiple);
return build($_builder, $_state, resultType, operand);
}]>
];
let extraClassDeclaration = [{
/// Returns the number of times the operand is replicated.
size_t getMultiple() {
auto opWidth = getInput().getType().cast<IntegerType>().getWidth();
return getType().cast<IntegerType>().getWidth()/opWidth;
}
}];
}
// Select one of two values based on a condition.
def MuxOp : CombOp<"mux",
[Pure, AllTypesMatch<["trueValue", "falseValue", "result"]>]> {
let summary = "Return one or the other operand depending on a selector bit";
let description = [{
```
%0 = mux %pred, %tvalue, %fvalue : i4
```
}];
let arguments = (ins I1:$cond, AnyType:$trueValue,
AnyType:$falseValue, UnitAttr:$twoState);
let results = (outs AnyType:$result);
let assemblyFormat =
"(`bin` $twoState^)? $cond `,` $trueValue `,` $falseValue attr-dict `:` qualified(type($result))";
}
def TruthTableOp : CombOp<"truth_table", [Pure, DeclareOpInterfaceMethods<LUTOpInterface>]> {
let summary = "Return a true/false based on a lookup table";
let description = [{
```
%a = ... : i1
%b = ... : i1
%0 = comb.truth_table %a, %b -> 6 : ui4
```
This operation assumes that the lookup table is described as an integer of
2^n bits to fully specify the table. Inputs are sorted MSB -> LSB from left
to right and the offset into `lookupTable` is computed from them. The
integer containing the truth table value's LSB is the output for the input
"all false", and the MSB is the output for the input "all true".
No difference from array_get into an array of constants except for xprop
behavior. If one of the inputs is unknown, but said input doesn't make a
difference in the output (based on the lookup table) the result should not
be 'x' -- it should be the well-known result.
}];
let arguments = (ins Variadic<I1>:$inputs, Builtin_IntegerAttr:$lookupTable);
let results = (outs I1:$result);
let assemblyFormat = [{
$inputs `->` $lookupTable attr-dict
}];
let hasVerifier = 1;
}
#endif // HEIR_INCLUDE_DIALECT_COMB_COMBINATIONAL_TD