Files
clang-p2996/mlir/test/Dialect/Func/duplicate-function-elimination.mlir
Longsheng Mou 2ce655cf1b [mlir][func] Fix multiple bugs in DuplicateFunctionElimination (#109571)
This PR fixes multiple bugs in `DuplicateFunctionElimination`.
- Prevents elimination of function declarations.
- Updates all symbol uses to reference unique function representatives.

Fixes #93483.
2024-10-22 09:19:12 +08:00

416 lines
9.9 KiB
MLIR

// RUN: mlir-opt %s --split-input-file --duplicate-function-elimination | \
// RUN: FileCheck %s
func.func @identity(%arg0: tensor<f32>) -> tensor<f32> {
return %arg0 : tensor<f32>
}
func.func @also_identity(%arg0: tensor<f32>) -> tensor<f32> {
return %arg0 : tensor<f32>
}
func.func @yet_another_identity(%arg0: tensor<f32>) -> tensor<f32> {
return %arg0 : tensor<f32>
}
func.func @user(%arg0: tensor<f32>) -> tensor<f32> {
%0 = call @identity(%arg0) : (tensor<f32>) -> tensor<f32>
%1 = call @also_identity(%0) : (tensor<f32>) -> tensor<f32>
%2 = call @yet_another_identity(%1) : (tensor<f32>) -> tensor<f32>
return %2 : tensor<f32>
}
// CHECK: @identity
// CHECK-NOT: @also_identity
// CHECK-NOT: @yet_another_identity
// CHECK: @user
// CHECK-3: call @identity
// -----
func.func @add_lr(%arg0: f32, %arg1: f32) -> f32 {
%0 = arith.addf %arg0, %arg1 : f32
return %0 : f32
}
func.func @also_add_lr(%arg0: f32, %arg1: f32) -> f32 {
%0 = arith.addf %arg0, %arg1 : f32
return %0 : f32
}
func.func @add_rl(%arg0: f32, %arg1: f32) -> f32 {
%0 = arith.addf %arg1, %arg0 : f32
return %0 : f32
}
func.func @also_add_rl(%arg0: f32, %arg1: f32) -> f32 {
%0 = arith.addf %arg1, %arg0 : f32
return %0 : f32
}
func.func @user(%arg0: f32, %arg1: f32) -> f32 {
%0 = call @add_lr(%arg0, %arg1) : (f32, f32) -> f32
%1 = call @also_add_lr(%arg0, %arg1) : (f32, f32) -> f32
%2 = call @add_rl(%0, %1) : (f32, f32) -> f32
%3 = call @also_add_rl(%arg0, %2) : (f32, f32) -> f32
return %3 : f32
}
// CHECK: @add_lr
// CHECK-NOT: @also_add_lr
// CHECK: @add_rl
// CHECK-NOT: @also_add_rl
// CHECK: @user
// CHECK-2: call @add_lr
// CHECK-2: call @add_rl
// -----
func.func @ite(%pred: i1, %then: f32, %else: f32) -> f32 {
%0 = scf.if %pred -> f32 {
scf.yield %then : f32
} else {
scf.yield %else : f32
}
return %0 : f32
}
func.func @also_ite(%pred: i1, %then: f32, %else: f32) -> f32 {
%0 = scf.if %pred -> f32 {
scf.yield %then : f32
} else {
scf.yield %else : f32
}
return %0 : f32
}
func.func @reverse_ite(%pred: i1, %then: f32, %else: f32) -> f32 {
%0 = scf.if %pred -> f32 {
scf.yield %else : f32
} else {
scf.yield %then : f32
}
return %0 : f32
}
func.func @user(%pred : i1, %arg0: f32, %arg1: f32) -> f32 {
%0 = call @also_ite(%pred, %arg0, %arg1) : (i1, f32, f32) -> f32
%1 = call @ite(%pred, %arg0, %arg1) : (i1, f32, f32) -> f32
%2 = call @reverse_ite(%pred, %0, %1) : (i1, f32, f32) -> f32
return %2 : f32
}
// CHECK: @ite
// CHECK-NOT: @also_ite
// CHECK: @reverse_ite
// CHECK: @user
// CHECK-2: call @ite
// CHECK: call @reverse_ite
// -----
func.func @deep_tree(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %even: f32, %odd: f32)
-> f32 {
%0 = scf.if %p0 -> f32 {
%1 = scf.if %p1 -> f32 {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
} else {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
}
scf.yield %1 : f32
} else {
%1 = scf.if %p1 -> f32 {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
} else {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
}
scf.yield %1 : f32
}
return %0 : f32
}
func.func @also_deep_tree(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %even: f32,
%odd: f32) -> f32 {
%0 = scf.if %p0 -> f32 {
%1 = scf.if %p1 -> f32 {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
} else {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
}
scf.yield %1 : f32
} else {
%1 = scf.if %p1 -> f32 {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
} else {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
}
scf.yield %1 : f32
}
return %0 : f32
}
func.func @reverse_deep_tree(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %even: f32,
%odd: f32) -> f32 {
%0 = scf.if %p0 -> f32 {
%1 = scf.if %p1 -> f32 {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
} else {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
}
scf.yield %1 : f32
} else {
%1 = scf.if %p1 -> f32 {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
} else {
%2 = scf.if %p2 -> f32 {
%3 = scf.if %p3 -> f32 {
scf.yield %odd : f32
} else {
scf.yield %even : f32
}
scf.yield %3 : f32
} else {
%3 = scf.if %p3 -> f32 {
scf.yield %even : f32
} else {
scf.yield %odd : f32
}
scf.yield %3 : f32
}
scf.yield %2 : f32
}
scf.yield %1 : f32
}
return %0 : f32
}
func.func @user(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %odd: f32, %even: f32)
-> (f32, f32, f32) {
%0 = call @deep_tree(%p0, %p1, %p2, %p3, %odd, %even)
: (i1, i1, i1, i1, f32, f32) -> f32
%1 = call @also_deep_tree(%p0, %p1, %p2, %p3, %odd, %even)
: (i1, i1, i1, i1, f32, f32) -> f32
%2 = call @reverse_deep_tree(%p0, %p1, %p2, %p3, %odd, %even)
: (i1, i1, i1, i1, f32, f32) -> f32
return %0, %1, %2 : f32, f32, f32
}
// CHECK: @deep_tree
// CHECK-NOT: @also_deep_tree
// CHECK: @reverse_deep_tree
// CHECK: @user
// CHECK-2: call @deep_tree
// CHECK: call @reverse_deep_tree
// -----
func.func private @func_declaration(i32, i32) -> i32
func.func private @func_declaration1(i32, i32) -> i32
func.func @user(%arg0: i32, %arg1: i32) -> (i32, i32) {
%0 = call @func_declaration(%arg0, %arg1) : (i32, i32) -> i32
%1 = call @func_declaration1(%arg0, %arg1) : (i32, i32) -> i32
return %0, %1 : i32, i32
}
// CHECK: @func_declaration
// CHECK: @func_declaration1
// CHECK: @user
// CHECK: call @func_declaration
// CHECK: call @func_declaration1
// -----
func.func @identity(%arg0: tensor<f32>) -> tensor<f32> {
return %arg0 : tensor<f32>
}
func.func @also_identity(%arg0: tensor<f32>) -> tensor<f32> {
return %arg0 : tensor<f32>
}
func.func @yet_another_identity(%arg0: tensor<f32>) -> tensor<f32> {
return %arg0 : tensor<f32>
}
func.func @user(%arg0: tensor<f32>) -> tensor<f32> {
%f = constant @identity : (tensor<f32>) -> tensor<f32>
%0 = call_indirect %f(%arg0) : (tensor<f32>) -> tensor<f32>
%f_0 = constant @also_identity : (tensor<f32>) -> tensor<f32>
%1 = call_indirect %f_0(%0) : (tensor<f32>) -> tensor<f32>
%2 = call @yet_another_identity(%1) : (tensor<f32>) -> tensor<f32>
return %2 : tensor<f32>
}
// CHECK: @identity
// CHECK-NOT: @also_identity
// CHECK-NOT: @yet_another_identity
// CHECK: @user
// CHECK-2: constant @identity
// CHECK: call @identity