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