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