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>
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 || attr.getEpsilon() != 0)
211 return rewriter.notifyMatchFailure(
212 op,
"non-zero delta or epsilon time components are 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);
311 LLVM::ConstantOp::create(rewriter, loc, zextAddrType,
312 rewriter.getI32IntegerAttr(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.getI64IntegerAttr(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>
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)
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)
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)
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)
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.getName(), namesArray);
1278 rewriter, op.getLoc(),
"_arc_trace_taps_" + op.getName(), tapArray);
1288 auto traceInfoStructType = LLVM::LLVMStructType::getLiteral(
1290 {rewriter.getI64Type(), ptrTy, ptrTy, rewriter.getI64Type()});
1292 "Unexpected size of ArcModelTraceInfo struct");
1294 auto globalSymName =
1295 rewriter.getStringAttr(
"_arc_trace_info_" + op.getName());
1296 auto traceInfoGlobalOp = LLVM::GlobalOp::create(
1297 rewriter, op.getLoc(), traceInfoStructType,
1298 false, LLVM::Linkage::Internal, globalSymName,
1300 OpBuilder::InsertionGuard g(rewriter);
1303 Region &initRegion = traceInfoGlobalOp.getInitializerRegion();
1304 Block *initBlock = rewriter.createBlock(&initRegion);
1305 rewriter.setInsertionPointToStart(initBlock);
1307 auto numTraceTapsCst = LLVM::ConstantOp::create(
1308 rewriter, op.getLoc(), rewriter.getI64IntegerAttr(tapArray.size()));
1309 auto traceTapArrayAddr =
1310 LLVM::AddressOfOp::create(rewriter, op.getLoc(), traceTapsArrayGlobal);
1311 auto tapNameArrayAddr =
1312 LLVM::AddressOfOp::create(rewriter, op.getLoc(), namesGlobal);
1313 auto bufferCapacityCst = LLVM::ConstantOp::create(
1314 rewriter, op.getLoc(),
1315 rewriter.getI64IntegerAttr(runtime::defaultTraceBufferCapacity));
1318 LLVM::PoisonOp::create(rewriter, op.getLoc(), traceInfoStructType);
1322 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1323 numTraceTapsCst, ArrayRef<int64_t>{0});
1325 "Unexpected offset of field numTraceTaps");
1328 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1329 traceTapArrayAddr, ArrayRef<int64_t>{1});
1331 "Unexpected offset of field traceTaps");
1334 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1335 tapNameArrayAddr, ArrayRef<int64_t>{2});
1337 "Unexpected offset of field traceTapNames");
1340 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1341 bufferCapacityCst, ArrayRef<int64_t>{3});
1343 "Unexpected offset of field traceBufferCapacity");
1344 LLVM::ReturnOp::create(rewriter, op.getLoc(), initStruct);
1346 return traceInfoGlobalOp;
1352 ConversionPatternRewriter &rewriter)
const final {
1354 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1355 auto modelInfoStructType = LLVM::LLVMStructType::getLiteral(
1357 {rewriter.getI64Type(), rewriter.getI64Type(), ptrTy, ptrTy});
1359 "Unexpected size of ArcRuntimeModelInfo struct");
1361 rewriter.setInsertionPoint(op);
1365 SmallVector<char, 16> modNameArray(op.getName().begin(),
1366 op.getName().end());
1367 modNameArray.push_back(
'\0');
1368 auto nameGlobalType =
1369 LLVM::LLVMArrayType::get(rewriter.getI8Type(), modNameArray.size());
1370 auto globalSymName =
1371 rewriter.getStringAttr(
"_arc_mod_name_" + op.getName());
1372 auto nameGlobal = LLVM::GlobalOp::create(
1373 rewriter, op.getLoc(), nameGlobalType,
true,
1374 LLVM::Linkage::Internal,
1375 globalSymName, rewriter.getStringAttr(modNameArray),
1382 auto modInfoGlobalOp =
1383 LLVM::GlobalOp::create(rewriter, op.getLoc(), modelInfoStructType,
1384 false, LLVM::Linkage::External,
1385 op.getSymName(), Attribute{});
1388 Region &initRegion = modInfoGlobalOp.getInitializerRegion();
1389 Block *initBlock = rewriter.createBlock(&initRegion);
1390 rewriter.setInsertionPointToStart(initBlock);
1391 auto apiVersionCst = LLVM::ConstantOp::create(
1393 auto numStateBytesCst = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1394 op.getNumStateBytesAttr());
1396 LLVM::AddressOfOp::create(rewriter, op.getLoc(), nameGlobal);
1398 if (traceInfoGlobal)
1400 LLVM::AddressOfOp::create(rewriter, op.getLoc(), traceInfoGlobal);
1402 traceInfoPtr = LLVM::ZeroOp::create(rewriter, op.getLoc(), ptrTy);
1405 LLVM::PoisonOp::create(rewriter, op.getLoc(), modelInfoStructType);
1408 initStruct = LLVM::InsertValueOp::create(
1409 rewriter, op.getLoc(), initStruct, apiVersionCst, ArrayRef<int64_t>{0});
1411 "Unexpected offset of field apiVersion");
1414 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1415 numStateBytesCst, ArrayRef<int64_t>{1});
1417 "Unexpected offset of field numStateBytes");
1419 initStruct = LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1420 nameAddr, ArrayRef<int64_t>{2});
1422 "Unexpected offset of field modelName");
1424 initStruct = LLVM::InsertValueOp::create(
1425 rewriter, op.getLoc(), initStruct, traceInfoPtr, ArrayRef<int64_t>{3});
1427 "Unexpected offset of field traceInfo");
1429 LLVM::ReturnOp::create(rewriter, op.getLoc(), initStruct);
1431 rewriter.replaceOp(op, modInfoGlobalOp);
1441 auto bitWidth = computeLLVMBitWidth(type);
1442 assert(bitWidth.has_value());
1443 return llvm::divideCeil(*bitWidth, 8);
1448 auto arrayBitWidth = computeLLVMBitWidth(arrayRefType);
1449 assert(arrayBitWidth.has_value());
1450 assert(arrayRefType.getNumElements() > 0 &&
1451 "Cannot compute stride for zero sized array");
1452 size_t elementBitWidth = *arrayBitWidth / arrayRefType.getNumElements();
1453 return llvm::divideCeil(elementBitWidth, 8);
1457 using OpConversionPattern::OpConversionPattern;
1461 ConversionPatternRewriter &rewriter)
const override {
1462 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1463 auto i8Ty = rewriter.getI8Type();
1464 ArrayRefType arrayRefType = op.getType();
1466 auto size = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1467 rewriter.getI64Type(), byteWidth);
1470 auto alloc = LLVM::AllocaOp::create(rewriter, op.getLoc(), ptrTy, i8Ty,
1473 if (op.getInitAttr()) {
1474 ArrayAttr initAttr = op.getInitAttr();
1476 auto i8Ty = rewriter.getI8Type();
1477 auto zero = LLVM::ConstantOp::create(rewriter, op.getLoc(), i8Ty, 0);
1478 LLVM::MemsetOp::create(rewriter, op.getLoc(), alloc, zero, size,
1481 initializeArray(rewriter, op.getLoc(), alloc, initAttr, arrayRefType);
1485 rewriter.replaceOp(op, alloc);
1495 auto dl = DataLayout::closest(op);
1497 hw::ArrayType::get(type.getElementType(), type.getNumElements());
1498 auto llvmType = getTypeConverter()->convertType(hwType);
1500 static_cast<unsigned>(dl.getTypePreferredAlignment(llvmType));
1501 alignment = std::max(4u, alignment);
1507 if (
auto intAttr = dyn_cast<IntegerAttr>(attr))
1508 return intAttr.getValue().isZero();
1509 if (
auto arrayAttr = dyn_cast<ArrayAttr>(attr))
1510 return llvm::all_of(arrayAttr, [](Attribute a) {
return isZero(a); });
1515 Value alloc, ArrayAttr initAttr,
1516 ArrayRefType arrayRefType)
const {
1518 Type ptrTy = LLVM::LLVMPointerType::get(getContext());
1519 Type i8Ty = rewriter.getI8Type();
1520 for (
unsigned i = 0; i < arrayRefType.getNumElements(); ++i) {
1521 unsigned elemIndex = arrayRefType.getNumElements() - i - 1;
1522 Value elemOffset = LLVM::ConstantOp::create(
1523 rewriter, loc, rewriter.getI64Type(), elemIndex * elemByteWidth);
1525 LLVM::GEPOp::create(rewriter, loc, ptrTy, i8Ty, alloc, elemOffset);
1526 auto elem = LLVM::ConstantOp::create(
1527 rewriter, loc, arrayRefType.getElementType(), initAttr[i]);
1528 LLVM::StoreOp::create(rewriter, loc, elem, elemAddr);
1537 using OpConversionPattern::OpConversionPattern;
1541 ConversionPatternRewriter &rewriter)
const override {
1542 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getType());
1543 Value alloc = adaptor.getInput();
1544 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1545 auto i8Ty = rewriter.getI8Type();
1547 auto elements = adaptor.getElements();
1548 for (
unsigned i = 0; i < elements.size(); ++i) {
1550 unsigned elemIndex = arrayRefType.getNumElements() - i - 1;
1552 LLVM::ConstantOp::create(rewriter, op.getLoc(), rewriter.getI64Type(),
1553 elemIndex * elemByteWidth);
1554 auto elemAddr = LLVM::GEPOp::create(rewriter, op.getLoc(), ptrTy, i8Ty,
1556 LLVM::StoreOp::create(rewriter, op.getLoc(), elements[i], elemAddr);
1558 rewriter.replaceOp(op, alloc);
1564 using OpConversionPattern::OpConversionPattern;
1568 ConversionPatternRewriter &rewriter)
const override {
1569 auto loc = op.getLoc();
1570 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getInput().getType());
1571 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1572 auto i8Ty = rewriter.getI8Type();
1573 auto i64Ty = rewriter.getI64Type();
1575 assert(!isa<ArrayRefType>(arrayRefType.getElementType()));
1578 LLVM::ConstantOp::create(rewriter, loc, i64Ty, elemByteWidth);
1580 LLVM::MulOp::create(rewriter, loc, adaptor.getIndex(), stride);
1583 size_t lastElementByteOffset =
1584 elemByteWidth * (arrayRefType.getNumElements() - 1);
1585 Value lastElementByteOffsetVal =
1586 LLVM::ConstantOp::create(rewriter, loc, i64Ty, lastElementByteOffset);
1587 Value clampedOffset = LLVM::UMinOp::create(rewriter, loc, i64Ty, byteOffset,
1588 lastElementByteOffsetVal);
1589 auto elemAddr = LLVM::GEPOp::create(rewriter, loc, ptrTy, i8Ty,
1590 adaptor.getInput(), clampedOffset);
1591 Value loaded = LLVM::LoadOp::create(
1592 rewriter, loc, typeConverter->convertType(op.getValue().getType()),
1594 rewriter.replaceOp(op, loaded);
1600 using OpConversionPattern::OpConversionPattern;
1604 ConversionPatternRewriter &rewriter)
const override {
1605 auto loc = op.getLoc();
1606 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getInput().getType());
1607 assert(!isa<ArrayRefType>(arrayRefType.getElementType()));
1608 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1609 auto i8Ty = rewriter.getI8Type();
1610 auto i64Ty = rewriter.getI64Type();
1615 LLVM::ConstantOp::create(rewriter, loc, i64Ty, elemByteWidth);
1617 LLVM::MulOp::create(rewriter, loc, adaptor.getIndex(), stride);
1618 Value totalSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty, byteWidth);
1621 Value isInbounds = LLVM::ICmpOp::create(
1622 rewriter, loc, LLVM::ICmpPredicate::ult, byteOffset, totalSize);
1623 scf::IfOp::create(rewriter, loc, isInbounds, [&](OpBuilder &b, Location) {
1624 auto elemAddr = LLVM::GEPOp::create(b, loc, ptrTy, i8Ty,
1625 adaptor.getInput(), byteOffset);
1626 LLVM::StoreOp::create(b, loc, adaptor.getElement(), elemAddr);
1627 scf::YieldOp::create(b, loc);
1632 rewriter.replaceOp(op, adaptor.getInput());
1638 using OpConversionPattern::OpConversionPattern;
1642 ConversionPatternRewriter &rewriter)
const override {
1643 auto loc = op.getLoc();
1645 ArrayRefType inputType = cast<ArrayRefType>(op.getInput().getType());
1646 ArrayRefType resultType = cast<ArrayRefType>(op.getOutput().getType());
1647 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1648 auto i8Ty = rewriter.getI8Type();
1649 auto i64Ty = rewriter.getI64Type();
1653 size_t maxLowIndex =
1654 inputType.getNumElements() - resultType.getNumElements();
1655 Value maxLowIndexVal =
1656 LLVM::ConstantOp::create(rewriter, loc, i64Ty, maxLowIndex);
1657 Value clampedLowIndex = LLVM::UMinOp::create(
1658 rewriter, loc, i64Ty, adaptor.getLowIndex(), maxLowIndexVal);
1662 LLVM::ConstantOp::create(rewriter, loc, i64Ty, elemByteWidth);
1664 LLVM::MulOp::create(rewriter, loc, clampedLowIndex, stride);
1665 auto sliceAddr = LLVM::GEPOp::create(rewriter, loc, ptrTy, i8Ty,
1666 adaptor.getInput(), byteOffset);
1667 rewriter.replaceOp(op, sliceAddr);
1673 using OpConversionPattern::OpConversionPattern;
1677 ConversionPatternRewriter &rewriter)
const override {
1678 auto loc = op.getLoc();
1679 ArrayRefType arrayRefType = cast<ArrayRefType>(op.getInput().getType());
1680 auto i64Ty = rewriter.getI64Type();
1682 Value size = LLVM::ConstantOp::create(rewriter, loc, i64Ty, byteWidth);
1684 LLVM::MemmoveOp::create(rewriter, loc, adaptor.getInput(),
1685 adaptor.getSource(), size,
1687 rewriter.replaceOp(op, adaptor.getInput());
1693 ArrayRefType arrayRefType,
1694 LLVM::LLVMArrayType llvmType) {
1695 auto i8Ty = builder.getI8Type();
1696 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
1698 Value v = LLVM::PoisonOp::create(builder, llvmType);
1699 int32_t size = arrayRefType.getNumElements();
1700 for (int32_t i = 0; i < size; i++) {
1701 int32_t byteOffset = i * elemByteWidth;
1702 Value gep = LLVM::GEPOp::create(builder, ptrTy, i8Ty, arrayRef,
1703 LLVM::GEPArg{byteOffset});
1704 Value load = LLVM::LoadOp::create(builder, llvmType.getElementType(), gep);
1705 v = LLVM::InsertValueOp::create(builder, v, load, i);
1711 Value arrayRef, ArrayRefType arrayRefType) {
1712 auto i8Ty = builder.getI8Type();
1713 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
1715 int32_t size = arrayRefType.getNumElements();
1716 for (int32_t i = 0; i < size; i++) {
1717 int32_t byteOffset = i * elemByteWidth;
1718 Value gep = LLVM::GEPOp::create(builder, ptrTy, i8Ty, arrayRef,
1719 LLVM::GEPArg{byteOffset});
1720 Value val = LLVM::ExtractValueOp::create(builder, array, i);
1721 LLVM::StoreOp::create(builder, val, gep);
1727 using OpConversionPattern::OpConversionPattern;
1731 ConversionPatternRewriter &rewriter)
const override {
1732 if (!isa<ArrayRefType>(op.getOperand(0).getType()) ||
1733 !isa<LLVM::LLVMArrayType>(op.getResult(0).getType())) {
1737 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1739 b, adaptor.getInputs().front(),
1740 cast<ArrayRefType>(op.getOperand(0).getType()),
1741 cast<LLVM::LLVMArrayType>(op.getResult(0).getType()));
1742 rewriter.replaceOp(op, loaded);
1749 using OpConversionPattern::OpConversionPattern;
1753 ConversionPatternRewriter &rewriter)
const override {
1754 Type resultType = getTypeConverter()->convertType(op.getResult().getType());
1755 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1757 b, adaptor.getInput(), cast<ArrayRefType>(op.getInput().getType()),
1758 cast<LLVM::LLVMArrayType>(resultType));
1759 rewriter.replaceOp(op, loaded);
1766 using OpConversionPattern::OpConversionPattern;
1770 ConversionPatternRewriter &rewriter)
const override {
1771 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1773 cast<ArrayRefType>(op.getInput().getType()));
1774 rewriter.replaceOp(op, adaptor.getInput());
1784struct LowerArcToLLVMPass
1785 :
public circt::impl::LowerArcToLLVMBase<LowerArcToLLVMPass> {
1786 void runOnOperation()
override;
1790void LowerArcToLLVMPass::runOnOperation() {
1793 for (func::FuncOp func : getOperation().getOps<func::FuncOp>()) {
1794 for (
int i = 0, e = func.getNumArguments(); i != e; ++i) {
1795 if (
auto arrayRefType =
1796 dyn_cast<ArrayRefType>(func.getArgumentTypes()[i])) {
1798 Builder builder(&getContext());
1799 func.setArgAttr(i, LLVM::LLVMDialect::getDereferenceableAttrName(),
1800 builder.getI64IntegerAttr(byteWidth));
1816 LLVMConversionTarget target(getContext());
1817 target.addLegalOp<mlir::ModuleOp>();
1818 target.addLegalOp<scf::YieldOp>();
1823 target.addLegalOp<sim::FormatLiteralOp, sim::FormatDecOp, sim::FormatHexOp,
1824 sim::FormatBinOp, sim::FormatOctOp, sim::FormatCharOp,
1825 sim::FormatStringConcatOp>();
1828 LLVMTypeConverter converter(&getContext());
1829 converter.addConversion([&](seq::ClockType type) {
1830 return IntegerType::get(type.getContext(), 1);
1832 converter.addConversion([&](StorageType type) {
1833 return LLVM::LLVMPointerType::get(type.getContext());
1835 converter.addConversion([&](ContextType type) {
1836 return LLVM::LLVMPointerType::get(type.getContext());
1838 converter.addConversion([&](MemoryType type) {
1839 return LLVM::LLVMPointerType::get(type.getContext());
1841 converter.addConversion([&](StateType type) {
1842 return LLVM::LLVMPointerType::get(type.getContext());
1844 converter.addConversion([&](SimModelInstanceType type) {
1845 return LLVM::LLVMPointerType::get(type.getContext());
1847 converter.addConversion([&](sim::FormatStringType type) {
1848 return LLVM::LLVMPointerType::get(type.getContext());
1850 converter.addConversion([&](sim::OutputStreamType type) {
1851 return LLVM::LLVMPointerType::get(type.getContext());
1853 converter.addConversion([&](llhd::TimeType type) {
1855 return IntegerType::get(type.getContext(), 64);
1857 converter.addConversion([&](ArrayRefType type) {
1858 return LLVM::LLVMPointerType::get(type.getContext());
1863 target.addDynamicallyLegalOp<UnrealizedConversionCastOp>([&](Operation *op) {
1864 Type src = op->getOperand(0).getType();
1865 Type dst = op->getResult(0).getType();
1866 bool needsConvert = isa<ArrayRefType>(src) && isa<LLVM::LLVMArrayType>(dst);
1867 return !needsConvert;
1874 populateSCFToControlFlowConversionPatterns(
patterns);
1875 populateFuncToLLVMConversionPatterns(converter,
patterns);
1876 cf::populateControlFlowToLLVMConversionPatterns(converter,
patterns);
1877 arith::populateArithToLLVMConversionPatterns(converter,
patterns);
1878 index::populateIndexToLLVMConversionPatterns(converter,
patterns);
1879 ub::populateUBToLLVMConversionPatterns(converter,
patterns);
1880 populateAnyFunctionOpInterfaceTypeConversionPattern(
patterns, converter);
1883 DataLayout layout = DataLayout::closest(getOperation());
1884 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
1886 std::optional<HWToLLVMArraySpillCache> spillCacheOpt =
1889 OpBuilder spillBuilder(getOperation());
1890 spillCacheOpt->spillNonHWOps(spillBuilder, converter, getOperation());
1893 constAggregateGlobalsMap, spillCacheOpt);
1901 AllocMemoryOpLowering,
1902 AllocStateLikeOpLowering<arc::AllocStateOp>,
1903 AllocStateLikeOpLowering<arc::RootInputOp>,
1904 AllocStateLikeOpLowering<arc::RootOutputOp>,
1905 AllocStorageOpLowering,
1906 AsContextOpLowering,
1907 ClockGateOpLowering,
1909 ConstantTimeOpLowering,
1910 CurrentTimeOpLowering,
1911 GetNextWakeupOpLowering,
1912 IntToTimeOpLowering,
1913 MemoryReadOpLowering,
1914 MemoryWriteOpLowering,
1916 ReplaceOpWithInputPattern<seq::ToClockOp>,
1917 ReplaceOpWithInputPattern<seq::FromClockOp>,
1919 SeqConstClockLowering,
1920 SetNextWakeupOpLowering,
1921 SimSetTimeOpLowering,
1922 StateReadOpLowering,
1923 StateWriteOpLowering,
1924 StorageGetOpLowering,
1925 TerminateOpLowering,
1926 TimeToIntOpLowering,
1927 ZeroCountOpLowering,
1937 >(converter, &getContext());
1941 StringCache stringCache;
1942 patterns.add<SimEmitValueOpLowering, SimPrintFormattedProcOpLowering>(
1943 converter, &getContext(), stringCache);
1944 patterns.add<SimStreamOpLowering<sim::StdoutStreamOp>>(
1945 converter, &getContext(), runtime::APICallbacks::symNameGetStdoutStream);
1946 patterns.add<SimStreamOpLowering<sim::StderrStreamOp>>(
1947 converter, &getContext(), runtime::APICallbacks::symNameGetStderrStream);
1949 auto &modelInfo = getAnalysis<ModelInfoAnalysis>();
1950 llvm::DenseMap<StringRef, ModelInfoMap> modelMap(modelInfo.infoMap.size());
1951 for (
auto &[_, modelInfo] : modelInfo.infoMap) {
1952 llvm::DenseMap<StringRef, StateInfo> states(modelInfo.states.size());
1953 for (StateInfo &stateInfo : modelInfo.states)
1954 states.insert({stateInfo.name, stateInfo});
1957 ModelInfoMap{modelInfo.numStateBytes, std::move(states),
1958 modelInfo.initialFnSym, modelInfo.finalFnSym}});
1961 patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
1962 SimGetPortOpLowering, SimStepOpLowering>(
1963 converter, &getContext(), modelMap);
1966 ConversionConfig config;
1967 config.allowPatternRollback =
false;
1968 if (failed(applyFullConversion(getOperation(), target, std::move(
patterns),
1970 signalPassFailure();
1974 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)
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.
void add(mlir::ModuleOp module)
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
#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
Helper class mapping array values (HW or LLVM Dialect) to pointers to buffers containing the array va...