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