Historically the builtin dialect has had an empty namespace. This has unfortunately created a very awkward situation, where many utilities either have to special case the empty namespace, or just don't work at all right now. This revision adds a namespace to the builtin dialect, and starts to cleanup some of the utilities to no longer handle empty namespaces. For now, the assembly form of builtin operations does not require the `builtin.` prefix. (This should likely be re-evaluated though) Differential Revision: https://reviews.llvm.org/D105149
273 lines
7.1 KiB
MLIR
273 lines
7.1 KiB
MLIR
// RUN: mlir-opt -allow-unregistered-dialect %s -sccp -split-input-file | FileCheck %s
|
|
// RUN: mlir-opt -allow-unregistered-dialect %s -pass-pipeline="builtin.module(sccp)" -split-input-file | FileCheck %s --check-prefix=NESTED
|
|
|
|
/// Check that a constant is properly propagated through the arguments and
|
|
/// results of a private function.
|
|
|
|
// CHECK-LABEL: func private @private(
|
|
func private @private(%arg0 : i32) -> i32 {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
return %arg0 : i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @simple_private(
|
|
func @simple_private() -> i32 {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result = call @private(%1) : (i32) -> i32
|
|
return %result : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that a constant is properly propagated through the arguments and
|
|
/// results of a visible nested function.
|
|
|
|
// CHECK: func nested @nested(
|
|
func nested @nested(%arg0 : i32) -> i32 {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
return %arg0 : i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @simple_nested(
|
|
func @simple_nested() -> i32 {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result = call @nested(%1) : (i32) -> i32
|
|
return %result : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that non-visible nested functions do not track arguments.
|
|
module {
|
|
// NESTED-LABEL: module @nested_module
|
|
module @nested_module attributes { sym_visibility = "public" } {
|
|
|
|
// NESTED: func nested @nested(
|
|
func nested @nested(%arg0 : i32) -> (i32, i32) {
|
|
// NESTED: %[[CST:.*]] = constant 1 : i32
|
|
// NESTED: return %[[CST]], %arg0 : i32, i32
|
|
|
|
%1 = constant 1 : i32
|
|
return %1, %arg0 : i32, i32
|
|
}
|
|
|
|
// NESTED: func @nested_not_all_uses_visible(
|
|
func @nested_not_all_uses_visible() -> (i32, i32) {
|
|
// NESTED: %[[CST:.*]] = constant 1 : i32
|
|
// NESTED: %[[CALL:.*]]:2 = call @nested
|
|
// NESTED: return %[[CST]], %[[CALL]]#1 : i32, i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result:2 = call @nested(%1) : (i32) -> (i32, i32)
|
|
return %result#0, %result#1 : i32, i32
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that public functions do not track arguments.
|
|
|
|
// CHECK-LABEL: func @public(
|
|
func @public(%arg0 : i32) -> (i32, i32) {
|
|
%1 = constant 1 : i32
|
|
return %1, %arg0 : i32, i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @simple_public(
|
|
func @simple_public() -> (i32, i32) {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: %[[CALL:.*]]:2 = call @public
|
|
// CHECK: return %[[CST]], %[[CALL]]#1 : i32, i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result:2 = call @public(%1) : (i32) -> (i32, i32)
|
|
return %result#0, %result#1 : i32, i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that functions with non-call users don't have arguments tracked.
|
|
|
|
func private @callable(%arg0 : i32) -> (i32, i32) {
|
|
%1 = constant 1 : i32
|
|
return %1, %arg0 : i32, i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @non_call_users(
|
|
func @non_call_users() -> (i32, i32) {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: %[[CALL:.*]]:2 = call @callable
|
|
// CHECK: return %[[CST]], %[[CALL]]#1 : i32, i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result:2 = call @callable(%1) : (i32) -> (i32, i32)
|
|
return %result#0, %result#1 : i32, i32
|
|
}
|
|
|
|
"live.user"() {uses = [@callable]} : () -> ()
|
|
|
|
// -----
|
|
|
|
/// Check that return values are overdefined in the presence of an unknown terminator.
|
|
|
|
func private @callable(%arg0 : i32) -> i32 {
|
|
"unknown.return"(%arg0) : (i32) -> ()
|
|
}
|
|
|
|
// CHECK-LABEL: func @unknown_terminator(
|
|
func @unknown_terminator() -> i32 {
|
|
// CHECK: %[[CALL:.*]] = call @callable
|
|
// CHECK: return %[[CALL]] : i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result = call @callable(%1) : (i32) -> i32
|
|
return %result : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that return values are overdefined when the constant conflicts.
|
|
|
|
func private @callable(%arg0 : i32) -> i32 {
|
|
return %arg0 : i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @conflicting_constant(
|
|
func @conflicting_constant() -> (i32, i32) {
|
|
// CHECK: %[[CALL1:.*]] = call @callable
|
|
// CHECK: %[[CALL2:.*]] = call @callable
|
|
// CHECK: return %[[CALL1]], %[[CALL2]] : i32, i32
|
|
|
|
%1 = constant 1 : i32
|
|
%2 = constant 2 : i32
|
|
%result = call @callable(%1) : (i32) -> i32
|
|
%result2 = call @callable(%2) : (i32) -> i32
|
|
return %result, %result2 : i32, i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that return values are overdefined when the constant conflicts with a
|
|
/// non-constant.
|
|
|
|
func private @callable(%arg0 : i32) -> i32 {
|
|
"unknown.return"(%arg0) : (i32) -> ()
|
|
}
|
|
|
|
// CHECK-LABEL: func @conflicting_constant(
|
|
func @conflicting_constant(%arg0 : i32) -> (i32, i32) {
|
|
// CHECK: %[[CALL1:.*]] = call @callable
|
|
// CHECK: %[[CALL2:.*]] = call @callable
|
|
// CHECK: return %[[CALL1]], %[[CALL2]] : i32, i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result = call @callable(%1) : (i32) -> i32
|
|
%result2 = call @callable(%arg0) : (i32) -> i32
|
|
return %result, %result2 : i32, i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check a more complex interaction with calls and control flow.
|
|
|
|
// CHECK-LABEL: func private @complex_inner_if(
|
|
func private @complex_inner_if(%arg0 : i32) -> i32 {
|
|
// CHECK-DAG: %[[TRUE:.*]] = constant true
|
|
// CHECK-DAG: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: cond_br %[[TRUE]], ^bb1
|
|
|
|
%cst_20 = constant 20 : i32
|
|
%cond = cmpi ult, %arg0, %cst_20 : i32
|
|
cond_br %cond, ^bb1, ^bb2
|
|
|
|
^bb1:
|
|
// CHECK: ^bb1:
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
%cst_1 = constant 1 : i32
|
|
return %cst_1 : i32
|
|
|
|
^bb2:
|
|
%cst_1_2 = constant 1 : i32
|
|
%arg_inc = addi %arg0, %cst_1_2 : i32
|
|
return %arg_inc : i32
|
|
}
|
|
|
|
func private @complex_cond() -> i1
|
|
|
|
// CHECK-LABEL: func private @complex_callee(
|
|
func private @complex_callee(%arg0 : i32) -> i32 {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
|
|
%loop_cond = call @complex_cond() : () -> i1
|
|
cond_br %loop_cond, ^bb1, ^bb2
|
|
|
|
^bb1:
|
|
// CHECK: ^bb1:
|
|
// CHECK-NEXT: return %[[CST]] : i32
|
|
return %arg0 : i32
|
|
|
|
^bb2:
|
|
// CHECK: ^bb2:
|
|
// CHECK: call @complex_inner_if(%[[CST]]) : (i32) -> i32
|
|
// CHECK: call @complex_callee(%[[CST]]) : (i32) -> i32
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
%updated_arg = call @complex_inner_if(%arg0) : (i32) -> i32
|
|
%res = call @complex_callee(%updated_arg) : (i32) -> i32
|
|
return %res : i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @complex_caller(
|
|
func @complex_caller(%arg0 : i32) -> i32 {
|
|
// CHECK: %[[CST:.*]] = constant 1 : i32
|
|
// CHECK: return %[[CST]] : i32
|
|
|
|
%1 = constant 1 : i32
|
|
%result = call @complex_callee(%1) : (i32) -> i32
|
|
return %result : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that non-symbol defining callables currently go to overdefined.
|
|
|
|
// CHECK-LABEL: func @non_symbol_defining_callable
|
|
func @non_symbol_defining_callable() -> i32 {
|
|
// CHECK: %[[RES:.*]] = call_indirect
|
|
// CHECK: return %[[RES]] : i32
|
|
|
|
%fn = "test.functional_region_op"() ({
|
|
%1 = constant 1 : i32
|
|
"test.return"(%1) : (i32) -> ()
|
|
}) : () -> (() -> i32)
|
|
%res = call_indirect %fn() : () -> (i32)
|
|
return %res : i32
|
|
}
|
|
|
|
// -----
|
|
|
|
/// Check that private callables don't get processed if they have no uses.
|
|
|
|
// CHECK-LABEL: func private @unreferenced_private_function
|
|
func private @unreferenced_private_function() -> i32 {
|
|
// CHECK: %[[RES:.*]] = select
|
|
// CHECK: return %[[RES]] : i32
|
|
%true = constant true
|
|
%cst0 = constant 0 : i32
|
|
%cst1 = constant 1 : i32
|
|
%result = select %true, %cst0, %cst1 : i32
|
|
return %result : i32
|
|
}
|