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<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();
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();
52OpFoldResult llhd::ConstantTimeOp::fold(FoldAdaptor adaptor) {
53 assert(adaptor.getOperands().empty() &&
"const has no operands");
54 return getValueAttr();
57void llhd::ConstantTimeOp::build(OpBuilder &builder, OperationState &result,
58 uint64_t 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);
71 return TypeSwitch<Type, Value>(val.getType())
72 .Case<hw::StructType>([&](hw::StructType ty) -> Value {
74 ty.getElements()[index].name);
76 .Case<hw::ArrayType>([&](hw::ArrayType ty) -> Value {
79 builder.getIntegerType(llvm::Log2_64_Ceil(ty.getNumElements())),
87 setNameFn(getResult(), *
getName());
90SmallVector<DestructurableMemorySlot> SignalOp::getDestructurableSlots() {
91 auto type = getType().getElementType();
93 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(type);
97 auto destructuredType = destructurable.getSubelementIndexMap();
98 if (!destructuredType)
101 return {DestructurableMemorySlot{{getResult(), type}, *destructuredType}};
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);
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});
122 llvm::sort(indices, [](
auto a,
auto b) {
return a.first < b.first; });
124 for (
auto [index, type] : indices) {
126 auto sigOp = SignalOp::create(builder,
getLoc(), getNameAttr(), init);
127 newAllocators.push_back(sigOp);
128 slotMap.try_emplace<MemorySlot>(
129 IntegerAttr::get(IndexType::get(getContext()), index),
130 {sigOp.getResult(), type});
136std::optional<DestructurableAllocationOpInterface>
137SignalOp::handleDestructuringComplete(
const DestructurableMemorySlot &slot,
138 OpBuilder &builder) {
139 assert(slot.ptr == getResult());
155 if (op.getResultWidth() == op.getInputWidth() &&
156 cast<IntegerAttr>(operands[1]).getValue().isZero())
157 return op.getInput();
162OpFoldResult llhd::SigExtractOp::fold(FoldAdaptor adaptor) {
166OpFoldResult llhd::PtrExtractOp::fold(FoldAdaptor adaptor) {
176 ArrayRef<Attribute> operands) {
181 if (op.getResultWidth() == op.getInputWidth() &&
182 cast<IntegerAttr>(operands[1]).getValue().isZero())
183 return op.getInput();
188OpFoldResult llhd::SigArraySliceOp::fold(FoldAdaptor adaptor) {
192OpFoldResult llhd::PtrArraySliceOp::fold(FoldAdaptor adaptor) {
198 PatternRewriter &rewriter) {
199 IntegerAttr indexAttr;
200 if (!matchPattern(op.getLowIndex(), m_Constant(&indexAttr)))
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());
212 rewriter, op->getLoc(), a.getValue() + indexAttr.getValue());
213 op.getLowIndexMutable().assign(newIndex);
222LogicalResult llhd::SigArraySliceOp::canonicalize(llhd::SigArraySliceOp op,
223 PatternRewriter &rewriter) {
227LogicalResult llhd::PtrArraySliceOp::canonicalize(llhd::PtrArraySliceOp op,
228 PatternRewriter &rewriter) {
236bool SigArrayGetOp::canRewire(
const DestructurableMemorySlot &slot,
237 SmallPtrSetImpl<Attribute> &usedIndices,
238 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
239 const DataLayout &dataLayout) {
240 if (slot.ptr != getInput())
243 if (!matchPattern(getIndex(), m_ConstantInt(&idx)))
246 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
247 if (!slot.subelementTypes.contains(index))
249 usedIndices.insert(index);
250 mustBeSafelyUsed.emplace_back<MemorySlot>(
252 cast<hw::InOutType>(getResult().getType()).getElementType()});
256DeletionKind SigArrayGetOp::rewire(
const DestructurableMemorySlot &slot,
257 DenseMap<Attribute, MemorySlot> &subslots,
259 const DataLayout &dataLayout) {
261 bool result = matchPattern(getIndex(), m_ConstantInt(&idx));
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;
272LogicalResult SigArrayGetOp::ensureOnlySafeAccesses(
273 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
274 const DataLayout &dataLayout) {
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);
289 cast<hw::StructType>(
290 cast<SigPtrType>(adaptor.getInput().getType()).getElementType())
291 .getFieldType(adaptor.getField());
293 context->getDiagEngine().emit(loc.value_or(UnknownLoc()),
294 DiagnosticSeverity::Error)
295 <<
"invalid field name specified";
298 results.push_back(SigPtrType::get(type));
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) {
308 context, loc, operands, attrs, properties, regions, results);
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) {
317 context, loc, operands, attrs, properties, regions, results);
320bool SigStructExtractOp::canRewire(
321 const DestructurableMemorySlot &slot,
322 SmallPtrSetImpl<Attribute> &usedIndices,
323 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
324 const DataLayout &dataLayout) {
325 if (slot.ptr != getInput())
327 auto index = cast<hw::StructType>(
328 cast<hw::InOutType>(getInput().getType()).getElementType())
329 .getFieldIndex(getFieldAttr());
332 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
333 if (!slot.subelementTypes.contains(indexAttr))
335 usedIndices.insert(indexAttr);
336 mustBeSafelyUsed.emplace_back<MemorySlot>(
338 cast<hw::InOutType>(getResult().getType()).getElementType()});
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;
357LogicalResult SigStructExtractOp::ensureOnlySafeAccesses(
358 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
359 const DataLayout &dataLayout) {
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});
374 llvm::sort(sorted, [](
auto a,
auto b) {
return a.first < b.first; });
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);
384 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
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;
393 for (
auto [_, val] : elements)
394 probed.push_back(PrbOp::create(builder,
getLoc(), val));
397 TypeSwitch<Type, Value>(getType())
398 .Case<hw::StructType>([&](
auto ty) {
402 .Case<hw::ArrayType>([&](
auto ty) {
406 replaceAllUsesWith(repl);
407 return DeletionKind::Delete;
411PrbOp::ensureOnlySafeAccesses(
const MemorySlot &slot,
412 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
413 const DataLayout &dataLayout) {
417void PrbOp::getEffects(
418 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
420 if (mayHaveSSADominance(*getOperation()->getParentRegion()))
421 effects.emplace_back(MemoryEffects::Read::get(), &getSignalMutable());
428LogicalResult llhd::DrvOp::fold(FoldAdaptor adaptor,
429 SmallVectorImpl<OpFoldResult> &result) {
433 if (matchPattern(getEnable(), m_One())) {
434 getEnableMutable().clear();
441LogicalResult llhd::DrvOp::canonicalize(llhd::DrvOp op,
442 PatternRewriter &rewriter) {
446 if (matchPattern(op.getEnable(), m_Zero())) {
447 rewriter.eraseOp(op);
454bool DrvOp::canRewire(
const DestructurableMemorySlot &slot,
455 SmallPtrSetImpl<Attribute> &usedIndices,
456 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
457 const DataLayout &dataLayout) {
458 for (
auto [key, _] : slot.subelementTypes)
459 usedIndices.insert(key);
461 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
464DeletionKind DrvOp::rewire(
const DestructurableMemorySlot &slot,
465 DenseMap<Attribute, MemorySlot> &subslots,
466 OpBuilder &builder,
const DataLayout &dataLayout) {
467 SmallVector<std::pair<unsigned, Value>> driven;
470 for (
auto [idx, sig] : driven)
471 DrvOp::create(builder,
getLoc(), sig,
473 getTime(), getEnable());
475 return DeletionKind::Delete;
479DrvOp::ensureOnlySafeAccesses(
const MemorySlot &slot,
480 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
481 const DataLayout &dataLayout) {
489LogicalResult ProcessOp::canonicalize(ProcessOp op, PatternRewriter &rewriter) {
490 if (!op.getBody().hasOneBlock())
493 auto &block = op.getBody().front();
494 auto haltOp = dyn_cast<HaltOp>(block.getTerminator());
498 if (op.getNumResults() == 0 && block.getOperations().size() == 1) {
499 rewriter.eraseOp(op);
504 if (!llvm::all_of(block.without_terminator(), [](
auto &bodyOp) {
505 return bodyOp.template hasTrait<OpTrait::ConstantLike>();
509 auto yieldOperands = haltOp.getYieldOperands();
512 llvm::BitVector operandsToErase(yieldOperands.size());
514 for (
auto [operandNo, operand] :
llvm::enumerate(yieldOperands)) {
515 auto *defOp = operand.getDefiningOp();
516 if (defOp && defOp->hasTrait<OpTrait::ConstantLike>()) {
519 if (!defOp->getParentRegion()->isProperAncestor(&op.getBody())) {
520 defOp->moveBefore(op);
522 rewriter.replaceAllUsesWith(op.getResult(operandNo), operand);
523 operandsToErase.set(operandNo);
529 if (!uniqueOperands.contains(operand)) {
530 const auto newPos = uniqueOperands.size();
531 uniqueOperands.insert(std::make_pair(operand, newPos));
532 origToNewPos.insert(std::make_pair(operandNo, newPos));
534 auto firstOccurrencePos = uniqueOperands.lookup(operand);
535 origToNewPos.insert(std::make_pair(operandNo, firstOccurrencePos));
536 operandsToErase.set(operandNo);
540 const auto countOperandsToErase = operandsToErase.count();
541 if (countOperandsToErase == 0)
546 if (countOperandsToErase == op.getNumResults()) {
547 rewriter.eraseOp(op);
551 haltOp->eraseOperands(operandsToErase);
553 SmallVector<Type> resultTypes = llvm::to_vector(haltOp->getOperandTypes());
554 auto newProcessOp = ProcessOp::create(rewriter, op.getLoc(), resultTypes,
555 op->getOperands(), op->getAttrs());
556 newProcessOp.getBody().takeBody(op.getBody());
559 for (
auto oldResult : op.getResults()) {
560 auto newResultPos = origToNewPos.find(oldResult.getResultNumber());
561 if (newResultPos == origToNewPos.end())
563 auto newResult = newProcessOp.getResult(newResultPos->getSecond());
564 rewriter.replaceAllUsesWith(oldResult, newResult);
567 rewriter.eraseOp(op);
575LogicalResult CombinationalOp::canonicalize(CombinationalOp op,
576 PatternRewriter &rewriter) {
579 if (op.getBody().hasOneBlock() && isMemoryEffectFree(op)) {
580 auto &block = op.getBody().front();
581 auto *terminator = block.getTerminator();
582 rewriter.inlineBlockBefore(&block, op, ValueRange{});
583 rewriter.replaceOp(op, terminator->getOperands());
584 rewriter.eraseOp(terminator);
595 ValueRange yieldOperands) {
597 auto *parentOp = op->getParentOp();
598 TypeRange resultTypes = TypeSwitch<Operation *, TypeRange>(parentOp)
599 .Case<ProcessOp, CombinationalOp>(
600 [](
auto op) {
return op.getResultTypes(); })
601 .Case<FinalOp>([](
auto) {
return TypeRange{}; });
604 if (yieldOperands.size() != resultTypes.size())
605 return op->emitOpError()
606 <<
"has " << yieldOperands.size()
607 <<
" yield operands, but enclosing '" << parentOp->getName()
608 <<
"' returns " << resultTypes.size();
611 for (
unsigned i = 0; i < yieldOperands.size(); ++i)
612 if (yieldOperands[i].getType() != resultTypes[i])
613 return op->emitError()
614 <<
"type of yield operand " << i <<
" ("
615 << yieldOperands[i].getType() <<
") does not match enclosing '"
616 << parentOp->getName() <<
"' result type (" << resultTypes[i]
622LogicalResult WaitOp::verify() {
630LogicalResult HaltOp::verify() {
638LogicalResult YieldOp::verify() {
646#define GET_OP_CLASSES
647#include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"
648#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 OpFoldResult foldSigPtrArraySliceOp(Op op, ArrayRef< Attribute > operands)
static LogicalResult canonicalizeSigPtrArraySliceOp(Op op, PatternRewriter &rewriter)
static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef< Attribute > operands)
static LogicalResult inferReturnTypesOfStructExtractOp(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results)
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