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