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