Complete MLIR Lowering to LLVM Dialect
Building upon the partial lowering described in the previous section, this process completes the transformation of the multi-dialect IR (comprising Affine, Standard, and legacy Toy.PrintOp dialects) into the LLVM dialect. The reuslting LLVM IR can then be executed using LLVM's JIT execution engine.
First, define a pattern for converting Toy.PrintOp operations to their LLVM counterparts. Since both Affine and Standard dialects contain relevant constructs, direct LLVM dialect patterns can be utilized.
class PrintOpLowering : public ConversionPattern {
public:
explicit PrintOpLowering(MLIRContext *context)
: ConversionPattern(toy::PrintOp::getOperationName(), 1, context) {}
LogicalResult
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
// Implementation details for rewriting PrintOp to LLVM printf call
}
};
This class implements the lowering of the Toy print operation to LLVM, including a matching and rewriting function that converts toy::PrintOp into calls to LLVM's printf intrinsic.
Next, perform full dialect conversion to LLVM representation. Begin by defining the ToyToLLVMLoweringPass class and implementing its runOnOperation method to set up the conversion target and patttern set:
LLVMConversionTarget target(getContext());
target.addLegalOp<ModuleOp>();
LLVMTypeConverter typeConverter(&getContext());
RewritePatternSet patterns(&getContext());
populateAffineToStdConversionPatterns(patterns);
populateSCFToControlFlowConversionPatterns(patterns);
mlir::arith::populateArithToLLVMConversionPatterns(typeConverter, patterns);
populateFinalizeMemRefToLLVMConversionPatterns(typeConverter, patterns);
cf::populateControlFlowToLLVMConversionPatterns(typeConverter, patterns);
populateFuncToLLVMConversionPatterns(typeConverter, patterns);
patterns.add<PrintOpLowering>(&getContext());
Then apply these patterns to execute the complete lowering transformation:
auto module = getOperation();
if (failed(applyFullConversion(module, target, std::move(patterns))))
signalPassFailure();
Finally, register the ToyToLLVMLoweringPass within the pass manager:
if (isLoweringToLLVM) {
pm.addPass(mlir::toy::createLowerToLLVMPass());
// Required for emitting line tables and enabling basic debugging.
// Future versions will support full debug information emission.
pm.addPass(mlir::LLVM::createDIScopeForLLVMFuncOpPass());
}
At this point, the MLIR module represents valid LLVM IR. To generate the actual LLVM IR text, invoke:
auto llvmModule = mlir::translateModuleToLLVMIR(module, llvmContext);
Alternatively, the LLVM IR can be executed directly through JIT compilation.