Files
clang-p2996/mlir/test/Transforms/inlining.mlir
Tobias Gysi f809eb4db2 [mlir] Argument and result attribute handling during inlining.
The revision adds the handleArgument and handleResult handlers that
allow users of the inlining interface to implement argument and result
conversions that take argument and result attributes into account. The
motivating use cases for this revision are taken from the LLVM dialect
inliner, which has to copy arguments that are marked as byval and that
also has to consider zeroext / signext when converting integers.

All type conversions are currently handled by the
materializeCallConversion hook. It runs before isLegalToInline and
supports only the introduction of a single cast operation since it may
have to rollback. The new handlers run shortly before and after
inlining and cannot fail. As a result, they can introduce more complex
ir such as copying a struct argument. At the moment, the new hooks
cannot be used to perform type conversions since all type conversions
have to be done using the materializeCallConversion. A follow up
revision will either relax this constraint or drop
materializeCallConversion in favor of the new and more flexible
handlers.

The revision also extends the CallableOpInterface to provide access
to the argument and result attributes if available.

Reviewed By: rriddle, Dinistro

Differential Revision: https://reviews.llvm.org/D145582
2023-03-22 09:02:15 +01:00

266 lines
8.9 KiB
MLIR

// RUN: mlir-opt %s -inline='default-pipeline=''' | FileCheck %s
// RUN: mlir-opt %s --mlir-disable-threading -inline='default-pipeline=''' | FileCheck %s
// RUN: mlir-opt %s -inline='default-pipeline=''' -mlir-print-debuginfo -mlir-print-local-scope | FileCheck %s --check-prefix INLINE-LOC
// RUN: mlir-opt %s -inline | FileCheck %s --check-prefix INLINE_SIMPLIFY
// RUN: mlir-opt %s -inline='op-pipelines=func.func(canonicalize,cse)' | FileCheck %s --check-prefix INLINE_SIMPLIFY
// Inline a function that takes an argument.
func.func @func_with_arg(%c : i32) -> i32 {
%b = arith.addi %c, %c : i32
return %b : i32
}
// CHECK-LABEL: func @inline_with_arg
func.func @inline_with_arg(%arg0 : i32) -> i32 {
// CHECK-NEXT: arith.addi
// CHECK-NEXT: return
%0 = call @func_with_arg(%arg0) : (i32) -> i32
return %0 : i32
}
// Inline a function that has multiple return operations.
func.func @func_with_multi_return(%a : i1) -> (i32) {
cf.cond_br %a, ^bb1, ^bb2
^bb1:
%const_0 = arith.constant 0 : i32
return %const_0 : i32
^bb2:
%const_55 = arith.constant 55 : i32
return %const_55 : i32
}
// CHECK-LABEL: func @inline_with_multi_return() -> i32
func.func @inline_with_multi_return() -> i32 {
// CHECK-NEXT: [[VAL_7:%.*]] = arith.constant false
// CHECK-NEXT: cf.cond_br [[VAL_7]], ^bb1, ^bb2
// CHECK: ^bb1:
// CHECK-NEXT: [[VAL_8:%.*]] = arith.constant 0 : i32
// CHECK-NEXT: cf.br ^bb3([[VAL_8]] : i32)
// CHECK: ^bb2:
// CHECK-NEXT: [[VAL_9:%.*]] = arith.constant 55 : i32
// CHECK-NEXT: cf.br ^bb3([[VAL_9]] : i32)
// CHECK: ^bb3([[VAL_10:%.*]]: i32):
// CHECK-NEXT: return [[VAL_10]] : i32
%false = arith.constant false
%x = call @func_with_multi_return(%false) : (i1) -> i32
return %x : i32
}
// Check that location information is updated for inlined instructions.
func.func @func_with_locations(%c : i32) -> i32 {
%b = arith.addi %c, %c : i32 loc("mysource.cc":10:8)
return %b : i32 loc("mysource.cc":11:2)
}
// INLINE-LOC-LABEL: func @inline_with_locations
func.func @inline_with_locations(%arg0 : i32) -> i32 {
// INLINE-LOC-NEXT: arith.addi %{{.*}}, %{{.*}} : i32 loc(callsite("mysource.cc":10:8 at "mysource.cc":55:14))
// INLINE-LOC-NEXT: return
%0 = call @func_with_locations(%arg0) : (i32) -> i32 loc("mysource.cc":55:14)
return %0 : i32
}
// Check that external function declarations are not inlined.
func.func private @func_external()
// CHECK-LABEL: func @no_inline_external
func.func @no_inline_external() {
// CHECK-NEXT: call @func_external()
call @func_external() : () -> ()
return
}
// Check that multiple levels of calls will be inlined.
func.func @multilevel_func_a() {
return
}
func.func @multilevel_func_b() {
call @multilevel_func_a() : () -> ()
return
}
// CHECK-LABEL: func @inline_multilevel
func.func @inline_multilevel() {
// CHECK-NOT: call
%fn = "test.functional_region_op"() ({
call @multilevel_func_b() : () -> ()
"test.return"() : () -> ()
}) : () -> (() -> ())
call_indirect %fn() : () -> ()
return
}
// Check that recursive calls are not inlined.
// CHECK-LABEL: func @no_inline_recursive
func.func @no_inline_recursive() {
// CHECK: test.functional_region_op
// CHECK-NOT: test.functional_region_op
%fn = "test.functional_region_op"() ({
call @no_inline_recursive() : () -> ()
"test.return"() : () -> ()
}) : () -> (() -> ())
return
}
// Check that we can convert types for inputs and results as necessary.
func.func @convert_callee_fn(%arg : i32) -> i32 {
return %arg : i32
}
func.func @convert_callee_fn_multi_arg(%a : i32, %b : i32) -> () {
return
}
func.func @convert_callee_fn_multi_res() -> (i32, i32) {
%res = arith.constant 0 : i32
return %res, %res : i32, i32
}
// CHECK-LABEL: func @inline_convert_call
func.func @inline_convert_call() -> i16 {
// CHECK: %[[INPUT:.*]] = arith.constant
%test_input = arith.constant 0 : i16
// CHECK: %[[CAST_INPUT:.*]] = "test.cast"(%[[INPUT]]) : (i16) -> i32
// CHECK: %[[CAST_RESULT:.*]] = "test.cast"(%[[CAST_INPUT]]) : (i32) -> i16
// CHECK-NEXT: return %[[CAST_RESULT]]
%res = "test.conversion_call_op"(%test_input) { callee=@convert_callee_fn } : (i16) -> (i16)
return %res : i16
}
func.func @convert_callee_fn_multiblock() -> i32 {
cf.br ^bb0
^bb0:
%0 = arith.constant 0 : i32
return %0 : i32
}
// CHECK-LABEL: func @inline_convert_result_multiblock
func.func @inline_convert_result_multiblock() -> i16 {
// CHECK: cf.br ^bb1 {inlined_conversion}
// CHECK: ^bb1:
// CHECK: %[[C:.+]] = arith.constant {inlined_conversion} 0 : i32
// CHECK: cf.br ^bb2(%[[C]] : i32)
// CHECK: ^bb2(%[[BBARG:.+]]: i32):
// CHECK: %[[CAST_RESULT:.+]] = "test.cast"(%[[BBARG]]) : (i32) -> i16
// CHECK: return %[[CAST_RESULT]] : i16
%res = "test.conversion_call_op"() { callee=@convert_callee_fn_multiblock } : () -> (i16)
return %res : i16
}
// CHECK-LABEL: func @no_inline_convert_call
func.func @no_inline_convert_call() {
// CHECK: "test.conversion_call_op"
%test_input_i16 = arith.constant 0 : i16
%test_input_i64 = arith.constant 0 : i64
"test.conversion_call_op"(%test_input_i16, %test_input_i64) { callee=@convert_callee_fn_multi_arg } : (i16, i64) -> ()
// CHECK: "test.conversion_call_op"
%res_2:2 = "test.conversion_call_op"() { callee=@convert_callee_fn_multi_res } : () -> (i16, i64)
return
}
// Check that we properly simplify when inlining.
func.func @simplify_return_constant() -> i32 {
%res = arith.constant 0 : i32
return %res : i32
}
func.func @simplify_return_reference() -> (() -> i32) {
%res = constant @simplify_return_constant : () -> i32
return %res : () -> i32
}
// INLINE_SIMPLIFY-LABEL: func @inline_simplify
func.func @inline_simplify() -> i32 {
// INLINE_SIMPLIFY-NEXT: %[[CST:.*]] = arith.constant 0 : i32
// INLINE_SIMPLIFY-NEXT: return %[[CST]]
%fn = call @simplify_return_reference() : () -> (() -> i32)
%res = call_indirect %fn() : () -> i32
return %res : i32
}
// CHECK-LABEL: func @no_inline_invalid_call
func.func @no_inline_invalid_call() -> i32 {
%res = "test.conversion_call_op"() { callee=@convert_callee_fn_multiblock, noinline } : () -> (i32)
return %res : i32
}
func.func @gpu_alloc() -> memref<1024xf32> {
%m = gpu.alloc [] () : memref<1024xf32>
return %m : memref<1024xf32>
}
// CHECK-LABEL: func @inline_gpu_ops
func.func @inline_gpu_ops() -> memref<1024xf32> {
// CHECK-NEXT: gpu.alloc
%m = call @gpu_alloc() : () -> memref<1024xf32>
return %m : memref<1024xf32>
}
// Test block arguments location propagation.
// Use two call-sites to force cloning.
func.func @func_with_block_args_location(%arg0 : i32) {
cf.br ^bb1(%arg0 : i32)
^bb1(%x : i32 loc("foo")):
"test.foo" (%x) : (i32) -> () loc("bar")
return
}
// INLINE-LOC-LABEL: func @func_with_block_args_location_callee1
// INLINE-LOC: cf.br
// INLINE-LOC: ^bb{{[0-9]+}}(%{{.*}}: i32 loc("foo")
func.func @func_with_block_args_location_callee1(%arg0 : i32) {
call @func_with_block_args_location(%arg0) : (i32) -> ()
return
}
// CHECK-LABEL: func @func_with_block_args_location_callee2
func.func @func_with_block_args_location_callee2(%arg0 : i32) {
call @func_with_block_args_location(%arg0) : (i32) -> ()
return
}
// Check that we can handle argument and result attributes.
test.conversion_func_op @handle_attr_callee_fn_multi_arg(%arg0 : i16, %arg1 : i16 {"test.handle_argument"}) -> (i16 {"test.handle_result"}, i16) {
%0 = arith.addi %arg0, %arg1 : i16
%1 = arith.subi %arg0, %arg1 : i16
"test.return"(%0, %1) : (i16, i16) -> ()
}
test.conversion_func_op @handle_attr_callee_fn(%arg0 : i32 {"test.handle_argument"}) -> (i32 {"test.handle_result"}) {
"test.return"(%arg0) : (i32) -> ()
}
// CHECK-LABEL: func @inline_handle_attr_call
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]
// CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]
func.func @inline_handle_attr_call(%arg0 : i16, %arg1 : i16) -> (i16, i16) {
// CHECK: %[[CHANGE_INPUT:.*]] = "test.type_changer"(%[[ARG1]]) : (i16) -> i16
// CHECK: %[[SUM:.*]] = arith.addi %[[ARG0]], %[[CHANGE_INPUT]]
// CHECK: %[[DIFF:.*]] = arith.subi %[[ARG0]], %[[CHANGE_INPUT]]
// CHECK: %[[CHANGE_RESULT:.*]] = "test.type_changer"(%[[SUM]]) : (i16) -> i16
// CHECK-NEXT: return %[[CHANGE_RESULT]], %[[DIFF]]
%res0, %res1 = "test.conversion_call_op"(%arg0, %arg1) { callee=@handle_attr_callee_fn_multi_arg } : (i16, i16) -> (i16, i16)
return %res0, %res1 : i16, i16
}
// CHECK-LABEL: func @inline_convert_and_handle_attr_call
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]
func.func @inline_convert_and_handle_attr_call(%arg0 : i16) -> (i16) {
// CHECK: %[[CAST_INPUT:.*]] = "test.cast"(%[[ARG0]]) : (i16) -> i32
// CHECK: %[[CHANGE_INPUT:.*]] = "test.type_changer"(%[[CAST_INPUT]]) : (i32) -> i32
// CHECK: %[[CHANGE_RESULT:.*]] = "test.type_changer"(%[[CHANGE_INPUT]]) : (i32) -> i32
// CHECK: %[[CAST_RESULT:.*]] = "test.cast"(%[[CHANGE_RESULT]]) : (i32) -> i16
// CHECK: return %[[CAST_RESULT]]
%res = "test.conversion_call_op"(%arg0) { callee=@handle_attr_callee_fn } : (i16) -> (i16)
return %res : i16
}