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"
27 if (
auto sig = dyn_cast<RefType>(type))
28 type = sig.getNestedType();
29 if (
auto array = dyn_cast<hw::ArrayType>(type))
30 return array.getNumElements();
31 if (
auto tup = dyn_cast<hw::StructType>(type))
32 return tup.getElements().size();
33 return type.getIntOrFloatBitWidth();
37 if (
auto sig = dyn_cast<RefType>(type))
38 type = sig.getNestedType();
39 if (
auto array = dyn_cast<hw::ArrayType>(type))
40 return array.getElementType();
48OpFoldResult llhd::ConstantTimeOp::fold(FoldAdaptor adaptor) {
49 assert(adaptor.getOperands().empty() &&
"const has no operands");
50 return getValueAttr();
53void llhd::ConstantTimeOp::build(OpBuilder &builder, OperationState &result,
54 uint64_t time,
const StringRef &timeUnit,
55 unsigned delta,
unsigned epsilon) {
56 auto *ctx = builder.getContext();
57 auto attr = TimeAttr::get(ctx, time, timeUnit, delta, epsilon);
58 return build(builder, result, TimeType::get(ctx), attr);
67 return TypeSwitch<Type, Value>(val.getType())
68 .Case<hw::StructType>([&](hw::StructType ty) -> Value {
70 ty.getElements()[index].name);
72 .Case<hw::ArrayType>([&](hw::ArrayType ty) -> Value {
75 builder.getIntegerType(llvm::Log2_64_Ceil(ty.getNumElements())),
83 setNameFn(getResult(), *
getName());
86SmallVector<DestructurableMemorySlot> SignalOp::getDestructurableSlots() {
87 auto type = getType().getNestedType();
89 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(type);
93 auto destructuredType = destructurable.getSubelementIndexMap();
94 if (!destructuredType)
97 return {DestructurableMemorySlot{{getResult(), type}, *destructuredType}};
100DenseMap<Attribute, MemorySlot> SignalOp::destructure(
101 const DestructurableMemorySlot &slot,
102 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
103 SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
104 assert(slot.ptr == getResult());
105 builder.setInsertionPointAfter(*
this);
107 auto destructurableType =
108 cast<DestructurableTypeInterface>(getType().getNestedType());
109 DenseMap<Attribute, MemorySlot> slotMap;
110 SmallVector<std::pair<unsigned, Type>> indices;
111 for (
auto attr : usedIndices) {
112 assert(isa<IntegerAttr>(attr));
113 auto elemType = destructurableType.getTypeAtIndex(attr);
114 assert(elemType &&
"used index must exist");
115 indices.push_back({cast<IntegerAttr>(attr).getInt(), elemType});
118 llvm::sort(indices, [](
auto a,
auto b) {
return a.first < b.first; });
120 for (
auto [index, type] : indices) {
122 auto sigOp = SignalOp::create(builder,
getLoc(), getNameAttr(), init);
123 newAllocators.push_back(sigOp);
124 slotMap.try_emplace<MemorySlot>(
125 IntegerAttr::get(IndexType::get(getContext()), index),
126 {sigOp.getResult(), type});
132std::optional<DestructurableAllocationOpInterface>
133SignalOp::handleDestructuringComplete(
const DestructurableMemorySlot &slot,
134 OpBuilder &builder) {
135 assert(slot.ptr == getResult());
151 if (op.getResultWidth() == op.getInputWidth() &&
152 cast<IntegerAttr>(operands[1]).getValue().isZero())
153 return op.getInput();
158OpFoldResult llhd::SigExtractOp::fold(FoldAdaptor adaptor) {
166OpFoldResult llhd::SigArraySliceOp::fold(FoldAdaptor adaptor) {
167 auto lowIndex = dyn_cast_or_null<IntegerAttr>(adaptor.getLowIndex());
172 if (getResultWidth() == getInputWidth() && lowIndex.getValue().isZero())
180 PatternRewriter &rewriter) {
181 IntegerAttr indexAttr;
182 if (!matchPattern(op.getLowIndex(), m_Constant(&indexAttr)))
188 if (matchPattern(op.getInput(),
189 m_Op<Op>(matchers::m_Any(), m_Constant(&a)))) {
190 auto sliceOp = op.getInput().template getDefiningOp<Op>();
191 rewriter.modifyOpInPlace(op, [&]() {
192 op.getInputMutable().assign(sliceOp.getInput());
194 rewriter, op->getLoc(), a.getValue() + indexAttr.getValue());
195 op.getLowIndexMutable().assign(newIndex);
204LogicalResult llhd::SigArraySliceOp::canonicalize(llhd::SigArraySliceOp op,
205 PatternRewriter &rewriter) {
213bool SigArrayGetOp::canRewire(
const DestructurableMemorySlot &slot,
214 SmallPtrSetImpl<Attribute> &usedIndices,
215 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
216 const DataLayout &dataLayout) {
217 if (slot.ptr != getInput())
220 if (!matchPattern(getIndex(), m_ConstantInt(&idx)))
223 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
224 if (!slot.subelementTypes.contains(index))
226 usedIndices.insert(index);
227 mustBeSafelyUsed.emplace_back<MemorySlot>(
228 {getResult(), cast<RefType>(getResult().getType()).getNestedType()});
232DeletionKind SigArrayGetOp::rewire(
const DestructurableMemorySlot &slot,
233 DenseMap<Attribute, MemorySlot> &subslots,
235 const DataLayout &dataLayout) {
237 bool result = matchPattern(getIndex(), m_ConstantInt(&idx));
241 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
242 auto it = subslots.find(index);
243 assert(it != subslots.end());
244 replaceAllUsesWith(it->getSecond().ptr);
245 return DeletionKind::Delete;
248LogicalResult SigArrayGetOp::ensureOnlySafeAccesses(
249 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
250 const DataLayout &dataLayout) {
258LogicalResult llhd::SigStructExtractOp::inferReturnTypes(
259 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
260 DictionaryAttr attrs, mlir::OpaqueProperties properties,
261 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
262 typename SigStructExtractOp::Adaptor adaptor(operands, attrs, properties,
264 Type type = cast<hw::StructType>(
265 cast<RefType>(adaptor.getInput().getType()).getNestedType())
266 .getFieldType(adaptor.getField());
268 context->getDiagEngine().emit(loc.value_or(UnknownLoc()),
269 DiagnosticSeverity::Error)
270 <<
"invalid field name specified";
273 results.push_back(RefType::get(type));
277bool SigStructExtractOp::canRewire(
278 const DestructurableMemorySlot &slot,
279 SmallPtrSetImpl<Attribute> &usedIndices,
280 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
281 const DataLayout &dataLayout) {
282 if (slot.ptr != getInput())
285 cast<hw::StructType>(cast<RefType>(getInput().getType()).getNestedType())
286 .getFieldIndex(getFieldAttr());
289 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
290 if (!slot.subelementTypes.contains(indexAttr))
292 usedIndices.insert(indexAttr);
293 mustBeSafelyUsed.emplace_back<MemorySlot>(
294 {getResult(), cast<RefType>(getResult().getType()).getNestedType()});
299SigStructExtractOp::rewire(
const DestructurableMemorySlot &slot,
300 DenseMap<Attribute, MemorySlot> &subslots,
301 OpBuilder &builder,
const DataLayout &dataLayout) {
303 cast<hw::StructType>(cast<RefType>(getInput().getType()).getNestedType())
304 .getFieldIndex(getFieldAttr());
305 assert(index.has_value());
306 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
307 auto it = subslots.find(indexAttr);
308 assert(it != subslots.end());
309 replaceAllUsesWith(it->getSecond().ptr);
310 return DeletionKind::Delete;
313LogicalResult SigStructExtractOp::ensureOnlySafeAccesses(
314 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
315 const DataLayout &dataLayout) {
324 SmallVectorImpl<std::pair<unsigned, Value>> &sorted) {
325 for (
auto [attr, mem] : subslots) {
326 assert(isa<IntegerAttr>(attr));
327 sorted.push_back({cast<IntegerAttr>(attr).getInt(), mem.ptr});
330 llvm::sort(sorted, [](
auto a,
auto b) {
return a.first < b.first; });
333bool ProbeOp::canRewire(
const DestructurableMemorySlot &slot,
334 SmallPtrSetImpl<Attribute> &usedIndices,
335 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
336 const DataLayout &dataLayout) {
337 for (
auto [key, _] : slot.subelementTypes)
338 usedIndices.insert(key);
340 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
343DeletionKind ProbeOp::rewire(
const DestructurableMemorySlot &slot,
344 DenseMap<Attribute, MemorySlot> &subslots,
345 OpBuilder &builder,
const DataLayout &dataLayout) {
346 SmallVector<std::pair<unsigned, Value>> elements;
347 SmallVector<Value> probed;
349 for (
auto [_, val] : elements)
350 probed.push_back(ProbeOp::create(builder,
getLoc(), val));
353 TypeSwitch<Type, Value>(getType())
354 .Case<hw::StructType>([&](
auto ty) {
358 .Case<hw::ArrayType>([&](
auto ty) {
362 replaceAllUsesWith(repl);
363 return DeletionKind::Delete;
367ProbeOp::ensureOnlySafeAccesses(
const MemorySlot &slot,
368 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
369 const DataLayout &dataLayout) {
373void ProbeOp::getEffects(
374 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
376 if (mayHaveSSADominance(*getOperation()->getParentRegion()))
377 effects.emplace_back(MemoryEffects::Read::get(), &getSignalMutable());
384LogicalResult llhd::DriveOp::fold(FoldAdaptor adaptor,
385 SmallVectorImpl<OpFoldResult> &result) {
389 if (matchPattern(getEnable(), m_One())) {
390 getEnableMutable().clear();
397LogicalResult llhd::DriveOp::canonicalize(llhd::DriveOp op,
398 PatternRewriter &rewriter) {
402 if (matchPattern(op.getEnable(), m_Zero())) {
403 rewriter.eraseOp(op);
410bool DriveOp::canRewire(
const DestructurableMemorySlot &slot,
411 SmallPtrSetImpl<Attribute> &usedIndices,
412 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
413 const DataLayout &dataLayout) {
414 for (
auto [key, _] : slot.subelementTypes)
415 usedIndices.insert(key);
417 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
420DeletionKind DriveOp::rewire(
const DestructurableMemorySlot &slot,
421 DenseMap<Attribute, MemorySlot> &subslots,
422 OpBuilder &builder,
const DataLayout &dataLayout) {
423 SmallVector<std::pair<unsigned, Value>> driven;
426 for (
auto [idx, sig] : driven)
427 DriveOp::create(builder,
getLoc(), sig,
429 getTime(), getEnable());
431 return DeletionKind::Delete;
435DriveOp::ensureOnlySafeAccesses(
const MemorySlot &slot,
436 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
437 const DataLayout &dataLayout) {
445LogicalResult ProcessOp::canonicalize(ProcessOp op, PatternRewriter &rewriter) {
446 if (!op.getBody().hasOneBlock())
449 auto &block = op.getBody().front();
450 auto haltOp = dyn_cast<HaltOp>(block.getTerminator());
454 if (op.getNumResults() == 0 && block.getOperations().size() == 1) {
455 rewriter.eraseOp(op);
460 if (!llvm::all_of(block.without_terminator(), [](
auto &bodyOp) {
461 return bodyOp.template hasTrait<OpTrait::ConstantLike>();
465 auto yieldOperands = haltOp.getYieldOperands();
468 llvm::BitVector operandsToErase(yieldOperands.size());
470 for (
auto [operandNo, operand] :
llvm::enumerate(yieldOperands)) {
471 auto *defOp = operand.getDefiningOp();
472 if (defOp && defOp->hasTrait<OpTrait::ConstantLike>()) {
475 if (!defOp->getParentRegion()->isProperAncestor(&op.getBody())) {
476 defOp->moveBefore(op);
478 rewriter.replaceAllUsesWith(op.getResult(operandNo), operand);
479 operandsToErase.set(operandNo);
485 if (!uniqueOperands.contains(operand)) {
486 const auto newPos = uniqueOperands.size();
487 uniqueOperands.insert(std::make_pair(operand, newPos));
488 origToNewPos.insert(std::make_pair(operandNo, newPos));
490 auto firstOccurrencePos = uniqueOperands.lookup(operand);
491 origToNewPos.insert(std::make_pair(operandNo, firstOccurrencePos));
492 operandsToErase.set(operandNo);
496 const auto countOperandsToErase = operandsToErase.count();
497 if (countOperandsToErase == 0)
502 if (countOperandsToErase == op.getNumResults()) {
503 rewriter.eraseOp(op);
507 haltOp->eraseOperands(operandsToErase);
509 SmallVector<Type> resultTypes = llvm::to_vector(haltOp->getOperandTypes());
510 auto newProcessOp = ProcessOp::create(rewriter, op.getLoc(), resultTypes,
511 op->getOperands(), op->getAttrs());
512 newProcessOp.getBody().takeBody(op.getBody());
515 for (
auto oldResult : op.getResults()) {
516 auto newResultPos = origToNewPos.find(oldResult.getResultNumber());
517 if (newResultPos == origToNewPos.end())
519 auto newResult = newProcessOp.getResult(newResultPos->getSecond());
520 rewriter.replaceAllUsesWith(oldResult, newResult);
523 rewriter.eraseOp(op);
531LogicalResult CombinationalOp::canonicalize(CombinationalOp op,
532 PatternRewriter &rewriter) {
535 if (op.getBody().hasOneBlock() && isMemoryEffectFree(op)) {
536 auto &block = op.getBody().front();
537 auto *terminator = block.getTerminator();
538 rewriter.inlineBlockBefore(&block, op, ValueRange{});
539 rewriter.replaceOp(op, terminator->getOperands());
540 rewriter.eraseOp(terminator);
551 ValueRange yieldOperands) {
553 auto *parentOp = op->getParentOp();
554 TypeRange resultTypes = TypeSwitch<Operation *, TypeRange>(parentOp)
555 .Case<ProcessOp, CombinationalOp>(
556 [](
auto op) {
return op.getResultTypes(); })
557 .Case<FinalOp>([](
auto) {
return TypeRange{}; });
560 if (yieldOperands.size() != resultTypes.size())
561 return op->emitOpError()
562 <<
"has " << yieldOperands.size()
563 <<
" yield operands, but enclosing '" << parentOp->getName()
564 <<
"' returns " << resultTypes.size();
567 for (
unsigned i = 0; i < yieldOperands.size(); ++i)
568 if (yieldOperands[i].getType() != resultTypes[i])
569 return op->emitError()
570 <<
"type of yield operand " << i <<
" ("
571 << yieldOperands[i].getType() <<
") does not match enclosing '"
572 << parentOp->getName() <<
"' result type (" << resultTypes[i]
578LogicalResult WaitOp::verify() {
586LogicalResult HaltOp::verify() {
594LogicalResult YieldOp::verify() {
602#define GET_OP_CLASSES
603#include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"
604#include "circt/Dialect/LLHD/IR/LLHDEnums.cpp.inc"
assert(baseType &&"element must be base type")
static Value getValueAtIndex(OpBuilder &builder, Location loc, Value val, unsigned index)
static void getSortedPtrs(DenseMap< Attribute, MemorySlot > &subslots, SmallVectorImpl< std::pair< unsigned, Value > > &sorted)
static LogicalResult verifyYieldResults(Operation *op, ValueRange yieldOperands)
static LogicalResult canonicalizeSigPtrArraySliceOp(Op op, PatternRewriter &rewriter)
static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef< Attribute > operands)
static Location getLoc(DefSlot slot)
static InstancePath empty
create(elements, Type result_type=None)
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)
Type getLLHDElementType(Type type)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn