1 //===- ControlFlowToLLVM.cpp - ControlFlow to LLVM dialect conversion -----===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements a pass to convert MLIR standard and builtin dialects 10 // into the LLVM IR dialect. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h" 15 #include "../PassDetail.h" 16 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h" 17 #include "mlir/Conversion/LLVMCommon/Pattern.h" 18 #include "mlir/Conversion/LLVMCommon/VectorPattern.h" 19 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" 20 #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h" 21 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 22 #include "mlir/IR/BuiltinOps.h" 23 #include "mlir/IR/PatternMatch.h" 24 #include "mlir/Transforms/DialectConversion.h" 25 #include "llvm/ADT/StringRef.h" 26 #include <functional> 27 28 using namespace mlir; 29 30 #define PASS_NAME "convert-cf-to-llvm" 31 32 namespace { 33 /// Lower `cf.assert`. The default lowering calls the `abort` function if the 34 /// assertion is violated and has no effect otherwise. The failure message is 35 /// ignored by the default lowering but should be propagated by any custom 36 /// lowering. 37 struct AssertOpLowering : public ConvertOpToLLVMPattern<cf::AssertOp> { 38 using ConvertOpToLLVMPattern<cf::AssertOp>::ConvertOpToLLVMPattern; 39 40 LogicalResult 41 matchAndRewrite(cf::AssertOp op, OpAdaptor adaptor, 42 ConversionPatternRewriter &rewriter) const override { 43 auto loc = op.getLoc(); 44 45 // Insert the `abort` declaration if necessary. 46 auto module = op->getParentOfType<ModuleOp>(); 47 auto abortFunc = module.lookupSymbol<LLVM::LLVMFuncOp>("abort"); 48 if (!abortFunc) { 49 OpBuilder::InsertionGuard guard(rewriter); 50 rewriter.setInsertionPointToStart(module.getBody()); 51 auto abortFuncTy = LLVM::LLVMFunctionType::get(getVoidType(), {}); 52 abortFunc = rewriter.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(), 53 "abort", abortFuncTy); 54 } 55 56 // Split block at `assert` operation. 57 Block *opBlock = rewriter.getInsertionBlock(); 58 auto opPosition = rewriter.getInsertionPoint(); 59 Block *continuationBlock = rewriter.splitBlock(opBlock, opPosition); 60 61 // Generate IR to call `abort`. 62 Block *failureBlock = rewriter.createBlock(opBlock->getParent()); 63 rewriter.create<LLVM::CallOp>(loc, abortFunc, llvm::None); 64 rewriter.create<LLVM::UnreachableOp>(loc); 65 66 // Generate assertion test. 67 rewriter.setInsertionPointToEnd(opBlock); 68 rewriter.replaceOpWithNewOp<LLVM::CondBrOp>( 69 op, adaptor.getArg(), continuationBlock, failureBlock); 70 71 return success(); 72 } 73 }; 74 75 /// The cf->LLVM lowerings for branching ops require that the blocks they jump 76 /// to first have updated types which should be handled by a pattern operating 77 /// on the parent op. 78 static LogicalResult verifyMatchingValues(ConversionPatternRewriter &rewriter, 79 ValueRange operands, 80 ValueRange blockArgs, Location loc, 81 llvm::StringRef messagePrefix) { 82 for (const auto &idxAndTypes : 83 llvm::enumerate(llvm::zip(blockArgs, operands))) { 84 int64_t i = idxAndTypes.index(); 85 Value argValue = 86 rewriter.getRemappedValue(std::get<0>(idxAndTypes.value())); 87 Type operandType = std::get<1>(idxAndTypes.value()).getType(); 88 // In the case of an invalid jump, the block argument will have been 89 // remapped to an UnrealizedConversionCast. In the case of a valid jump, 90 // there might still be a no-op conversion cast with both types being equal. 91 // Consider both of these details to see if the jump would be invalid. 92 if (auto op = dyn_cast_or_null<UnrealizedConversionCastOp>( 93 argValue.getDefiningOp())) { 94 if (op.getOperandTypes().front() != operandType) { 95 return rewriter.notifyMatchFailure(loc, [&](Diagnostic &diag) { 96 diag << messagePrefix; 97 diag << "mismatched types from operand # " << i << " "; 98 diag << operandType; 99 diag << " not compatible with destination block argument type "; 100 diag << argValue.getType(); 101 diag << " which should be converted with the parent op."; 102 }); 103 } 104 } 105 } 106 return success(); 107 } 108 109 /// Ensure that all block types were updated and then create an LLVM::BrOp 110 struct BranchOpLowering : public ConvertOpToLLVMPattern<cf::BranchOp> { 111 using ConvertOpToLLVMPattern<cf::BranchOp>::ConvertOpToLLVMPattern; 112 113 LogicalResult 114 matchAndRewrite(cf::BranchOp op, typename cf::BranchOp::Adaptor adaptor, 115 ConversionPatternRewriter &rewriter) const override { 116 if (failed(verifyMatchingValues(rewriter, adaptor.getDestOperands(), 117 op.getSuccessor()->getArguments(), 118 op.getLoc(), 119 /*messagePrefix=*/""))) 120 return failure(); 121 122 rewriter.replaceOpWithNewOp<LLVM::BrOp>( 123 op, adaptor.getOperands(), op->getSuccessors(), op->getAttrs()); 124 return success(); 125 } 126 }; 127 128 /// Ensure that all block types were updated and then create an LLVM::CondBrOp 129 struct CondBranchOpLowering : public ConvertOpToLLVMPattern<cf::CondBranchOp> { 130 using ConvertOpToLLVMPattern<cf::CondBranchOp>::ConvertOpToLLVMPattern; 131 132 LogicalResult 133 matchAndRewrite(cf::CondBranchOp op, 134 typename cf::CondBranchOp::Adaptor adaptor, 135 ConversionPatternRewriter &rewriter) const override { 136 if (failed(verifyMatchingValues(rewriter, adaptor.getFalseDestOperands(), 137 op.getFalseDest()->getArguments(), 138 op.getLoc(), "in false case branch "))) 139 return failure(); 140 if (failed(verifyMatchingValues(rewriter, adaptor.getTrueDestOperands(), 141 op.getTrueDest()->getArguments(), 142 op.getLoc(), "in true case branch "))) 143 return failure(); 144 145 rewriter.replaceOpWithNewOp<LLVM::CondBrOp>( 146 op, adaptor.getOperands(), op->getSuccessors(), op->getAttrs()); 147 return success(); 148 } 149 }; 150 151 /// Ensure that all block types were updated and then create an LLVM::SwitchOp 152 struct SwitchOpLowering : public ConvertOpToLLVMPattern<cf::SwitchOp> { 153 using ConvertOpToLLVMPattern<cf::SwitchOp>::ConvertOpToLLVMPattern; 154 155 LogicalResult 156 matchAndRewrite(cf::SwitchOp op, typename cf::SwitchOp::Adaptor adaptor, 157 ConversionPatternRewriter &rewriter) const override { 158 if (failed(verifyMatchingValues(rewriter, adaptor.getDefaultOperands(), 159 op.getDefaultDestination()->getArguments(), 160 op.getLoc(), "in switch default case "))) 161 return failure(); 162 163 for (const auto &i : llvm::enumerate( 164 llvm::zip(adaptor.getCaseOperands(), op.getCaseDestinations()))) { 165 if (failed(verifyMatchingValues( 166 rewriter, std::get<0>(i.value()), 167 std::get<1>(i.value())->getArguments(), op.getLoc(), 168 "in switch case " + std::to_string(i.index()) + " "))) { 169 return failure(); 170 } 171 } 172 173 rewriter.replaceOpWithNewOp<LLVM::SwitchOp>( 174 op, adaptor.getOperands(), op->getSuccessors(), op->getAttrs()); 175 return success(); 176 } 177 }; 178 179 } // namespace 180 181 void mlir::cf::populateControlFlowToLLVMConversionPatterns( 182 LLVMTypeConverter &converter, RewritePatternSet &patterns) { 183 // clang-format off 184 patterns.add< 185 AssertOpLowering, 186 BranchOpLowering, 187 CondBranchOpLowering, 188 SwitchOpLowering>(converter); 189 // clang-format on 190 } 191 192 //===----------------------------------------------------------------------===// 193 // Pass Definition 194 //===----------------------------------------------------------------------===// 195 196 namespace { 197 /// A pass converting MLIR operations into the LLVM IR dialect. 198 struct ConvertControlFlowToLLVM 199 : public ConvertControlFlowToLLVMBase<ConvertControlFlowToLLVM> { 200 ConvertControlFlowToLLVM() = default; 201 202 /// Run the dialect converter on the module. 203 void runOnOperation() override { 204 LLVMConversionTarget target(getContext()); 205 RewritePatternSet patterns(&getContext()); 206 207 LowerToLLVMOptions options(&getContext()); 208 if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout) 209 options.overrideIndexBitwidth(indexBitwidth); 210 211 LLVMTypeConverter converter(&getContext(), options); 212 mlir::cf::populateControlFlowToLLVMConversionPatterns(converter, patterns); 213 214 if (failed(applyPartialConversion(getOperation(), target, 215 std::move(patterns)))) 216 signalPassFailure(); 217 } 218 }; 219 } // namespace 220 221 std::unique_ptr<Pass> mlir::cf::createConvertControlFlowToLLVMPass() { 222 return std::make_unique<ConvertControlFlowToLLVM>(); 223 } 224