CIRCT 20.0.0git
Loading...
Searching...
No Matches
LLHDOps.cpp
Go to the documentation of this file.
1//===- LLHDOps.cpp - Implement the LLHD operations ------------------------===//
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 implements the LLHD ops.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/Attributes.h"
17#include "mlir/IR/BuiltinTypes.h"
18#include "mlir/IR/Matchers.h"
19#include "mlir/IR/PatternMatch.h"
20#include "mlir/IR/Region.h"
21#include "mlir/IR/Types.h"
22#include "mlir/IR/Value.h"
23#include "llvm/ADT/ArrayRef.h"
24#include "llvm/ADT/SmallVector.h"
25
26using namespace circt;
27using namespace mlir;
28using namespace llhd;
29
30unsigned circt::llhd::getLLHDTypeWidth(Type type) {
31 if (auto sig = dyn_cast<hw::InOutType>(type))
32 type = sig.getElementType();
33 else if (auto ptr = dyn_cast<llhd::PtrType>(type))
34 type = ptr.getElementType();
35 if (auto array = dyn_cast<hw::ArrayType>(type))
36 return array.getNumElements();
37 if (auto tup = dyn_cast<hw::StructType>(type))
38 return tup.getElements().size();
39 return type.getIntOrFloatBitWidth();
40}
41
43 if (auto sig = dyn_cast<hw::InOutType>(type))
44 type = sig.getElementType();
45 else if (auto ptr = dyn_cast<llhd::PtrType>(type))
46 type = ptr.getElementType();
47 if (auto array = dyn_cast<hw::ArrayType>(type))
48 return array.getElementType();
49 return type;
50}
51
52//===----------------------------------------------------------------------===//
53// ConstantTimeOp
54//===----------------------------------------------------------------------===//
55
56OpFoldResult llhd::ConstantTimeOp::fold(FoldAdaptor adaptor) {
57 assert(adaptor.getOperands().empty() && "const has no operands");
58 return getValueAttr();
59}
60
61void llhd::ConstantTimeOp::build(OpBuilder &builder, OperationState &result,
62 unsigned time, const StringRef &timeUnit,
63 unsigned delta, unsigned epsilon) {
64 auto *ctx = builder.getContext();
65 auto attr = TimeAttr::get(ctx, time, timeUnit, delta, epsilon);
66 return build(builder, result, TimeType::get(ctx), attr);
67}
68
69//===----------------------------------------------------------------------===//
70// SignalOp
71//===----------------------------------------------------------------------===//
72
73static Value getValueAtIndex(OpBuilder &builder, Location loc, Value val,
74 unsigned index) {
75 return TypeSwitch<Type, Value>(val.getType())
76 .Case<hw::StructType>([&](hw::StructType ty) -> Value {
77 return builder.create<hw::StructExtractOp>(
78 loc, val, ty.getElements()[index].name);
79 })
80 .Case<hw::ArrayType>([&](hw::ArrayType ty) -> Value {
81 Value idx = builder.create<hw::ConstantOp>(
82 loc,
83 builder.getIntegerType(llvm::Log2_64_Ceil(ty.getNumElements())),
84 index);
85 return builder.create<hw::ArrayGetOp>(loc, val, idx);
86 });
87}
88
89void SignalOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
90 if (getName() && !getName()->empty())
91 setNameFn(getResult(), *getName());
92}
93
94SmallVector<DestructurableMemorySlot> SignalOp::getDestructurableSlots() {
95 auto type = getType().getElementType();
96
97 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(type);
98 if (!destructurable)
99 return {};
100
101 auto destructuredType = destructurable.getSubelementIndexMap();
102 if (!destructuredType)
103 return {};
104
105 return {DestructurableMemorySlot{{getResult(), type}, *destructuredType}};
106}
107
108DenseMap<Attribute, MemorySlot> SignalOp::destructure(
109 const DestructurableMemorySlot &slot,
110 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
111 SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
112 assert(slot.ptr == getResult());
113 builder.setInsertionPointAfter(*this);
114
115 auto destructurableType =
116 cast<DestructurableTypeInterface>(getType().getElementType());
117 DenseMap<Attribute, MemorySlot> slotMap;
118 SmallVector<std::pair<unsigned, Type>> indices;
119 for (auto attr : usedIndices) {
120 assert(isa<IntegerAttr>(attr));
121 auto elemType = destructurableType.getTypeAtIndex(attr);
122 assert(elemType && "used index must exist");
123 indices.push_back({cast<IntegerAttr>(attr).getInt(), elemType});
124 }
125
126 llvm::sort(indices, [](auto a, auto b) { return a.first < b.first; });
127
128 for (auto [index, type] : indices) {
129 Value init = getValueAtIndex(builder, getLoc(), getInit(), index);
130 auto sigOp = builder.create<SignalOp>(getLoc(), getNameAttr(), init);
131 newAllocators.push_back(sigOp);
132 slotMap.try_emplace<MemorySlot>(
133 IntegerAttr::get(IndexType::get(getContext()), index),
134 {sigOp.getResult(), type});
135 }
136
137 return slotMap;
138}
139
140std::optional<DestructurableAllocationOpInterface>
141SignalOp::handleDestructuringComplete(const DestructurableMemorySlot &slot,
142 OpBuilder &builder) {
143 assert(slot.ptr == getResult());
144 this->erase();
145 return std::nullopt;
146}
147
148//===----------------------------------------------------------------------===//
149// SigExtractOp and PtrExtractOp
150//===----------------------------------------------------------------------===//
151
152template <class Op>
153static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef<Attribute> operands) {
154
155 if (!operands[1])
156 return nullptr;
157
158 // llhd.sig.extract(input, 0) with inputWidth == resultWidth => input
159 if (op.getResultWidth() == op.getInputWidth() &&
160 cast<IntegerAttr>(operands[1]).getValue().isZero())
161 return op.getInput();
162
163 return nullptr;
164}
165
166OpFoldResult llhd::SigExtractOp::fold(FoldAdaptor adaptor) {
167 return foldSigPtrExtractOp(*this, adaptor.getOperands());
168}
169
170OpFoldResult llhd::PtrExtractOp::fold(FoldAdaptor adaptor) {
171 return foldSigPtrExtractOp(*this, adaptor.getOperands());
172}
173
174//===----------------------------------------------------------------------===//
175// SigArraySliceOp and PtrArraySliceOp
176//===----------------------------------------------------------------------===//
177
178template <class Op>
179static OpFoldResult foldSigPtrArraySliceOp(Op op,
180 ArrayRef<Attribute> operands) {
181 if (!operands[1])
182 return nullptr;
183
184 // llhd.sig.array_slice(input, 0) with inputWidth == resultWidth => input
185 if (op.getResultWidth() == op.getInputWidth() &&
186 cast<IntegerAttr>(operands[1]).getValue().isZero())
187 return op.getInput();
188
189 return nullptr;
190}
191
192OpFoldResult llhd::SigArraySliceOp::fold(FoldAdaptor adaptor) {
193 return foldSigPtrArraySliceOp(*this, adaptor.getOperands());
194}
195
196OpFoldResult llhd::PtrArraySliceOp::fold(FoldAdaptor adaptor) {
197 return foldSigPtrArraySliceOp(*this, adaptor.getOperands());
198}
199
200template <class Op>
201static LogicalResult canonicalizeSigPtrArraySliceOp(Op op,
202 PatternRewriter &rewriter) {
203 IntegerAttr indexAttr;
204 if (!matchPattern(op.getLowIndex(), m_Constant(&indexAttr)))
205 return failure();
206
207 // llhd.sig.array_slice(llhd.sig.array_slice(target, a), b)
208 // => llhd.sig.array_slice(target, a+b)
209 IntegerAttr a;
210 if (matchPattern(op.getInput(),
211 m_Op<Op>(matchers::m_Any(), m_Constant(&a)))) {
212 auto sliceOp = op.getInput().template getDefiningOp<Op>();
213 rewriter.modifyOpInPlace(op, [&]() {
214 op.getInputMutable().assign(sliceOp.getInput());
215 Value newIndex = rewriter.create<hw::ConstantOp>(
216 op->getLoc(), a.getValue() + indexAttr.getValue());
217 op.getLowIndexMutable().assign(newIndex);
218 });
219
220 return success();
221 }
222
223 return failure();
224}
225
226LogicalResult llhd::SigArraySliceOp::canonicalize(llhd::SigArraySliceOp op,
227 PatternRewriter &rewriter) {
228 return canonicalizeSigPtrArraySliceOp(op, rewriter);
229}
230
231LogicalResult llhd::PtrArraySliceOp::canonicalize(llhd::PtrArraySliceOp op,
232 PatternRewriter &rewriter) {
233 return canonicalizeSigPtrArraySliceOp(op, rewriter);
234}
235
236//===----------------------------------------------------------------------===//
237// SigArrayGetOp
238//===----------------------------------------------------------------------===//
239
240bool SigArrayGetOp::canRewire(const DestructurableMemorySlot &slot,
241 SmallPtrSetImpl<Attribute> &usedIndices,
242 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
243 const DataLayout &dataLayout) {
244 if (slot.ptr != getInput())
245 return false;
246 APInt idx;
247 if (!matchPattern(getIndex(), m_ConstantInt(&idx)))
248 return false;
249 auto index =
250 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
251 if (!slot.subelementTypes.contains(index))
252 return false;
253 usedIndices.insert(index);
254 mustBeSafelyUsed.emplace_back<MemorySlot>(
255 {getResult(),
256 cast<hw::InOutType>(getResult().getType()).getElementType()});
257 return true;
258}
259
260DeletionKind SigArrayGetOp::rewire(const DestructurableMemorySlot &slot,
261 DenseMap<Attribute, MemorySlot> &subslots,
262 OpBuilder &builder,
263 const DataLayout &dataLayout) {
264 APInt idx;
265 bool result = matchPattern(getIndex(), m_ConstantInt(&idx));
266 (void)result;
267 assert(result);
268 auto index =
269 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
270 auto it = subslots.find(index);
271 assert(it != subslots.end());
272 replaceAllUsesWith(it->getSecond().ptr);
273 return DeletionKind::Delete;
274}
275
276LogicalResult SigArrayGetOp::ensureOnlySafeAccesses(
277 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
278 const DataLayout &dataLayout) {
279 return success();
280}
281
282//===----------------------------------------------------------------------===//
283// SigStructExtractOp and PtrStructExtractOp
284//===----------------------------------------------------------------------===//
285
286template <class OpType, class SigPtrType>
288 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
289 DictionaryAttr attrs, mlir::OpaqueProperties properties,
290 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
291 typename OpType::Adaptor adaptor(operands, attrs, properties, regions);
292 Type type =
293 cast<hw::StructType>(
294 cast<SigPtrType>(adaptor.getInput().getType()).getElementType())
295 .getFieldType(adaptor.getField());
296 if (!type) {
297 context->getDiagEngine().emit(loc.value_or(UnknownLoc()),
298 DiagnosticSeverity::Error)
299 << "invalid field name specified";
300 return failure();
301 }
302 results.push_back(SigPtrType::get(type));
303 return success();
304}
305
306LogicalResult llhd::SigStructExtractOp::inferReturnTypes(
307 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
308 DictionaryAttr attrs, mlir::OpaqueProperties properties,
309 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
310 return inferReturnTypesOfStructExtractOp<llhd::SigStructExtractOp,
311 hw::InOutType>(
312 context, loc, operands, attrs, properties, regions, results);
313}
314
315LogicalResult llhd::PtrStructExtractOp::inferReturnTypes(
316 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
317 DictionaryAttr attrs, mlir::OpaqueProperties properties,
318 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
319 return inferReturnTypesOfStructExtractOp<llhd::PtrStructExtractOp,
320 llhd::PtrType>(
321 context, loc, operands, attrs, properties, regions, results);
322}
323
324bool SigStructExtractOp::canRewire(
325 const DestructurableMemorySlot &slot,
326 SmallPtrSetImpl<Attribute> &usedIndices,
327 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
328 const DataLayout &dataLayout) {
329 if (slot.ptr != getInput())
330 return false;
331 auto index = cast<hw::StructType>(
332 cast<hw::InOutType>(getInput().getType()).getElementType())
333 .getFieldIndex(getFieldAttr());
334 if (!index)
335 return false;
336 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
337 if (!slot.subelementTypes.contains(indexAttr))
338 return false;
339 usedIndices.insert(indexAttr);
340 mustBeSafelyUsed.emplace_back<MemorySlot>(
341 {getResult(),
342 cast<hw::InOutType>(getResult().getType()).getElementType()});
343 return true;
344}
345
346DeletionKind
347SigStructExtractOp::rewire(const DestructurableMemorySlot &slot,
348 DenseMap<Attribute, MemorySlot> &subslots,
349 OpBuilder &builder, const DataLayout &dataLayout) {
350 auto index = cast<hw::StructType>(
351 cast<hw::InOutType>(getInput().getType()).getElementType())
352 .getFieldIndex(getFieldAttr());
353 assert(index.has_value());
354 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
355 auto it = subslots.find(indexAttr);
356 assert(it != subslots.end());
357 replaceAllUsesWith(it->getSecond().ptr);
358 return DeletionKind::Delete;
359}
360
361LogicalResult SigStructExtractOp::ensureOnlySafeAccesses(
362 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
363 const DataLayout &dataLayout) {
364 return success();
365}
366
367//===----------------------------------------------------------------------===//
368// PrbOp
369//===----------------------------------------------------------------------===//
370
371static void getSortedPtrs(DenseMap<Attribute, MemorySlot> &subslots,
372 SmallVectorImpl<std::pair<unsigned, Value>> &sorted) {
373 for (auto [attr, mem] : subslots) {
374 assert(isa<IntegerAttr>(attr));
375 sorted.push_back({cast<IntegerAttr>(attr).getInt(), mem.ptr});
376 }
377
378 llvm::sort(sorted, [](auto a, auto b) { return a.first < b.first; });
379}
380
381bool PrbOp::canRewire(const DestructurableMemorySlot &slot,
382 SmallPtrSetImpl<Attribute> &usedIndices,
383 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
384 const DataLayout &dataLayout) {
385 for (auto [key, _] : slot.subelementTypes)
386 usedIndices.insert(key);
387
388 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
389}
390
391DeletionKind PrbOp::rewire(const DestructurableMemorySlot &slot,
392 DenseMap<Attribute, MemorySlot> &subslots,
393 OpBuilder &builder, const DataLayout &dataLayout) {
394 SmallVector<std::pair<unsigned, Value>> elements;
395 SmallVector<Value> probed;
396 getSortedPtrs(subslots, elements);
397 for (auto [_, val] : elements)
398 probed.push_back(builder.create<PrbOp>(getLoc(), val));
399
400 Value repl = TypeSwitch<Type, Value>(getType())
401 .Case<hw::StructType>([&](auto ty) {
402 return builder.create<hw::StructCreateOp>(
403 getLoc(), getType(), probed);
404 })
405 .Case<hw::ArrayType>([&](auto ty) {
406 return builder.create<hw::ArrayCreateOp>(getLoc(), probed);
407 });
408
409 replaceAllUsesWith(repl);
410 return DeletionKind::Delete;
411}
412
413LogicalResult
414PrbOp::ensureOnlySafeAccesses(const MemorySlot &slot,
415 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
416 const DataLayout &dataLayout) {
417 return success();
418}
419
420//===----------------------------------------------------------------------===//
421// DrvOp
422//===----------------------------------------------------------------------===//
423
424LogicalResult llhd::DrvOp::fold(FoldAdaptor adaptor,
425 SmallVectorImpl<OpFoldResult> &result) {
426 if (!getEnable())
427 return failure();
428
429 if (matchPattern(getEnable(), m_One())) {
430 getEnableMutable().clear();
431 return success();
432 }
433
434 return failure();
435}
436
437LogicalResult llhd::DrvOp::canonicalize(llhd::DrvOp op,
438 PatternRewriter &rewriter) {
439 if (!op.getEnable())
440 return failure();
441
442 if (matchPattern(op.getEnable(), m_Zero())) {
443 rewriter.eraseOp(op);
444 return success();
445 }
446
447 return failure();
448}
449
450bool DrvOp::canRewire(const DestructurableMemorySlot &slot,
451 SmallPtrSetImpl<Attribute> &usedIndices,
452 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
453 const DataLayout &dataLayout) {
454 for (auto [key, _] : slot.subelementTypes)
455 usedIndices.insert(key);
456
457 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
458}
459
460DeletionKind DrvOp::rewire(const DestructurableMemorySlot &slot,
461 DenseMap<Attribute, MemorySlot> &subslots,
462 OpBuilder &builder, const DataLayout &dataLayout) {
463 SmallVector<std::pair<unsigned, Value>> driven;
464 getSortedPtrs(subslots, driven);
465
466 for (auto [idx, sig] : driven)
467 builder.create<DrvOp>(getLoc(), sig,
468 getValueAtIndex(builder, getLoc(), getValue(), idx),
469 getTime(), getEnable());
470
471 return DeletionKind::Delete;
472}
473
474LogicalResult
475DrvOp::ensureOnlySafeAccesses(const MemorySlot &slot,
476 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
477 const DataLayout &dataLayout) {
478 return success();
479}
480
481//===----------------------------------------------------------------------===//
482// WaitOp
483//===----------------------------------------------------------------------===//
484
485// Implement this operation for the BranchOpInterface
486SuccessorOperands llhd::WaitOp::getSuccessorOperands(unsigned index) {
487 assert(index == 0 && "invalid successor index");
488 return SuccessorOperands(getDestOpsMutable());
489}
490
491//===----------------------------------------------------------------------===//
492// ConnectOp
493//===----------------------------------------------------------------------===//
494
495LogicalResult llhd::ConnectOp::canonicalize(llhd::ConnectOp op,
496 PatternRewriter &rewriter) {
497 if (op.getLhs() == op.getRhs())
498 rewriter.eraseOp(op);
499 return success();
500}
501
502#include "circt/Dialect/LLHD/IR/LLHDEnums.cpp.inc"
503
504#define GET_OP_CLASSES
505#include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"
assert(baseType &&"element must be base type")
static InstancePath empty
static Value getValueAtIndex(OpBuilder &builder, Location loc, Value val, unsigned index)
Definition LLHDOps.cpp:73
static void getSortedPtrs(DenseMap< Attribute, MemorySlot > &subslots, SmallVectorImpl< std::pair< unsigned, Value > > &sorted)
Definition LLHDOps.cpp:371
static OpFoldResult foldSigPtrArraySliceOp(Op op, ArrayRef< Attribute > operands)
Definition LLHDOps.cpp:179
static LogicalResult canonicalizeSigPtrArraySliceOp(Op op, PatternRewriter &rewriter)
Definition LLHDOps.cpp:201
static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef< Attribute > operands)
Definition LLHDOps.cpp:153
static LogicalResult inferReturnTypesOfStructExtractOp(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results)
Definition LLHDOps.cpp:287
create(data_type, value)
Definition hw.py:433
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
unsigned getLLHDTypeWidth(Type type)
Definition LLHDOps.cpp:30
Type getLLHDElementType(Type type)
Definition LLHDOps.cpp:42
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:182