MLIR 24.0.0git
ControlFlowToLLVM.cpp
Go to the documentation of this file.
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
15
23#include "mlir/IR/BuiltinOps.h"
25#include "mlir/Pass/Pass.h"
27
28namespace mlir {
29#define GEN_PASS_DEF_CONVERTCONTROLFLOWTOLLVMPASS
30#include "mlir/Conversion/Passes.h.inc"
31} // namespace mlir
32
33using namespace mlir;
34
35#define PASS_NAME "convert-cf-to-llvm"
36
37namespace {
38/// Lower `cf.assert`. The default lowering calls the `abort` function if the
39/// assertion is violated and has no effect otherwise. The failure message is
40/// ignored by the default lowering but should be propagated by any custom
41/// lowering.
42struct AssertOpLowering : public ConvertOpToLLVMPattern<cf::AssertOp> {
43 explicit AssertOpLowering(const LLVMTypeConverter &typeConverter,
44 bool abortOnFailedAssert = true,
45 SymbolTableCollection *symbolTables = nullptr)
46 : ConvertOpToLLVMPattern<cf::AssertOp>(typeConverter, /*benefit=*/1),
47 abortOnFailedAssert(abortOnFailedAssert), symbolTables(symbolTables) {}
48
49 LogicalResult
50 matchAndRewrite(cf::AssertOp op, OpAdaptor adaptor,
51 ConversionPatternRewriter &rewriter) const override {
52 auto loc = op.getLoc();
53 auto module = op->getParentOfType<ModuleOp>();
54
55 // Split block at `assert` operation.
56 Block *opBlock = rewriter.getInsertionBlock();
57 auto opPosition = rewriter.getInsertionPoint();
58 Block *continuationBlock = rewriter.splitBlock(opBlock, opPosition);
59
60 // Failed block: Generate IR to print the message and call `abort`.
61 Block *failureBlock = rewriter.createBlock(opBlock->getParent());
62 auto createResult = LLVM::createPrintStrCall(
63 rewriter, loc, module, "assert_msg", op.getMsg(), *getTypeConverter(),
64 /*addNewLine=*/false,
65 /*runtimeFunctionName=*/"puts", symbolTables);
66 if (createResult.failed())
67 return failure();
68
69 if (abortOnFailedAssert) {
70 // Insert the `abort` declaration if necessary.
71 auto abortFunc = module.lookupSymbol<LLVM::LLVMFuncOp>("abort");
72 if (!abortFunc) {
73 OpBuilder::InsertionGuard guard(rewriter);
74 rewriter.setInsertionPointToStart(module.getBody());
75 auto abortFuncTy = LLVM::LLVMFunctionType::get(getVoidType(), {});
76 abortFunc = LLVM::LLVMFuncOp::create(rewriter, rewriter.getUnknownLoc(),
77 "abort", abortFuncTy);
78 }
79 LLVM::CallOp::create(rewriter, loc, abortFunc, ValueRange());
80 LLVM::UnreachableOp::create(rewriter, loc);
81 } else {
82 LLVM::BrOp::create(rewriter, loc, ValueRange(), continuationBlock);
83 }
84
85 // Generate assertion test.
86 rewriter.setInsertionPointToEnd(opBlock);
87 rewriter.replaceOpWithNewOp<LLVM::CondBrOp>(
88 op, adaptor.getArg(), continuationBlock, failureBlock);
89
90 return success();
91 }
92
93private:
94 /// If set to `false`, messages are printed but program execution continues.
95 /// This is useful for testing asserts.
96 bool abortOnFailedAssert = true;
97
98 SymbolTableCollection *symbolTables = nullptr;
99};
100
101/// Helper function for converting branch ops. This function converts the
102/// signature of the given block. If the new block signature is different from
103/// `expectedTypes`, returns "failure".
104static FailureOr<Block *> getConvertedBlock(ConversionPatternRewriter &rewriter,
105 const TypeConverter *converter,
106 Operation *branchOp, Block *block,
107 TypeRange expectedTypes) {
108 assert(converter && "expected non-null type converter");
109 assert(!block->isEntryBlock() && "entry blocks have no predecessors");
110
111 // There is nothing to do if the types already match.
112 if (block->getArgumentTypes() == expectedTypes)
113 return block;
114
115 // Compute the new block argument types and convert the block.
116 std::optional<TypeConverter::SignatureConversion> conversion =
117 converter->convertBlockSignature(block);
118 if (!conversion)
119 return rewriter.notifyMatchFailure(branchOp,
120 "could not compute block signature");
121 if (expectedTypes != conversion->getConvertedTypes())
122 return rewriter.notifyMatchFailure(
123 branchOp,
124 "mismatch between adaptor operand types and computed block signature");
125 return rewriter.applySignatureConversion(block, *conversion, converter);
126}
127
128/// Flatten the given value ranges into a single vector of values.
131 for (const ValueRange &vals : values)
132 llvm::append_range(result, vals);
133 return result;
134}
135
136/// Set attributes on an operation using its inherent/discardable split.
137static void setConvertedAttrs(Operation *op, DictionaryAttr attrs) {
138 SmallVector<NamedAttribute> discardableAttrs;
139 for (NamedAttribute attr : attrs) {
140 if (op->getInherentAttr(attr.getName()).has_value())
141 op->setInherentAttr(attr.getName(), attr.getValue());
142 else
143 discardableAttrs.push_back(attr);
144 }
145 op->setDiscardableAttrs(discardableAttrs);
146}
147
148/// Convert the destination block signature (if necessary) and lower the branch
149/// op to llvm.br.
150struct BranchOpLowering : public ConvertOpToLLVMPattern<cf::BranchOp> {
153
154 LogicalResult
155 matchAndRewrite(cf::BranchOp op, Adaptor adaptor,
156 ConversionPatternRewriter &rewriter) const override {
157 SmallVector<Value> flattenedAdaptor = flattenValues(adaptor.getOperands());
158 FailureOr<Block *> convertedBlock =
159 getConvertedBlock(rewriter, getTypeConverter(), op, op.getSuccessor(),
160 TypeRange(ValueRange(flattenedAdaptor)));
161 if (failed(convertedBlock))
162 return failure();
163 DictionaryAttr attrs = op->getDiscardableAttrDictionary();
164 auto loopAnnotation =
165 op->getAttrOfType<LLVM::LoopAnnotationAttr>("loop_annotation");
166 Operation *newOp = rewriter.replaceOpWithNewOp<LLVM::BrOp>(
167 op, flattenedAdaptor, loopAnnotation, *convertedBlock);
168 // TODO: We should not just forward all attributes like that. But there are
169 // existing Flang tests that depend on this behavior.
170 setConvertedAttrs(newOp, attrs);
171 return success();
172 }
173};
174
175/// Convert the destination block signatures (if necessary) and lower the
176/// branch op to llvm.cond_br.
177struct CondBranchOpLowering : public ConvertOpToLLVMPattern<cf::CondBranchOp> {
180
181 LogicalResult
182 matchAndRewrite(cf::CondBranchOp op, Adaptor adaptor,
183 ConversionPatternRewriter &rewriter) const override {
184 SmallVector<Value> flattenedAdaptorTrue =
185 flattenValues(adaptor.getTrueDestOperands());
186 SmallVector<Value> flattenedAdaptorFalse =
187 flattenValues(adaptor.getFalseDestOperands());
188 if (!llvm::hasSingleElement(adaptor.getCondition()))
189 return rewriter.notifyMatchFailure(op,
190 "expected single element condition");
191 FailureOr<Block *> convertedTrueBlock =
192 getConvertedBlock(rewriter, getTypeConverter(), op, op.getTrueDest(),
193 TypeRange(ValueRange(flattenedAdaptorTrue)));
194 if (failed(convertedTrueBlock))
195 return failure();
196 FailureOr<Block *> convertedFalseBlock =
197 getConvertedBlock(rewriter, getTypeConverter(), op, op.getFalseDest(),
198 TypeRange(ValueRange(flattenedAdaptorFalse)));
199 if (failed(convertedFalseBlock))
200 return failure();
201 DictionaryAttr attrs = op->getDiscardableAttrDictionary();
202 auto loopAnnotation =
203 op->getAttrOfType<LLVM::LoopAnnotationAttr>("loop_annotation");
204 auto newOp = rewriter.replaceOpWithNewOp<LLVM::CondBrOp>(
205 op, llvm::getSingleElement(adaptor.getCondition()),
206 flattenedAdaptorTrue, flattenedAdaptorFalse, op.getBranchWeightsAttr(),
207 loopAnnotation, *convertedTrueBlock, *convertedFalseBlock);
208 // TODO: We should not just forward all attributes like that. But there are
209 // existing Flang tests that depend on this behavior.
210 setConvertedAttrs(newOp, attrs);
211 return success();
212 }
213};
214
215/// Convert the destination block signatures (if necessary) and lower the
216/// switch op to llvm.switch.
217struct SwitchOpLowering : public ConvertOpToLLVMPattern<cf::SwitchOp> {
219
220 LogicalResult
221 matchAndRewrite(cf::SwitchOp op, cf::SwitchOp::Adaptor adaptor,
222 ConversionPatternRewriter &rewriter) const override {
223 // Get or convert default block.
224 FailureOr<Block *> convertedDefaultBlock = getConvertedBlock(
225 rewriter, getTypeConverter(), op, op.getDefaultDestination(),
226 TypeRange(adaptor.getDefaultOperands()));
227 if (failed(convertedDefaultBlock))
228 return failure();
229
230 // Get or convert all case blocks.
231 SmallVector<Block *> caseDestinations;
232 SmallVector<ValueRange> caseOperands = adaptor.getCaseOperands();
233 for (auto it : llvm::enumerate(op.getCaseDestinations())) {
234 Block *b = it.value();
235 FailureOr<Block *> convertedBlock =
236 getConvertedBlock(rewriter, getTypeConverter(), op, b,
237 TypeRange(caseOperands[it.index()]));
238 if (failed(convertedBlock))
239 return failure();
240 caseDestinations.push_back(*convertedBlock);
241 }
242
243 rewriter.replaceOpWithNewOp<LLVM::SwitchOp>(
244 op, adaptor.getFlag(), *convertedDefaultBlock,
245 adaptor.getDefaultOperands(), adaptor.getCaseValuesAttr(),
246 caseDestinations, caseOperands);
247 return success();
248 }
249};
250
251} // namespace
252
254 const LLVMTypeConverter &converter, RewritePatternSet &patterns) {
255 // clang-format off
256 patterns.add<
257 BranchOpLowering,
258 CondBranchOpLowering,
259 SwitchOpLowering>(converter);
260 // clang-format on
261}
262
264 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
265 bool abortOnFailure, SymbolTableCollection *symbolTables) {
266 patterns.add<AssertOpLowering>(converter, abortOnFailure, symbolTables);
267}
268
269//===----------------------------------------------------------------------===//
270// Pass Definition
271//===----------------------------------------------------------------------===//
272
273namespace {
274/// A pass converting MLIR operations into the LLVM IR dialect.
275struct ConvertControlFlowToLLVM
276 : public impl::ConvertControlFlowToLLVMPassBase<ConvertControlFlowToLLVM> {
277
278 using Base::Base;
279
280 /// Run the dialect converter on the module.
281 void runOnOperation() override {
282 MLIRContext *ctx = &getContext();
284 // This pass lowers only CF dialect ops, but it also modifies block
285 // signatures inside other ops. These ops should be treated as legal. They
286 // are lowered by other passes.
287 target.markUnknownOpDynamicallyLegal([&](Operation *op) {
288 return op->getDialect() !=
289 ctx->getLoadedDialect<cf::ControlFlowDialect>();
290 });
291
293 if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
294 options.overrideIndexBitwidth(indexBitwidth);
295
296 LLVMTypeConverter converter(ctx, options);
297 RewritePatternSet patterns(ctx);
300
301 if (failed(applyPartialConversion(getOperation(), target,
302 std::move(patterns))))
303 signalPassFailure();
304 }
305};
306} // namespace
307
308//===----------------------------------------------------------------------===//
309// ConvertToLLVMPatternInterface implementation
310//===----------------------------------------------------------------------===//
311
312namespace {
313/// Implement the interface to convert MemRef to LLVM.
314struct ControlFlowToLLVMDialectInterface
315 : public ConvertToLLVMPatternInterface {
316 ControlFlowToLLVMDialectInterface(Dialect *dialect)
317 : ConvertToLLVMPatternInterface(dialect) {}
318
319 void loadDependentDialects(MLIRContext *context) const final {
320 context->loadDialect<LLVM::LLVMDialect>();
321 }
322
323 /// Hook for derived dialect interface to provide conversion patterns
324 /// and mark dialect legal for the conversion target.
325 void populateConvertToLLVMConversionPatterns(
326 ConversionTarget &target, LLVMTypeConverter &typeConverter,
327 RewritePatternSet &patterns) const final {
329 patterns);
331 }
332};
333} // namespace
334
336 DialectRegistry &registry) {
337 registry.addExtension(+[](MLIRContext *ctx, cf::ControlFlowDialect *dialect) {
338 dialect->addInterfaces<ControlFlowToLLVMDialectInterface>();
339 });
340}
return success()
static SmallVector< Value > flattenValues(ArrayRef< ValueRange > values)
Flatten the given value ranges into a single vector of values.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
static llvm::ManagedStatic< PassManagerOptions > options
Block represents an ordered list of Operations.
Definition Block.h:33
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition Block.cpp:36
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:233
typename SourceOp::template GenericAdaptor< ArrayRef< ValueRange > > OneToNOpAdaptor
Definition Pattern.h:236
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
Derived class that automatically populates legalization information for different LLVM ops.
Conversion from types to the LLVM IR dialect.
Options to control the LLVM lowering.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void setInherentAttr(StringAttr name, Attribute value)
Set an inherent attribute by name.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
void setDiscardableAttrs(DictionaryAttr newAttrs)
Set the discardable attribute dictionary on this operation.
Definition Operation.h:575
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class represents a collection of SymbolTables.
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
LogicalResult createPrintStrCall(OpBuilder &builder, Location loc, ModuleOp moduleOp, StringRef symbolName, StringRef string, const LLVMTypeConverter &typeConverter, bool addNewline=true, std::optional< StringRef > runtimeFunctionName={}, SymbolTableCollection *symbolTables=nullptr)
Generate IR that prints the given string to stdout.
void registerConvertControlFlowToLLVMInterface(DialectRegistry &registry)
void populateControlFlowToLLVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the patterns to convert from the ControlFlow dialect to LLVM.
void populateAssertToLLVMConversionPattern(const LLVMTypeConverter &converter, RewritePatternSet &patterns, bool abortOnFailure=true, SymbolTableCollection *symbolTables=nullptr)
Populate the cf.assert to LLVM conversion pattern.
Include the generated interface declarations.
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...