CIRCT  19.0.0git
LLHDToLLVM.cpp
Go to the documentation of this file.
1 //===- LLHDToLLVM.cpp - LLHD to LLVM Conversion Pass ----------------------===//
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 is the main LLHD to LLVM Conversion Pass Implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "../PassDetail.h"
20 #include "circt/Support/LLVM.h"
22 #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
23 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
24 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
25 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h"
26 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
27 #include "mlir/Conversion/LLVMCommon/Pattern.h"
28 #include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h"
29 #include "mlir/Dialect/Arith/IR/Arith.h"
30 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
31 #include "mlir/IR/IRMapping.h"
32 #include "mlir/Pass/Pass.h"
33 #include "mlir/Transforms/DialectConversion.h"
34 
35 using namespace mlir;
36 using namespace circt;
37 using namespace circt::llhd;
38 
39 //===----------------------------------------------------------------------===//
40 // Helpers
41 //===----------------------------------------------------------------------===//
42 
43 /// Get an existing global string.
44 static Value getGlobalString(Location loc, OpBuilder &builder,
45  const TypeConverter *typeConverter,
46  LLVM::GlobalOp &str) {
47  auto voidPtrTy = LLVM::LLVMPointerType::get(builder.getContext());
48  auto i32Ty = IntegerType::get(builder.getContext(), 32);
49 
50  auto addr = builder.create<LLVM::AddressOfOp>(loc, voidPtrTy, str.getName());
51  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,
52  builder.getI32IntegerAttr(0));
53  std::array<Value, 2> idxs({idx, idx});
54  return builder.create<LLVM::GEPOp>(loc, voidPtrTy, str.getType(), addr, idxs);
55 }
56 
57 /// Looks up a symbol and inserts a new functino at the beginning of the
58 /// module's region in case the function does not exists. If
59 /// insertBodyAndTerminator is set, also adds the entry block and return
60 /// terminator.
61 static LLVM::LLVMFuncOp
62 getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,
63  Location loc, std::string name, Type signature,
64  bool insertBodyAndTerminator = false) {
65  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);
66  if (!func) {
67  OpBuilder moduleBuilder(module.getBodyRegion());
68  func = moduleBuilder.create<LLVM::LLVMFuncOp>(loc, name, signature);
69  if (insertBodyAndTerminator) {
70  func.addEntryBlock();
71  OpBuilder b(func.getBody());
72  b.create<LLVM::ReturnOp>(loc, ValueRange());
73  }
74  }
75  return func;
76 }
77 
78 /// Return the LLVM type used to represent a signal. It corresponds to a struct
79 /// with the format: {valuePtr, bitOffset, instanceIndex, globalIndex}.
80 static Type getLLVMSigType(LLVM::LLVMDialect *dialect) {
81  auto voidPtrTy = LLVM::LLVMPointerType::get(dialect->getContext());
82  auto i64Ty = IntegerType::get(dialect->getContext(), 64);
83  return LLVM::LLVMStructType::getLiteral(dialect->getContext(),
84  {voidPtrTy, i64Ty, i64Ty, i64Ty});
85 }
86 
87 /// Extract the details from the given signal struct. The details are returned
88 /// in the original struct order.
89 static std::vector<Value> getSignalDetail(ConversionPatternRewriter &rewriter,
90  LLVM::LLVMDialect *dialect,
91  Location loc, Value signal,
92  bool extractIndices = false) {
93 
94  auto voidPtrTy = LLVM::LLVMPointerType::get(dialect->getContext());
95  auto i64Ty = IntegerType::get(dialect->getContext(), 64);
96  auto sigTy = getLLVMSigType(dialect);
97 
98  std::vector<Value> result;
99 
100  // Extract the value and offset elements.
101  auto sigPtrPtr = rewriter.create<LLVM::GEPOp>(loc, voidPtrTy, sigTy, signal,
102  ArrayRef<LLVM::GEPArg>({0, 0}));
103  result.push_back(rewriter.create<LLVM::LoadOp>(loc, voidPtrTy, sigPtrPtr));
104 
105  auto offsetPtr = rewriter.create<LLVM::GEPOp>(loc, voidPtrTy, sigTy, signal,
106  ArrayRef<LLVM::GEPArg>({0, 1}));
107  result.push_back(rewriter.create<LLVM::LoadOp>(loc, i64Ty, offsetPtr));
108 
109  // Extract the instance and global indices.
110  if (extractIndices) {
111  auto instIndexPtr = rewriter.create<LLVM::GEPOp>(
112  loc, voidPtrTy, sigTy, signal, ArrayRef<LLVM::GEPArg>({0, 2}));
113  result.push_back(rewriter.create<LLVM::LoadOp>(loc, i64Ty, instIndexPtr));
114 
115  auto globalIndexPtr = rewriter.create<LLVM::GEPOp>(
116  loc, voidPtrTy, sigTy, signal, ArrayRef<LLVM::GEPArg>({0, 3}));
117  result.push_back(rewriter.create<LLVM::LoadOp>(loc, i64Ty, globalIndexPtr));
118  }
119 
120  return result;
121 }
122 
123 /// Create a subsignal struct.
124 static Value createSubSig(LLVM::LLVMDialect *dialect,
125  ConversionPatternRewriter &rewriter, Location loc,
126  std::vector<Value> originDetail, Value newPtr,
127  Value newOffset) {
128  auto i32Ty = IntegerType::get(dialect->getContext(), 32);
129  auto sigTy = getLLVMSigType(dialect);
130 
131  // Create signal struct.
132  auto sigUndef = rewriter.create<LLVM::UndefOp>(loc, sigTy);
133  auto storeSubPtr =
134  rewriter.create<LLVM::InsertValueOp>(loc, sigUndef, newPtr, 0);
135  auto storeSubOffset =
136  rewriter.create<LLVM::InsertValueOp>(loc, storeSubPtr, newOffset, 1);
137  auto storeSubInstIndex = rewriter.create<LLVM::InsertValueOp>(
138  loc, storeSubOffset, originDetail[2], 2);
139  auto storeSubGlobalIndex = rewriter.create<LLVM::InsertValueOp>(
140  loc, storeSubInstIndex, originDetail[3], 3);
141 
142  // Allocate and store the subsignal.
143  auto oneC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,
144  rewriter.getI32IntegerAttr(1));
145  auto allocaSubSig = rewriter.create<LLVM::AllocaOp>(
146  loc, LLVM::LLVMPointerType::get(dialect->getContext()), sigTy, oneC, 4);
147  rewriter.create<LLVM::StoreOp>(loc, storeSubGlobalIndex, allocaSubSig);
148 
149  return allocaSubSig;
150 }
151 
152 /// Returns true if the given value is passed as an argument to the destination
153 /// block of the given WaitOp.
154 static bool isWaitDestArg(WaitOp op, Value val) {
155  for (auto arg : op.getDestOps()) {
156  if (arg == val)
157  return true;
158  }
159  return false;
160 }
161 
162 // Returns true if the given operation is used as a destination argument in a
163 // WaitOp.
164 static bool isWaitDestArg(Operation *op) {
165  for (auto user : op->getUsers()) {
166  if (auto wait = dyn_cast<WaitOp>(user))
167  return isWaitDestArg(wait, op->getResult(0));
168  }
169  return false;
170 }
171 
172 /// Gather the types of values that are used outside of the block they're
173 /// defined in. An LLVMType structure containing those types, in order of
174 /// appearance, is returned.
175 static Type getProcPersistenceTy(LLVM::LLVMDialect *dialect,
176  const TypeConverter *converter, ProcOp &proc) {
177  SmallVector<Type, 3> types = SmallVector<Type, 3>();
178  proc.walk([&](Operation *op) -> void {
179  if (op->isUsedOutsideOfBlock(op->getBlock()) || isWaitDestArg(op)) {
180  auto ty = op->getResult(0).getType();
181  auto convertedTy = converter->convertType(ty);
182  types.push_back(convertedTy);
183  }
184  });
185 
186  // Also persist block arguments escaping their defining block.
187  for (auto &block : proc.getBlocks()) {
188  // Skip entry block (contains the function signature in its args).
189  if (block.isEntryBlock())
190  continue;
191 
192  for (auto arg : block.getArguments()) {
193  if (arg.isUsedOutsideOfBlock(&block)) {
194  types.push_back(converter->convertType(arg.getType()));
195  }
196  }
197  }
198 
199  return LLVM::LLVMStructType::getLiteral(dialect->getContext(), types);
200 }
201 
202 /// Insert a comparison block that either jumps to the trueDest block, if the
203 /// resume index mathces the current index, or to falseDest otherwise. If no
204 /// falseDest is provided, the next block is taken insead.
205 static void insertComparisonBlock(ConversionPatternRewriter &rewriter,
206  LLVM::LLVMDialect *dialect, Location loc,
207  Region *body, Value resumeIdx, int currIdx,
208  Block *trueDest, ValueRange trueDestArgs,
209  Block *falseDest = nullptr) {
210  auto i32Ty = IntegerType::get(dialect->getContext(), 32);
211  auto secondBlock = ++body->begin();
212  auto newBlock = rewriter.createBlock(body, secondBlock);
213  auto cmpIdx = rewriter.create<LLVM::ConstantOp>(
214  loc, i32Ty, rewriter.getI32IntegerAttr(currIdx));
215  auto cmpRes = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq,
216  resumeIdx, cmpIdx);
217 
218  // Default to jumping to the next block for the false case, if no explicit
219  // block is provided.
220  if (!falseDest)
221  falseDest = &*secondBlock;
222 
223  rewriter.create<LLVM::CondBrOp>(loc, cmpRes, trueDest, trueDestArgs,
224  falseDest, ValueRange());
225 
226  // Redirect the entry block terminator to the new comparison block.
227  auto entryTer = body->front().getTerminator();
228  entryTer->setSuccessor(newBlock, 0);
229 }
230 
231 /// Insert a GEP operation to the pointer of the i-th value in the process
232 /// persistence table.
233 static Value gepPersistenceState(LLVM::LLVMDialect *dialect, Location loc,
234  ConversionPatternRewriter &rewriter,
235  Type stateTy, int index, Value state) {
236  return rewriter.create<LLVM::GEPOp>(
237  loc, LLVM::LLVMPointerType::get(dialect->getContext()), stateTy, state,
238  ArrayRef<LLVM::GEPArg>({0, 3, index}));
239 }
240 
241 /// Persist a `Value` by storing it into the process persistence table, and
242 /// substituting the uses that escape the block the operation is defined in with
243 /// a load from the persistence table.
244 static void persistValue(LLVM::LLVMDialect *dialect, Location loc,
245  const TypeConverter *converter,
246  ConversionPatternRewriter &rewriter, Type stateTy,
247  int &i, Value state, Value persist) {
248  auto elemTy = stateTy.cast<LLVM::LLVMStructType>()
249  .getBody()[3]
250  .cast<LLVM::LLVMStructType>()
251  .getBody()[i];
252 
253  if (auto arg = persist.dyn_cast<BlockArgument>()) {
254  rewriter.setInsertionPointToStart(arg.getParentBlock());
255  } else {
256  rewriter.setInsertionPointAfter(persist.getDefiningOp());
257  }
258 
259  Value convPersist = converter->materializeTargetConversion(
260  rewriter, loc, converter->convertType(persist.getType()), {persist});
261 
262  auto gep0 = gepPersistenceState(dialect, loc, rewriter, stateTy, i, state);
263 
264  Value toStore;
265  if (auto ptr = persist.getType().dyn_cast<PtrType>()) {
266  // Unwrap the pointer and store it's value.
267  auto elemTy = converter->convertType(ptr.getUnderlyingType());
268  toStore = rewriter.create<LLVM::LoadOp>(loc, elemTy, convPersist);
269  } else if (persist.getType().isa<SigType>()) {
270  // Unwrap and store the signal struct.
271  toStore = rewriter.create<LLVM::LoadOp>(loc, getLLVMSigType(dialect),
272  convPersist);
273  } else {
274  // Store the value directly.
275  toStore = convPersist;
276  }
277 
278  rewriter.create<LLVM::StoreOp>(loc, toStore, gep0);
279 
280  // Load the value from the persistence table and substitute the original
281  // use with it, whenever it is in a different block.
282  for (auto &use : llvm::make_early_inc_range(persist.getUses())) {
283  auto user = use.getOwner();
284  if (persist.getType().isa<PtrType>() && user != toStore.getDefiningOp() &&
285  user != convPersist.getDefiningOp() &&
286  persist.getParentBlock() == user->getBlock()) {
287  // Redirect uses of the pointer in the same block to the pointer in the
288  // persistence state. This ensures that stores and loads all operate on
289  // the same value.
290  use.set(gep0);
291  } else if (persist.getParentBlock() != user->getBlock() ||
292  (isa<WaitOp>(user) &&
293  isWaitDestArg(cast<WaitOp>(user), persist))) {
294  // The destination args of a wait op have to be loaded in the entry block
295  // of the function, before jumping to the resume destination, so they can
296  // be passed as block arguments by the comparison block.
297  if (isa<WaitOp>(user) && isWaitDestArg(cast<WaitOp>(user), persist))
298  rewriter.setInsertionPoint(
299  user->getParentRegion()->front().getTerminator());
300  else
301  rewriter.setInsertionPointToStart(user->getBlock());
302 
303  auto gep1 =
304  gepPersistenceState(dialect, loc, rewriter, stateTy, i, state);
305  // Use the pointer in the state struct directly for pointer and signal
306  // types.
307  if (persist.getType().isa<PtrType, SigType>()) {
308  use.set(gep1);
309  } else {
310  auto load1 = rewriter.create<LLVM::LoadOp>(loc, elemTy, gep1);
311  // Load the value otherwise.
312  use.set(load1);
313  }
314  }
315  }
316  i++;
317 }
318 
319 /// Insert the blocks and operations needed to persist values across suspension,
320 /// as well as ones needed to resume execution at the right spot.
321 static void insertPersistence(const TypeConverter *converter,
322  ConversionPatternRewriter &rewriter,
323  LLVM::LLVMDialect *dialect, Location loc,
324  ProcOp &proc, Type &stateTy,
325  LLVM::LLVMFuncOp &converted,
326  Operation *splitEntryBefore) {
327  auto i32Ty = IntegerType::get(dialect->getContext(), 32);
328 
329  auto &firstBB = converted.getBody().front();
330 
331  // Split entry block such that all the operations contained in it in the
332  // original process appear after the comparison blocks.
333  auto splitFirst =
334  rewriter.splitBlock(&firstBB, splitEntryBefore->getIterator());
335 
336  // Insert dummy branch terminator at the new end of the function's entry
337  // block.
338  rewriter.setInsertionPointToEnd(&firstBB);
339  rewriter.create<LLVM::BrOp>(loc, ValueRange(), splitFirst);
340 
341  // Load the resume index from the process state argument.
342  rewriter.setInsertionPoint(firstBB.getTerminator());
343  auto gep = rewriter.create<LLVM::GEPOp>(
344  loc, LLVM::LLVMPointerType::get(dialect->getContext()), i32Ty,
345  converted.getArgument(1), ArrayRef<LLVM::GEPArg>({1}));
346 
347  auto larg = rewriter.create<LLVM::LoadOp>(loc, i32Ty, gep);
348 
349  auto body = &converted.getBody();
350 
351  // Insert an abort block as the last block.
352  auto abortBlock = rewriter.createBlock(body, body->end());
353  rewriter.create<LLVM::ReturnOp>(loc, ValueRange());
354 
355  // Redirect the entry block to a first comparison block. If on a first
356  // execution, jump to the new (splitted) entry block, else the process is in
357  // an illegal state and jump to the abort block.
358  insertComparisonBlock(rewriter, dialect, loc, body, larg, 0, splitFirst,
359  ValueRange(), abortBlock);
360 
361  // Keep track of the index in the presistence table of the operation we
362  // are currently processing.
363  int i = 0;
364  // Keep track of the current resume index for comparison blocks.
365  int waitInd = 0;
366 
367  // Insert operations required to persist values across process suspension.
368  converted.walk([&](Operation *op) -> void {
369  if ((op->isUsedOutsideOfBlock(op->getBlock()) || isWaitDestArg(op)) &&
370  op->getResult(0) != larg.getResult()) {
371  persistValue(dialect, loc, converter, rewriter, stateTy, i,
372  converted.getArgument(1), op->getResult(0));
373  }
374 
375  // Insert a comparison block for wait operations.
376  if (auto wait = dyn_cast<WaitOp>(op)) {
377  insertComparisonBlock(rewriter, dialect, loc, body, larg, ++waitInd,
378  wait.getDest(), wait.getDestOps());
379 
380  // Insert the resume index update at the wait operation location.
381  rewriter.setInsertionPoint(op);
382  auto procState = op->getParentOfType<LLVM::LLVMFuncOp>().getArgument(1);
383  auto resumeIdxC = rewriter.create<LLVM::ConstantOp>(
384  loc, i32Ty, rewriter.getI32IntegerAttr(waitInd));
385  auto resumeIdxPtr = rewriter.create<LLVM::GEPOp>(
386  loc, LLVM::LLVMPointerType::get(dialect->getContext()), i32Ty,
387  procState, ArrayRef<LLVM::GEPArg>({1}));
388  rewriter.create<LLVM::StoreOp>(op->getLoc(), resumeIdxC, resumeIdxPtr);
389  }
390  });
391 
392  // Also persist argument blocks escaping their defining block.
393  for (auto &block : converted.getBlocks()) {
394  // Skip entry block as it contains the function signature.
395  if (block.isEntryBlock())
396  continue;
397 
398  for (auto arg : block.getArguments()) {
399  if (arg.isUsedOutsideOfBlock(&block)) {
400  persistValue(dialect, loc, converter, rewriter, stateTy, i,
401  converted.getArgument(1), arg);
402  }
403  }
404  }
405 }
406 
407 /// Return a struct type of arrays containing one entry for each RegOp condition
408 /// that require more than one state of the trigger to infer it (i.e. `both`,
409 /// `rise` and `fall`).
410 static LLVM::LLVMStructType getRegStateTy(LLVM::LLVMDialect *dialect,
411  Operation *entity) {
412  SmallVector<Type, 4> types;
413  entity->walk([&](RegOp op) {
414  size_t count = 0;
415  for (size_t i = 0; i < op.getModes().size(); ++i) {
416  auto mode = op.getRegModeAt(i);
417  if (mode == RegMode::fall || mode == RegMode::rise ||
418  mode == RegMode::both)
419  ++count;
420  }
421  if (count > 0)
422  types.push_back(LLVM::LLVMArrayType::get(
423  IntegerType::get(dialect->getContext(), 1), count));
424  });
425  return LLVM::LLVMStructType::getLiteral(dialect->getContext(), types);
426 }
427 
428 /// Create a zext operation by one bit on the given value. This is useful when
429 /// passing unsigned indexes to a GEP instruction, which treats indexes as
430 /// signed values, to avoid unexpected "sign overflows".
431 static Value zextByOne(Location loc, ConversionPatternRewriter &rewriter,
432  Value value) {
433  auto valueTy = value.getType();
434  auto zextTy = IntegerType::get(valueTy.getContext(),
435  valueTy.getIntOrFloatBitWidth() + 1);
436  return rewriter.create<LLVM::ZExtOp>(loc, zextTy, value);
437 }
438 
439 /// Adjust the bithwidth of value to be the same as targetTy's bitwidth.
440 static Value adjustBitWidth(Location loc, ConversionPatternRewriter &rewriter,
441  Type targetTy, Value value) {
442  auto valueWidth = value.getType().getIntOrFloatBitWidth();
443  auto targetWidth = targetTy.getIntOrFloatBitWidth();
444 
445  if (valueWidth < targetWidth)
446  return rewriter.create<LLVM::ZExtOp>(loc, targetTy, value);
447 
448  if (valueWidth > targetWidth)
449  return rewriter.create<LLVM::TruncOp>(loc, targetTy, value);
450 
451  return value;
452 }
453 
454 static unsigned getIndexOfOperandResult(Operation *op, Value result) {
455  for (unsigned j = 0, e = op->getNumResults(); j < e; ++j) {
456  if (result == result.getDefiningOp()->getResult(j))
457  return j;
458  }
459  llvm_unreachable(
460  "no way to recurse to an operation that does not return any value");
461 }
462 
463 /// Recursively clone the init origin of a sig operation into the init function,
464 /// up to the initial constant value(s). This is required to clone the
465 /// initialization of array and struct signals, where the init operand cannot
466 /// originate from a constant operation.
467 static Value recursiveCloneInit(OpBuilder &initBuilder, IRMapping &mapping,
468  Value init) {
469  SmallVector<Value> clonedOperands;
470  Operation *initOp = init.getDefiningOp();
471 
472  // If we end up at a value that we get via BlockArgument or as a result of a
473  // llhd.prb op, return a nullptr to signal that something went wrong, because
474  // these cases are not supported.
475  if (!initOp || isa<llhd::PrbOp>(initOp))
476  return nullptr;
477 
478  for (size_t i = 0, e = initOp->getNumOperands(); i < e; ++i) {
479  Value operand = initOp->getOperand(i);
480 
481  // If we have some value that is used multiple times (e.g., broadcasted to
482  // an array) then don't emit the ops to create this value several times,
483  // but instead remember the cloned value and use it again.
484  if (auto memorizedOperand = mapping.lookupOrNull(operand)) {
485  clonedOperands.push_back(memorizedOperand);
486  continue;
487  }
488 
489  // Recursively follow operands.
490  Value clonedOperand = recursiveCloneInit(initBuilder, mapping, operand);
491  if (!clonedOperand)
492  return nullptr;
493 
494  mapping.map(operand, clonedOperand);
495  clonedOperands.push_back(clonedOperand);
496  }
497 
498  Operation *clone = initOp->clone();
499  clone->setOperands(clonedOperands);
500 
501  // If we have cloned an operation that returns several values, we have to
502  // find the result value of the cloned operation we want to return.
503  unsigned index = getIndexOfOperandResult(initOp, init);
504  return initBuilder.insert(clone)->getResult(index);
505 }
506 
507 /// Check if the given type is either of LLHD's ArrayType, StructType, or LLVM
508 /// array or struct type.
509 static bool isArrayOrStruct(Type type) {
510  return type.isa<LLVM::LLVMArrayType, LLVM::LLVMStructType, hw::ArrayType,
511  hw::StructType>();
512 }
513 
514 /// Shift an integer signal pointer to obtain a view of the underlying value as
515 /// if it was shifted.
516 static std::pair<Value, Value>
517 shiftIntegerSigPointer(Location loc, LLVM::LLVMDialect *dialect,
518  ConversionPatternRewriter &rewriter, Value pointer,
519  Value index) {
520  auto voidPtrTy = LLVM::LLVMPointerType::get(dialect->getContext());
521  auto i64Ty = IntegerType::get(dialect->getContext(), 64);
522 
523  auto ptrToInt = rewriter.create<LLVM::PtrToIntOp>(loc, i64Ty, pointer);
524  auto const8 = rewriter.create<LLVM::ConstantOp>(
525  loc, index.getType(), rewriter.getI64IntegerAttr(8));
526  auto ptrOffset = rewriter.create<LLVM::UDivOp>(loc, index, const8);
527  auto shiftedPtr = rewriter.create<LLVM::AddOp>(loc, ptrToInt, ptrOffset);
528  auto newPtr = rewriter.create<LLVM::IntToPtrOp>(loc, voidPtrTy, shiftedPtr);
529 
530  // Compute the new offset into the first byte.
531  auto bitOffset = rewriter.create<LLVM::URemOp>(loc, index, const8);
532 
533  return std::make_pair(newPtr, bitOffset);
534 }
535 
536 /// Shift the pointer of a structured-type (array or struct) signal, to change
537 /// its view as if the desired slice/element was extracted.
538 static Value shiftStructuredSigPointer(Location loc,
539  ConversionPatternRewriter &rewriter,
540  Type elemTy, Value pointer,
541  LLVM::GEPArg index) {
542  // TODO: Remove unused args
543  auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
544  return rewriter.create<LLVM::GEPOp>(loc, voidPtrTy, elemTy, pointer,
545  ArrayRef<LLVM::GEPArg>({0, index}));
546 }
547 
548 /// Shift the pointer of an array-typed signal, to change its view as if the
549 /// desired slice/element was extracted.
550 static Value shiftArraySigPointer(Location loc,
551  ConversionPatternRewriter &rewriter,
552  Type arrTy, Value pointer,
553  LLVM::GEPArg index) {
554  if (auto indexValue = dyn_cast<Value>(index))
555  index = zextByOne(loc, rewriter, indexValue);
556  return shiftStructuredSigPointer(loc, rewriter, arrTy, pointer, index);
557 }
558 
559 //===----------------------------------------------------------------------===//
560 // Type conversions
561 //===----------------------------------------------------------------------===//
562 
563 static Type convertSigType(SigType type, LLVMTypeConverter &converter) {
564  auto &context = converter.getContext();
565  // auto i64Ty = IntegerType::get(&context, 64);
566  auto voidPtrTy = LLVM::LLVMPointerType::get(&context);
567  // LLVM::LLVMStructType::getLiteral(&context, {voidPtrTy, i64Ty, i64Ty,
568  // i64Ty})
569  return voidPtrTy;
570 }
571 
572 static Type convertTimeType(TimeType type, LLVMTypeConverter &converter) {
573  auto i64Ty = IntegerType::get(&converter.getContext(), 64);
574  return LLVM::LLVMArrayType::get(i64Ty, 3);
575 }
576 
577 static Type convertPtrType(PtrType type, LLVMTypeConverter &converter) {
578  // converter.convertType(type.getUnderlyingType())
579  return LLVM::LLVMPointerType::get(type.getContext());
580 }
581 
582 //===----------------------------------------------------------------------===//
583 // Unit conversions
584 //===----------------------------------------------------------------------===//
585 
586 namespace {
587 /// Convert an `llhd.entity` entity to LLVM dialect. The result is an
588 /// `llvm.func` which takes a pointer to the global simulation state, a pointer
589 /// to the entity's local state, and a pointer to the instance's signal table as
590 /// arguments.
591 struct EntityOpConversion : public ConvertToLLVMPattern {
592  explicit EntityOpConversion(MLIRContext *ctx,
593  LLVMTypeConverter &typeConverter,
594  size_t &sigCounter, size_t &regCounter)
595  : ConvertToLLVMPattern(llhd::EntityOp::getOperationName(), ctx,
596  typeConverter),
597  sigCounter(sigCounter), regCounter(regCounter) {}
598 
599  LogicalResult
600  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
601  ConversionPatternRewriter &rewriter) const override {
602  // Get adapted operands.
603  EntityOpAdaptor transformed(operands);
604  // Get entity operation.
605  auto entityOp = cast<EntityOp>(op);
606 
607  // Collect used llvm types.
608  auto voidTy = getVoidType();
609  auto voidPtrTy = getVoidPtrType();
610  auto sigTy = getLLVMSigType(&getDialect());
611 
612  regCounter = 0;
613 
614  // Use an intermediate signature conversion to add the arguments for the
615  // state and signal table pointer arguments.
616  LLVMTypeConverter::SignatureConversion intermediate(
617  entityOp.getNumArguments());
618  // Add state and signal table arguments.
619  intermediate.addInputs(
620  std::array<Type, 3>({voidPtrTy, voidPtrTy, voidPtrTy}));
621  for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i)
622  intermediate.addInputs(i, voidTy);
623  rewriter.applySignatureConversion(&entityOp.getBody(), intermediate,
624  typeConverter);
625 
626  OpBuilder bodyBuilder =
627  OpBuilder::atBlockBegin(&entityOp.getBlocks().front());
628  LLVMTypeConverter::SignatureConversion final(
629  intermediate.getConvertedTypes().size());
630  final.addInputs(0, voidPtrTy);
631  final.addInputs(1, voidPtrTy);
632  final.addInputs(2, voidPtrTy);
633 
634  // The first n elements of the signal table represent the entity arguments,
635  // while the remaining elements represent the entity's owned signals.
636  sigCounter = entityOp.getNumArguments();
637  for (size_t i = 0; i < sigCounter; ++i) {
638  // Create gep operations from the signal table for each original argument.
639  auto gep = bodyBuilder.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, sigTy,
640  entityOp.getArgument(2),
641  LLVM::GEPArg(i));
642  // Remap i-th original argument to the gep'd signal pointer.
643  final.remapInput(i + 3, gep.getResult());
644  }
645 
646  rewriter.applySignatureConversion(&entityOp.getBody(), final,
647  typeConverter);
648 
649  // Get the converted entity signature.
650  auto funcTy =
651  LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy});
652 
653  // Create the a new llvm function to house the lowered entity.
654  auto llvmFunc = rewriter.create<LLVM::LLVMFuncOp>(
655  op->getLoc(), entityOp.getName(), funcTy);
656 
657  // Add a return to the entity for later inclusion into the LLVM function.
658  rewriter.setInsertionPointToEnd(&entityOp.getBlocks().front());
659  rewriter.create<LLVM::ReturnOp>(op->getLoc(), ValueRange{});
660 
661  // Inline the entity region in the new llvm function.
662  rewriter.inlineRegionBefore(entityOp.getBody(), llvmFunc.getBody(),
663  llvmFunc.end());
664 
665  // Erase the original operation.
666  rewriter.eraseOp(op);
667 
668  return success();
669  }
670 
671 private:
672  size_t &sigCounter;
673  size_t &regCounter;
674 };
675 } // namespace
676 
677 namespace {
678 /// Convert an `llhd.proc` operation to LLVM dialect. This inserts the required
679 /// logic to resume execution after an `llhd.wait` operation, as well as state
680 /// keeping for values that need to persist across suspension.
681 struct ProcOpConversion : public ConvertToLLVMPattern {
682  explicit ProcOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
683  : ConvertToLLVMPattern(ProcOp::getOperationName(), ctx, typeConverter) {}
684 
685  LogicalResult
686  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
687  ConversionPatternRewriter &rewriter) const override {
688  auto procOp = cast<ProcOp>(op);
689 
690  // Get adapted operands.
691  ProcOpAdaptor transformed(operands);
692 
693  // Collect used llvm types.
694  auto voidTy = getVoidType();
695  auto voidPtrTy = getVoidPtrType();
696  auto i32Ty = IntegerType::get(rewriter.getContext(), 32);
697  auto stateTy = LLVM::LLVMStructType::getLiteral(
698  rewriter.getContext(),
699  {/*currentInstance=*/i32Ty, /*resumeIndex=*/i32Ty,
700  /*senseFlags=*/voidPtrTy /*senseTableTy*/,
701  /*persistence=*/
702  getProcPersistenceTy(&getDialect(), typeConverter, procOp)});
703  auto sigTy = getLLVMSigType(&getDialect());
704 
705  // Keep track of the original first operation of the process, to know where
706  // to split the first block to insert comparison blocks.
707  auto &firstOp = op->getRegion(0).front().front();
708 
709  // Have an intermediate signature conversion to add the arguments for the
710  // state, process-specific state and signal table.
711  LLVMTypeConverter::SignatureConversion intermediate(
712  procOp.getNumArguments());
713  // Add state, process state table and signal table arguments.
714  std::array<Type, 3> procArgTys({voidPtrTy, voidPtrTy, voidPtrTy});
715  intermediate.addInputs(procArgTys);
716  for (size_t i = 0, e = procOp.getNumArguments(); i < e; ++i)
717  intermediate.addInputs(i, voidTy);
718  rewriter.applySignatureConversion(&procOp.getBody(), intermediate,
719  typeConverter);
720 
721  // Get the final signature conversion.
722  OpBuilder bodyBuilder =
723  OpBuilder::atBlockBegin(&procOp.getBlocks().front());
724  LLVMTypeConverter::SignatureConversion final(
725  intermediate.getConvertedTypes().size());
726  final.addInputs(0, voidPtrTy);
727  final.addInputs(1, voidPtrTy);
728  final.addInputs(2, voidPtrTy);
729 
730  for (size_t i = 0, e = procOp.getNumArguments(); i < e; ++i) {
731  // Create gep operations from the signal table for each original argument.
732  auto gep = bodyBuilder.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, sigTy,
733  procOp.getArgument(2),
734  LLVM::GEPArg(i));
735 
736  // Remap the i-th original argument to the gep'd value.
737  final.remapInput(i + 3, gep.getResult());
738  }
739 
740  // Get the converted process signature.
741  auto funcTy =
742  LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy});
743  // Create a new llvm function to house the lowered process.
744  auto llvmFunc = rewriter.create<LLVM::LLVMFuncOp>(op->getLoc(),
745  procOp.getName(), funcTy);
746  llvmFunc->setAttr("llhd.argument_count",
747  rewriter.getI32IntegerAttr(procOp.getNumArguments()));
748 
749  // Inline the process region in the new llvm function.
750  rewriter.inlineRegionBefore(procOp.getBody(), llvmFunc.getBody(),
751  llvmFunc.end());
752 
753  insertPersistence(typeConverter, rewriter, &getDialect(), op->getLoc(),
754  procOp, stateTy, llvmFunc, &firstOp);
755 
756  // Convert the block argument types after inserting the persistence, as this
757  // would otherwise interfere with the persistence generation.
758  if (failed(rewriter.convertRegionTypes(&llvmFunc.getBody(), *typeConverter,
759  &final))) {
760  return failure();
761  }
762 
763  rewriter.eraseOp(op);
764 
765  return success();
766  }
767 };
768 } // namespace
769 
770 namespace {
771 /// Convert an `llhd.halt` operation to LLVM dialect. This zeroes out all the
772 /// senses and returns, effectively making the process unable to be invoked
773 /// again.
774 struct HaltOpConversion : public ConvertToLLVMPattern {
775  explicit HaltOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
776  : ConvertToLLVMPattern(HaltOp::getOperationName(), ctx, typeConverter) {}
777 
778  LogicalResult
779  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
780  ConversionPatternRewriter &rewriter) const override {
781  auto i1Ty = IntegerType::get(rewriter.getContext(), 1);
782  auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
783  auto llvmFunc = op->getParentOfType<LLVM::LLVMFuncOp>();
784  auto procState = llvmFunc.getArgument(1);
785 
786  // Get senses ptr from the process state argument.
787  auto sensePtrGep =
788  rewriter.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, voidPtrTy,
789  procState, ArrayRef<LLVM::GEPArg>({2}));
790  auto sensePtr =
791  rewriter.create<LLVM::LoadOp>(op->getLoc(), voidPtrTy, sensePtrGep);
792 
793  // Zero out all the senses flags.
794  unsigned numSenseEntries =
795  llvmFunc->getAttrOfType<IntegerAttr>("llhd.argument_count")
796  .getValue()
797  .getZExtValue();
798  auto zeroB = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i1Ty,
799  rewriter.getBoolAttr(false));
800  for (unsigned i = 0; i < numSenseEntries; ++i) {
801  auto senseElemPtr = rewriter.create<LLVM::GEPOp>(
802  op->getLoc(), voidPtrTy, i1Ty, sensePtr, ArrayRef<LLVM::GEPArg>({i}));
803  rewriter.create<LLVM::StoreOp>(op->getLoc(), zeroB, senseElemPtr);
804  }
805 
806  rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, ValueRange());
807  return success();
808  }
809 };
810 } // namespace
811 
812 namespace {
813 /// Convert an `llhd.wait` operation to LLVM dialect. This sets the current
814 /// resume point, sets the observed senses (if present) and schedules the timed
815 /// wake up (if present).
816 struct WaitOpConversion : public ConvertToLLVMPattern {
817  explicit WaitOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
818  : ConvertToLLVMPattern(WaitOp::getOperationName(), ctx, typeConverter) {}
819 
820  LogicalResult
821  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
822  ConversionPatternRewriter &rewriter) const override {
823  auto waitOp = cast<WaitOp>(op);
824  WaitOpAdaptor transformed(operands, op->getAttrDictionary());
825  auto llvmFunc = op->getParentOfType<LLVM::LLVMFuncOp>();
826 
827  auto voidTy = getVoidType();
828  auto voidPtrTy = getVoidPtrType();
829  auto i1Ty = IntegerType::get(rewriter.getContext(), 1);
830  auto i64Ty = IntegerType::get(rewriter.getContext(), 64);
831 
832  // Get the llhdSuspend runtime function.
833  auto llhdSuspendTy = LLVM::LLVMFunctionType::get(
834  voidTy, {voidPtrTy, voidPtrTy, i64Ty, i64Ty, i64Ty});
835  auto module = op->getParentOfType<ModuleOp>();
836  auto llhdSuspendFunc = getOrInsertFunction(module, rewriter, op->getLoc(),
837  "llhdSuspend", llhdSuspendTy);
838 
839  auto statePtr = llvmFunc.getArgument(0);
840  auto procState = llvmFunc.getArgument(1);
841 
842  // Get senses ptr.
843  auto sensePtrGep =
844  rewriter.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, voidPtrTy,
845  procState, ArrayRef<LLVM::GEPArg>({2}));
846  auto sensePtr =
847  rewriter.create<LLVM::LoadOp>(op->getLoc(), voidPtrTy, sensePtrGep);
848 
849  // Reset sense table, if not all signals are observed.
850  unsigned numSenseEntries =
851  llvmFunc->getAttrOfType<IntegerAttr>("llhd.argument_count")
852  .getValue()
853  .getZExtValue();
854  if (waitOp.getObs().size() < numSenseEntries) {
855  auto zeroB = rewriter.create<LLVM::ConstantOp>(
856  op->getLoc(), i1Ty, rewriter.getBoolAttr(false));
857  for (size_t i = 0; i < numSenseEntries; ++i) {
858  auto senseElemPtr =
859  rewriter.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, i1Ty,
860  sensePtr, ArrayRef<LLVM::GEPArg>({i}));
861  rewriter.create<LLVM::StoreOp>(op->getLoc(), zeroB, senseElemPtr);
862  }
863  }
864 
865  // Set sense flags for observed signals.
866  for (auto observed : transformed.getObs()) {
867  auto instIndexPtr =
868  rewriter.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, i64Ty, observed,
869  ArrayRef<LLVM::GEPArg>({2}));
870  auto instIndex =
871  rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, instIndexPtr)
872  .getResult();
873  auto oneB = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i1Ty,
874  rewriter.getBoolAttr(true));
875  auto senseElementPtr =
876  rewriter.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, i1Ty, sensePtr,
877  ArrayRef<LLVM::GEPArg>({instIndex}));
878  rewriter.create<LLVM::StoreOp>(op->getLoc(), oneB, senseElementPtr);
879  }
880 
881  // Update and store the new resume index in the process state.
882  // Spawn scheduled event, if present.
883  if (waitOp.getTime()) {
884  auto realTime = rewriter.create<LLVM::ExtractValueOp>(
885  op->getLoc(), transformed.getTime(), 0);
886  auto delta = rewriter.create<LLVM::ExtractValueOp>(
887  op->getLoc(), transformed.getTime(), 1);
888  auto eps = rewriter.create<LLVM::ExtractValueOp>(
889  op->getLoc(), transformed.getTime(), 2);
890 
891  std::array<Value, 5> args({statePtr, procState, realTime, delta, eps});
892  rewriter.create<LLVM::CallOp>(op->getLoc(), std::nullopt,
893  SymbolRefAttr::get(llhdSuspendFunc), args);
894  }
895 
896  rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, ValueRange());
897  return success();
898  }
899 };
900 } // namespace
901 
902 namespace {
903 /// Lower an llhd.inst operation to LLVM dialect. This generates malloc calls
904 /// and allocSignal calls (to store the pointer into the state) for each signal
905 /// in the instantiated entity.
906 struct InstOpConversion : public ConvertToLLVMPattern {
907  explicit InstOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
908  : ConvertToLLVMPattern(InstOp::getOperationName(), ctx, typeConverter) {}
909 
910  LogicalResult
911  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
912  ConversionPatternRewriter &rewriter) const override {
913  // Get the inst operation.
914  auto instOp = cast<InstOp>(op);
915  // Get the parent module.
916  auto module = op->getParentOfType<ModuleOp>();
917  auto entity = op->getParentOfType<EntityOp>();
918 
919  auto voidTy = getVoidType();
920  auto voidPtrTy = getVoidPtrType();
921  auto i1Ty = IntegerType::get(rewriter.getContext(), 1);
922  auto i32Ty = IntegerType::get(rewriter.getContext(), 32);
923  auto i64Ty = IntegerType::get(rewriter.getContext(), 64);
924 
925  // Init function signature: (i8* %state) -> void.
926  auto initFuncTy = LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy});
927  auto initFunc =
928  getOrInsertFunction(module, rewriter, op->getLoc(), "llhd_init",
929  initFuncTy, /*insertBodyAndTerminator=*/true);
930 
931  // Get or insert the malloc function definition.
932  // Malloc function signature: (i64 %size) -> i8* %pointer.
933  auto mallocSigFuncTy = LLVM::LLVMFunctionType::get(voidPtrTy, {i64Ty});
934  auto mallFunc = getOrInsertFunction(module, rewriter, op->getLoc(),
935  "malloc", mallocSigFuncTy);
936 
937  // Get or insert the allocSignal library call definition.
938  // allocSignal function signature: (i8* %state, i8* %sig_name, i8*
939  // %sig_owner, i32 %value) -> i32 %sig_index.
940  auto allocSigFuncTy = LLVM::LLVMFunctionType::get(
941  i32Ty, {voidPtrTy, i32Ty, voidPtrTy, voidPtrTy, i64Ty});
942  auto sigFunc = getOrInsertFunction(module, rewriter, op->getLoc(),
943  "allocSignal", allocSigFuncTy);
944 
945  // Add information about the elements of an array signal to the state.
946  // Signature: (i8* state, i32 signalIndex, i32 size, i32 numElements) ->
947  // void
948  auto addSigArrElemFuncTy =
949  LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, i32Ty, i32Ty, i32Ty});
950  auto addSigElemFunc =
951  getOrInsertFunction(module, rewriter, op->getLoc(),
952  "addSigArrayElements", addSigArrElemFuncTy);
953 
954  // Add information about one element of a struct signal to the state.
955  // Signature: (i8* state, i32 signalIndex, i32 offset, i32 size) -> void
956  auto addSigStructElemFuncTy =
957  LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, i32Ty, i32Ty, i32Ty});
958  auto addSigStructFunc =
959  getOrInsertFunction(module, rewriter, op->getLoc(),
960  "addSigStructElement", addSigStructElemFuncTy);
961 
962  // Get or insert allocProc library call definition.
963  auto allocProcFuncTy =
964  LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy});
965  auto allocProcFunc = getOrInsertFunction(module, rewriter, op->getLoc(),
966  "allocProc", allocProcFuncTy);
967 
968  // Get or insert allocEntity library call definition.
969  auto allocEntityFuncTy =
970  LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy});
971  auto allocEntityFunc = getOrInsertFunction(
972  module, rewriter, op->getLoc(), "allocEntity", allocEntityFuncTy);
973 
974  Value initStatePtr = initFunc.getArgument(0);
975 
976  // Get a builder for the init function.
977  OpBuilder initBuilder =
978  OpBuilder::atBlockTerminator(&initFunc.getBody().getBlocks().front());
979 
980  // Use the instance name to retrieve the instance from the state.
981  auto ownerName = entity.getName().str() + "." + instOp.getName().str();
982 
983  // Get or create owner name string
984  Value owner;
985  auto parentSym =
986  module.lookupSymbol<LLVM::GlobalOp>("instance." + ownerName);
987  if (!parentSym) {
988  owner = LLVM::createGlobalString(
989  op->getLoc(), initBuilder, "instance." + ownerName, ownerName + '\0',
990  LLVM::Linkage::Internal);
991  parentSym = module.lookupSymbol<LLVM::GlobalOp>("instance." + ownerName);
992  } else {
993  owner =
994  getGlobalString(op->getLoc(), initBuilder, typeConverter, parentSym);
995  }
996 
997  // Handle entity instantiation.
998  if (auto child = module.lookupSymbol<EntityOp>(instOp.getCallee())) {
999  auto regStateTy = getRegStateTy(&getDialect(), child.getOperation());
1000 
1001  // Get reg state size.
1002  auto regNull = initBuilder.create<LLVM::ZeroOp>(op->getLoc(), voidPtrTy);
1003  auto regGep =
1004  initBuilder.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy, regStateTy,
1005  regNull, ArrayRef<LLVM::GEPArg>({1}));
1006  auto regSize =
1007  initBuilder.create<LLVM::PtrToIntOp>(op->getLoc(), i64Ty, regGep);
1008 
1009  // Malloc reg state.
1010  auto regMall = initBuilder
1011  .create<LLVM::CallOp>(op->getLoc(), voidPtrTy,
1012  SymbolRefAttr::get(mallFunc),
1013  ArrayRef<Value>({regSize}))
1014  .getResult();
1015  auto zeroB = initBuilder.create<LLVM::ConstantOp>(
1016  op->getLoc(), i1Ty, rewriter.getBoolAttr(false));
1017 
1018  // Zero-initialize reg state entries.
1019  for (size_t i = 0,
1020  e = regStateTy.cast<LLVM::LLVMStructType>().getBody().size();
1021  i < e; ++i) {
1022  size_t f = regStateTy.cast<LLVM::LLVMStructType>()
1023  .getBody()[i]
1024  .cast<LLVM::LLVMArrayType>()
1025  .getNumElements();
1026  for (size_t j = 0; j < f; ++j) {
1027  auto regGep = initBuilder.create<LLVM::GEPOp>(
1028  op->getLoc(), voidPtrTy, regStateTy, regMall,
1029  ArrayRef<LLVM::GEPArg>({0, i, j}));
1030  initBuilder.create<LLVM::StoreOp>(op->getLoc(), zeroB, regGep);
1031  }
1032  }
1033 
1034  // Add reg state pointer to global state.
1035  initBuilder.create<LLVM::CallOp>(
1036  op->getLoc(), std::nullopt, SymbolRefAttr::get(allocEntityFunc),
1037  ArrayRef<Value>({initStatePtr, owner, regMall}));
1038 
1039  // Index of the signal in the entity's signal table.
1040  int initCounter = 0;
1041  // Walk over the entity and generate mallocs for each one of its signals.
1042  WalkResult sigWalkResult = child.walk([&](SigOp op) -> WalkResult {
1043  // if (auto sigOp = dyn_cast<SigOp>(op)) {
1044  auto underlyingTy = typeConverter->convertType(op.getInit().getType());
1045  // Get index constant of the signal in the entity's signal table.
1046  auto indexConst = initBuilder.create<LLVM::ConstantOp>(
1047  op.getLoc(), i32Ty, rewriter.getI32IntegerAttr(initCounter));
1048  initCounter++;
1049 
1050  // Clone and insert the operation that defines the signal's init
1051  // operand (assmued to be a constant/array op)
1052  IRMapping mapping;
1053  Value initDef = recursiveCloneInit(initBuilder, mapping, op.getInit());
1054 
1055  if (!initDef)
1056  return WalkResult::interrupt();
1057 
1058  Value initDefCast = typeConverter->materializeTargetConversion(
1059  initBuilder, initDef.getLoc(),
1060  typeConverter->convertType(initDef.getType()), initDef);
1061 
1062  // Compute the required space to malloc.
1063  auto twoC = initBuilder.create<LLVM::ConstantOp>(
1064  op.getLoc(), i64Ty, rewriter.getI32IntegerAttr(2));
1065  auto nullPtr = initBuilder.create<LLVM::ZeroOp>(op.getLoc(), voidPtrTy);
1066  auto sizeGep = initBuilder.create<LLVM::GEPOp>(
1067  op.getLoc(), voidPtrTy, underlyingTy, nullPtr,
1068  ArrayRef<LLVM::GEPArg>({1}));
1069  auto size =
1070  initBuilder.create<LLVM::PtrToIntOp>(op.getLoc(), i64Ty, sizeGep);
1071  // Malloc double the required space to make sure signal
1072  // shifts do not segfault.
1073  auto mallocSize =
1074  initBuilder.create<LLVM::MulOp>(op.getLoc(), i64Ty, size, twoC);
1075  std::array<Value, 1> margs({mallocSize});
1076  auto mall =
1077  initBuilder
1078  .create<LLVM::CallOp>(op.getLoc(), voidPtrTy,
1079  SymbolRefAttr::get(mallFunc), margs)
1080  .getResult();
1081 
1082  // Store the initial value.
1083  initBuilder.create<LLVM::StoreOp>(op.getLoc(), initDefCast, mall);
1084 
1085  // Get the amount of bytes required to represent an integer underlying
1086  // type. Use the whole size of the type if not an integer.
1087  Value passSize;
1088  if (auto intTy = underlyingTy.dyn_cast<IntegerType>()) {
1089  auto byteWidth = llvm::divideCeil(intTy.getWidth(), 8);
1090  passSize = initBuilder.create<LLVM::ConstantOp>(
1091  op.getLoc(), i64Ty, rewriter.getI64IntegerAttr(byteWidth));
1092  } else {
1093  passSize = size;
1094  }
1095 
1096  std::array<Value, 5> args(
1097  {initStatePtr, indexConst, owner, mall, passSize});
1098  auto sigIndex =
1099  initBuilder
1100  .create<LLVM::CallOp>(op.getLoc(), i32Ty,
1101  SymbolRefAttr::get(sigFunc), args)
1102  .getResult();
1103 
1104  // Add structured underlying type information.
1105  if (auto arrayTy = underlyingTy.dyn_cast<LLVM::LLVMArrayType>()) {
1106  auto numElements = initBuilder.create<LLVM::ConstantOp>(
1107  op.getLoc(), i32Ty,
1108  rewriter.getI32IntegerAttr(arrayTy.getNumElements()));
1109 
1110  // Get element size.
1111  auto null = initBuilder.create<LLVM::ZeroOp>(op.getLoc(), voidPtrTy);
1112  auto gepFirst = initBuilder.create<LLVM::GEPOp>(
1113  op.getLoc(), voidPtrTy, arrayTy, null,
1114  ArrayRef<LLVM::GEPArg>({0, 1}));
1115  auto toInt = initBuilder.create<LLVM::PtrToIntOp>(op.getLoc(), i32Ty,
1116  gepFirst);
1117 
1118  // Add information to the state.
1119  initBuilder.create<LLVM::CallOp>(
1120  op.getLoc(), std::nullopt, SymbolRefAttr::get(addSigElemFunc),
1121  ArrayRef<Value>({initStatePtr, sigIndex, toInt, numElements}));
1122  } else if (auto structTy =
1123  underlyingTy.dyn_cast<LLVM::LLVMStructType>()) {
1124  auto null = initBuilder.create<LLVM::ZeroOp>(op.getLoc(), voidPtrTy);
1125  for (size_t i = 0, e = structTy.getBody().size(); i < e; ++i) {
1126  // Get pointer offset.
1127  auto gepElem = initBuilder.create<LLVM::GEPOp>(
1128  op.getLoc(), voidPtrTy, structTy, null,
1129  ArrayRef<LLVM::GEPArg>({0, i}));
1130  auto elemToInt = initBuilder.create<LLVM::PtrToIntOp>(
1131  op.getLoc(), i32Ty, gepElem);
1132 
1133  // Get element size.
1134  auto elemNull =
1135  initBuilder.create<LLVM::ZeroOp>(op.getLoc(), voidPtrTy);
1136  auto gepElemSize = initBuilder.create<LLVM::GEPOp>(
1137  op.getLoc(), voidPtrTy, structTy.getBody()[i], elemNull,
1138  ArrayRef<LLVM::GEPArg>({1}));
1139  auto elemSizeToInt = initBuilder.create<LLVM::PtrToIntOp>(
1140  op.getLoc(), i32Ty, gepElemSize);
1141 
1142  // Add information to the state.
1143  initBuilder.create<LLVM::CallOp>(
1144  op.getLoc(), std::nullopt, SymbolRefAttr::get(addSigStructFunc),
1145  ArrayRef<Value>(
1146  {initStatePtr, sigIndex, elemToInt, elemSizeToInt}));
1147  }
1148  }
1149  return WalkResult::advance();
1150  });
1151 
1152  if (sigWalkResult.wasInterrupted())
1153  return failure();
1154 
1155  } else if (auto proc = module.lookupSymbol<ProcOp>(instOp.getCallee())) {
1156  // Handle process instantiation.
1157  auto sensesTy = LLVM::LLVMArrayType::get(i1Ty, proc.getNumArguments());
1158  auto procStateTy = LLVM::LLVMStructType::getLiteral(
1159  rewriter.getContext(),
1160  {i32Ty, i32Ty, voidPtrTy /*ptr(sensesTy)*/,
1161  getProcPersistenceTy(&getDialect(), typeConverter, proc)});
1162 
1163  auto zeroC = initBuilder.create<LLVM::ConstantOp>(
1164  op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(0));
1165 
1166  // Malloc space for the process state.
1167  auto procStateNullPtr =
1168  initBuilder.create<LLVM::ZeroOp>(op->getLoc(), voidPtrTy);
1169  auto procStateGep = initBuilder.create<LLVM::GEPOp>(
1170  op->getLoc(), voidPtrTy, procStateTy, procStateNullPtr,
1171  ArrayRef<LLVM::GEPArg>({1}));
1172  auto procStateSize = initBuilder.create<LLVM::PtrToIntOp>(
1173  op->getLoc(), i64Ty, procStateGep);
1174  std::array<Value, 1> procStateMArgs({procStateSize});
1175  auto procStateMall = initBuilder
1176  .create<LLVM::CallOp>(
1177  op->getLoc(), voidPtrTy,
1178  SymbolRefAttr::get(mallFunc), procStateMArgs)
1179  .getResult();
1180 
1181  // Store the initial resume index.
1182  auto resumeGep = initBuilder.create<LLVM::GEPOp>(
1183  op->getLoc(), voidPtrTy, procStateTy, procStateMall,
1184  ArrayRef<LLVM::GEPArg>({0, 1}));
1185  initBuilder.create<LLVM::StoreOp>(op->getLoc(), zeroC, resumeGep);
1186 
1187  // Malloc space for the senses table.
1188  auto sensesNullPtr =
1189  initBuilder.create<LLVM::ZeroOp>(op->getLoc(), voidPtrTy);
1190  auto sensesGep = initBuilder.create<LLVM::GEPOp>(
1191  op->getLoc(), voidPtrTy, sensesTy, sensesNullPtr,
1192  ArrayRef<LLVM::GEPArg>({1}));
1193  auto sensesSize =
1194  initBuilder.create<LLVM::PtrToIntOp>(op->getLoc(), i64Ty, sensesGep);
1195  std::array<Value, 1> senseMArgs({sensesSize});
1196  auto sensesMall =
1197  initBuilder
1198  .create<LLVM::CallOp>(op->getLoc(), voidPtrTy,
1199  SymbolRefAttr::get(mallFunc), senseMArgs)
1200  .getResult();
1201 
1202  // Set all initial senses to 1.
1203  auto oneB = initBuilder.create<LLVM::ConstantOp>(
1204  op->getLoc(), i1Ty, rewriter.getBoolAttr(true));
1205  for (size_t i = 0, e = sensesTy.getNumElements(); i < e; ++i) {
1206  auto senseGep = initBuilder.create<LLVM::GEPOp>(
1207  op->getLoc(), voidPtrTy, i1Ty, sensesMall,
1208  ArrayRef<LLVM::GEPArg>({i}));
1209  initBuilder.create<LLVM::StoreOp>(op->getLoc(), oneB, senseGep);
1210  }
1211 
1212  // Store the senses pointer in the process state.
1213  auto procStateSensesPtr = initBuilder.create<LLVM::GEPOp>(
1214  op->getLoc(), voidPtrTy, procStateTy, procStateMall,
1215  ArrayRef<LLVM::GEPArg>({0, 2}));
1216  initBuilder.create<LLVM::StoreOp>(op->getLoc(), sensesMall,
1217  procStateSensesPtr);
1218 
1219  std::array<Value, 3> allocProcArgs({initStatePtr, owner, procStateMall});
1220  initBuilder.create<LLVM::CallOp>(op->getLoc(), std::nullopt,
1221  SymbolRefAttr::get(allocProcFunc),
1222  allocProcArgs);
1223  }
1224 
1225  rewriter.eraseOp(op);
1226  return success();
1227  }
1228 };
1229 } // namespace
1230 
1231 //===----------------------------------------------------------------------===//
1232 // Signal conversions
1233 //===----------------------------------------------------------------------===//
1234 
1235 namespace {
1236 /// Convert an `llhd.sig` operation to LLVM dialect. The i-th signal of an
1237 /// entity get's lowered to a load of the i-th element of the signal table,
1238 /// passed as an argument.
1239 struct SigOpConversion : public ConvertToLLVMPattern {
1240  explicit SigOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter,
1241  size_t &sigCounter)
1242  : ConvertToLLVMPattern(llhd::SigOp::getOperationName(), ctx,
1243  typeConverter),
1244  sigCounter(sigCounter) {}
1245 
1246  LogicalResult
1247  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1248  ConversionPatternRewriter &rewriter) const override {
1249  // Get the adapted opreands.
1250  SigOpAdaptor transformed(operands);
1251 
1252  // Collect the used llvm types.
1253  auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
1254  auto sigTy = getLLVMSigType(&getDialect());
1255 
1256  // Get the signal table pointer from the arguments.
1257  Value sigTablePtr = op->getParentOfType<LLVM::LLVMFuncOp>().getArgument(2);
1258 
1259  // Get the index in the signal table and increase counter.
1260  // Insert a gep to the signal index in the signal table argument.
1261  rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, voidPtrTy, sigTy, sigTablePtr,
1262  LLVM::GEPArg(sigCounter));
1263  ++sigCounter;
1264 
1265  return success();
1266  }
1267 
1268 private:
1269  size_t &sigCounter;
1270 };
1271 } // namespace
1272 
1273 namespace {
1274 /// Convert an `llhd.prb` operation to LLVM dialect. The result is a library
1275 /// call to the
1276 /// `@probe_signal` function. The signal details are then extracted and used to
1277 /// load the final probe value.
1278 struct PrbOpConversion : public ConvertToLLVMPattern {
1279  explicit PrbOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
1280  : ConvertToLLVMPattern(llhd::PrbOp::getOperationName(), ctx,
1281  typeConverter) {}
1282 
1283  LogicalResult
1284  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1285  ConversionPatternRewriter &rewriter) const override {
1286  // Get the adapted operands.
1287  PrbOpAdaptor transformed(operands);
1288  // Get the prb operation.
1289  auto prbOp = cast<PrbOp>(op);
1290 
1291  // Collect the used llvm types.
1292  auto resTy = prbOp.getType();
1293  auto finalTy = typeConverter->convertType(resTy);
1294 
1295  // Get the signal details from the signal struct.
1296  auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(),
1297  transformed.getSignal());
1298 
1299  if (resTy.isa<IntegerType>()) {
1300  // Get the amount of bytes to load. An extra byte is always loaded to
1301  // cover the case where a subsignal spans halfway in the last byte.
1302  int resWidth = resTy.getIntOrFloatBitWidth();
1303  int loadWidth = (llvm::divideCeil(resWidth, 8) + 1) * 8;
1304  auto loadTy = IntegerType::get(rewriter.getContext(), loadWidth);
1305 
1306  auto loadSig =
1307  rewriter.create<LLVM::LoadOp>(op->getLoc(), loadTy, sigDetail[0]);
1308 
1309  // Shift the loaded value by the offset and truncate to the final width.
1310  auto trOff = adjustBitWidth(op->getLoc(), rewriter, loadTy, sigDetail[1]);
1311  auto shifted =
1312  rewriter.create<LLVM::LShrOp>(op->getLoc(), loadTy, loadSig, trOff);
1313  rewriter.replaceOpWithNewOp<LLVM::TruncOp>(op, finalTy, shifted);
1314 
1315  return success();
1316  }
1317 
1318  if (resTy.isa<hw::ArrayType, hw::StructType>()) {
1319  rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, finalTy, sigDetail[0]);
1320 
1321  return success();
1322  }
1323 
1324  return failure();
1325  }
1326 };
1327 } // namespace
1328 
1329 namespace {
1330 /// Convert an `llhd.drv` operation to LLVM dialect. The result is a library
1331 /// call to the
1332 /// `@driveSignal` function, which declaration is inserted at the beginning of
1333 /// the module if missing. The required arguments are either generated or
1334 /// fetched.
1335 struct DrvOpConversion : public ConvertToLLVMPattern {
1336  explicit DrvOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
1337  : ConvertToLLVMPattern(llhd::DrvOp::getOperationName(), ctx,
1338  typeConverter) {}
1339 
1340  LogicalResult
1341  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1342  ConversionPatternRewriter &rewriter) const override {
1343  // Get the adapted operands.
1344  DrvOpAdaptor transformed(operands);
1345  // Get the drive operation.
1346  auto drvOp = cast<DrvOp>(op);
1347  // Get the parent module.
1348  auto module = op->getParentOfType<ModuleOp>();
1349 
1350  // Collect used llvm types.
1351  auto voidTy = getVoidType();
1352  auto voidPtrTy = getVoidPtrType();
1353  auto i1Ty = IntegerType::get(rewriter.getContext(), 1);
1354  auto i32Ty = IntegerType::get(rewriter.getContext(), 32);
1355  auto i64Ty = IntegerType::get(rewriter.getContext(), 64);
1356 
1357  // Get or insert the drive library call.
1358  auto drvFuncTy = LLVM::LLVMFunctionType::get(
1359  voidTy, {voidPtrTy, voidPtrTy, voidPtrTy, i64Ty, i64Ty, i64Ty, i64Ty});
1360  auto drvFunc = getOrInsertFunction(module, rewriter, op->getLoc(),
1361  "driveSignal", drvFuncTy);
1362 
1363  // Get the state pointer from the function arguments.
1364  Value statePtr = op->getParentOfType<LLVM::LLVMFuncOp>().getArgument(0);
1365 
1366  // Get signal width.
1367  Value sigWidth;
1368  auto underlyingTy = drvOp.getValue().getType();
1369  if (isArrayOrStruct(underlyingTy)) {
1370  auto underlyingTyConv = typeConverter->convertType(underlyingTy);
1371  auto eightC = rewriter.create<LLVM::ConstantOp>(
1372  op->getLoc(), i64Ty, rewriter.getI64IntegerAttr(8));
1373  auto nullPtr = rewriter.create<LLVM::ZeroOp>(op->getLoc(), voidPtrTy);
1374  auto gepOne = rewriter.create<LLVM::GEPOp>(op->getLoc(), voidPtrTy,
1375  underlyingTyConv, nullPtr,
1376  ArrayRef<LLVM::GEPArg>({1}));
1377  auto toInt =
1378  rewriter.create<LLVM::PtrToIntOp>(op->getLoc(), i64Ty, gepOne);
1379  sigWidth = rewriter.create<LLVM::MulOp>(op->getLoc(), toInt, eightC);
1380  } else {
1381  sigWidth = rewriter.create<LLVM::ConstantOp>(
1382  op->getLoc(), i64Ty,
1383  rewriter.getI64IntegerAttr(underlyingTy.getIntOrFloatBitWidth()));
1384  }
1385 
1386  // Insert enable comparison. Skip if the enable operand is 0.
1387  if (auto gate = drvOp.getEnable()) {
1388  auto block = op->getBlock();
1389  auto continueBlock =
1390  rewriter.splitBlock(rewriter.getInsertionBlock(), op->getIterator());
1391  auto drvBlock = rewriter.createBlock(continueBlock);
1392  rewriter.setInsertionPointToEnd(drvBlock);
1393  rewriter.create<LLVM::BrOp>(op->getLoc(), ValueRange(), continueBlock);
1394 
1395  rewriter.setInsertionPointToEnd(block);
1396  auto oneC = rewriter.create<LLVM::ConstantOp>(
1397  op->getLoc(), i1Ty, rewriter.getI16IntegerAttr(1));
1398  auto cmp = rewriter.create<LLVM::ICmpOp>(
1399  op->getLoc(), LLVM::ICmpPredicate::eq, transformed.getEnable(), oneC);
1400  rewriter.create<LLVM::CondBrOp>(op->getLoc(), cmp, drvBlock,
1401  continueBlock);
1402 
1403  rewriter.setInsertionPointToStart(drvBlock);
1404  }
1405 
1406  Type valTy = typeConverter->convertType(transformed.getValue().getType());
1407  Value castVal = typeConverter->materializeTargetConversion(
1408  rewriter, transformed.getValue().getLoc(), valTy,
1409  transformed.getValue());
1410 
1411  auto oneConst = rewriter.create<LLVM::ConstantOp>(
1412  op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(1));
1413 
1414  // This assumes that alloca does always allocate full bytes (round up to a
1415  // multiple of 8 bits).
1416  auto alloca = rewriter.create<LLVM::AllocaOp>(op->getLoc(), voidPtrTy,
1417  valTy, oneConst, 4);
1418  rewriter.create<LLVM::StoreOp>(op->getLoc(), castVal, alloca);
1419 
1420  // Get the time values.
1421  auto realTime = rewriter.create<LLVM::ExtractValueOp>(
1422  op->getLoc(), transformed.getTime(), 0);
1423  auto delta = rewriter.create<LLVM::ExtractValueOp>(
1424  op->getLoc(), transformed.getTime(), 1);
1425  auto eps = rewriter.create<LLVM::ExtractValueOp>(op->getLoc(),
1426  transformed.getTime(), 2);
1427 
1428  // Define the driveSignal library call arguments.
1429  std::array<Value, 7> args({statePtr, transformed.getSignal(), alloca,
1430  sigWidth, realTime, delta, eps});
1431  // Create the library call.
1432  rewriter.create<LLVM::CallOp>(op->getLoc(), std::nullopt,
1433  SymbolRefAttr::get(drvFunc), args);
1434 
1435  rewriter.eraseOp(op);
1436  return success();
1437  }
1438 };
1439 } // namespace
1440 
1441 namespace {
1442 /// Convert an `llhd.reg` operation to LLVM dialect. This generates a series of
1443 /// comparisons (blocks) that end up driving the signal with the arguments of
1444 /// the first matching trigger from the trigger list.
1445 struct RegOpConversion : public ConvertToLLVMPattern {
1446  explicit RegOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter,
1447  size_t &regCounter)
1448  : ConvertToLLVMPattern(RegOp::getOperationName(), ctx, typeConverter),
1449  regCounter(regCounter) {}
1450 
1451  LogicalResult
1452  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1453  ConversionPatternRewriter &rewriter) const override {
1454  auto regOp = cast<RegOp>(op);
1455  RegOpAdaptor transformed(operands, op->getAttrDictionary());
1456 
1457  auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
1458  auto i1Ty = IntegerType::get(rewriter.getContext(), 1);
1459  auto func = op->getParentOfType<LLVM::LLVMFuncOp>();
1460 
1461  // Retrieve and update previous trigger values for rising/falling edge
1462  // detection.
1463  size_t triggerIndex = 0;
1464  SmallVector<Value, 4> prevTriggers;
1465  for (int i = 0, e = regOp.getValues().size(); i < e; ++i) {
1466  auto mode = regOp.getRegModeAt(i);
1467  if (mode == RegMode::both || mode == RegMode::fall ||
1468  mode == RegMode::rise) {
1469  auto gep = rewriter.create<LLVM::GEPOp>(
1470  op->getLoc(), voidPtrTy, i1Ty, func.getArgument(1),
1471  ArrayRef<LLVM::GEPArg>({0, regCounter, triggerIndex++}));
1472  prevTriggers.push_back(
1473  rewriter.create<LLVM::LoadOp>(op->getLoc(), i1Ty, gep));
1474  rewriter.create<LLVM::StoreOp>(op->getLoc(),
1475  transformed.getTriggers()[i], gep);
1476  }
1477  }
1478 
1479  // Create blocks for drive and continue.
1480  auto block = op->getBlock();
1481  auto continueBlock = block->splitBlock(op);
1482 
1483  auto drvBlock = rewriter.createBlock(continueBlock);
1484  auto valArg = drvBlock->addArgument(transformed.getValues()[0].getType(),
1485  transformed.getValues()[0].getLoc());
1486  auto delayArg = drvBlock->addArgument(transformed.getDelays()[0].getType(),
1487  transformed.getDelays()[0].getLoc());
1488  auto gateArg = drvBlock->addArgument(i1Ty, rewriter.getUnknownLoc());
1489 
1490  // Create a drive with the block arguments.
1491  rewriter.setInsertionPointToStart(drvBlock);
1492  rewriter.create<DrvOp>(op->getLoc(), regOp.getSignal(), valArg, delayArg,
1493  gateArg);
1494  rewriter.create<LLVM::BrOp>(op->getLoc(), ValueRange(), continueBlock);
1495 
1496  int j = prevTriggers.size() - 1;
1497  // Create a comparison block for each of the reg tuples.
1498  for (int i = regOp.getValues().size() - 1, e = i; i >= 0; --i) {
1499  auto cmpBlock = rewriter.createBlock(block->getNextNode());
1500  rewriter.setInsertionPointToStart(cmpBlock);
1501 
1502  Value gate;
1503  if (regOp.hasGate(i)) {
1504  gate = regOp.getGateAt(i);
1505  } else {
1506  gate = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i1Ty,
1507  rewriter.getBoolAttr(true));
1508  }
1509 
1510  auto drvArgs = std::array<Value, 3>(
1511  {transformed.getValues()[i], transformed.getDelays()[i], gate});
1512 
1513  RegMode mode = regOp.getRegModeAt(i);
1514 
1515  // Create comparison constants for all modes other than both.
1516  Value rhs;
1517  if (mode == RegMode::low || mode == RegMode::fall) {
1518  rhs = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i1Ty,
1519  rewriter.getBoolAttr(false));
1520  } else if (mode == RegMode::high || mode == RegMode::rise) {
1521  rhs = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i1Ty,
1522  rewriter.getBoolAttr(true));
1523  }
1524 
1525  // Create comparison for non-both modes.
1526  Value comp;
1527  if (rhs)
1528  comp =
1529  rewriter.create<LLVM::ICmpOp>(op->getLoc(), LLVM::ICmpPredicate::eq,
1530  transformed.getTriggers()[i], rhs);
1531 
1532  // Create comparison for modes needing more than one state of the trigger.
1533  Value brCond;
1534  if (mode == RegMode::rise || mode == RegMode::fall ||
1535  mode == RegMode::both) {
1536 
1537  auto cmpPrev = rewriter.create<LLVM::ICmpOp>(
1538  op->getLoc(), LLVM::ICmpPredicate::ne, transformed.getTriggers()[i],
1539  prevTriggers[j--]);
1540  if (mode == RegMode::both)
1541  brCond = cmpPrev;
1542  else
1543  brCond =
1544  rewriter.create<LLVM::AndOp>(op->getLoc(), i1Ty, comp, cmpPrev);
1545  } else {
1546  brCond = comp;
1547  }
1548 
1549  Block *nextBlock;
1550  nextBlock = cmpBlock->getNextNode();
1551  // Don't go to next block for last comparison's false branch (skip the
1552  // drive block).
1553  if (i == e)
1554  nextBlock = continueBlock;
1555 
1556  rewriter.create<LLVM::CondBrOp>(op->getLoc(), brCond, drvBlock, drvArgs,
1557  nextBlock, ValueRange());
1558  }
1559 
1560  rewriter.setInsertionPointToEnd(block);
1561  rewriter.create<LLVM::BrOp>(op->getLoc(), ArrayRef<Value>(),
1562  block->getNextNode());
1563 
1564  rewriter.eraseOp(op);
1565 
1566  ++regCounter;
1567 
1568  return success();
1569  }
1570 
1571 private:
1572  size_t &regCounter;
1573 }; // namespace
1574 } // namespace
1575 
1576 //===----------------------------------------------------------------------===//
1577 // Value creation conversions
1578 //===----------------------------------------------------------------------===//
1579 
1580 namespace {
1581 /// Lower an LLHD constant operation to an equivalent LLVM dialect constant
1582 /// operation.
1583 struct ConstantTimeOpConversion : public ConvertToLLVMPattern {
1584  explicit ConstantTimeOpConversion(MLIRContext *ctx,
1585  LLVMTypeConverter &typeConverter)
1586  : ConvertToLLVMPattern(llhd::ConstantTimeOp::getOperationName(), ctx,
1587  typeConverter) {}
1588 
1589  LogicalResult
1590  matchAndRewrite(Operation *op, ArrayRef<Value> operand,
1591  ConversionPatternRewriter &rewriter) const override {
1592  // Get the ConstOp.
1593  auto constOp = cast<ConstantTimeOp>(op);
1594  // Get the constant's attribute.
1595  TimeAttr timeAttr = constOp.getValueAttr();
1596  // Handle the time const special case: create a new array containing the
1597  // three time values.
1598  auto timeTy = typeConverter->convertType(constOp.getResult().getType());
1599 
1600  // Convert real-time element to ps.
1601  llvm::StringMap<uint64_t> map = {
1602  {"s", 12}, {"ms", 9}, {"us", 6}, {"ns", 3}, {"ps", 0}};
1603  uint64_t adjusted =
1604  std::pow(10, map[timeAttr.getTimeUnit()]) * timeAttr.getTime();
1605 
1606  // Get sub-steps.
1607  uint64_t delta = timeAttr.getDelta();
1608  uint64_t eps = timeAttr.getEpsilon();
1609 
1610  // Create time constant.
1611  auto denseAttr =
1612  DenseElementsAttr::get(RankedTensorType::get(3, rewriter.getI64Type()),
1613  {adjusted, delta, eps});
1614  rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(op, timeTy, denseAttr);
1615  return success();
1616  }
1617 };
1618 } // namespace
1619 
1620 //===----------------------------------------------------------------------===//
1621 // Extraction operation conversions
1622 //===----------------------------------------------------------------------===//
1623 
1624 namespace {
1625 /// Convert a DynExtractSliceOp to LLVM dialect.
1626 struct SigArraySliceOpConversion
1627  : public ConvertOpToLLVMPattern<llhd::SigArraySliceOp> {
1628  using ConvertOpToLLVMPattern<llhd::SigArraySliceOp>::ConvertOpToLLVMPattern;
1629 
1630  LogicalResult
1631  matchAndRewrite(llhd::SigArraySliceOp op, OpAdaptor adaptor,
1632  ConversionPatternRewriter &rewriter) const override {
1633 
1634  Type llvmArrTy = typeConverter->convertType(op.getInputArrayType());
1635  Type inputTy = typeConverter->convertType(op.getInput().getType());
1636  Type lowIndexTy = typeConverter->convertType(op.getLowIndex().getType());
1637  Value castInput = typeConverter->materializeTargetConversion(
1638  rewriter, op->getLoc(), inputTy, op.getInput());
1639  Value castLowIndex = typeConverter->materializeTargetConversion(
1640  rewriter, op->getLoc(), lowIndexTy, op.getLowIndex());
1641 
1642  auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(),
1643  castInput, /*extractIndices=*/true);
1644 
1645  auto adjustedPtr = shiftArraySigPointer(op->getLoc(), rewriter, llvmArrTy,
1646  sigDetail[0], castLowIndex);
1647  rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(),
1648  sigDetail, adjustedPtr, sigDetail[1]));
1649  return success();
1650  }
1651 };
1652 } // namespace
1653 
1654 namespace {
1655 /// Convert a DynExtractSliceOp to LLVM dialect.
1656 struct SigExtractOpConversion
1657  : public ConvertOpToLLVMPattern<llhd::SigExtractOp> {
1658  using ConvertOpToLLVMPattern<llhd::SigExtractOp>::ConvertOpToLLVMPattern;
1659 
1660  LogicalResult
1661  matchAndRewrite(llhd::SigExtractOp op, OpAdaptor adaptor,
1662  ConversionPatternRewriter &rewriter) const override {
1663 
1664  Type inputTy = typeConverter->convertType(op.getInput().getType());
1665  Type lowBitTy = typeConverter->convertType(op.getLowBit().getType());
1666  Value castInput = typeConverter->materializeTargetConversion(
1667  rewriter, op->getLoc(), inputTy, op.getInput());
1668  Value castLowBit = typeConverter->materializeTargetConversion(
1669  rewriter, op->getLoc(), lowBitTy, op.getLowBit());
1670 
1671  auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(),
1672  castInput, /*extractIndices=*/true);
1673 
1674  auto zextStart = adjustBitWidth(op->getLoc(), rewriter,
1675  rewriter.getI64Type(), castLowBit);
1676  // Adjust the slice starting point by the signal's offset.
1677  auto adjustedStart =
1678  rewriter.create<LLVM::AddOp>(op->getLoc(), sigDetail[1], zextStart);
1679 
1680  auto adjusted = shiftIntegerSigPointer(
1681  op->getLoc(), &getDialect(), rewriter, sigDetail[0], adjustedStart);
1682  // Create a new subsignal with the new pointer and offset.
1683  rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(),
1684  sigDetail, adjusted.first,
1685  adjusted.second));
1686  return success();
1687  }
1688 };
1689 } // namespace
1690 
1691 namespace {
1692 /// Convert
1693 struct SigStructExtractOpConversion
1694  : public ConvertOpToLLVMPattern<llhd::SigStructExtractOp> {
1695  using ConvertOpToLLVMPattern<
1696  llhd::SigStructExtractOp>::ConvertOpToLLVMPattern;
1697 
1698  LogicalResult
1699  matchAndRewrite(llhd::SigStructExtractOp op, OpAdaptor adaptor,
1700  ConversionPatternRewriter &rewriter) const override {
1701 
1702  Type llvmStructTy = typeConverter->convertType(op.getStructType());
1703  Type inputTy = typeConverter->convertType(op.getInput().getType());
1704  Value castInput = typeConverter->materializeTargetConversion(
1705  rewriter, op->getLoc(), inputTy, op.getInput());
1706 
1707  std::vector<Value> sigDetail =
1708  getSignalDetail(rewriter, &getDialect(), op->getLoc(), castInput,
1709  /*extractIndices=*/true);
1710 
1711  uint32_t index = HWToLLVMEndianessConverter::llvmIndexOfStructField(
1712  op.getStructType(), op.getField());
1713 
1714  Value adjusted = shiftStructuredSigPointer(
1715  op->getLoc(), rewriter, llvmStructTy, sigDetail[0], index);
1716 
1717  rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(),
1718  sigDetail, adjusted, sigDetail[1]));
1719 
1720  return success();
1721  }
1722 };
1723 } // namespace
1724 
1725 namespace {
1726 /// Convert a DynExtractElementOp to LLVM dialect.
1727 struct SigArrayGetOpConversion
1728  : public ConvertOpToLLVMPattern<llhd::SigArrayGetOp> {
1729  using ConvertOpToLLVMPattern<llhd::SigArrayGetOp>::ConvertOpToLLVMPattern;
1730 
1731  LogicalResult
1732  matchAndRewrite(llhd::SigArrayGetOp op, OpAdaptor adaptor,
1733  ConversionPatternRewriter &rewriter) const override {
1734 
1735  auto llvmArrTy = typeConverter->convertType(op.getArrayType());
1736  Type inputTy = typeConverter->convertType(op.getInput().getType());
1737  Type indexTy = typeConverter->convertType(op.getIndex().getType());
1738  Value castInput = typeConverter->materializeTargetConversion(
1739  rewriter, op->getLoc(), inputTy, op.getInput());
1740  Value castIndex = typeConverter->materializeTargetConversion(
1741  rewriter, op->getLoc(), indexTy, op.getIndex());
1742 
1743  auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(),
1744  castInput, /*extractIndices=*/true);
1745 
1746  auto adjustedPtr = shiftArraySigPointer(op->getLoc(), rewriter, llvmArrTy,
1747  sigDetail[0], castIndex);
1748  rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(),
1749  sigDetail, adjustedPtr, sigDetail[1]));
1750 
1751  return success();
1752  }
1753 };
1754 } // namespace
1755 
1756 //===----------------------------------------------------------------------===//
1757 // Memory operations
1758 //===----------------------------------------------------------------------===//
1759 
1760 namespace {
1761 /// Lower a `llhd.var` operation to the LLVM dialect. This results in an alloca,
1762 /// followed by storing the initial value.
1763 struct VarOpConversion : ConvertToLLVMPattern {
1764  explicit VarOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
1765  : ConvertToLLVMPattern(VarOp::getOperationName(), ctx, typeConverter) {}
1766 
1767  LogicalResult
1768  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1769  ConversionPatternRewriter &rewriter) const override {
1770  VarOpAdaptor transformed(operands);
1771 
1772  auto i32Ty = IntegerType::get(rewriter.getContext(), 32);
1773  Type initTy = typeConverter->convertType(transformed.getInit().getType());
1774 
1775  auto oneC = rewriter.create<LLVM::ConstantOp>(
1776  op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(1));
1777  auto alloca = rewriter.create<LLVM::AllocaOp>(
1778  op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()), initTy,
1779  oneC, 4);
1780  rewriter.create<LLVM::StoreOp>(op->getLoc(), transformed.getInit(), alloca);
1781  rewriter.replaceOp(op, alloca.getResult());
1782  return success();
1783  }
1784 };
1785 } // namespace
1786 
1787 namespace {
1788 /// Convert an `llhd.store` operation to LLVM. This lowers the store
1789 /// one-to-one as an LLVM store, but with the operands flipped.
1790 struct StoreOpConversion : ConvertToLLVMPattern {
1791  explicit StoreOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)
1792  : ConvertToLLVMPattern(llhd::StoreOp::getOperationName(), ctx,
1793  typeConverter) {}
1794 
1795  LogicalResult
1796  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1797  ConversionPatternRewriter &rewriter) const override {
1798  llhd::StoreOpAdaptor transformed(operands);
1799 
1800  rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, transformed.getValue(),
1801  transformed.getPointer());
1802 
1803  return success();
1804  }
1805 };
1806 } // namespace
1807 
1809  OneToOneConvertToLLVMPattern<llhd::LoadOp, LLVM::LoadOp>;
1810 
1811 //===----------------------------------------------------------------------===//
1812 // Pass initialization
1813 //===----------------------------------------------------------------------===//
1814 
1815 namespace {
1816 struct LLHDToLLVMLoweringPass
1817  : public ConvertLLHDToLLVMBase<LLHDToLLVMLoweringPass> {
1818  void runOnOperation() override;
1819 };
1820 } // namespace
1821 
1822 void circt::populateLLHDToLLVMConversionPatterns(LLVMTypeConverter &converter,
1823  RewritePatternSet &patterns,
1824  size_t &sigCounter,
1825  size_t &regCounter) {
1826  MLIRContext *ctx = converter.getDialect()->getContext();
1827 
1828  // Value creation conversion patterns.
1829  patterns.add<ConstantTimeOpConversion>(ctx, converter);
1830 
1831  // Extract conversion patterns.
1832  patterns.add<SigExtractOpConversion, SigArraySliceOpConversion,
1833  SigArrayGetOpConversion, SigStructExtractOpConversion>(
1834  converter);
1835 
1836  // Unit conversion patterns.
1837  patterns.add<ProcOpConversion, WaitOpConversion, HaltOpConversion>(ctx,
1838  converter);
1839  patterns.add<EntityOpConversion>(ctx, converter, sigCounter, regCounter);
1840 
1841  // Signal conversion patterns.
1842  patterns.add<PrbOpConversion, DrvOpConversion>(ctx, converter);
1843  patterns.add<SigOpConversion>(ctx, converter, sigCounter);
1844  patterns.add<RegOpConversion>(ctx, converter, regCounter);
1845 
1846  // Memory conversion patterns.
1847  patterns.add<VarOpConversion, StoreOpConversion>(ctx, converter);
1848  patterns.add<LoadOpConversion>(converter);
1849 }
1850 
1851 void circt::populateLLHDToLLVMTypeConversions(LLVMTypeConverter &converter) {
1852  converter.addConversion(
1853  [&](SigType sig) { return convertSigType(sig, converter); });
1854  converter.addConversion(
1855  [&](TimeType time) { return convertTimeType(time, converter); });
1856  converter.addConversion(
1857  [&](PtrType ptr) { return convertPtrType(ptr, converter); });
1858 }
1859 
1860 void LLHDToLLVMLoweringPass::runOnOperation() {
1861  Namespace globals;
1862  SymbolCache cache;
1863  cache.addDefinitions(getOperation());
1864  globals.add(cache);
1865 
1866  // Keep a counter to infer a signal's index in his entity's signal table.
1867  size_t sigCounter = 0;
1868 
1869  // Keep a counter to infer a reg's index in his entity.
1870  size_t regCounter = 0;
1871 
1872  RewritePatternSet patterns(&getContext());
1873  auto converter = mlir::LLVMTypeConverter(&getContext());
1875 
1876  // Also populate with HW type conversions
1878 
1879  // Apply a partial conversion first, lowering only the instances, to generate
1880  // the init function.
1881  patterns.add<InstOpConversion>(&getContext(), converter);
1882 
1883  LLVMConversionTarget target(getContext());
1884  target.addIllegalOp<InstOp>();
1885  target.addLegalOp<UnrealizedConversionCastOp>();
1886  cf::populateControlFlowToLLVMConversionPatterns(converter, patterns);
1887  arith::populateArithToLLVMConversionPatterns(converter, patterns);
1888 
1889  // Apply the partial conversion.
1890  if (failed(
1891  applyPartialConversion(getOperation(), target, std::move(patterns))))
1892  signalPassFailure();
1893  patterns.clear();
1894 
1895  // Setup the full conversion.
1896  populateFuncToLLVMConversionPatterns(converter, patterns);
1897  populateLLHDToLLVMConversionPatterns(converter, patterns, sigCounter,
1898  regCounter);
1899 
1900  // Populate with HW and Comb conversion patterns
1901  DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
1902  populateHWToLLVMConversionPatterns(converter, patterns, globals,
1903  constAggregateGlobalsMap);
1906  arith::populateArithToLLVMConversionPatterns(converter, patterns);
1907 
1908  target.addLegalDialect<LLVM::LLVMDialect>();
1909  target.addLegalOp<ModuleOp>();
1910 
1911  // Apply a full conversion to remove unrealized conversion casts.
1912  if (failed(applyFullConversion(getOperation(), target, std::move(patterns))))
1913  signalPassFailure();
1914 
1915  patterns.clear();
1916 
1917  mlir::populateReconcileUnrealizedCastsPatterns(patterns);
1918  target.addIllegalOp<UnrealizedConversionCastOp>();
1919 
1920  // Apply the full conversion.
1921  if (failed(applyFullConversion(getOperation(), target, std::move(patterns))))
1922  signalPassFailure();
1923 }
1924 
1925 /// Create an LLHD to LLVM conversion pass.
1926 std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertLLHDToLLVMPass() {
1927  return std::make_unique<LLHDToLLVMLoweringPass>();
1928 }
MlirType uint64_t numElements
Definition: CHIRRTL.cpp:30
static Value recursiveCloneInit(OpBuilder &initBuilder, IRMapping &mapping, Value init)
Recursively clone the init origin of a sig operation into the init function, up to the initial consta...
Definition: LLHDToLLVM.cpp:467
static void insertPersistence(const TypeConverter *converter, ConversionPatternRewriter &rewriter, LLVM::LLVMDialect *dialect, Location loc, ProcOp &proc, Type &stateTy, LLVM::LLVMFuncOp &converted, Operation *splitEntryBefore)
Insert the blocks and operations needed to persist values across suspension, as well as ones needed t...
Definition: LLHDToLLVM.cpp:321
static Value shiftStructuredSigPointer(Location loc, ConversionPatternRewriter &rewriter, Type elemTy, Value pointer, LLVM::GEPArg index)
Shift the pointer of a structured-type (array or struct) signal, to change its view as if the desired...
Definition: LLHDToLLVM.cpp:538
static Value shiftArraySigPointer(Location loc, ConversionPatternRewriter &rewriter, Type arrTy, Value pointer, LLVM::GEPArg index)
Shift the pointer of an array-typed signal, to change its view as if the desired slice/element was ex...
Definition: LLHDToLLVM.cpp:550
static Type convertSigType(SigType type, LLVMTypeConverter &converter)
Definition: LLHDToLLVM.cpp:563
static void persistValue(LLVM::LLVMDialect *dialect, Location loc, const TypeConverter *converter, ConversionPatternRewriter &rewriter, Type stateTy, int &i, Value state, Value persist)
Persist a Value by storing it into the process persistence table, and substituting the uses that esca...
Definition: LLHDToLLVM.cpp:244
static Value gepPersistenceState(LLVM::LLVMDialect *dialect, Location loc, ConversionPatternRewriter &rewriter, Type stateTy, int index, Value state)
Insert a GEP operation to the pointer of the i-th value in the process persistence table.
Definition: LLHDToLLVM.cpp:233
static Value getGlobalString(Location loc, OpBuilder &builder, const TypeConverter *typeConverter, LLVM::GlobalOp &str)
Get an existing global string.
Definition: LLHDToLLVM.cpp:44
static bool isWaitDestArg(WaitOp op, Value val)
Returns true if the given value is passed as an argument to the destination block of the given WaitOp...
Definition: LLHDToLLVM.cpp:154
static bool isArrayOrStruct(Type type)
Check if the given type is either of LLHD's ArrayType, StructType, or LLVM array or struct type.
Definition: LLHDToLLVM.cpp:509
static Type getLLVMSigType(LLVM::LLVMDialect *dialect)
Return the LLVM type used to represent a signal.
Definition: LLHDToLLVM.cpp:80
static LLVM::LLVMFuncOp getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter, Location loc, std::string name, Type signature, bool insertBodyAndTerminator=false)
Looks up a symbol and inserts a new functino at the beginning of the module's region in case the func...
Definition: LLHDToLLVM.cpp:62
static void insertComparisonBlock(ConversionPatternRewriter &rewriter, LLVM::LLVMDialect *dialect, Location loc, Region *body, Value resumeIdx, int currIdx, Block *trueDest, ValueRange trueDestArgs, Block *falseDest=nullptr)
Insert a comparison block that either jumps to the trueDest block, if the resume index mathces the cu...
Definition: LLHDToLLVM.cpp:205
static Value zextByOne(Location loc, ConversionPatternRewriter &rewriter, Value value)
Create a zext operation by one bit on the given value.
Definition: LLHDToLLVM.cpp:431
static std::pair< Value, Value > shiftIntegerSigPointer(Location loc, LLVM::LLVMDialect *dialect, ConversionPatternRewriter &rewriter, Value pointer, Value index)
Shift an integer signal pointer to obtain a view of the underlying value as if it was shifted.
Definition: LLHDToLLVM.cpp:517
static Type convertTimeType(TimeType type, LLVMTypeConverter &converter)
Definition: LLHDToLLVM.cpp:572
static Type convertPtrType(PtrType type, LLVMTypeConverter &converter)
Definition: LLHDToLLVM.cpp:577
static Value adjustBitWidth(Location loc, ConversionPatternRewriter &rewriter, Type targetTy, Value value)
Adjust the bithwidth of value to be the same as targetTy's bitwidth.
Definition: LLHDToLLVM.cpp:440
static unsigned getIndexOfOperandResult(Operation *op, Value result)
Definition: LLHDToLLVM.cpp:454
OneToOneConvertToLLVMPattern< llhd::LoadOp, LLVM::LoadOp > LoadOpConversion
static std::vector< Value > getSignalDetail(ConversionPatternRewriter &rewriter, LLVM::LLVMDialect *dialect, Location loc, Value signal, bool extractIndices=false)
Extract the details from the given signal struct.
Definition: LLHDToLLVM.cpp:89
static LLVM::LLVMStructType getRegStateTy(LLVM::LLVMDialect *dialect, Operation *entity)
Return a struct type of arrays containing one entry for each RegOp condition that require more than o...
Definition: LLHDToLLVM.cpp:410
static Type getProcPersistenceTy(LLVM::LLVMDialect *dialect, const TypeConverter *converter, ProcOp &proc)
Gather the types of values that are used outside of the block they're defined in.
Definition: LLHDToLLVM.cpp:175
static Value createSubSig(LLVM::LLVMDialect *dialect, ConversionPatternRewriter &rewriter, Location loc, std::vector< Value > originDetail, Value newPtr, Value newOffset)
Create a subsignal struct.
Definition: LLHDToLLVM.cpp:124
Builder builder
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:29
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
Definition: Namespace.h:47
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Definition: SymCache.cpp:23
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition: SymCache.h:85
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
void populateHWToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, Namespace &globals, DenseMap< std::pair< Type, ArrayAttr >, mlir::LLVM::GlobalOp > &constAggregateGlobalsMap)
Get the HW to LLVM conversion patterns.
void populateCombToArithConversionPatterns(TypeConverter &converter, RewritePatternSet &patterns)
void populateCombToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns)
Get the Comb to LLVM conversion patterns.
void populateHWToLLVMTypeConversions(mlir::LLVMTypeConverter &converter)
Get the HW to LLVM type conversions.
void populateLLHDToLLVMTypeConversions(mlir::LLVMTypeConverter &converter)
Get the LLHD to LLVM type conversions.
std::unique_ptr< OperationPass< ModuleOp > > createConvertLLHDToLLVMPass()
Create an LLHD to LLVM conversion pass.
void populateLLHDToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, size_t &sigCounter, size_t &regCounter)
Get the LLHD to LLVM conversion patterns.