CIRCT 22.0.0git
Loading...
Searching...
No Matches
ESITypes.cpp
Go to the documentation of this file.
1//===- ESITypes.cpp - ESI types code defs -----------------------*- 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// Definitions for esi data types. Anything which doesn't have to be public
10// should go in here.
11//
12//===----------------------------------------------------------------------===//
13
17#include "mlir/IR/Attributes.h"
18#include "mlir/IR/DialectImplementation.h"
19#include "llvm/ADT/ArrayRef.h"
20#include "llvm/ADT/DenseMap.h"
21#include "llvm/ADT/TypeSwitch.h"
22
23using namespace circt;
24using namespace circt::esi;
25
26AnyType AnyType::get(MLIRContext *context) { return Base::get(context); }
27
28/// Get the list of users with snoops filtered out. Returns a filtered range
29/// which is lazily constructed.
30static auto getChannelConsumers(mlir::TypedValue<ChannelType> chan) {
31 return llvm::make_filter_range(chan.getUses(), [](auto &use) {
32 return !isa<SnoopValidReadyOp, SnoopTransactionOp>(use.getOwner());
33 });
34}
35SmallVector<std::reference_wrapper<OpOperand>, 4>
36ChannelType::getConsumers(mlir::TypedValue<ChannelType> chan) {
37 return SmallVector<std::reference_wrapper<OpOperand>, 4>(
39}
40bool ChannelType::hasOneConsumer(mlir::TypedValue<ChannelType> chan) {
41 auto consumers = getChannelConsumers(chan);
42 if (consumers.empty())
43 return false;
44 return ++consumers.begin() == consumers.end();
45}
46bool ChannelType::hasNoConsumers(mlir::TypedValue<ChannelType> chan) {
47 return getChannelConsumers(chan).empty();
48}
49OpOperand *ChannelType::getSingleConsumer(mlir::TypedValue<ChannelType> chan) {
50 auto consumers = getChannelConsumers(chan);
51 auto iter = consumers.begin();
52 if (iter == consumers.end())
53 return nullptr;
54 OpOperand *result = &*iter;
55 if (++iter != consumers.end())
56 return nullptr;
57 return result;
58}
59LogicalResult ChannelType::verifyChannel(mlir::TypedValue<ChannelType> chan) {
60 auto consumers = getChannelConsumers(chan);
61 if (consumers.empty() || ++consumers.begin() == consumers.end())
62 return success();
63 auto err = chan.getDefiningOp()->emitOpError(
64 "channels must have at most one consumer");
65 for (auto &consumer : consumers)
66 err.attachNote(consumer.getOwner()->getLoc()) << "channel used here";
67 return err;
68}
69
70LogicalResult
71WindowType::verify(llvm::function_ref<InFlightDiagnostic()> emitError,
72 StringAttr name, Type into,
73 ArrayRef<WindowFrameType> frames) {
74 auto structInto = hw::type_dyn_cast<hw::StructType>(into);
75 if (!structInto)
76 return emitError() << "only windows into structs are currently supported";
77
78 auto fields = structInto.getElements();
79 for (auto frame : frames) {
80 bool encounteredArrayOrListWithNumItems = false;
81
82 // Efficiently look up fields in the frame.
83 DenseMap<StringAttr, WindowFieldType> frameFields;
84 for (auto field : frame.getMembers())
85 frameFields[field.getFieldName()] = field;
86
87 // Iterate through the list of struct fields until we've encountered all the
88 // fields listed in the frame.
89 while (!fields.empty() && !frameFields.empty()) {
90 hw::StructType::FieldInfo field = fields.front();
91 fields = fields.drop_front();
92 auto f = frameFields.find(field.name);
93
94 // If a field in the struct isn't listed, it's being omitted from the
95 // window so we just skip it.
96 if (f == frameFields.end())
97 continue;
98
99 // If we encounter an array or list field, no subsequent fields can be
100 // arrays or lists.
101 bool isArrayOrListWithNumItems =
102 hw::type_isa<hw::ArrayType, esi::ListType>(field.type) &&
103 f->getSecond().getNumItems() > 0;
104 if (isArrayOrListWithNumItems) {
105 if (encounteredArrayOrListWithNumItems)
106 return emitError()
107 << "cannot have two array or list fields with num items (in "
108 << field.name << ")";
109 encounteredArrayOrListWithNumItems = true;
110 }
111
112 // If 'numItems' is specified, gotta run more checks.
113 uint64_t numItems = f->getSecond().getNumItems();
114 if (numItems > 0) {
115 if (auto arrField = hw::type_dyn_cast<hw::ArrayType>(field.type)) {
116 if (numItems > arrField.getNumElements())
117 return emitError()
118 << "num items is larger than array size in field "
119 << field.name;
120 } else if (!hw::type_isa<esi::ListType>(field.type)) {
121 return emitError() << "specification of num items only allowed on "
122 "array or list fields (in "
123 << field.name << ")";
124 }
125 }
126 frameFields.erase(f);
127 }
128
129 // If there is anything left in the frame list, it either refers to a
130 // non-existant field or said frame was already consumed by a previous
131 // frame.
132 if (!frameFields.empty())
133 return emitError() << "invalid field name: "
134 << frameFields.begin()->getSecond().getFieldName();
135 }
136 return success();
137}
138
139Type WindowType::getLoweredType() const {
140 // Assemble a fast lookup of struct fields to types.
141 auto into = hw::type_cast<hw::StructType>(getInto());
143 for (hw::StructType::FieldInfo field : into.getElements())
144 intoFields[field.name] = field.type;
145
146 auto getInnerTypeOrSelf = [&](Type t) {
147 return TypeSwitch<Type, Type>(t)
148 .Case<hw::ArrayType>(
149 [](hw::ArrayType arr) { return arr.getElementType(); })
150 .Case<esi::ListType>(
151 [](esi::ListType list) { return list.getElementType(); })
152 .Default([&](Type t) { return t; });
153 };
154
155 // Helper to wrap a lowered type in a TypeAlias if the 'into' type is a
156 // TypeAlias.
157 auto wrapInTypeAliasIfNeeded = [&](Type loweredType) -> Type {
158 if (auto intoAlias = dyn_cast<hw::TypeAliasType>(getInto())) {
159 auto intoRef = intoAlias.getRef();
160 std::string aliasName = (Twine(intoRef.getLeafReference().getValue()) +
161 "_" + getName().getValue())
162 .str();
163 auto newRef = SymbolRefAttr::get(
164 intoRef.getRootReference(),
165 {FlatSymbolRefAttr::get(StringAttr::get(getContext(), aliasName))});
166 return hw::TypeAliasType::get(newRef, loweredType);
167 }
168 return loweredType;
169 };
170
171 // Build the union, frame by frame
172 SmallVector<hw::UnionType::FieldInfo, 4> unionFields;
173 for (WindowFrameType frame : getFrames()) {
174
175 // ... field by field.
176 SmallVector<hw::StructType::FieldInfo, 4> fields;
177 SmallVector<hw::StructType::FieldInfo, 4> leftOverFields;
178 bool hasLeftOver = false;
179 StringAttr leftOverName;
180
181 for (WindowFieldType field : frame.getMembers()) {
182 auto fieldTypeIter = intoFields.find(field.getFieldName());
183 assert(fieldTypeIter != intoFields.end());
184 auto fieldType = fieldTypeIter->getSecond();
185
186 // If the number of items isn't specified, just use the type.
187 if (field.getNumItems() == 0) {
188 // Directly use the type from the struct unless it's an array or list,
189 // in which case we want the inner type.
190 auto type = getInnerTypeOrSelf(fieldType);
191 fields.push_back({field.getFieldName(), type});
192 leftOverFields.push_back({field.getFieldName(), type});
193
194 if (hw::type_isa<esi::ListType>(fieldType)) {
195 // Lists need a 'last' signal to indicate the end of the list.
196 auto lastType = IntegerType::get(getContext(), 1);
197 auto lastField = StringAttr::get(getContext(), "last");
198 fields.push_back({lastField, lastType});
199 leftOverFields.push_back({lastField, lastType});
200 }
201 } else {
202 if (auto array =
203 hw::type_dyn_cast<hw::ArrayType>(fieldTypeIter->getSecond())) {
204 // The first union entry should be an array of length numItems.
205 fields.push_back(
206 {field.getFieldName(), hw::ArrayType::get(array.getElementType(),
207 field.getNumItems())});
208
209 // If the array size is not a multiple of numItems, we need another
210 // frame for the left overs.
211 size_t leftOver = array.getNumElements() % field.getNumItems();
212 if (leftOver) {
213 // The verifier checks that there is only one field per frame with
214 // numItems > 0.
215 assert(!hasLeftOver);
216 hasLeftOver = true;
217 leftOverFields.push_back(
218 {field.getFieldName(),
219 hw::ArrayType::get(array.getElementType(), leftOver)});
220
221 leftOverName = StringAttr::get(
222 getContext(), Twine(frame.getName().getValue(), "_leftOver"));
223 }
224 } else if (auto list = hw::type_cast<esi::ListType>(
225 fieldTypeIter->getSecond())) {
226 // The first union entry should be a list of length numItems.
227 fields.push_back(
228 {field.getFieldName(),
229 hw::ArrayType::get(list.getElementType(), field.getNumItems())});
230 // Next needs to be the number of valid items in the array. Should be
231 // clog2(numItems) in width.
232 fields.push_back(
233 {StringAttr::get(getContext(),
234 Twine(field.getFieldName().getValue(), "_size")),
235 IntegerType::get(getContext(),
236 llvm::Log2_64_Ceil(field.getNumItems()))});
237 // Lists need a 'last' signal to indicate the end of the list.
238 fields.push_back({StringAttr::get(getContext(), "last"),
239 IntegerType::get(getContext(), 1)});
240 } else {
241 llvm_unreachable("numItems specified on non-array/list field");
242 }
243 }
244 }
245
246 // Special case: if we have one data frame and it doesn't have a name, don't
247 // use a union.
248 if (getFrames().size() == 1 && frame.getName().getValue().empty() &&
249 !hasLeftOver) {
250 auto loweredStruct = hw::StructType::get(getContext(), fields);
251 return wrapInTypeAliasIfNeeded(loweredStruct);
252 }
253
254 if (!fields.empty())
255 unionFields.push_back(
256 {frame.getName(), hw::StructType::get(getContext(), fields), 0});
257
258 if (hasLeftOver)
259 unionFields.push_back(
260 {leftOverName, hw::StructType::get(getContext(), leftOverFields), 0});
261 }
262
263 auto unionType = hw::UnionType::get(getContext(), unionFields);
264 return wrapInTypeAliasIfNeeded(unionType);
265}
266
267namespace mlir {
268template <>
269struct FieldParser<::BundledChannel, ::BundledChannel> {
270 static FailureOr<::BundledChannel> parse(AsmParser &p) {
271 ChannelType type;
272 std::string name;
273 if (p.parseType(type))
274 return failure();
275 auto dir = FieldParser<::ChannelDirection>::parse(p);
276 if (failed(dir))
277 return failure();
278 if (p.parseKeywordOrString(&name))
279 return failure();
280 return BundledChannel{StringAttr::get(p.getContext(), name), *dir, type};
281 }
282};
283} // namespace mlir
284
285namespace llvm {
286inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p,
287 ::BundledChannel channel) {
288 p << channel.type << " " << channel.direction << " " << channel.name;
289 return p;
290}
291} // namespace llvm
292
293ChannelBundleType ChannelBundleType::getReversed() const {
294 SmallVector<BundledChannel, 4> reversed;
295 for (auto channel : getChannels())
296 reversed.push_back({channel.name, flip(channel.direction), channel.type});
297 return ChannelBundleType::get(getContext(), reversed, getResettable());
298}
299
300#define GET_TYPEDEF_CLASSES
301#include "circt/Dialect/ESI/ESITypes.cpp.inc"
302
303void ESIDialect::registerTypes() {
304 addTypes<
305#define GET_TYPEDEF_LIST
306#include "circt/Dialect/ESI/ESITypes.cpp.inc"
307 >();
308}
309
310mlir::Type circt::esi::innerType(mlir::Type type) {
311 circt::esi::ChannelType chan =
312 dyn_cast_or_null<circt::esi::ChannelType>(type);
313 if (chan) // Unwrap the channel if it's a channel.
314 type = chan.getInner();
315
316 return type;
317}
assert(baseType &&"element must be base type")
static auto getChannelConsumers(mlir::TypedValue< ChannelType > chan)
Get the list of users with snoops filtered out.
Definition ESITypes.cpp:30
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:216
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:310
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ModulePort::Direction flip(ModulePort::Direction direction)
Flip a port direction.
Definition HWOps.cpp:36
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
inline ::llvm::raw_ostream & operator<<(::llvm::raw_ostream &p, ::BundledChannel channel)
Definition ESITypes.cpp:286
ChannelDirection direction
Definition ESITypes.h:38
static FailureOr<::BundledChannel > parse(AsmParser &p)
Definition ESITypes.cpp:270