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