28#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
29#include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
30#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
31#include "mlir/Conversion/IndexToLLVM/IndexToLLVM.h"
32#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
33#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
34#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
35#include "mlir/Conversion/UBToLLVM/UBToLLVM.h"
36#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
37#include "mlir/Dialect/Func/IR/FuncOps.h"
38#include "mlir/Dialect/Index/IR/IndexOps.h"
39#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
40#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
41#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
42#include "mlir/Dialect/SCF/IR/SCF.h"
43#include "mlir/IR/Builders.h"
44#include "mlir/IR/BuiltinDialect.h"
45#include "mlir/Interfaces/DataLayoutInterfaces.h"
46#include "mlir/Pass/Pass.h"
47#include "mlir/Transforms/DialectConversion.h"
48#include "llvm/Support/Debug.h"
49#include "llvm/Support/FormatVariadic.h"
53#define DEBUG_TYPE "lower-arc-to-llvm"
56#define GEN_PASS_DEF_LOWERARCTOLLVM
57#include "circt/Conversion/Passes.h.inc"
64using namespace runtime;
71 return modelName +
"_eval";
77 using OpConversionPattern::OpConversionPattern;
79 matchAndRewrite(arc::ModelOp op, OpAdaptor adaptor,
80 ConversionPatternRewriter &rewriter)
const final {
82 IRRewriter::InsertionGuard guard(rewriter);
83 rewriter.setInsertionPointToEnd(&op.getBodyBlock());
84 func::ReturnOp::create(rewriter, op.getLoc());
89 rewriter.getFunctionType(op.getBody().getArgumentTypes(), {});
91 mlir::func::FuncOp::create(rewriter, op.getLoc(), funcName, funcType);
92 rewriter.inlineRegionBefore(op.getRegion(), func.getBody(), func.end());
98struct AllocStorageOpLowering
100 using OpConversionPattern::OpConversionPattern;
102 matchAndRewrite(arc::AllocStorageOp op, OpAdaptor adaptor,
103 ConversionPatternRewriter &rewriter)
const final {
104 auto type = typeConverter->convertType(op.getType());
105 if (!op.getOffset().has_value())
107 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, type, rewriter.getI8Type(),
109 LLVM::GEPArg(*op.getOffset()));
114template <
class ConcreteOp>
116 using OpConversionPattern<ConcreteOp>::OpConversionPattern;
117 using OpConversionPattern<ConcreteOp>::typeConverter;
118 using OpAdaptor =
typename ConcreteOp::Adaptor;
121 matchAndRewrite(ConcreteOp op, OpAdaptor adaptor,
122 ConversionPatternRewriter &rewriter)
const final {
124 auto offsetAttr = op->template getAttrOfType<IntegerAttr>(
"offset");
127 Value ptr = LLVM::GEPOp::create(
128 rewriter, op->getLoc(), adaptor.getStorage().getType(),
129 rewriter.getI8Type(), adaptor.getStorage(),
130 LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
131 rewriter.replaceOp(op, ptr);
137 using OpConversionPattern::OpConversionPattern;
139 matchAndRewrite(arc::StateReadOp op, OpAdaptor adaptor,
140 ConversionPatternRewriter &rewriter)
const final {
142 if (isa<ArrayRefType>(op.getType())) {
143 rewriter.replaceOp(op, adaptor.getState());
147 auto type = typeConverter->convertType(op.getType());
148 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, type, adaptor.getState());
154 using OpConversionPattern::OpConversionPattern;
156 matchAndRewrite(arc::StateWriteOp op, OpAdaptor adaptor,
157 ConversionPatternRewriter &rewriter)
const final {
158 if (!isa<ArrayRefType>(op.getValue().getType())) {
159 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
164 int numBytes = op.getState().getType().getByteWidth();
165 Value size = LLVM::ConstantOp::create(rewriter, op.getLoc(),
166 rewriter.getI64Type(), numBytes);
167 rewriter.replaceOpWithNewOp<LLVM::MemcpyOp>(
168 op, adaptor.getState(), adaptor.getValue(), size,
false);
174 using OpConversionPattern::OpConversionPattern;
176 matchAndRewrite(arc::AsContextOp op, OpAdaptor adaptor,
177 ConversionPatternRewriter &rewriter)
const final {
179 rewriter.replaceOp(op, adaptor.getInput());
180 return llvm::success();
189 using OpConversionPattern::OpConversionPattern;
191 matchAndRewrite(arc::CurrentTimeOp op, OpAdaptor adaptor,
192 ConversionPatternRewriter &rewriter)
const final {
194 Value ptr = adaptor.getArcContext();
195 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, rewriter.getI64Type(), ptr);
203struct ConstantTimeOpLowering
205 using OpConversionPattern::OpConversionPattern;
207 matchAndRewrite(llhd::ConstantTimeOp op, OpAdaptor adaptor,
208 ConversionPatternRewriter &rewriter)
const final {
209 auto attr = op.getValue();
210 if (attr.getDelta() != 0)
211 return rewriter.notifyMatchFailure(
212 op,
"non-zero delta time component is not supported");
213 uint64_t value = attr.getTime();
214 StringRef unit = attr.getTimeUnit();
218 else if (unit ==
"ps")
220 else if (unit ==
"ns")
221 scale = 1'000'000ULL;
222 else if (unit ==
"us")
223 scale = 1'000'000'000ULL;
224 else if (unit ==
"ms")
225 scale = 1'000'000'000'000ULL;
226 else if (unit ==
"s")
227 scale = 1'000'000'000'000'000ULL;
229 return rewriter.notifyMatchFailure(
230 op,
"time units smaller than `fs` are not supported");
231 if (value > std::numeric_limits<uint64_t>::max() / scale)
232 return rewriter.notifyMatchFailure(
233 op,
"time value does not fit into `i64` femtoseconds");
234 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(op, rewriter.getI64Type(),
242 using OpConversionPattern::OpConversionPattern;
244 matchAndRewrite(llhd::IntToTimeOp op, OpAdaptor adaptor,
245 ConversionPatternRewriter &rewriter)
const final {
246 rewriter.replaceOp(op, adaptor.getInput());
253 using OpConversionPattern::OpConversionPattern;
255 matchAndRewrite(llhd::TimeToIntOp op, OpAdaptor adaptor,
256 ConversionPatternRewriter &rewriter)
const final {
257 rewriter.replaceOp(op, adaptor.getInput());
267 using OpConversionPattern::OpConversionPattern;
269 matchAndRewrite(arc::AllocMemoryOp op, OpAdaptor adaptor,
270 ConversionPatternRewriter &rewriter)
const final {
271 auto offsetAttr = op->getAttrOfType<IntegerAttr>(
"offset");
274 Value ptr = LLVM::GEPOp::create(
275 rewriter, op.getLoc(), adaptor.getStorage().getType(),
276 rewriter.getI8Type(), adaptor.getStorage(),
277 LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
279 rewriter.replaceOp(op, ptr);
285 using OpConversionPattern::OpConversionPattern;
287 matchAndRewrite(arc::StorageGetOp op, OpAdaptor adaptor,
288 ConversionPatternRewriter &rewriter)
const final {
289 Value offset = LLVM::ConstantOp::create(
290 rewriter, op.getLoc(), rewriter.getI32Type(), op.getOffsetAttr());
291 Value ptr = LLVM::GEPOp::create(
292 rewriter, op.getLoc(), adaptor.getStorage().getType(),
293 rewriter.getI8Type(), adaptor.getStorage(), offset);
294 rewriter.replaceOp(op, ptr);
304static MemoryAccess prepareMemoryAccess(Location loc, Value memory,
305 Value address, MemoryType type,
306 ConversionPatternRewriter &rewriter) {
307 auto zextAddrType = rewriter.getIntegerType(
308 cast<IntegerType>(address.getType()).getWidth() + 1);
309 Value
addr = LLVM::ZExtOp::create(rewriter, loc, zextAddrType, address);
310 Value addrLimit = LLVM::ConstantOp::create(
311 rewriter, loc, zextAddrType,
312 rewriter.getIntegerAttr(zextAddrType, type.getNumWords()));
313 Value withinBounds = LLVM::ICmpOp::create(
314 rewriter, loc, LLVM::ICmpPredicate::ult, addr, addrLimit);
315 Value ptr = LLVM::GEPOp::create(
316 rewriter, loc, LLVM::LLVMPointerType::get(memory.getContext()),
317 rewriter.getIntegerType(type.getStride() * 8), memory, ValueRange{addr});
318 return {ptr, withinBounds};
322 using OpConversionPattern::OpConversionPattern;
324 matchAndRewrite(arc::MemoryReadOp op, OpAdaptor adaptor,
325 ConversionPatternRewriter &rewriter)
const final {
326 auto type = typeConverter->convertType(op.getType());
327 auto memoryType = cast<MemoryType>(op.getMemory().getType());
329 prepareMemoryAccess(op.getLoc(), adaptor.getMemory(),
330 adaptor.getAddress(), memoryType, rewriter);
334 rewriter.replaceOpWithNewOp<scf::IfOp>(
335 op, access.withinBounds,
336 [&](
auto &builder,
auto loc) {
337 Value loadOp = LLVM::LoadOp::create(
338 builder, loc, memoryType.getWordType(), access.ptr);
339 scf::YieldOp::create(builder, loc, loadOp);
341 [&](
auto &builder,
auto loc) {
342 Value zeroValue = LLVM::ConstantOp::create(
343 builder, loc, type, builder.getIntegerAttr(type, 0));
344 scf::YieldOp::create(builder, loc, zeroValue);
351 using OpConversionPattern::OpConversionPattern;
353 matchAndRewrite(arc::MemoryWriteOp op, OpAdaptor adaptor,
354 ConversionPatternRewriter &rewriter)
const final {
355 auto access = prepareMemoryAccess(
356 op.getLoc(), adaptor.getMemory(), adaptor.getAddress(),
357 cast<MemoryType>(op.getMemory().getType()), rewriter);
358 auto enable = access.withinBounds;
361 rewriter.replaceOpWithNewOp<scf::IfOp>(
362 op, enable, [&](
auto &builder,
auto loc) {
363 LLVM::StoreOp::create(builder, loc, adaptor.getData(), access.ptr);
364 scf::YieldOp::create(builder, loc);
372 using OpConversionPattern::OpConversionPattern;
374 matchAndRewrite(seq::ClockGateOp op, OpAdaptor adaptor,
375 ConversionPatternRewriter &rewriter)
const final {
376 rewriter.replaceOpWithNewOp<LLVM::AndOp>(op, adaptor.getInput(),
377 adaptor.getEnable());
384 using OpConversionPattern::OpConversionPattern;
386 matchAndRewrite(seq::ClockInverterOp op, OpAdaptor adaptor,
387 ConversionPatternRewriter &rewriter)
const final {
388 auto constTrue = LLVM::ConstantOp::create(rewriter, op->getLoc(),
389 rewriter.getI1Type(), 1);
390 rewriter.replaceOpWithNewOp<LLVM::XOrOp>(op, adaptor.getInput(), constTrue);
396 using OpConversionPattern::OpConversionPattern;
398 matchAndRewrite(arc::ZeroCountOp op, OpAdaptor adaptor,
399 ConversionPatternRewriter &rewriter)
const override {
401 IntegerAttr isZeroPoison = rewriter.getBoolAttr(
true);
403 if (op.getPredicate() == arc::ZeroCountPredicate::leading) {
404 rewriter.replaceOpWithNewOp<LLVM::CountLeadingZerosOp>(
405 op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
409 rewriter.replaceOpWithNewOp<LLVM::CountTrailingZerosOp>(
410 op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
416 using OpConversionPattern::OpConversionPattern;
418 matchAndRewrite(seq::ConstClockOp op, OpAdaptor adaptor,
419 ConversionPatternRewriter &rewriter)
const override {
420 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
421 op, rewriter.getI1Type(),
static_cast<int64_t
>(op.getValue()));
426template <
typename OpTy>
428 using OpConversionPattern<OpTy>::OpConversionPattern;
429 using OpAdaptor =
typename OpTy::Adaptor;
431 matchAndRewrite(OpTy op, OpAdaptor adaptor,
432 ConversionPatternRewriter &rewriter)
const override {
433 rewriter.replaceOp(op, adaptor.getInput());
447 size_t numStateBytes;
448 llvm::DenseMap<StringRef, StateInfo> states;
449 mlir::FlatSymbolRefAttr initialFnSymbol;
450 mlir::FlatSymbolRefAttr finalFnSymbol;
453template <
typename OpTy>
455 ModelAwarePattern(
const TypeConverter &typeConverter, MLIRContext *
context,
456 llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo)
457 : OpConversionPattern<OpTy>(typeConverter,
context),
458 modelInfo(modelInfo) {}
461 Value createPtrToPortState(ConversionPatternRewriter &rewriter, Location loc,
462 Value state,
const StateInfo &port)
const {
463 MLIRContext *ctx = rewriter.getContext();
464 return LLVM::GEPOp::create(rewriter, loc, LLVM::LLVMPointerType::get(ctx),
465 IntegerType::get(ctx, 8), state,
466 LLVM::GEPArg(port.offset));
469 llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo;
474struct SimInstantiateOpLowering
475 :
public ModelAwarePattern<arc::SimInstantiateOp> {
476 using ModelAwarePattern::ModelAwarePattern;
479 matchAndRewrite(arc::SimInstantiateOp op, OpAdaptor adaptor,
480 ConversionPatternRewriter &rewriter)
const final {
481 auto modelIt = modelInfo.find(
482 cast<SimModelInstanceType>(op.getBody().getArgument(0).getType())
485 ModelInfoMap &model = modelIt->second;
487 bool useRuntime = op.getRuntimeModel().has_value();
489 ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
493 ConversionPatternRewriter::InsertionGuard guard(rewriter);
497 Type convertedIndex = typeConverter->convertType(rewriter.getIndexType());
498 Location loc = op.getLoc();
503 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
507 if (op.getRuntimeArgs().has_value()) {
508 SmallVector<int8_t> argStringVec(op.getRuntimeArgsAttr().begin(),
509 op.getRuntimeArgsAttr().end());
510 argStringVec.push_back(
'\0');
511 auto strAttr = mlir::DenseElementsAttr::get(
512 mlir::RankedTensorType::get({(int64_t)argStringVec.size()},
513 rewriter.getI8Type()),
514 llvm::ArrayRef(argStringVec));
516 auto arrayCst = LLVM::ConstantOp::create(
518 LLVM::LLVMArrayType::get(rewriter.getI8Type(), argStringVec.size()),
520 auto cst1 = LLVM::ConstantOp::create(rewriter, loc,
521 rewriter.getI32IntegerAttr(1));
522 runtimeArgs = LLVM::AllocaOp::create(rewriter, loc, ptrTy,
523 arrayCst.getType(), cst1);
524 LLVM::LifetimeStartOp::create(rewriter, loc, runtimeArgs);
525 LLVM::StoreOp::create(rewriter, loc, arrayCst, runtimeArgs);
527 runtimeArgs = LLVM::ZeroOp::create(rewriter, loc, ptrTy).getResult();
530 auto rtModelPtr = LLVM::AddressOfOp::create(rewriter, loc, ptrTy,
531 op.getRuntimeModelAttr())
534 LLVM::CallOp::create(rewriter, loc, {ptrTy},
535 runtime::APICallbacks::symNameAllocInstance,
536 {rtModelPtr, runtimeArgs})
539 if (op.getRuntimeArgs().has_value())
540 LLVM::LifetimeEndOp::create(rewriter, loc, runtimeArgs);
544 FailureOr<LLVM::LLVMFuncOp> mallocFunc =
545 LLVM::lookupOrCreateMallocFn(rewriter, moduleOp, convertedIndex);
546 if (failed(mallocFunc))
549 Value numStateBytes = LLVM::ConstantOp::create(
550 rewriter, loc, convertedIndex, model.numStateBytes);
551 allocated = LLVM::CallOp::create(rewriter, loc, mallocFunc.value(),
552 ValueRange{numStateBytes})
555 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI8Type(), 0);
556 LLVM::MemsetOp::create(rewriter, loc, allocated, zero, numStateBytes,
561 if (model.initialFnSymbol) {
562 auto initialFnType = LLVM::LLVMFunctionType::get(
563 LLVM::LLVMVoidType::get(op.getContext()),
564 {LLVM::LLVMPointerType::get(op.getContext())});
565 LLVM::CallOp::create(rewriter, loc, initialFnType, model.initialFnSymbol,
566 ValueRange{allocated});
571 LLVM::CallOp::create(rewriter, loc, TypeRange{},
572 runtime::APICallbacks::symNameOnInitialized,
576 rewriter.inlineBlockBefore(&adaptor.getBody().getBlocks().front(), op,
580 if (model.finalFnSymbol) {
581 auto finalFnType = LLVM::LLVMFunctionType::get(
582 LLVM::LLVMVoidType::get(op.getContext()),
583 {LLVM::LLVMPointerType::get(op.getContext())});
584 LLVM::CallOp::create(rewriter, loc, finalFnType, model.finalFnSymbol,
585 ValueRange{allocated});
589 LLVM::CallOp::create(rewriter, loc, TypeRange{},
590 runtime::APICallbacks::symNameDeleteInstance,
593 FailureOr<LLVM::LLVMFuncOp> freeFunc =
594 LLVM::lookupOrCreateFreeFn(rewriter, moduleOp);
595 if (failed(freeFunc))
598 LLVM::CallOp::create(rewriter, loc, freeFunc.value(),
599 ValueRange{allocated});
602 rewriter.eraseOp(op);
607struct SimSetInputOpLowering :
public ModelAwarePattern<arc::SimSetInputOp> {
608 using ModelAwarePattern::ModelAwarePattern;
611 matchAndRewrite(arc::SimSetInputOp op, OpAdaptor adaptor,
612 ConversionPatternRewriter &rewriter)
const final {
614 modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
617 ModelInfoMap &model = modelIt->second;
619 auto portIt = model.states.find(op.getInput());
620 if (portIt == model.states.end()) {
623 rewriter.eraseOp(op);
627 StateInfo &port = portIt->second;
628 Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
629 adaptor.getInstance(), port);
630 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
637struct SimGetPortOpLowering :
public ModelAwarePattern<arc::SimGetPortOp> {
638 using ModelAwarePattern::ModelAwarePattern;
641 matchAndRewrite(arc::SimGetPortOp op, OpAdaptor adaptor,
642 ConversionPatternRewriter &rewriter)
const final {
644 modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
647 ModelInfoMap &model = modelIt->second;
649 auto type = typeConverter->convertType(op.getValue().getType());
652 auto portIt = model.states.find(op.getPort());
653 if (portIt == model.states.end()) {
656 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(op, type, 0);
660 StateInfo &port = portIt->second;
661 Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
662 adaptor.getInstance(), port);
663 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, type, statePtr);
669struct SimStepOpLowering :
public ModelAwarePattern<arc::SimStepOp> {
670 using ModelAwarePattern::ModelAwarePattern;
673 matchAndRewrite(arc::SimStepOp op, OpAdaptor adaptor,
674 ConversionPatternRewriter &rewriter)
const final {
675 StringRef modelName = cast<SimModelInstanceType>(op.getInstance().getType())
679 if (adaptor.getTimePostIncrement()) {
681 OpBuilder::InsertionGuard g(rewriter);
682 rewriter.setInsertionPointAfter(op);
684 arc::AsContextOp::create(rewriter, op.getLoc(), op.getInstance());
686 arc::CurrentTimeOp::create(rewriter, op.getLoc(), arcContext);
687 auto newTime = LLVM::AddOp::create(rewriter, op.getLoc(), oldTime,
688 adaptor.getTimePostIncrement());
689 arc::SimSetTimeOp::create(rewriter, op.getLoc(), op.getInstance(),
693 StringAttr evalFunc =
695 rewriter.replaceOpWithNewOp<LLVM::CallOp>(op, mlir::TypeRange(), evalFunc,
696 adaptor.getInstance());
705 using OpConversionPattern::OpConversionPattern;
708 matchAndRewrite(arc::SimSetTimeOp op, OpAdaptor adaptor,
709 ConversionPatternRewriter &rewriter)
const final {
711 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getTime(),
712 adaptor.getInstance());
720 Value getOrCreate(OpBuilder &b, StringRef formatStr) {
721 auto it = cache.find(formatStr);
722 if (it != cache.end()) {
723 return LLVM::AddressOfOp::create(b, b.getUnknownLoc(), it->second);
726 Location loc = b.getUnknownLoc();
727 LLVM::GlobalOp global;
729 OpBuilder::InsertionGuard guard(b);
731 b.getInsertionBlock()->getParent()->getParentOfType<ModuleOp>();
732 b.setInsertionPointToStart(m.getBody());
734 SmallVector<char> strVec(formatStr.begin(), formatStr.end());
737 auto name = llvm::formatv(
"_arc_str_{0}", cache.size()).str();
738 auto globalType = LLVM::LLVMArrayType::get(b.getI8Type(), strVec.size());
739 global = LLVM::GlobalOp::create(b, loc, globalType,
true,
740 LLVM::Linkage::Internal,
741 name, b.getStringAttr(strVec),
745 cache[formatStr] = global;
746 return LLVM::AddressOfOp::create(b, loc, global);
750 llvm::StringMap<LLVM::GlobalOp> cache;
753FailureOr<LLVM::CallOp> emitPrintfCall(OpBuilder &builder, Location loc,
754 StringCache &cache, StringRef formatStr,
757 builder.getInsertionBlock()->getParent()->getParentOfType<ModuleOp>();
759 MLIRContext *ctx = builder.getContext();
760 auto printfFunc = LLVM::lookupOrCreateFn(builder, moduleOp,
"printf",
761 LLVM::LLVMPointerType::get(ctx),
762 LLVM::LLVMVoidType::get(ctx),
true);
763 if (failed(printfFunc))
766 Value formatStrPtr = cache.getOrCreate(builder, formatStr);
767 SmallVector<Value> argsVec(1, formatStrPtr);
768 argsVec.append(args.begin(), args.end());
769 return LLVM::CallOp::create(builder, loc, printfFunc.value(), argsVec);
775struct SimEmitValueOpLowering
777 SimEmitValueOpLowering(
const TypeConverter &typeConverter,
778 MLIRContext *
context, StringCache &formatStringCache)
779 : OpConversionPattern(typeConverter,
context),
780 formatStringCache(formatStringCache) {}
783 matchAndRewrite(arc::SimEmitValueOp op, OpAdaptor adaptor,
784 ConversionPatternRewriter &rewriter)
const final {
785 auto valueType = dyn_cast<IntegerType>(adaptor.getValue().getType());
789 Location loc = op.getLoc();
791 ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
795 SmallVector<Value> printfVariadicArgs;
796 SmallString<16> printfFormatStr;
797 int remainingBits = valueType.getWidth();
798 Value value = adaptor.getValue();
802 constexpr llvm::StringRef intFormatter =
"llx";
803 auto intType = IntegerType::get(getContext(), 64);
804 Value shiftValue = LLVM::ConstantOp::create(
805 rewriter, loc, rewriter.getIntegerAttr(valueType, intType.getWidth()));
807 if (valueType.getWidth() < intType.getWidth()) {
808 int width = llvm::divideCeil(valueType.getWidth(), 4);
809 printfFormatStr = llvm::formatv(
"%0{0}{1}", width, intFormatter);
810 printfVariadicArgs.push_back(
811 LLVM::ZExtOp::create(rewriter, loc, intType, value));
816 int otherChunkWidth = intType.getWidth() / 4;
817 int firstChunkWidth =
818 llvm::divideCeil(valueType.getWidth() % intType.getWidth(), 4);
819 if (firstChunkWidth == 0) {
820 firstChunkWidth = otherChunkWidth;
823 std::string firstChunkFormat =
824 llvm::formatv(
"%0{0}{1}", firstChunkWidth, intFormatter);
825 std::string otherChunkFormat =
826 llvm::formatv(
"%0{0}{1}", otherChunkWidth, intFormatter);
828 for (
int i = 0; remainingBits > 0; ++i) {
831 printfVariadicArgs.push_back(
832 LLVM::TruncOp::create(rewriter, loc, intType, value));
835 printfFormatStr.append(i == 0 ? firstChunkFormat : otherChunkFormat);
838 LLVM::LShrOp::create(rewriter, loc, value, shiftValue).getResult();
839 remainingBits -= intType.getWidth();
843 std::reverse(printfVariadicArgs.begin(), printfVariadicArgs.end());
845 SmallString<16> formatStr = adaptor.getValueName();
846 formatStr.append(
" = ");
847 formatStr.append(printfFormatStr);
848 formatStr.append(
"\n");
850 auto callOp = emitPrintfCall(rewriter, op->getLoc(), formatStringCache,
851 formatStr, printfVariadicArgs);
854 rewriter.replaceOp(op, *callOp);
859 StringCache &formatStringCache;
868 SmallVector<FmtDescriptor> descriptors;
869 SmallVector<Value> args;
876static Value reg2mem(ConversionPatternRewriter &rewriter, Location loc,
879 int64_t origBitwidth = cast<IntegerType>(value.getType()).getWidth();
880 int64_t bitwidth = llvm::divideCeil(origBitwidth, 64) * 64;
881 int64_t numWords = bitwidth / 64;
884 LLVM::ConstantOp alloca_size =
885 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), numWords);
886 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
887 auto allocaOp = LLVM::AllocaOp::create(rewriter, loc, ptrType,
888 rewriter.getI64Type(), alloca_size);
889 LLVM::LifetimeStartOp::create(rewriter, loc, allocaOp);
893 for (int64_t wordIdx = 0; wordIdx < numWords; ++wordIdx) {
894 Value cst = LLVM::ConstantOp::create(
895 rewriter, loc, rewriter.getIntegerType(origBitwidth), wordIdx * 64);
896 Value v = LLVM::LShrOp::create(rewriter, loc, value, cst);
897 if (origBitwidth > 64) {
898 v = LLVM::TruncOp::create(rewriter, loc, rewriter.getI64Type(), v);
899 }
else if (origBitwidth < 64) {
900 v = LLVM::ZExtOp::create(rewriter, loc, rewriter.getI64Type(), v);
902 Value gep = LLVM::GEPOp::create(rewriter, loc, ptrType,
903 rewriter.getI64Type(), allocaOp, {wordIdx});
904 LLVM::StoreOp::create(rewriter, loc, v, gep);
911static FailureOr<FormatInfo>
912foldFormatString(ConversionPatternRewriter &rewriter, Value fstringValue,
913 StringCache &cache) {
914 Operation *op = fstringValue.getDefiningOp();
915 return llvm::TypeSwitch<Operation *, FailureOr<FormatInfo>>(op)
916 .Case<sim::FormatCharOp>(
917 [&](sim::FormatCharOp op) -> FailureOr<FormatInfo> {
918 FmtDescriptor
d = FmtDescriptor::createChar();
919 return FormatInfo{{
d}, {op.getValue()}};
921 .Case<sim::FormatDecOp>([&](sim::FormatDecOp op)
922 -> FailureOr<FormatInfo> {
923 FmtDescriptor
d = FmtDescriptor::createInt(
924 op.getValue().getType().getWidth(), 10, op.getIsLeftAligned(),
925 op.getSpecifierWidth().value_or(-1), op.getPaddingChar(),
false,
927 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
929 .Case<sim::FormatHexOp>([&](sim::FormatHexOp op)
930 -> FailureOr<FormatInfo> {
931 FmtDescriptor
d = FmtDescriptor::createInt(
932 op.getValue().getType().getWidth(), 16, op.getIsLeftAligned(),
933 op.getSpecifierWidth().value_or(-1), op.getPaddingChar(),
934 op.getIsHexUppercase(),
false);
935 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
937 .Case<sim::FormatOctOp>([&](sim::FormatOctOp op)
938 -> FailureOr<FormatInfo> {
939 FmtDescriptor
d = FmtDescriptor::createInt(
940 op.getValue().getType().getWidth(), 8, op.getIsLeftAligned(),
941 op.getSpecifierWidth().value_or(-1), op.getPaddingChar(),
false,
943 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
945 .Case<sim::FormatBinOp>([&](sim::FormatBinOp op)
946 -> FailureOr<FormatInfo> {
947 FmtDescriptor
d = FmtDescriptor::createInt(
948 op.getValue().getType().getWidth(), 2, op.getIsLeftAligned(),
949 op.getSpecifierWidth().value_or(-1), op.getPaddingChar(),
false,
951 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
953 .Case<sim::FormatLiteralOp>(
954 [&](sim::FormatLiteralOp op) -> FailureOr<FormatInfo> {
955 if (op.getLiteral().size() < 8 &&
956 op.getLiteral().find(
'\0') == StringRef::npos) {
959 FmtDescriptor::createSmallLiteral(op.getLiteral());
960 return FormatInfo{{
d}, {}};
963 FmtDescriptor::createLiteral(op.getLiteral().size());
964 Value value = cache.getOrCreate(rewriter, op.getLiteral());
965 return FormatInfo{{
d}, {value}};
967 .Case<sim::FormatStringConcatOp>(
968 [&](sim::FormatStringConcatOp op) -> FailureOr<FormatInfo> {
969 auto fmt = foldFormatString(rewriter, op.getInputs()[0], cache);
972 for (
auto input : op.getInputs().drop_front()) {
973 auto next = foldFormatString(rewriter, input, cache);
976 fmt->descriptors.append(next->descriptors);
977 fmt->args.append(next->args);
982 [](Operation *op) -> FailureOr<FormatInfo> {
return failure(); });
985FailureOr<LLVM::CallOp> emitFmtCall(OpBuilder &builder, Location loc,
986 StringCache &stringCache,
987 ArrayRef<FmtDescriptor> descriptors,
988 ValueRange args, Value stream = {}) {
990 builder.getInsertionBlock()->getParent()->getParentOfType<ModuleOp>();
991 MLIRContext *ctx = builder.getContext();
992 auto ptrType = LLVM::LLVMPointerType::get(ctx);
993 SmallVector<Type, 2> paramTypes;
994 StringRef symbolName = runtime::APICallbacks::symNameFormat;
996 symbolName = runtime::APICallbacks::symNameFormatToStream;
997 paramTypes.push_back(ptrType);
999 paramTypes.push_back(ptrType);
1001 auto func = LLVM::lookupOrCreateFn(builder, moduleOp, symbolName, paramTypes,
1002 LLVM::LLVMVoidType::get(ctx),
true);
1006 StringRef rawDescriptors(
reinterpret_cast<const char *
>(descriptors.data()),
1007 descriptors.size() *
sizeof(FmtDescriptor));
1008 Value fmtPtr = stringCache.getOrCreate(builder, rawDescriptors);
1010 SmallVector<Value> argsVec;
1012 argsVec.push_back(stream);
1013 argsVec.push_back(fmtPtr);
1014 argsVec.append(args.begin(), args.end());
1015 auto result = LLVM::CallOp::create(builder, loc,
func.value(), argsVec);
1017 for (Value arg : args) {
1018 Operation *definingOp = arg.getDefiningOp();
1019 if (
auto alloca = dyn_cast_if_present<LLVM::AllocaOp>(definingOp)) {
1020 LLVM::LifetimeEndOp::create(builder, loc, arg);
1027template <
typename SourceOp>
1029 SimStreamOpLowering(
const TypeConverter &typeConverter, MLIRContext *
context,
1030 StringRef symbolName)
1031 : OpConversionPattern<SourceOp>(typeConverter,
context),
1032 symbolName(symbolName) {}
1035 matchAndRewrite(SourceOp op,
typename SourceOp::Adaptor adaptor,
1036 ConversionPatternRewriter &rewriter)
const override {
1037 ModuleOp moduleOp = op->template getParentOfType<ModuleOp>();
1041 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
1043 LLVM::lookupOrCreateFn(rewriter, moduleOp, symbolName, {}, ptrType);
1048 LLVM::CallOp::create(rewriter, op.getLoc(), func.value(), ValueRange{});
1049 rewriter.replaceOp(op, call.getResults());
1053 StringRef symbolName;
1056struct SimPrintFormattedProcOpLowering
1058 SimPrintFormattedProcOpLowering(
const TypeConverter &typeConverter,
1060 StringCache &stringCache)
1061 : OpConversionPattern<sim::PrintFormattedProcOp>(typeConverter,
context),
1062 stringCache(stringCache) {}
1065 matchAndRewrite(sim::PrintFormattedProcOp op, OpAdaptor adaptor,
1066 ConversionPatternRewriter &rewriter)
const override {
1067 auto formatInfo = foldFormatString(rewriter, op.getInput(), stringCache);
1068 if (failed(formatInfo))
1069 return rewriter.notifyMatchFailure(op,
"unsupported format string");
1072 formatInfo->descriptors.push_back(FmtDescriptor());
1075 emitFmtCall(rewriter, op.getLoc(), stringCache, formatInfo->descriptors,
1076 formatInfo->args, adaptor.getStream());
1079 rewriter.replaceOp(op, result.value());
1084 StringCache &stringCache;
1088 using OpConversionPattern::OpConversionPattern;
1091 matchAndRewrite(arc::TerminateOp op, OpAdaptor adaptor,
1092 ConversionPatternRewriter &rewriter)
const override {
1093 auto loc = op.getLoc();
1095 auto i8Type = rewriter.getI8Type();
1096 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
1098 Value flagPtr = LLVM::GEPOp::create(
1099 rewriter, loc, ptrType, i8Type, adaptor.getArcContext(),
1100 ArrayRef<LLVM::GEPArg>{arc::kTerminateFlagOffset});
1102 uint8_t statusCode = op.getSuccess() ? 1 : 2;
1103 Value codeVal = LLVM::ConstantOp::create(
1104 rewriter, loc, i8Type, rewriter.getI8IntegerAttr(statusCode));
1106 LLVM::StoreOp::create(rewriter, loc, codeVal, flagPtr);
1108 rewriter.eraseOp(op);
1115struct GetNextWakeupOpLowering
1117 using OpConversionPattern::OpConversionPattern;
1120 matchAndRewrite(arc::GetNextWakeupOp op, OpAdaptor adaptor,
1121 ConversionPatternRewriter &rewriter)
const override {
1122 auto loc = op.getLoc();
1123 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
1124 Value slotPtr = LLVM::GEPOp::create(
1125 rewriter, loc, ptrType, rewriter.getI8Type(), adaptor.getArcContext(),
1126 ArrayRef<LLVM::GEPArg>{arc::kNextWakeupOffset});
1127 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, rewriter.getI64Type(),
1135struct SetNextWakeupOpLowering
1137 using OpConversionPattern::OpConversionPattern;
1140 matchAndRewrite(arc::SetNextWakeupOp op, OpAdaptor adaptor,
1141 ConversionPatternRewriter &rewriter)
const override {
1142 auto loc = op.getLoc();
1143 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
1144 Value slotPtr = LLVM::GEPOp::create(
1145 rewriter, loc, ptrType, rewriter.getI8Type(), adaptor.getArcContext(),
1146 ArrayRef<LLVM::GEPArg>{arc::kNextWakeupOffset});
1147 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getTime(), slotPtr);
1154static LogicalResult
convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor,
1155 ConversionPatternRewriter &rewriter,
1156 const TypeConverter &converter) {
1158 if (failed(rewriter.convertRegionTypes(&op.getBody(), converter)))
1164 auto *blockBefore = rewriter.getInsertionBlock();
1166 rewriter.splitBlock(blockBefore, rewriter.getInsertionPoint());
1169 rewriter.setInsertionPointToEnd(blockBefore);
1170 mlir::cf::BranchOp::create(rewriter, op.getLoc(), &op.getBody().front(),
1171 adaptor.getInputs());
1175 for (
auto &block : op.getBody()) {
1176 auto outputOp = dyn_cast<arc::OutputOp>(block.getTerminator());
1179 rewriter.setInsertionPointToEnd(&block);
1180 rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(outputOp, blockAfter,
1181 outputOp.getOperands());
1185 rewriter.inlineRegionBefore(op.getBody(), blockAfter);
1189 SmallVector<Value> args;
1190 args.reserve(op.getNumResults());
1191 for (
auto result : op.getResults())
1192 args.push_back(blockAfter->addArgument(result.getType(), result.getLoc()));
1193 rewriter.replaceOp(op, args);
1194 auto conversion = converter.convertBlockSignature(blockAfter);
1197 rewriter.applySignatureConversion(blockAfter, *conversion, &converter);
1205template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
1206static LLVM::GlobalOp
1208 SmallVectorImpl<T> &data,
1209 unsigned alignment =
alignof(T)) {
1210 auto intType = builder.getIntegerType(8 *
sizeof(T));
1211 Attribute denseAttr = mlir::DenseElementsAttr::get(
1212 mlir::RankedTensorType::get({(int64_t)data.size()}, intType),
1213 llvm::ArrayRef(data));
1214 auto globalOp = LLVM::GlobalOp::create(
1215 builder, loc, LLVM::LLVMArrayType::get(intType, data.size()),
1216 true, LLVM::Linkage::Internal,
1217 builder.getStringAttr(symName), denseAttr);
1218 globalOp.setAlignmentAttr(builder.getI64IntegerAttr(alignment));
1223template <
typename T>
1224static LLVM::GlobalOp
1227 SmallVectorImpl<T> &array) {
1229 static_assert(std::is_standard_layout<T>(),
1230 "Runtime struct must have standard layout");
1231 int64_t numBytes =
sizeof(T) * array.size();
1232 Attribute denseAttr = mlir::DenseElementsAttr::get(
1233 mlir::RankedTensorType::get({numBytes}, builder.getI8Type()),
1234 llvm::ArrayRef(
reinterpret_cast<uint8_t *
>(array.data()), numBytes));
1235 auto globalOp = LLVM::GlobalOp::create(
1236 builder, loc, LLVM::LLVMArrayType::get(builder.getI8Type(), numBytes),
1237 true, LLVM::Linkage::Internal,
1238 builder.getStringAttr(symName), denseAttr,
alignof(T));
1244 using OpConversionPattern::OpConversionPattern;
1251 ConversionPatternRewriter &rewriter)
const {
1252 if (!op.getTraceTaps().has_value() || op.getTraceTaps()->empty())
1255 SmallVector<char> namesArray;
1256 SmallVector<ArcTraceTap> tapArray;
1257 tapArray.reserve(op.getTraceTaps()->size());
1258 for (
auto attr : op.getTraceTapsAttr()) {
1259 auto tap = cast<TraceTapAttr>(attr);
1260 assert(!tap.getNames().empty() &&
1261 "Expected trace tap to have at least one name");
1262 for (
auto alias : tap.getNames()) {
1263 auto aliasStr = cast<StringAttr>(alias);
1264 namesArray.append(aliasStr.begin(), aliasStr.end());
1265 namesArray.push_back(
'\0');
1269 tapStruct.
nameOffset = namesArray.size() - 1;
1270 tapStruct.
typeBits = tap.getSigType().getValue().getIntOrFloatBitWidth();
1272 tapArray.emplace_back(tapStruct);
1274 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1276 rewriter, op.getLoc(),
"_arc_tap_names_" + op.getModelName(),
1279 rewriter, op.getLoc(),
"_arc_trace_taps_" + op.getModelName(),
1290 auto traceInfoStructType = LLVM::LLVMStructType::getLiteral(
1292 {rewriter.getI64Type(), ptrTy, ptrTy, rewriter.getI64Type()});
1294 "Unexpected size of ArcModelTraceInfo struct");
1296 auto globalSymName =
1297 rewriter.getStringAttr(
"_arc_trace_info_" + op.getModelName());
1298 auto traceInfoGlobalOp = LLVM::GlobalOp::create(
1299 rewriter, op.getLoc(), traceInfoStructType,
1300 false, LLVM::Linkage::Internal, globalSymName,
1302 OpBuilder::InsertionGuard g(rewriter);
1305 Region &initRegion = traceInfoGlobalOp.getInitializerRegion();
1306 Block *initBlock = rewriter.createBlock(&initRegion);
1307 rewriter.setInsertionPointToStart(initBlock);
1309 auto numTraceTapsCst = LLVM::ConstantOp::create(
1310 rewriter, op.getLoc(), rewriter.getI64IntegerAttr(tapArray.size()));
1311 auto traceTapArrayAddr =
1312 LLVM::AddressOfOp::create(rewriter, op.getLoc(), traceTapsArrayGlobal);
1313 auto tapNameArrayAddr =
1314 LLVM::AddressOfOp::create(rewriter, op.getLoc(), namesGlobal);
1315 auto bufferCapacityCst = LLVM::ConstantOp::create(
1316 rewriter, op.getLoc(),
1317 rewriter.getI64IntegerAttr(runtime::defaultTraceBufferCapacity));
1320 LLVM::PoisonOp::create(rewriter, op.getLoc(), traceInfoStructType);
1324 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1325 numTraceTapsCst, ArrayRef<int64_t>{0});
1327 "Unexpected offset of field numTraceTaps");
1330 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1331 traceTapArrayAddr, ArrayRef<int64_t>{1});
1333 "Unexpected offset of field traceTaps");
1336 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1337 tapNameArrayAddr, ArrayRef<int64_t>{2});
1339 "Unexpected offset of field traceTapNames");
1342 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1343 bufferCapacityCst, ArrayRef<int64_t>{3});
1345 "Unexpected offset of field traceBufferCapacity");
1346 LLVM::ReturnOp::create(rewriter, op.getLoc(), initStruct);
1348 return traceInfoGlobalOp;
1354 ConversionPatternRewriter &rewriter)
const final {
1356 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1357 auto modelInfoStructType = LLVM::LLVMStructType::getLiteral(
1359 {rewriter.getI64Type(), rewriter.getI64Type(), ptrTy, ptrTy});
1361 "Unexpected size of ArcRuntimeModelInfo struct");
1363 rewriter.setInsertionPoint(op);
1367 SmallVector<char, 16> modNameArray(op.getModelName().begin(),
1368 op.getModelName().end());
1369 modNameArray.push_back(
'\0');
1370 auto nameGlobalType =
1371 LLVM::LLVMArrayType::get(rewriter.getI8Type(), modNameArray.size());
1372 auto globalSymName =
1373 rewriter.getStringAttr(
"_arc_mod_name_" + op.getModelName());
1374 auto nameGlobal = LLVM::GlobalOp::create(
1375 rewriter, op.getLoc(), nameGlobalType,
true,
1376 LLVM::Linkage::Internal,
1377 globalSymName, rewriter.getStringAttr(modNameArray),
1384 auto modInfoGlobalOp =
1385 LLVM::GlobalOp::create(rewriter, op.getLoc(), modelInfoStructType,
1386 false, LLVM::Linkage::External,
1387 op.getSymName(), Attribute{});
1390 Region &initRegion = modInfoGlobalOp.getInitializerRegion();
1391 Block *initBlock = rewriter.createBlock(&initRegion);
1392 rewriter.setInsertionPointToStart(initBlock);
1393 auto apiVersionCst = LLVM::ConstantOp::create(
1395 auto numStateBytesCst = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1396 op.getNumStateBytesAttr());
1398 LLVM::AddressOfOp::create(rewriter, op.getLoc(), nameGlobal);
1400 if (traceInfoGlobal)
1402 LLVM::AddressOfOp::create(rewriter, op.getLoc(), traceInfoGlobal);
1404 traceInfoPtr = LLVM::ZeroOp::create(rewriter, op.getLoc(), ptrTy);
1407 LLVM::PoisonOp::create(rewriter, op.getLoc(), modelInfoStructType);
1410 initStruct = LLVM::InsertValueOp::create(
1411 rewriter, op.getLoc(), initStruct, apiVersionCst, ArrayRef<int64_t>{0});
1413 "Unexpected offset of field apiVersion");
1416 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1417 numStateBytesCst, ArrayRef<int64_t>{1});
1419 "Unexpected offset of field numStateBytes");
1421 initStruct = LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1422 nameAddr, ArrayRef<int64_t>{2});
1424 "Unexpected offset of field modelName");
1426 initStruct = LLVM::InsertValueOp::create(
1427 rewriter, op.getLoc(), initStruct, traceInfoPtr, ArrayRef<int64_t>{3});
1429 "Unexpected offset of field traceInfo");
1431 LLVM::ReturnOp::create(rewriter, op.getLoc(), initStruct);
1433 rewriter.replaceOp(op, modInfoGlobalOp);
1443 auto bitWidth = computeLLVMBitWidth(type);
1444 assert(bitWidth.has_value());
1445 return llvm::divideCeil(*bitWidth, 8);
1450 auto arrayBitWidth = computeLLVMBitWidth(arrayRefType);
1451 assert(arrayBitWidth.has_value());
1452 assert(arrayRefType.getNumElements() > 0 &&
1453 "Cannot compute stride for zero sized array");
1454 size_t elementBitWidth = *arrayBitWidth / arrayRefType.getNumElements();
1455 return llvm::divideCeil(elementBitWidth, 8);
1459 using OpConversionPattern::OpConversionPattern;
1463 ConversionPatternRewriter &rewriter)
const override {
1464 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1465 auto i8Ty = rewriter.getI8Type();
1466 ArrayRefType arrayRefType = op.getType();
1468 auto size = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1469 rewriter.getI64Type(), byteWidth);
1472 auto alloc = LLVM::AllocaOp::create(rewriter, op.getLoc(), ptrTy, i8Ty,
1475 if (op.getInitAttr()) {
1476 ArrayAttr initAttr = op.getInitAttr();
1478 auto i8Ty = rewriter.getI8Type();
1479 auto zero = LLVM::ConstantOp::create(rewriter, op.getLoc(), i8Ty, 0);
1480 LLVM::MemsetOp::create(rewriter, op.getLoc(), alloc, zero, size,
1483 initializeArray(rewriter, op.getLoc(), alloc, initAttr, arrayRefType);
1487 rewriter.replaceOp(op, alloc);
1497 auto dl = DataLayout::closest(op);
1499 hw::ArrayType::get(type.getElementType(), type.getNumElements());
1500 auto llvmType = getTypeConverter()->convertType(hwType);
1502 static_cast<unsigned>(dl.getTypePreferredAlignment(llvmType));
1503 alignment = std::max(4u, alignment);
1509 if (
auto intAttr = dyn_cast<IntegerAttr>(attr))
1510 return intAttr.getValue().isZero();
1511 if (
auto arrayAttr = dyn_cast<ArrayAttr>(attr))
1512 return llvm::all_of(arrayAttr, [](Attribute a) {
return isZero(a); });
1517 Value alloc, ArrayAttr initAttr,
1518 ArrayRefType arrayRefType)
const {
1520 Type ptrTy = LLVM::LLVMPointerType::get(getContext());
1521 Type i8Ty = rewriter.getI8Type();
1522 for (
unsigned i = 0; i < arrayRefType.getNumElements(); ++i) {
1523 unsigned elemIndex = arrayRefType.getNumElements() - i - 1;
1524 Value elemOffset = LLVM::ConstantOp::create(
1525 rewriter, loc, rewriter.getI64Type(), elemIndex * elemByteWidth);
1527 LLVM::GEPOp::create(rewriter, loc, ptrTy, i8Ty, alloc, elemOffset);
1528 auto elem = LLVM::ConstantOp::create(
1529 rewriter, loc, arrayRefType.getElementType(), initAttr[i]);
1530 LLVM::StoreOp::create(rewriter, loc, elem, elemAddr);
1539 using OpConversionPattern::OpConversionPattern;
1543 ConversionPatternRewriter &rewriter)
const override {
1544 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getType());
1545 Value alloc = adaptor.getInput();
1546 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1547 auto i8Ty = rewriter.getI8Type();
1549 auto elements = adaptor.getElements();
1550 for (
unsigned i = 0; i < elements.size(); ++i) {
1552 unsigned elemIndex = arrayRefType.getNumElements() - i - 1;
1554 LLVM::ConstantOp::create(rewriter, op.getLoc(), rewriter.getI64Type(),
1555 elemIndex * elemByteWidth);
1556 auto elemAddr = LLVM::GEPOp::create(rewriter, op.getLoc(), ptrTy, i8Ty,
1558 LLVM::StoreOp::create(rewriter, op.getLoc(), elements[i], elemAddr);
1560 rewriter.replaceOp(op, alloc);
1566 using OpConversionPattern::OpConversionPattern;
1570 ConversionPatternRewriter &rewriter)
const override {
1571 auto loc = op.getLoc();
1572 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getInput().getType());
1573 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1574 auto i8Ty = rewriter.getI8Type();
1575 auto i64Ty = rewriter.getI64Type();
1577 assert(!isa<ArrayRefType>(arrayRefType.getElementType()));
1580 LLVM::ConstantOp::create(rewriter, loc, i64Ty, elemByteWidth);
1582 LLVM::MulOp::create(rewriter, loc, adaptor.getIndex(), stride);
1585 size_t lastElementByteOffset =
1586 elemByteWidth * (arrayRefType.getNumElements() - 1);
1587 Value lastElementByteOffsetVal =
1588 LLVM::ConstantOp::create(rewriter, loc, i64Ty, lastElementByteOffset);
1589 Value clampedOffset = LLVM::UMinOp::create(rewriter, loc, i64Ty, byteOffset,
1590 lastElementByteOffsetVal);
1591 auto elemAddr = LLVM::GEPOp::create(rewriter, loc, ptrTy, i8Ty,
1592 adaptor.getInput(), clampedOffset);
1593 Value loaded = LLVM::LoadOp::create(
1594 rewriter, loc, typeConverter->convertType(op.getValue().getType()),
1596 rewriter.replaceOp(op, loaded);
1602 using OpConversionPattern::OpConversionPattern;
1606 ConversionPatternRewriter &rewriter)
const override {
1607 auto loc = op.getLoc();
1608 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getInput().getType());
1609 assert(!isa<ArrayRefType>(arrayRefType.getElementType()));
1610 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1611 auto i8Ty = rewriter.getI8Type();
1612 auto i64Ty = rewriter.getI64Type();
1617 LLVM::ConstantOp::create(rewriter, loc, i64Ty, elemByteWidth);
1619 LLVM::MulOp::create(rewriter, loc, adaptor.getIndex(), stride);
1620 Value totalSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty, byteWidth);
1623 Value isInbounds = LLVM::ICmpOp::create(
1624 rewriter, loc, LLVM::ICmpPredicate::ult, byteOffset, totalSize);
1625 scf::IfOp::create(rewriter, loc, isInbounds, [&](OpBuilder &b, Location) {
1626 auto elemAddr = LLVM::GEPOp::create(b, loc, ptrTy, i8Ty,
1627 adaptor.getInput(), byteOffset);
1628 LLVM::StoreOp::create(b, loc, adaptor.getElement(), elemAddr);
1629 scf::YieldOp::create(b, loc);
1634 rewriter.replaceOp(op, adaptor.getInput());
1640 using OpConversionPattern::OpConversionPattern;
1644 ConversionPatternRewriter &rewriter)
const override {
1645 auto loc = op.getLoc();
1647 ArrayRefType inputType = cast<ArrayRefType>(op.getInput().getType());
1648 ArrayRefType resultType = cast<ArrayRefType>(op.getOutput().getType());
1649 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1650 auto i8Ty = rewriter.getI8Type();
1651 auto i64Ty = rewriter.getI64Type();
1655 size_t maxLowIndex =
1656 inputType.getNumElements() - resultType.getNumElements();
1657 Value maxLowIndexVal =
1658 LLVM::ConstantOp::create(rewriter, loc, i64Ty, maxLowIndex);
1659 Value clampedLowIndex = LLVM::UMinOp::create(
1660 rewriter, loc, i64Ty, adaptor.getLowIndex(), maxLowIndexVal);
1664 LLVM::ConstantOp::create(rewriter, loc, i64Ty, elemByteWidth);
1666 LLVM::MulOp::create(rewriter, loc, clampedLowIndex, stride);
1667 auto sliceAddr = LLVM::GEPOp::create(rewriter, loc, ptrTy, i8Ty,
1668 adaptor.getInput(), byteOffset);
1669 rewriter.replaceOp(op, sliceAddr);
1675 using OpConversionPattern::OpConversionPattern;
1679 ConversionPatternRewriter &rewriter)
const override {
1680 auto loc = op.getLoc();
1681 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getInput().getType());
1682 auto i64Ty = rewriter.getI64Type();
1684 Value size = LLVM::ConstantOp::create(rewriter, loc, i64Ty, byteWidth);
1686 LLVM::MemmoveOp::create(rewriter, loc, adaptor.getInput(),
1687 adaptor.getSource(), size,
1689 rewriter.replaceOp(op, adaptor.getInput());
1695 ArrayRefType arrayRefType,
1696 LLVM::LLVMArrayType llvmType) {
1697 auto i8Ty = builder.getI8Type();
1698 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
1700 Value v = LLVM::PoisonOp::create(builder, llvmType);
1701 int32_t size = arrayRefType.getNumElements();
1702 for (int32_t i = 0; i < size; i++) {
1703 int32_t byteOffset = i * elemByteWidth;
1704 Value gep = LLVM::GEPOp::create(builder, ptrTy, i8Ty, arrayRef,
1705 LLVM::GEPArg{byteOffset});
1706 Value load = LLVM::LoadOp::create(builder, llvmType.getElementType(), gep);
1707 v = LLVM::InsertValueOp::create(builder, v, load, i);
1713 Value arrayRef, ArrayRefType arrayRefType) {
1714 auto i8Ty = builder.getI8Type();
1715 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
1717 int32_t size = arrayRefType.getNumElements();
1718 for (int32_t i = 0; i < size; i++) {
1719 int32_t byteOffset = i * elemByteWidth;
1720 Value gep = LLVM::GEPOp::create(builder, ptrTy, i8Ty, arrayRef,
1721 LLVM::GEPArg{byteOffset});
1722 Value val = LLVM::ExtractValueOp::create(builder, array, i);
1723 LLVM::StoreOp::create(builder, val, gep);
1729 using OpConversionPattern::OpConversionPattern;
1733 ConversionPatternRewriter &rewriter)
const override {
1734 if (!isa<ArrayRefType>(op.getOperand(0).getType()) ||
1735 !isa<LLVM::LLVMArrayType>(op.getResult(0).getType())) {
1739 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1741 b, adaptor.getInputs().front(),
1742 cast<ArrayRefType>(op.getOperand(0).getType()),
1743 cast<LLVM::LLVMArrayType>(op.getResult(0).getType()));
1744 rewriter.replaceOp(op, loaded);
1751 using OpConversionPattern::OpConversionPattern;
1755 ConversionPatternRewriter &rewriter)
const override {
1756 Type resultType = getTypeConverter()->convertType(op.getResult().getType());
1757 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1759 b, adaptor.getInput(), cast<ArrayRefType>(op.getInput().getType()),
1760 cast<LLVM::LLVMArrayType>(resultType));
1761 rewriter.replaceOp(op, loaded);
1768 using OpConversionPattern::OpConversionPattern;
1772 ConversionPatternRewriter &rewriter)
const override {
1773 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1775 cast<ArrayRefType>(op.getInput().getType()));
1776 rewriter.replaceOp(op, adaptor.getInput());
1786struct LowerArcToLLVMPass
1787 :
public circt::impl::LowerArcToLLVMBase<LowerArcToLLVMPass> {
1788 void runOnOperation()
override;
1792void LowerArcToLLVMPass::runOnOperation() {
1795 for (func::FuncOp func : getOperation().getOps<func::FuncOp>()) {
1796 for (
int i = 0, e = func.getNumArguments(); i != e; ++i) {
1797 if (
auto arrayRefType =
1798 dyn_cast<ArrayRefType>(func.getArgumentTypes()[i])) {
1800 Builder builder(&getContext());
1801 func.setArgAttr(i, LLVM::LLVMDialect::getDereferenceableAttrName(),
1802 builder.getI64IntegerAttr(byteWidth));
1818 LLVMConversionTarget target(getContext());
1819 target.addLegalOp<mlir::ModuleOp>();
1820 target.addLegalOp<scf::YieldOp>();
1825 target.addLegalOp<sim::FormatLiteralOp, sim::FormatDecOp, sim::FormatHexOp,
1826 sim::FormatBinOp, sim::FormatOctOp, sim::FormatCharOp,
1827 sim::FormatStringConcatOp>();
1830 LLVMTypeConverter converter(&getContext());
1831 converter.addConversion([&](seq::ClockType type) {
1832 return IntegerType::get(type.getContext(), 1);
1834 converter.addConversion([&](StorageType type) {
1835 return LLVM::LLVMPointerType::get(type.getContext());
1837 converter.addConversion([&](ContextType type) {
1838 return LLVM::LLVMPointerType::get(type.getContext());
1840 converter.addConversion([&](MemoryType type) {
1841 return LLVM::LLVMPointerType::get(type.getContext());
1843 converter.addConversion([&](StateType type) {
1844 return LLVM::LLVMPointerType::get(type.getContext());
1846 converter.addConversion([&](SimModelInstanceType type) {
1847 return LLVM::LLVMPointerType::get(type.getContext());
1849 converter.addConversion([&](sim::FormatStringType type) {
1850 return LLVM::LLVMPointerType::get(type.getContext());
1852 converter.addConversion([&](sim::OutputStreamType type) {
1853 return LLVM::LLVMPointerType::get(type.getContext());
1855 converter.addConversion([&](llhd::TimeType type) {
1857 return IntegerType::get(type.getContext(), 64);
1859 converter.addConversion([&](ArrayRefType type) {
1860 return LLVM::LLVMPointerType::get(type.getContext());
1865 target.addDynamicallyLegalOp<UnrealizedConversionCastOp>([&](Operation *op) {
1866 Type src = op->getOperand(0).getType();
1867 Type dst = op->getResult(0).getType();
1868 bool needsConvert = isa<ArrayRefType>(src) && isa<LLVM::LLVMArrayType>(dst);
1869 return !needsConvert;
1873 ConversionPatternSet patterns(&getContext(), converter);
1876 populateSCFToControlFlowConversionPatterns(patterns);
1877 populateFuncToLLVMConversionPatterns(converter, patterns);
1878 cf::populateControlFlowToLLVMConversionPatterns(converter, patterns);
1879 arith::populateArithToLLVMConversionPatterns(converter, patterns);
1880 index::populateIndexToLLVMConversionPatterns(converter, patterns);
1881 ub::populateUBToLLVMConversionPatterns(converter, patterns);
1882 populateAnyFunctionOpInterfaceTypeConversionPattern(patterns, converter);
1885 DataLayout layout = DataLayout::closest(getOperation());
1886 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
1888 std::optional<HWToLLVMArraySpillCache> spillCacheOpt =
1889 HWToLLVMArraySpillCache();
1891 OpBuilder spillBuilder(getOperation());
1892 spillCacheOpt->spillNonHWOps(spillBuilder, converter, getOperation());
1895 constAggregateGlobalsMap, spillCacheOpt);
1903 AllocMemoryOpLowering,
1904 AllocStateLikeOpLowering<arc::AllocStateOp>,
1905 AllocStateLikeOpLowering<arc::RootInputOp>,
1906 AllocStateLikeOpLowering<arc::RootOutputOp>,
1907 AllocStorageOpLowering,
1908 AsContextOpLowering,
1909 ClockGateOpLowering,
1911 ConstantTimeOpLowering,
1912 CurrentTimeOpLowering,
1913 GetNextWakeupOpLowering,
1914 IntToTimeOpLowering,
1915 MemoryReadOpLowering,
1916 MemoryWriteOpLowering,
1918 ReplaceOpWithInputPattern<seq::ToClockOp>,
1919 ReplaceOpWithInputPattern<seq::FromClockOp>,
1920 RuntimeModelOpLowering,
1921 SeqConstClockLowering,
1922 SetNextWakeupOpLowering,
1923 SimSetTimeOpLowering,
1924 StateReadOpLowering,
1925 StateWriteOpLowering,
1926 StorageGetOpLowering,
1927 TerminateOpLowering,
1928 TimeToIntOpLowering,
1929 ZeroCountOpLowering,
1930 ArrayRefCreateOpLowering,
1931 ArrayRefAllocOpLowering,
1932 ArrayRefGetOpLowering,
1933 ArrayRefInjectOpLowering,
1934 ArrayRefSliceOpLowering,
1935 ArrayRefCopyOpLowering,
1936 ArrayRefToLLVMArrayOpLowering,
1937 ArrayRefToArrayOpLowering,
1938 ArrayRefFromArrayOpLowering
1939 >(converter, &getContext());
1941 patterns.add<ExecuteOp>(
convert);
1943 StringCache stringCache;
1944 patterns.add<SimEmitValueOpLowering, SimPrintFormattedProcOpLowering>(
1945 converter, &getContext(), stringCache);
1946 patterns.add<SimStreamOpLowering<sim::StdoutStreamOp>>(
1947 converter, &getContext(), runtime::APICallbacks::symNameGetStdoutStream);
1948 patterns.add<SimStreamOpLowering<sim::StderrStreamOp>>(
1949 converter, &getContext(), runtime::APICallbacks::symNameGetStderrStream);
1951 auto &modelInfo = getAnalysis<ModelInfoAnalysis>();
1952 llvm::DenseMap<StringRef, ModelInfoMap> modelMap(modelInfo.infoMap.size());
1953 for (
auto &[_, modelInfo] : modelInfo.infoMap) {
1954 llvm::DenseMap<StringRef, StateInfo> states(modelInfo.states.size());
1955 for (StateInfo &stateInfo : modelInfo.states)
1956 states.insert({stateInfo.name, stateInfo});
1959 ModelInfoMap{modelInfo.numStateBytes, std::move(states),
1960 modelInfo.initialFnSym, modelInfo.finalFnSym}});
1963 patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
1964 SimGetPortOpLowering, SimStepOpLowering>(
1965 converter, &getContext(), modelMap);
1968 ConversionConfig config;
1969 config.allowPatternRollback =
false;
1970 if (failed(applyFullConversion(getOperation(), target, std::move(patterns),
1972 signalPassFailure();
1976 return std::make_unique<LowerArcToLLVMPass>();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LLVM::GlobalOp buildGlobalConstantIntArray(OpBuilder &builder, Location loc, Twine symName, SmallVectorImpl< T > &data, unsigned alignment=alignof(T))
static LLVM::GlobalOp buildGlobalConstantRuntimeStructArray(OpBuilder &builder, Location loc, Twine symName, SmallVectorImpl< T > &array)
static Value loadArrayRefAsArray(ImplicitLocOpBuilder &builder, Value arrayRef, ArrayRefType arrayRefType, LLVM::LLVMArrayType llvmType)
size_t computeByteWidth(ArrayRefType type)
static llvm::Twine evalSymbolFromModelName(StringRef modelName)
size_t computeElementByteWidth(ArrayRefType arrayRefType)
static void storeArrayAsArrayRef(ImplicitLocOpBuilder &builder, Value array, Value arrayRef, ArrayRefType arrayRefType)
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
void add(mlir::ModuleOp module)
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
#define ARC_RUNTIME_API_VERSION
Version of the combined public and internal API.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
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, mlir::DataLayout &layout)
Get the HW to LLVM type conversions.
void populateHWToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, Namespace &globals, DenseMap< std::pair< Type, ArrayAttr >, mlir::LLVM::GlobalOp > &constAggregateGlobalsMap, std::optional< HWToLLVMArraySpillCache > &spillCacheOpt)
Get the HW to LLVM conversion patterns.
std::unique_ptr< OperationPass< ModuleOp > > createLowerArcToLLVMPass()
Static information for a compiled hardware model, generated by the MLIR lowering.
uint32_t typeBits
Bit width of the traced signal.
uint64_t stateOffset
Byte offset of the traced value within the model state.
uint64_t nameOffset
Byte offset to the null terminator of this signal's last alias in the names array.
uint32_t reserved
Padding and reserved for future use.
void initializeArray(ConversionPatternRewriter &rewriter, Location loc, Value alloc, ArrayAttr initAttr, ArrayRefType arrayRefType) const
size_t computeAllocaAlignment(ArrayRefType type, Operation *op) const
LogicalResult matchAndRewrite(ArrayRefAllocOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
static bool isZero(Attribute attr)
DenseMap< ArrayRefType, size_t > alignmentCache
LogicalResult matchAndRewrite(ArrayRefCopyOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ArrayRefCreateOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ArrayRefFromArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ArrayRefGetOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ArrayRefInjectOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ArrayRefSliceOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ArrayRefToArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LLVM::GlobalOp buildTraceInfoStruct(arc::RuntimeModelOp &op, ConversionPatternRewriter &rewriter) const
static constexpr uint64_t runtimeApiVersion
LogicalResult matchAndRewrite(arc::RuntimeModelOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final