CIRCT  20.0.0git
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 
14 #include "circt/Dialect/HW/HWOps.h"
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 
26 using namespace circt;
27 using namespace mlir;
28 using namespace llhd;
29 
30 unsigned 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 
56 OpFoldResult llhd::ConstantTimeOp::fold(FoldAdaptor adaptor) {
57  assert(adaptor.getOperands().empty() && "const has no operands");
58  return getValueAttr();
59 }
60 
61 void 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 
73 static 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 
90  if (getName() && !getName()->empty())
91  setNameFn(getResult(), *getName());
92 }
93 
94 SmallVector<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 
108 DenseMap<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 
140 std::optional<DestructurableAllocationOpInterface>
141 SignalOp::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 
152 template <class Op>
153 static 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 
166 OpFoldResult llhd::SigExtractOp::fold(FoldAdaptor adaptor) {
167  return foldSigPtrExtractOp(*this, adaptor.getOperands());
168 }
169 
170 OpFoldResult llhd::PtrExtractOp::fold(FoldAdaptor adaptor) {
171  return foldSigPtrExtractOp(*this, adaptor.getOperands());
172 }
173 
174 //===----------------------------------------------------------------------===//
175 // SigArraySliceOp and PtrArraySliceOp
176 //===----------------------------------------------------------------------===//
177 
178 template <class Op>
179 static 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 
192 OpFoldResult llhd::SigArraySliceOp::fold(FoldAdaptor adaptor) {
193  return foldSigPtrArraySliceOp(*this, adaptor.getOperands());
194 }
195 
196 OpFoldResult llhd::PtrArraySliceOp::fold(FoldAdaptor adaptor) {
197  return foldSigPtrArraySliceOp(*this, adaptor.getOperands());
198 }
199 
200 template <class Op>
201 static 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 
226 LogicalResult llhd::SigArraySliceOp::canonicalize(llhd::SigArraySliceOp op,
227  PatternRewriter &rewriter) {
228  return canonicalizeSigPtrArraySliceOp(op, rewriter);
229 }
230 
231 LogicalResult llhd::PtrArraySliceOp::canonicalize(llhd::PtrArraySliceOp op,
232  PatternRewriter &rewriter) {
233  return canonicalizeSigPtrArraySliceOp(op, rewriter);
234 }
235 
236 //===----------------------------------------------------------------------===//
237 // SigArrayGetOp
238 //===----------------------------------------------------------------------===//
239 
240 bool 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 
260 DeletionKind 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 
276 LogicalResult 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 
286 template <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 
306 LogicalResult 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 
315 LogicalResult 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 
324 bool 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 
346 DeletionKind
347 SigStructExtractOp::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 
361 LogicalResult SigStructExtractOp::ensureOnlySafeAccesses(
362  const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
363  const DataLayout &dataLayout) {
364  return success();
365 }
366 
367 //===----------------------------------------------------------------------===//
368 // PrbOp
369 //===----------------------------------------------------------------------===//
370 
371 static 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 
381 bool 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 
391 DeletionKind 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 
413 LogicalResult
414 PrbOp::ensureOnlySafeAccesses(const MemorySlot &slot,
415  SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
416  const DataLayout &dataLayout) {
417  return success();
418 }
419 
420 //===----------------------------------------------------------------------===//
421 // DrvOp
422 //===----------------------------------------------------------------------===//
423 
424 LogicalResult 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 
437 LogicalResult 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 
450 bool 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 
460 DeletionKind 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 
474 LogicalResult
475 DrvOp::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
486 SuccessorOperands llhd::WaitOp::getSuccessorOperands(unsigned index) {
487  assert(index == 0 && "invalid successor index");
488  return SuccessorOperands(getDestOpsMutable());
489 }
490 
491 //===----------------------------------------------------------------------===//
492 // ConnectOp
493 //===----------------------------------------------------------------------===//
494 
495 LogicalResult 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 OpFoldResult foldSigPtrArraySliceOp(Op op, ArrayRef< Attribute > operands)
Definition: LLHDOps.cpp:179
static LogicalResult canonicalizeSigPtrArraySliceOp(Op op, PatternRewriter &rewriter)
Definition: LLHDOps.cpp:201
static void getSortedPtrs(DenseMap< Attribute, MemorySlot > &subslots, SmallVectorImpl< std::pair< unsigned, Value >> &sorted)
Definition: LLHDOps.cpp:371
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
def create(data_type, value)
Definition: hw.py:433
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
Definition: VerifOps.cpp:66
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
unsigned getLLHDTypeWidth(Type type)
Definition: LLHDOps.cpp:30
Type getLLHDElementType(Type type)
Definition: LLHDOps.cpp:42
circt::hw::InOutType InOutType
Definition: SVTypes.h:25
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:182