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 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);
71 return TypeSwitch<Type, Value>(val.getType())
72 .Case<hw::StructType>([&](hw::StructType ty) -> Value {
74 loc, val, 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 = 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});
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 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(builder.create<PrbOp>(
getLoc(), val));
396 Value repl = TypeSwitch<Type, Value>(getType())
397 .Case<hw::StructType>([&](
auto ty) {
399 getLoc(), getType(), probed);
401 .Case<hw::ArrayType>([&](
auto ty) {
405 replaceAllUsesWith(repl);
406 return DeletionKind::Delete;
410PrbOp::ensureOnlySafeAccesses(
const MemorySlot &slot,
411 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
412 const DataLayout &dataLayout) {
416void PrbOp::getEffects(
417 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
419 if (mayHaveSSADominance(*getOperation()->getParentRegion()))
420 effects.emplace_back(MemoryEffects::Read::get(), &getSignalMutable());
427LogicalResult llhd::DrvOp::fold(FoldAdaptor adaptor,
428 SmallVectorImpl<OpFoldResult> &result) {
432 if (matchPattern(getEnable(), m_One())) {
433 getEnableMutable().clear();
440LogicalResult llhd::DrvOp::canonicalize(llhd::DrvOp op,
441 PatternRewriter &rewriter) {
445 if (matchPattern(op.getEnable(), m_Zero())) {
446 rewriter.eraseOp(op);
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);
460 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
463DeletionKind DrvOp::rewire(
const DestructurableMemorySlot &slot,
464 DenseMap<Attribute, MemorySlot> &subslots,
465 OpBuilder &builder,
const DataLayout &dataLayout) {
466 SmallVector<std::pair<unsigned, Value>> driven;
469 for (
auto [idx, sig] : driven)
470 builder.create<DrvOp>(
getLoc(), sig,
472 getTime(), getEnable());
474 return DeletionKind::Delete;
478DrvOp::ensureOnlySafeAccesses(
const MemorySlot &slot,
479 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
480 const DataLayout &dataLayout) {
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);
503 ValueRange yieldOperands) {
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{}; });
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();
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]
530LogicalResult WaitOp::verify() {
538LogicalResult HaltOp::verify() {
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)
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)
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