CIRCT  19.0.0git
LowerArcToLLVM.cpp
Go to the documentation of this file.
1 //===- LowerArcToLLVM.cpp -------------------------------------------------===//
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 #include "../PassDetail.h"
18 #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
19 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
20 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
21 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
22 #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
23 #include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
24 #include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
25 #include "mlir/Dialect/Func/IR/FuncOps.h"
26 #include "mlir/Dialect/Index/IR/IndexOps.h"
27 #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
28 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
29 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
30 #include "mlir/Dialect/SCF/IR/SCF.h"
31 #include "mlir/IR/BuiltinDialect.h"
32 #include "mlir/Transforms/DialectConversion.h"
33 #include "llvm/Support/Debug.h"
34 
35 #define DEBUG_TYPE "lower-arc-to-llvm"
36 
37 using namespace mlir;
38 using namespace circt;
39 using namespace arc;
40 using namespace hw;
41 
42 //===----------------------------------------------------------------------===//
43 // Lowering Patterns
44 //===----------------------------------------------------------------------===//
45 
46 static llvm::Twine evalSymbolFromModelName(StringRef modelName) {
47  return modelName + "_eval";
48 }
49 
50 namespace {
51 
52 struct ModelOpLowering : public OpConversionPattern<arc::ModelOp> {
53  using OpConversionPattern::OpConversionPattern;
54  LogicalResult
55  matchAndRewrite(arc::ModelOp op, OpAdaptor adaptor,
56  ConversionPatternRewriter &rewriter) const final {
57  {
58  IRRewriter::InsertionGuard guard(rewriter);
59  rewriter.setInsertionPointToEnd(&op.getBodyBlock());
60  rewriter.create<func::ReturnOp>(op.getLoc());
61  }
62  auto funcName =
63  rewriter.getStringAttr(evalSymbolFromModelName(op.getName()));
64  auto funcType =
65  rewriter.getFunctionType(op.getBody().getArgumentTypes(), {});
66  auto func =
67  rewriter.create<mlir::func::FuncOp>(op.getLoc(), funcName, funcType);
68  rewriter.inlineRegionBefore(op.getRegion(), func.getBody(), func.end());
69  rewriter.eraseOp(op);
70  return success();
71  }
72 };
73 
74 struct AllocStorageOpLowering
75  : public OpConversionPattern<arc::AllocStorageOp> {
76  using OpConversionPattern::OpConversionPattern;
77  LogicalResult
78  matchAndRewrite(arc::AllocStorageOp op, OpAdaptor adaptor,
79  ConversionPatternRewriter &rewriter) const final {
80  auto type = typeConverter->convertType(op.getType());
81  if (!op.getOffset().has_value())
82  return failure();
83  rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, type, rewriter.getI8Type(),
84  adaptor.getInput(),
85  LLVM::GEPArg(*op.getOffset()));
86  return success();
87  }
88 };
89 
90 template <class ConcreteOp>
91 struct AllocStateLikeOpLowering : public OpConversionPattern<ConcreteOp> {
94  using OpAdaptor = typename ConcreteOp::Adaptor;
95 
96  LogicalResult
97  matchAndRewrite(ConcreteOp op, OpAdaptor adaptor,
98  ConversionPatternRewriter &rewriter) const final {
99  // Get a pointer to the correct offset in the storage.
100  auto offsetAttr = op->template getAttrOfType<IntegerAttr>("offset");
101  if (!offsetAttr)
102  return failure();
103  Value ptr = rewriter.create<LLVM::GEPOp>(
104  op->getLoc(), adaptor.getStorage().getType(), rewriter.getI8Type(),
105  adaptor.getStorage(),
106  LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
107  rewriter.replaceOp(op, ptr);
108  return success();
109  }
110 };
111 
112 struct StateReadOpLowering : public OpConversionPattern<arc::StateReadOp> {
113  using OpConversionPattern::OpConversionPattern;
114  LogicalResult
115  matchAndRewrite(arc::StateReadOp op, OpAdaptor adaptor,
116  ConversionPatternRewriter &rewriter) const final {
117  auto type = typeConverter->convertType(op.getType());
118  rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, type, adaptor.getState());
119  return success();
120  }
121 };
122 
123 struct StateWriteOpLowering : public OpConversionPattern<arc::StateWriteOp> {
124  using OpConversionPattern::OpConversionPattern;
125  LogicalResult
126  matchAndRewrite(arc::StateWriteOp op, OpAdaptor adaptor,
127  ConversionPatternRewriter &rewriter) const final {
128  if (adaptor.getCondition()) {
129  rewriter.replaceOpWithNewOp<scf::IfOp>(
130  op, adaptor.getCondition(), [&](auto &builder, auto loc) {
131  builder.template create<LLVM::StoreOp>(loc, adaptor.getValue(),
132  adaptor.getState());
133  builder.template create<scf::YieldOp>(loc);
134  });
135  } else {
136  rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
137  adaptor.getState());
138  }
139  return success();
140  }
141 };
142 
143 struct AllocMemoryOpLowering : public OpConversionPattern<arc::AllocMemoryOp> {
144  using OpConversionPattern::OpConversionPattern;
145  LogicalResult
146  matchAndRewrite(arc::AllocMemoryOp op, OpAdaptor adaptor,
147  ConversionPatternRewriter &rewriter) const final {
148  auto offsetAttr = op->getAttrOfType<IntegerAttr>("offset");
149  if (!offsetAttr)
150  return failure();
151  Value ptr = rewriter.create<LLVM::GEPOp>(
152  op.getLoc(), adaptor.getStorage().getType(), rewriter.getI8Type(),
153  adaptor.getStorage(),
154  LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
155 
156  rewriter.replaceOp(op, ptr);
157  return success();
158  }
159 };
160 
161 struct StorageGetOpLowering : public OpConversionPattern<arc::StorageGetOp> {
162  using OpConversionPattern::OpConversionPattern;
163  LogicalResult
164  matchAndRewrite(arc::StorageGetOp op, OpAdaptor adaptor,
165  ConversionPatternRewriter &rewriter) const final {
166  Value offset = rewriter.create<LLVM::ConstantOp>(
167  op.getLoc(), rewriter.getI32Type(), op.getOffsetAttr());
168  Value ptr = rewriter.create<LLVM::GEPOp>(
169  op.getLoc(), adaptor.getStorage().getType(), rewriter.getI8Type(),
170  adaptor.getStorage(), offset);
171  rewriter.replaceOp(op, ptr);
172  return success();
173  }
174 };
175 
176 struct MemoryAccess {
177  Value ptr;
178  Value withinBounds;
179 };
180 
181 static MemoryAccess prepareMemoryAccess(Location loc, Value memory,
182  Value address, MemoryType type,
183  ConversionPatternRewriter &rewriter) {
184  auto zextAddrType = rewriter.getIntegerType(
185  address.getType().cast<IntegerType>().getWidth() + 1);
186  Value addr = rewriter.create<LLVM::ZExtOp>(loc, zextAddrType, address);
187  Value addrLimit = rewriter.create<LLVM::ConstantOp>(
188  loc, zextAddrType, rewriter.getI32IntegerAttr(type.getNumWords()));
189  Value withinBounds = rewriter.create<LLVM::ICmpOp>(
190  loc, LLVM::ICmpPredicate::ult, addr, addrLimit);
191  Value ptr = rewriter.create<LLVM::GEPOp>(
192  loc, LLVM::LLVMPointerType::get(memory.getContext()),
193  rewriter.getIntegerType(type.getStride() * 8), memory, ValueRange{addr});
194  return {ptr, withinBounds};
195 }
196 
197 struct MemoryReadOpLowering : public OpConversionPattern<arc::MemoryReadOp> {
198  using OpConversionPattern::OpConversionPattern;
199  LogicalResult
200  matchAndRewrite(arc::MemoryReadOp op, OpAdaptor adaptor,
201  ConversionPatternRewriter &rewriter) const final {
202  auto type = typeConverter->convertType(op.getType());
203  auto memoryType = op.getMemory().getType().cast<MemoryType>();
204  auto access =
205  prepareMemoryAccess(op.getLoc(), adaptor.getMemory(),
206  adaptor.getAddress(), memoryType, rewriter);
207 
208  // Only attempt to read the memory if the address is within bounds,
209  // otherwise produce a zero value.
210  rewriter.replaceOpWithNewOp<scf::IfOp>(
211  op, access.withinBounds,
212  [&](auto &builder, auto loc) {
213  Value loadOp = builder.template create<LLVM::LoadOp>(
214  loc, memoryType.getWordType(), access.ptr);
215  builder.template create<scf::YieldOp>(loc, loadOp);
216  },
217  [&](auto &builder, auto loc) {
218  Value zeroValue = builder.template create<LLVM::ConstantOp>(
219  loc, type, builder.getI64IntegerAttr(0));
220  builder.template create<scf::YieldOp>(loc, zeroValue);
221  });
222  return success();
223  }
224 };
225 
226 struct MemoryWriteOpLowering : public OpConversionPattern<arc::MemoryWriteOp> {
227  using OpConversionPattern::OpConversionPattern;
228  LogicalResult
229  matchAndRewrite(arc::MemoryWriteOp op, OpAdaptor adaptor,
230  ConversionPatternRewriter &rewriter) const final {
231  auto access = prepareMemoryAccess(
232  op.getLoc(), adaptor.getMemory(), adaptor.getAddress(),
233  op.getMemory().getType().cast<MemoryType>(), rewriter);
234  auto enable = access.withinBounds;
235  if (adaptor.getEnable())
236  enable = rewriter.create<LLVM::AndOp>(op.getLoc(), adaptor.getEnable(),
237  enable);
238 
239  // Only attempt to write the memory if the address is within bounds.
240  rewriter.replaceOpWithNewOp<scf::IfOp>(
241  op, enable, [&](auto &builder, auto loc) {
242  builder.template create<LLVM::StoreOp>(loc, adaptor.getData(),
243  access.ptr);
244  builder.template create<scf::YieldOp>(loc);
245  });
246  return success();
247  }
248 };
249 
250 /// A dummy lowering for clock gates to an AND gate.
251 struct ClockGateOpLowering : public OpConversionPattern<seq::ClockGateOp> {
252  using OpConversionPattern::OpConversionPattern;
253  LogicalResult
254  matchAndRewrite(seq::ClockGateOp op, OpAdaptor adaptor,
255  ConversionPatternRewriter &rewriter) const final {
256  rewriter.replaceOpWithNewOp<comb::AndOp>(op, adaptor.getInput(),
257  adaptor.getEnable(), true);
258  return success();
259  }
260 };
261 
262 struct ZeroCountOpLowering : public OpConversionPattern<arc::ZeroCountOp> {
263  using OpConversionPattern::OpConversionPattern;
264  LogicalResult
265  matchAndRewrite(arc::ZeroCountOp op, OpAdaptor adaptor,
266  ConversionPatternRewriter &rewriter) const override {
267  // Use poison when input is zero.
268  IntegerAttr isZeroPoison = rewriter.getBoolAttr(true);
269 
270  if (op.getPredicate() == arc::ZeroCountPredicate::leading) {
271  rewriter.replaceOpWithNewOp<LLVM::CountLeadingZerosOp>(
272  op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
273  return success();
274  }
275 
276  rewriter.replaceOpWithNewOp<LLVM::CountTrailingZerosOp>(
277  op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
278  return success();
279  }
280 };
281 
282 struct SeqConstClockLowering : public OpConversionPattern<seq::ConstClockOp> {
283  using OpConversionPattern::OpConversionPattern;
284  LogicalResult
285  matchAndRewrite(seq::ConstClockOp op, OpAdaptor adaptor,
286  ConversionPatternRewriter &rewriter) const override {
287  rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
288  op, rewriter.getI1Type(), static_cast<int64_t>(op.getValue()));
289  return success();
290  }
291 };
292 
293 template <typename OpTy>
294 struct ReplaceOpWithInputPattern : public OpConversionPattern<OpTy> {
296  using OpAdaptor = typename OpTy::Adaptor;
297  LogicalResult
298  matchAndRewrite(OpTy op, OpAdaptor adaptor,
299  ConversionPatternRewriter &rewriter) const override {
300  rewriter.replaceOp(op, adaptor.getInput());
301  return success();
302  }
303 };
304 
305 } // namespace
306 
307 //===----------------------------------------------------------------------===//
308 // Simulation Orchestration Lowering Patterns
309 //===----------------------------------------------------------------------===//
310 
311 namespace {
312 
313 struct ModelInfoMap {
314  size_t numStateBytes;
315  llvm::DenseMap<StringRef, StateInfo> states;
316 };
317 
318 template <typename OpTy>
319 struct ModelAwarePattern : public OpConversionPattern<OpTy> {
320  ModelAwarePattern(const TypeConverter &typeConverter, MLIRContext *context,
321  llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo)
322  : OpConversionPattern<OpTy>(typeConverter, context),
323  modelInfo(modelInfo) {}
324 
325 protected:
326  Value createPtrToPortState(ConversionPatternRewriter &rewriter, Location loc,
327  Value state, const StateInfo &port) const {
328  MLIRContext *ctx = rewriter.getContext();
329  return rewriter.create<LLVM::GEPOp>(loc, LLVM::LLVMPointerType::get(ctx),
330  IntegerType::get(ctx, 8), state,
331  LLVM::GEPArg(port.offset));
332  }
333 
334  llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo;
335 };
336 
337 /// Lowers SimInstantiateOp to a malloc and memset call. This pattern will
338 /// mutate the global module.
339 struct SimInstantiateOpLowering
340  : public ModelAwarePattern<arc::SimInstantiateOp> {
341  using ModelAwarePattern::ModelAwarePattern;
342 
343  LogicalResult
344  matchAndRewrite(arc::SimInstantiateOp op, OpAdaptor adaptor,
345  ConversionPatternRewriter &rewriter) const final {
346  auto modelIt = modelInfo.find(
347  cast<SimModelInstanceType>(op.getBody().getArgument(0).getType())
348  .getModel()
349  .getValue());
350  ModelInfoMap &model = modelIt->second;
351 
352  ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
353  if (!moduleOp)
354  return failure();
355 
356  ConversionPatternRewriter::InsertionGuard guard(rewriter);
357 
358  // FIXME: like the rest of MLIR, this assumes sizeof(intptr_t) ==
359  // sizeof(size_t) on the target architecture.
360  Type convertedIndex = typeConverter->convertType(rewriter.getIndexType());
361 
362  LLVM::LLVMFuncOp mallocFunc =
363  LLVM::lookupOrCreateMallocFn(moduleOp, convertedIndex);
364  LLVM::LLVMFuncOp freeFunc = LLVM::lookupOrCreateFreeFn(moduleOp);
365 
366  Location loc = op.getLoc();
367  Value numStateBytes = rewriter.create<LLVM::ConstantOp>(
368  loc, convertedIndex, model.numStateBytes);
369  Value allocated =
370  rewriter
371  .create<LLVM::CallOp>(loc, mallocFunc, ValueRange{numStateBytes})
372  .getResult();
373  Value zero =
374  rewriter.create<LLVM::ConstantOp>(loc, rewriter.getI8Type(), 0);
375  rewriter.create<LLVM::MemsetOp>(loc, allocated, zero, numStateBytes, false);
376  rewriter.inlineBlockBefore(&adaptor.getBody().getBlocks().front(), op,
377  {allocated});
378  rewriter.create<LLVM::CallOp>(loc, freeFunc, ValueRange{allocated});
379  rewriter.eraseOp(op);
380 
381  return success();
382  }
383 };
384 
385 struct SimSetInputOpLowering : public ModelAwarePattern<arc::SimSetInputOp> {
386  using ModelAwarePattern::ModelAwarePattern;
387 
388  LogicalResult
389  matchAndRewrite(arc::SimSetInputOp op, OpAdaptor adaptor,
390  ConversionPatternRewriter &rewriter) const final {
391  auto modelIt =
392  modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
393  .getModel()
394  .getValue());
395  ModelInfoMap &model = modelIt->second;
396 
397  auto portIt = model.states.find(op.getInput());
398  if (portIt == model.states.end()) {
399  // If the port is not found in the state, it means the model does not
400  // actually use it. Thus this operation is a no-op.
401  rewriter.eraseOp(op);
402  return success();
403  }
404 
405  StateInfo &port = portIt->second;
406  Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
407  adaptor.getInstance(), port);
408  rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
409  statePtr);
410 
411  return success();
412  }
413 };
414 
415 struct SimGetPortOpLowering : public ModelAwarePattern<arc::SimGetPortOp> {
416  using ModelAwarePattern::ModelAwarePattern;
417 
418  LogicalResult
419  matchAndRewrite(arc::SimGetPortOp op, OpAdaptor adaptor,
420  ConversionPatternRewriter &rewriter) const final {
421  auto modelIt =
422  modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
423  .getModel()
424  .getValue());
425  ModelInfoMap &model = modelIt->second;
426 
427  auto portIt = model.states.find(op.getPort());
428  if (portIt == model.states.end()) {
429  // If the port is not found in the state, it means the model does not
430  // actually set it. Thus this operation returns 0.
431  rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
432  op, typeConverter->convertType(op.getValue().getType()), 0);
433  return success();
434  }
435 
436  StateInfo &port = portIt->second;
437  Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
438  adaptor.getInstance(), port);
439  rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, op.getValue().getType(),
440  statePtr);
441 
442  return success();
443  }
444 };
445 
446 struct SimStepOpLowering : public ModelAwarePattern<arc::SimStepOp> {
447  using ModelAwarePattern::ModelAwarePattern;
448 
449  LogicalResult
450  matchAndRewrite(arc::SimStepOp op, OpAdaptor adaptor,
451  ConversionPatternRewriter &rewriter) const final {
452  StringRef modelName = cast<SimModelInstanceType>(op.getInstance().getType())
453  .getModel()
454  .getValue();
455 
456  StringAttr evalFunc =
457  rewriter.getStringAttr(evalSymbolFromModelName(modelName));
458  rewriter.replaceOpWithNewOp<LLVM::CallOp>(op, std::nullopt, evalFunc,
459  adaptor.getInstance());
460 
461  return success();
462  }
463 };
464 
465 /// Lowers SimEmitValueOp to a printf call. The integer will be printed in its
466 /// entirety if it is of size up to size_t, and explicitly truncated otherwise.
467 /// This pattern will mutate the global module.
468 struct SimEmitValueOpLowering
469  : public OpConversionPattern<arc::SimEmitValueOp> {
470  using OpConversionPattern::OpConversionPattern;
471 
472  LogicalResult
473  matchAndRewrite(arc::SimEmitValueOp op, OpAdaptor adaptor,
474  ConversionPatternRewriter &rewriter) const final {
475  auto valueType = dyn_cast<IntegerType>(adaptor.getValue().getType());
476  if (!valueType)
477  return failure();
478 
479  Location loc = op.getLoc();
480 
481  ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
482  if (!moduleOp)
483  return failure();
484 
485  // Cast the value to a size_t.
486  // FIXME: like the rest of MLIR, this assumes sizeof(intptr_t) ==
487  // sizeof(size_t) on the target architecture.
488  Value toPrint = adaptor.getValue();
489  DataLayout layout = DataLayout::closest(op);
490  llvm::TypeSize sizeOfSizeT =
491  layout.getTypeSizeInBits(rewriter.getIndexType());
492  assert(!sizeOfSizeT.isScalable() &&
493  sizeOfSizeT.getFixedValue() <= std::numeric_limits<unsigned>::max());
494  bool truncated = false;
495  if (valueType.getWidth() > sizeOfSizeT) {
496  toPrint = rewriter.create<LLVM::TruncOp>(
497  loc, IntegerType::get(getContext(), sizeOfSizeT.getFixedValue()),
498  toPrint);
499  truncated = true;
500  } else if (valueType.getWidth() < sizeOfSizeT)
501  toPrint = rewriter.create<LLVM::ZExtOp>(
502  loc, IntegerType::get(getContext(), sizeOfSizeT.getFixedValue()),
503  toPrint);
504 
505  // Lookup of create printf function symbol.
506  auto printfFunc = LLVM::lookupOrCreateFn(
507  moduleOp, "printf", LLVM::LLVMPointerType::get(getContext()),
508  LLVM::LLVMVoidType::get(getContext()), true);
509 
510  // Insert the format string if not already available.
511  SmallString<16> formatStrName{"_arc_sim_emit_"};
512  formatStrName.append(truncated ? "trunc_" : "full_");
513  formatStrName.append(adaptor.getValueName());
514  LLVM::GlobalOp formatStrGlobal;
515  if (!(formatStrGlobal =
516  moduleOp.lookupSymbol<LLVM::GlobalOp>(formatStrName))) {
517  ConversionPatternRewriter::InsertionGuard insertGuard(rewriter);
518 
519  SmallString<16> formatStr = adaptor.getValueName();
520  formatStr.append(" = ");
521  if (truncated)
522  formatStr.append("(truncated) ");
523  formatStr.append("%zx\n");
524  SmallVector<char> formatStrVec{formatStr.begin(), formatStr.end()};
525  formatStrVec.push_back(0);
526 
527  rewriter.setInsertionPointToStart(moduleOp.getBody());
528  auto globalType =
529  LLVM::LLVMArrayType::get(rewriter.getI8Type(), formatStrVec.size());
530  formatStrGlobal = rewriter.create<LLVM::GlobalOp>(
531  loc, globalType, /*isConstant=*/true, LLVM::Linkage::Internal,
532  /*name=*/formatStrName, rewriter.getStringAttr(formatStrVec),
533  /*alignment=*/0);
534  }
535 
536  Value formatStrGlobalPtr =
537  rewriter.create<LLVM::AddressOfOp>(loc, formatStrGlobal);
538  rewriter.replaceOpWithNewOp<LLVM::CallOp>(
539  op, printfFunc, ValueRange{formatStrGlobalPtr, toPrint});
540 
541  return success();
542  }
543 };
544 
545 } // namespace
546 
547 //===----------------------------------------------------------------------===//
548 // Pass Implementation
549 //===----------------------------------------------------------------------===//
550 
551 namespace {
552 struct LowerArcToLLVMPass : public LowerArcToLLVMBase<LowerArcToLLVMPass> {
553  void runOnOperation() override;
554 };
555 } // namespace
556 
557 void LowerArcToLLVMPass::runOnOperation() {
558  // Collect the symbols in the root op such that the HW-to-LLVM lowering can
559  // create LLVM globals with non-colliding names.
560  Namespace globals;
561  SymbolCache cache;
562  cache.addDefinitions(getOperation());
563  globals.add(cache);
564 
565  // Setup the conversion target. Explicitly mark `scf.yield` legal since it
566  // does not have a conversion itself, which would cause it to fail
567  // legalization and for the conversion to abort. (It relies on its parent op's
568  // conversion to remove it.)
569  LLVMConversionTarget target(getContext());
570  target.addLegalOp<mlir::ModuleOp>();
571  target.addLegalOp<scf::YieldOp>(); // quirk of SCF dialect conversion
572 
573  // Setup the arc dialect type conversion.
574  LLVMTypeConverter converter(&getContext());
575  converter.addConversion([&](seq::ClockType type) {
576  return IntegerType::get(type.getContext(), 1);
577  });
578  converter.addConversion([&](StorageType type) {
579  return LLVM::LLVMPointerType::get(type.getContext());
580  });
581  converter.addConversion([&](MemoryType type) {
582  return LLVM::LLVMPointerType::get(type.getContext());
583  });
584  converter.addConversion([&](StateType type) {
585  return LLVM::LLVMPointerType::get(type.getContext());
586  });
587  converter.addConversion([&](SimModelInstanceType type) {
588  return LLVM::LLVMPointerType::get(type.getContext());
589  });
590 
591  // Setup the conversion patterns.
592  RewritePatternSet patterns(&getContext());
593 
594  // MLIR patterns.
595  populateSCFToControlFlowConversionPatterns(patterns);
596  populateFuncToLLVMConversionPatterns(converter, patterns);
597  cf::populateControlFlowToLLVMConversionPatterns(converter, patterns);
598  arith::populateArithToLLVMConversionPatterns(converter, patterns);
599  populateAnyFunctionOpInterfaceTypeConversionPattern(patterns, converter);
600 
601  // CIRCT patterns.
602  DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
603  populateHWToLLVMConversionPatterns(converter, patterns, globals,
604  constAggregateGlobalsMap);
607 
608  // Arc patterns.
609  // clang-format off
610  patterns.add<
611  AllocMemoryOpLowering,
612  AllocStateLikeOpLowering<arc::AllocStateOp>,
613  AllocStateLikeOpLowering<arc::RootInputOp>,
614  AllocStateLikeOpLowering<arc::RootOutputOp>,
615  AllocStorageOpLowering,
616  ClockGateOpLowering,
617  MemoryReadOpLowering,
618  MemoryWriteOpLowering,
619  ModelOpLowering,
620  ReplaceOpWithInputPattern<seq::ToClockOp>,
621  ReplaceOpWithInputPattern<seq::FromClockOp>,
622  SeqConstClockLowering,
623  SimEmitValueOpLowering,
624  StateReadOpLowering,
625  StateWriteOpLowering,
626  StorageGetOpLowering,
627  ZeroCountOpLowering
628  >(converter, &getContext());
629  // clang-format on
630 
631  SmallVector<ModelInfo> models;
632  if (failed(collectModels(getOperation(), models))) {
633  signalPassFailure();
634  return;
635  }
636 
637  llvm::DenseMap<StringRef, ModelInfoMap> modelMap(models.size());
638  for (ModelInfo &modelInfo : models) {
639  llvm::DenseMap<StringRef, StateInfo> states(modelInfo.states.size());
640  for (StateInfo &stateInfo : modelInfo.states)
641  states.insert({stateInfo.name, stateInfo});
642  modelMap.insert({modelInfo.name,
643  ModelInfoMap{modelInfo.numStateBytes, std::move(states)}});
644  }
645 
646  patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
647  SimGetPortOpLowering, SimStepOpLowering>(
648  converter, &getContext(), modelMap);
649 
650  // Apply the conversion.
651  if (failed(applyFullConversion(getOperation(), target, std::move(patterns))))
652  signalPassFailure();
653 }
654 
655 std::unique_ptr<OperationPass<ModuleOp>> circt::createLowerArcToLLVMPass() {
656  return std::make_unique<LowerArcToLLVMPass>();
657 }
assert(baseType &&"element must be base type")
static llvm::Twine evalSymbolFromModelName(StringRef modelName)
Builder builder
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:29
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
Definition: Namespace.h:47
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Definition: SymCache.cpp:23
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition: SymCache.h:85
mlir::LogicalResult collectModels(mlir::ModuleOp module, llvm::SmallVector< ModelInfo > &models)
Collects information about all Arc models in the provided module, and adds it to models.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
void populateHWToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, Namespace &globals, DenseMap< std::pair< Type, ArrayAttr >, mlir::LLVM::GlobalOp > &constAggregateGlobalsMap)
Get the HW to LLVM conversion patterns.
void 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.
std::unique_ptr< OperationPass< ModuleOp > > createLowerArcToLLVMPass()
Definition: hw.py:1
Gathers information about a given Arc model.
Definition: ModelInfo.h:35
Gathers information about a given Arc state.
Definition: ModelInfo.h:25
std::string name
Definition: ModelInfo.h:27