9 #include "../PassDetail.h"
18 #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
19 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
20 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
21 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
22 #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
23 #include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
24 #include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
25 #include "mlir/Dialect/Func/IR/FuncOps.h"
26 #include "mlir/Dialect/Index/IR/IndexOps.h"
27 #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
28 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
29 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
30 #include "mlir/Dialect/SCF/IR/SCF.h"
31 #include "mlir/IR/BuiltinDialect.h"
32 #include "mlir/Transforms/DialectConversion.h"
33 #include "llvm/Support/Debug.h"
35 #define DEBUG_TYPE "lower-arc-to-llvm"
38 using namespace circt;
47 return modelName +
"_eval";
53 using OpConversionPattern::OpConversionPattern;
55 matchAndRewrite(arc::ModelOp op, OpAdaptor adaptor,
56 ConversionPatternRewriter &rewriter)
const final {
58 IRRewriter::InsertionGuard guard(rewriter);
59 rewriter.setInsertionPointToEnd(&op.getBodyBlock());
60 rewriter.create<func::ReturnOp>(op.getLoc());
65 rewriter.getFunctionType(op.getBody().getArgumentTypes(), {});
67 rewriter.create<mlir::func::FuncOp>(op.getLoc(), funcName, funcType);
68 rewriter.inlineRegionBefore(op.getRegion(), func.getBody(), func.end());
74 struct AllocStorageOpLowering
76 using OpConversionPattern::OpConversionPattern;
78 matchAndRewrite(arc::AllocStorageOp op, OpAdaptor adaptor,
79 ConversionPatternRewriter &rewriter)
const final {
80 auto type = typeConverter->convertType(op.getType());
81 if (!op.getOffset().has_value())
83 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, type, rewriter.getI8Type(),
85 LLVM::GEPArg(*op.getOffset()));
90 template <
class ConcreteOp>
94 using OpAdaptor =
typename ConcreteOp::Adaptor;
97 matchAndRewrite(ConcreteOp op, OpAdaptor adaptor,
98 ConversionPatternRewriter &rewriter)
const final {
100 auto offsetAttr = op->template getAttrOfType<IntegerAttr>(
"offset");
103 Value ptr = rewriter.create<LLVM::GEPOp>(
104 op->getLoc(), adaptor.getStorage().getType(), rewriter.getI8Type(),
105 adaptor.getStorage(),
106 LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
107 rewriter.replaceOp(op, ptr);
113 using OpConversionPattern::OpConversionPattern;
115 matchAndRewrite(arc::StateReadOp op, OpAdaptor adaptor,
116 ConversionPatternRewriter &rewriter)
const final {
117 auto type = typeConverter->convertType(op.getType());
118 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, type, adaptor.getState());
124 using OpConversionPattern::OpConversionPattern;
126 matchAndRewrite(arc::StateWriteOp op, OpAdaptor adaptor,
127 ConversionPatternRewriter &rewriter)
const final {
128 if (adaptor.getCondition()) {
129 rewriter.replaceOpWithNewOp<scf::IfOp>(
130 op, adaptor.getCondition(), [&](
auto &
builder,
auto loc) {
131 builder.template create<LLVM::StoreOp>(loc, adaptor.getValue(),
133 builder.template create<scf::YieldOp>(loc);
136 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
144 using OpConversionPattern::OpConversionPattern;
146 matchAndRewrite(arc::AllocMemoryOp op, OpAdaptor adaptor,
147 ConversionPatternRewriter &rewriter)
const final {
148 auto offsetAttr = op->getAttrOfType<IntegerAttr>(
"offset");
151 Value ptr = rewriter.create<LLVM::GEPOp>(
152 op.getLoc(), adaptor.getStorage().getType(), rewriter.getI8Type(),
153 adaptor.getStorage(),
154 LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
156 rewriter.replaceOp(op, ptr);
162 using OpConversionPattern::OpConversionPattern;
164 matchAndRewrite(arc::StorageGetOp op, OpAdaptor adaptor,
165 ConversionPatternRewriter &rewriter)
const final {
166 Value offset = rewriter.create<LLVM::ConstantOp>(
167 op.getLoc(), rewriter.getI32Type(), op.getOffsetAttr());
168 Value ptr = rewriter.create<LLVM::GEPOp>(
169 op.getLoc(), adaptor.getStorage().getType(), rewriter.getI8Type(),
170 adaptor.getStorage(), offset);
171 rewriter.replaceOp(op, ptr);
176 struct MemoryAccess {
181 static MemoryAccess prepareMemoryAccess(Location loc, Value memory,
182 Value address, MemoryType type,
183 ConversionPatternRewriter &rewriter) {
184 auto zextAddrType = rewriter.getIntegerType(
185 address.getType().cast<IntegerType>().getWidth() + 1);
186 Value
addr = rewriter.create<LLVM::ZExtOp>(loc, zextAddrType, address);
187 Value addrLimit = rewriter.create<LLVM::ConstantOp>(
188 loc, zextAddrType, rewriter.getI32IntegerAttr(type.getNumWords()));
189 Value withinBounds = rewriter.create<LLVM::ICmpOp>(
190 loc, LLVM::ICmpPredicate::ult,
addr, addrLimit);
191 Value ptr = rewriter.create<LLVM::GEPOp>(
193 rewriter.getIntegerType(type.getStride() * 8), memory, ValueRange{
addr});
194 return {ptr, withinBounds};
198 using OpConversionPattern::OpConversionPattern;
200 matchAndRewrite(arc::MemoryReadOp op, OpAdaptor adaptor,
201 ConversionPatternRewriter &rewriter)
const final {
202 auto type = typeConverter->convertType(op.getType());
203 auto memoryType = op.getMemory().getType().cast<MemoryType>();
205 prepareMemoryAccess(op.getLoc(), adaptor.getMemory(),
206 adaptor.getAddress(), memoryType, rewriter);
210 rewriter.replaceOpWithNewOp<scf::IfOp>(
211 op, access.withinBounds,
213 Value loadOp =
builder.template create<LLVM::LoadOp>(
214 loc, memoryType.getWordType(), access.ptr);
215 builder.template create<scf::YieldOp>(loc, loadOp);
218 Value zeroValue =
builder.template create<LLVM::ConstantOp>(
219 loc, type,
builder.getI64IntegerAttr(0));
220 builder.template create<scf::YieldOp>(loc, zeroValue);
227 using OpConversionPattern::OpConversionPattern;
229 matchAndRewrite(arc::MemoryWriteOp op, OpAdaptor adaptor,
230 ConversionPatternRewriter &rewriter)
const final {
231 auto access = prepareMemoryAccess(
232 op.getLoc(), adaptor.getMemory(), adaptor.getAddress(),
233 op.getMemory().getType().cast<MemoryType>(), rewriter);
234 auto enable = access.withinBounds;
235 if (adaptor.getEnable())
236 enable = rewriter.create<LLVM::AndOp>(op.getLoc(), adaptor.getEnable(),
240 rewriter.replaceOpWithNewOp<scf::IfOp>(
241 op, enable, [&](
auto &
builder,
auto loc) {
242 builder.template create<LLVM::StoreOp>(loc, adaptor.getData(),
244 builder.template create<scf::YieldOp>(loc);
252 using OpConversionPattern::OpConversionPattern;
254 matchAndRewrite(seq::ClockGateOp op, OpAdaptor adaptor,
255 ConversionPatternRewriter &rewriter)
const final {
256 rewriter.replaceOpWithNewOp<
comb::AndOp>(op, adaptor.getInput(),
257 adaptor.getEnable(),
true);
263 using OpConversionPattern::OpConversionPattern;
265 matchAndRewrite(arc::ZeroCountOp op, OpAdaptor adaptor,
266 ConversionPatternRewriter &rewriter)
const override {
268 IntegerAttr isZeroPoison = rewriter.getBoolAttr(
true);
270 if (op.getPredicate() == arc::ZeroCountPredicate::leading) {
271 rewriter.replaceOpWithNewOp<LLVM::CountLeadingZerosOp>(
272 op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
276 rewriter.replaceOpWithNewOp<LLVM::CountTrailingZerosOp>(
277 op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
283 using OpConversionPattern::OpConversionPattern;
285 matchAndRewrite(seq::ConstClockOp op, OpAdaptor adaptor,
286 ConversionPatternRewriter &rewriter)
const override {
287 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
288 op, rewriter.getI1Type(),
static_cast<int64_t
>(op.getValue()));
293 template <
typename OpTy>
296 using OpAdaptor =
typename OpTy::Adaptor;
298 matchAndRewrite(OpTy op, OpAdaptor adaptor,
299 ConversionPatternRewriter &rewriter)
const override {
300 rewriter.replaceOp(op, adaptor.getInput());
313 struct ModelInfoMap {
314 size_t numStateBytes;
315 llvm::DenseMap<StringRef, StateInfo> states;
318 template <
typename OpTy>
320 ModelAwarePattern(
const TypeConverter &typeConverter, MLIRContext *context,
321 llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo)
323 modelInfo(modelInfo) {}
326 Value createPtrToPortState(ConversionPatternRewriter &rewriter, Location loc,
327 Value state,
const StateInfo &port)
const {
328 MLIRContext *ctx = rewriter.getContext();
331 LLVM::GEPArg(port.
offset));
334 llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo;
339 struct SimInstantiateOpLowering
340 :
public ModelAwarePattern<arc::SimInstantiateOp> {
341 using ModelAwarePattern::ModelAwarePattern;
344 matchAndRewrite(arc::SimInstantiateOp op, OpAdaptor adaptor,
345 ConversionPatternRewriter &rewriter)
const final {
346 auto modelIt = modelInfo.find(
347 cast<SimModelInstanceType>(op.getBody().getArgument(0).getType())
350 ModelInfoMap &model = modelIt->second;
352 ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
356 ConversionPatternRewriter::InsertionGuard guard(rewriter);
360 Type convertedIndex = typeConverter->convertType(rewriter.getIndexType());
362 LLVM::LLVMFuncOp mallocFunc =
363 LLVM::lookupOrCreateMallocFn(moduleOp, convertedIndex);
364 LLVM::LLVMFuncOp freeFunc = LLVM::lookupOrCreateFreeFn(moduleOp);
366 Location loc = op.getLoc();
367 Value numStateBytes = rewriter.create<LLVM::ConstantOp>(
368 loc, convertedIndex, model.numStateBytes);
371 .create<LLVM::CallOp>(loc, mallocFunc, ValueRange{numStateBytes})
374 rewriter.create<LLVM::ConstantOp>(loc, rewriter.getI8Type(), 0);
375 rewriter.create<LLVM::MemsetOp>(loc, allocated, zero, numStateBytes,
false);
376 rewriter.inlineBlockBefore(&adaptor.getBody().getBlocks().front(), op,
378 rewriter.create<LLVM::CallOp>(loc, freeFunc, ValueRange{allocated});
379 rewriter.eraseOp(op);
385 struct SimSetInputOpLowering :
public ModelAwarePattern<arc::SimSetInputOp> {
386 using ModelAwarePattern::ModelAwarePattern;
389 matchAndRewrite(arc::SimSetInputOp op, OpAdaptor adaptor,
390 ConversionPatternRewriter &rewriter)
const final {
392 modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
395 ModelInfoMap &model = modelIt->second;
397 auto portIt = model.states.find(op.getInput());
398 if (portIt == model.states.end()) {
401 rewriter.eraseOp(op);
406 Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
407 adaptor.getInstance(), port);
408 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
415 struct SimGetPortOpLowering :
public ModelAwarePattern<arc::SimGetPortOp> {
416 using ModelAwarePattern::ModelAwarePattern;
419 matchAndRewrite(arc::SimGetPortOp op, OpAdaptor adaptor,
420 ConversionPatternRewriter &rewriter)
const final {
422 modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
425 ModelInfoMap &model = modelIt->second;
427 auto portIt = model.states.find(op.getPort());
428 if (portIt == model.states.end()) {
431 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
432 op, typeConverter->convertType(op.getValue().getType()), 0);
437 Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
438 adaptor.getInstance(), port);
439 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, op.getValue().getType(),
446 struct SimStepOpLowering :
public ModelAwarePattern<arc::SimStepOp> {
447 using ModelAwarePattern::ModelAwarePattern;
450 matchAndRewrite(arc::SimStepOp op, OpAdaptor adaptor,
451 ConversionPatternRewriter &rewriter)
const final {
452 StringRef modelName = cast<SimModelInstanceType>(op.getInstance().getType())
456 StringAttr evalFunc =
458 rewriter.replaceOpWithNewOp<LLVM::CallOp>(op, std::nullopt, evalFunc,
459 adaptor.getInstance());
468 struct SimEmitValueOpLowering
470 using OpConversionPattern::OpConversionPattern;
473 matchAndRewrite(arc::SimEmitValueOp op, OpAdaptor adaptor,
474 ConversionPatternRewriter &rewriter)
const final {
475 auto valueType = dyn_cast<IntegerType>(adaptor.getValue().getType());
479 Location loc = op.getLoc();
481 ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
488 Value toPrint = adaptor.getValue();
489 DataLayout layout = DataLayout::closest(op);
490 llvm::TypeSize sizeOfSizeT =
491 layout.getTypeSizeInBits(rewriter.getIndexType());
492 assert(!sizeOfSizeT.isScalable() &&
493 sizeOfSizeT.getFixedValue() <= std::numeric_limits<unsigned>::max());
494 bool truncated =
false;
495 if (valueType.getWidth() > sizeOfSizeT) {
496 toPrint = rewriter.create<LLVM::TruncOp>(
500 }
else if (valueType.getWidth() < sizeOfSizeT)
501 toPrint = rewriter.create<LLVM::ZExtOp>(
506 auto printfFunc = LLVM::lookupOrCreateFn(
511 SmallString<16> formatStrName{
"_arc_sim_emit_"};
512 formatStrName.append(truncated ?
"trunc_" :
"full_");
513 formatStrName.append(adaptor.getValueName());
514 LLVM::GlobalOp formatStrGlobal;
515 if (!(formatStrGlobal =
516 moduleOp.lookupSymbol<LLVM::GlobalOp>(formatStrName))) {
517 ConversionPatternRewriter::InsertionGuard insertGuard(rewriter);
519 SmallString<16> formatStr = adaptor.getValueName();
520 formatStr.append(
" = ");
522 formatStr.append(
"(truncated) ");
523 formatStr.append(
"%zx\n");
524 SmallVector<char> formatStrVec{formatStr.begin(), formatStr.end()};
525 formatStrVec.push_back(0);
527 rewriter.setInsertionPointToStart(moduleOp.getBody());
530 formatStrGlobal = rewriter.create<LLVM::GlobalOp>(
531 loc, globalType,
true, LLVM::Linkage::Internal,
532 formatStrName, rewriter.getStringAttr(formatStrVec),
536 Value formatStrGlobalPtr =
537 rewriter.create<LLVM::AddressOfOp>(loc, formatStrGlobal);
538 rewriter.replaceOpWithNewOp<LLVM::CallOp>(
539 op, printfFunc, ValueRange{formatStrGlobalPtr, toPrint});
552 struct LowerArcToLLVMPass :
public LowerArcToLLVMBase<LowerArcToLLVMPass> {
553 void runOnOperation()
override;
557 void LowerArcToLLVMPass::runOnOperation() {
569 LLVMConversionTarget target(getContext());
570 target.addLegalOp<mlir::ModuleOp>();
571 target.addLegalOp<scf::YieldOp>();
574 LLVMTypeConverter converter(&getContext());
575 converter.addConversion([&](seq::ClockType type) {
578 converter.addConversion([&](StorageType type) {
581 converter.addConversion([&](MemoryType type) {
584 converter.addConversion([&](StateType type) {
587 converter.addConversion([&](SimModelInstanceType type) {
592 RewritePatternSet
patterns(&getContext());
595 populateSCFToControlFlowConversionPatterns(
patterns);
596 populateFuncToLLVMConversionPatterns(converter,
patterns);
597 cf::populateControlFlowToLLVMConversionPatterns(converter,
patterns);
598 arith::populateArithToLLVMConversionPatterns(converter,
patterns);
599 populateAnyFunctionOpInterfaceTypeConversionPattern(
patterns, converter);
602 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
604 constAggregateGlobalsMap);
611 AllocMemoryOpLowering,
612 AllocStateLikeOpLowering<arc::AllocStateOp>,
613 AllocStateLikeOpLowering<arc::RootInputOp>,
614 AllocStateLikeOpLowering<arc::RootOutputOp>,
615 AllocStorageOpLowering,
617 MemoryReadOpLowering,
618 MemoryWriteOpLowering,
620 ReplaceOpWithInputPattern<seq::ToClockOp>,
621 ReplaceOpWithInputPattern<seq::FromClockOp>,
622 SeqConstClockLowering,
623 SimEmitValueOpLowering,
625 StateWriteOpLowering,
626 StorageGetOpLowering,
628 >(converter, &getContext());
631 SmallVector<ModelInfo> models;
637 llvm::DenseMap<StringRef, ModelInfoMap> modelMap(models.size());
639 llvm::DenseMap<StringRef, StateInfo> states(modelInfo.states.size());
640 for (
StateInfo &stateInfo : modelInfo.states)
641 states.insert({stateInfo.
name, stateInfo});
642 modelMap.insert({modelInfo.name,
643 ModelInfoMap{modelInfo.numStateBytes, std::move(states)}});
646 patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
647 SimGetPortOpLowering, SimStepOpLowering>(
648 converter, &getContext(), modelMap);
651 if (failed(applyFullConversion(getOperation(), target, std::move(
patterns))))
656 return std::make_unique<LowerArcToLLVMPass>();
assert(baseType &&"element must be base type")
static llvm::Twine evalSymbolFromModelName(StringRef modelName)
A namespace that is used to store existing names and generate new names in some scope within the IR.
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
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...
mlir::LogicalResult collectModels(mlir::ModuleOp module, llvm::SmallVector< ModelInfo > &models)
Collects information about all Arc models in the provided module, and adds it to models.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void populateHWToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, Namespace &globals, DenseMap< std::pair< Type, ArrayAttr >, mlir::LLVM::GlobalOp > &constAggregateGlobalsMap)
Get the HW to LLVM conversion patterns.
void populateCombToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns)
Get the Comb to LLVM conversion patterns.
void populateHWToLLVMTypeConversions(mlir::LLVMTypeConverter &converter)
Get the HW to LLVM type conversions.
std::unique_ptr< OperationPass< ModuleOp > > createLowerArcToLLVMPass()
Gathers information about a given Arc model.
Gathers information about a given Arc state.