Skip to content

Commit 3a43e68

Browse files
Allow inline of all pure ops from the LLVM dialect.
This allows to inline regions containing pure LLVM ops into their call sites. (Note that this is not related to inlining of llvm.func but to any the inlining of any Callable.) For now, we only allow the inlining of Pure ops to be conservative but other ops may be considered inlinable in the future. Testing for purity of ops requires the C++ equivalent of the Pure trait from SideEffectInterfaces.td, which this patch also provide. Its implementation calls the C++ equivalents of the two traits that the Pure trait is based on and, thus, has to be kept with the tablegen trait. Reviewed By: ftynse Differential Revision: https://reviews.llvm.org/D139187
1 parent 15a6e3c commit 3a43e68

File tree

4 files changed

+61
-1
lines changed

4 files changed

+61
-1
lines changed

mlir/include/mlir/Interfaces/SideEffectInterfaces.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ bool isMemoryEffectFree(Operation *op);
337337
/// getSpeculatability hook in the ConditionallySpeculatable op interface.
338338
bool isSpeculatable(Operation *op);
339339

340+
/// Returns true if the given operation is pure, i.e., is speculatable that does
341+
/// not touch memory.
342+
///
343+
/// This function is the C++ equivalent of the `Pure` trait.
344+
bool isPure(Operation *op);
345+
340346
} // namespace mlir
341347

342348
//===----------------------------------------------------------------------===//

mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "mlir/IR/FunctionImplementation.h"
2121
#include "mlir/IR/MLIRContext.h"
2222
#include "mlir/IR/Matchers.h"
23+
#include "mlir/Transforms/InliningUtils.h"
2324

2425
#include "llvm/ADT/TypeSwitch.h"
2526
#include "llvm/AsmParser/Parser.h"
@@ -2583,6 +2584,22 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
25832584
};
25842585
} // namespace
25852586

2587+
//===----------------------------------------------------------------------===//
2588+
// DialectInlinerInterface
2589+
//===----------------------------------------------------------------------===//
2590+
2591+
namespace {
2592+
struct LLVMInlinerInterface : public DialectInlinerInterface {
2593+
using DialectInlinerInterface::DialectInlinerInterface;
2594+
2595+
/// Conservatively only allow inlining of pure ops.
2596+
bool isLegalToInline(Operation *op, Region *, bool,
2597+
BlockAndValueMapping &) const final {
2598+
return isPure(op);
2599+
}
2600+
};
2601+
} // end anonymous namespace
2602+
25862603
//===----------------------------------------------------------------------===//
25872604
// LLVMDialect initialization, type parsing, and registration.
25882605
//===----------------------------------------------------------------------===//
@@ -2611,7 +2628,10 @@ void LLVMDialect::initialize() {
26112628

26122629
// Support unknown operations because not all LLVM operations are registered.
26132630
allowUnknownOperations();
2614-
addInterfaces<LLVMOpAsmDialectInterface>();
2631+
// clang-format off
2632+
addInterfaces<LLVMOpAsmDialectInterface,
2633+
LLVMInlinerInterface>();
2634+
// clang-format on
26152635
}
26162636

26172637
#define GET_OP_CLASSES

mlir/lib/Interfaces/SideEffectInterfaces.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,9 @@ bool mlir::isSpeculatable(Operation *op) {
202202

203203
llvm_unreachable("Unhandled enum in mlir::isSpeculatable!");
204204
}
205+
206+
/// The implementation of this function replicates the `def Pure : TraitList`
207+
/// in `SideEffectInterfaces.td` and has to be kept in sync manually.
208+
bool mlir::isPure(Operation *op) {
209+
return isSpeculatable(op) && isMemoryEffectFree(op);
210+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: mlir-opt %s -inline | FileCheck %s
2+
3+
// CHECK-LABEL: func.func @test_inline() -> i32 {
4+
// CHECK-NEXT: %[[RES:.*]] = llvm.mlir.constant(42 : i32) : i32
5+
// CHECK-NEXT: return %[[RES]] : i32
6+
func.func @test_inline() -> i32 {
7+
%0 = call @inner_func_inlinable() : () -> i32
8+
return %0 : i32
9+
}
10+
11+
func.func @inner_func_inlinable() -> i32 {
12+
%0 = llvm.mlir.constant(42 : i32) : i32
13+
return %0 : i32
14+
}
15+
16+
// CHECK-LABEL: func.func @test_not_inline() -> !llvm.ptr<f64> {
17+
// CHECK-NEXT: %[[RES:.*]] = call @inner_func_not_inlinable() : () -> !llvm.ptr<f64>
18+
// CHECK-NEXT: return %[[RES]] : !llvm.ptr<f64>
19+
func.func @test_not_inline() -> !llvm.ptr<f64> {
20+
%0 = call @inner_func_not_inlinable() : () -> !llvm.ptr<f64>
21+
return %0 : !llvm.ptr<f64>
22+
}
23+
24+
func.func @inner_func_not_inlinable() -> !llvm.ptr<f64> {
25+
%0 = llvm.mlir.constant(0 : i32) : i32
26+
%1 = llvm.alloca %0 x f64 : (i32) -> !llvm.ptr<f64>
27+
return %1 : !llvm.ptr<f64>
28+
}

0 commit comments

Comments
 (0)