1// RUN: mlir-opt %s -split-input-file \
2// RUN:   -test-one-to-n-type-conversion="convert-func-ops convert-scf-ops" \
3// RUN: | FileCheck %s
4
5// Test case: Nested 1:N type conversion is carried through scf.if and
6// scf.yield.
7
8// CHECK-LABEL: func.func @if_result(
9// CHECK-SAME:                       %[[ARG0:.*]]: i1,
10// CHECK-SAME:                       %[[ARG1:.*]]: i2,
11// CHECK-SAME:                       %[[ARG2:.*]]: i1) -> (i1, i2) {
12// CHECK-NEXT:    %[[V0:.*]]:2 = scf.if %[[ARG2]] -> (i1, i2) {
13// CHECK-NEXT:     scf.yield %[[ARG0]], %[[ARG1]] : i1, i2
14// CHECK-NEXT:   } else {
15// CHECK-NEXT:     scf.yield %[[ARG0]], %[[ARG1]] : i1, i2
16// CHECK-NEXT:   }
17// CHECK-NEXT:   return %[[V0]]#0, %[[V0]]#1 : i1, i2
18func.func @if_result(%arg0: tuple<tuple<>, i1, tuple<i2>>, %arg1: i1) -> tuple<tuple<>, i1, tuple<i2>> {
19  %0 = scf.if %arg1 -> (tuple<tuple<>, i1, tuple<i2>>) {
20    scf.yield %arg0 : tuple<tuple<>, i1, tuple<i2>>
21  } else {
22    scf.yield %arg0 : tuple<tuple<>, i1, tuple<i2>>
23  }
24  return %0 : tuple<tuple<>, i1, tuple<i2>>
25}
26
27// -----
28
29// Test case: Nested 1:N type conversion is carried through scf.if and
30// scf.yield and unconverted ops inside have proper materializations.
31
32// CHECK-LABEL: func.func @if_tuple_ops(
33// CHECK-SAME:                          %[[ARG0:.*]]: i1,
34// CHECK-SAME:                          %[[ARG1:.*]]: i1) -> i1 {
35// CHECK-NEXT:    %[[V0:.*]] = "test.make_tuple"() : () -> tuple<>
36// CHECK-NEXT:    %[[V1:.*]] = "test.make_tuple"(%[[V0]], %[[ARG0]]) : (tuple<>, i1) -> tuple<tuple<>, i1>
37// CHECK-NEXT:    %[[V2:.*]] = scf.if %[[ARG1]] -> (i1) {
38// CHECK-NEXT:      %[[V3:.*]] = "test.op"(%[[V1]]) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
39// CHECK-NEXT:      %[[V4:.*]] = "test.get_tuple_element"(%[[V3]]) <{index = 0 : i32}> : (tuple<tuple<>, i1>) -> tuple<>
40// CHECK-NEXT:      %[[V5:.*]] = "test.get_tuple_element"(%[[V3]]) <{index = 1 : i32}> : (tuple<tuple<>, i1>) -> i1
41// CHECK-NEXT:      scf.yield %[[V5]] : i1
42// CHECK-NEXT:    } else {
43// CHECK-NEXT:      %[[V6:.*]] = "test.source"() : () -> tuple<tuple<>, i1>
44// CHECK-NEXT:      %[[V7:.*]] = "test.get_tuple_element"(%[[V6]]) <{index = 0 : i32}> : (tuple<tuple<>, i1>) -> tuple<>
45// CHECK-NEXT:      %[[V8:.*]] = "test.get_tuple_element"(%[[V6]]) <{index = 1 : i32}> : (tuple<tuple<>, i1>) -> i1
46// CHECK-NEXT:      scf.yield %[[V8]] : i1
47// CHECK-NEXT:    }
48// CHECK-NEXT:    return %[[V2]] : i1
49func.func @if_tuple_ops(%arg0: tuple<tuple<>, i1>, %arg1: i1) -> tuple<tuple<>, i1> {
50  %0 = scf.if %arg1 -> (tuple<tuple<>, i1>) {
51    %1 = "test.op"(%arg0) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
52    scf.yield %1 : tuple<tuple<>, i1>
53  } else {
54    %1 = "test.source"() : () -> tuple<tuple<>, i1>
55    scf.yield %1 : tuple<tuple<>, i1>
56  }
57  return %0 : tuple<tuple<>, i1>
58}
59// -----
60
61// Test case: Nested 1:N type conversion is carried through scf.while,
62// scf.condition, and scf.yield.
63
64// CHECK-LABEL: func.func @while_operands_results(
65// CHECK-SAME:                                    %[[ARG0:.*]]: i1,
66// CHECK-SAME:                                    %[[ARG1:.*]]: i2,
67// CHECK-SAME:                                    %[[ARG2:.*]]: i1) -> (i1, i2) {
68//   %[[V0:.*]]:2 = scf.while (%[[ARG3:.*]] = %[[ARG0]], %[[ARG4:.*]] = %[[ARG1]]) : (i1, i2) -> (i1, i2) {
69//     scf.condition(%arg2) %[[ARG3]], %[[ARG4]] : i1, i2
70//   } do {
71//   ^bb0(%[[ARG5:.*]]: i1, %[[ARG6:.*]]: i2):
72//     scf.yield %[[ARG5]], %[[ARG4]] : i1, i2
73//   }
74//   return %[[V0]]#0, %[[V0]]#1 : i1, i2
75func.func @while_operands_results(%arg0: tuple<tuple<>, i1, tuple<i2>>, %arg1: i1) -> tuple<tuple<>, i1, tuple<i2>> {
76  %0 = scf.while (%arg2 = %arg0) : (tuple<tuple<>, i1, tuple<i2>>) -> tuple<tuple<>, i1, tuple<i2>> {
77    scf.condition(%arg1) %arg2 : tuple<tuple<>, i1, tuple<i2>>
78  } do {
79  ^bb0(%arg2: tuple<tuple<>, i1, tuple<i2>>):
80    scf.yield %arg2 : tuple<tuple<>, i1, tuple<i2>>
81  }
82  return %0 : tuple<tuple<>, i1, tuple<i2>>
83}
84
85// -----
86
87// Test case: Nested 1:N type conversion is carried through scf.while,
88// scf.condition, and unconverted ops inside have proper materializations.
89
90// CHECK-LABEL: func.func @while_tuple_ops(
91// CHECK-SAME:                             %[[ARG0:.*]]: i1,
92// CHECK-SAME:                             %[[ARG1:.*]]: i1) -> i1 {
93// CHECK-NEXT:    %[[V0:.*]] = scf.while (%[[ARG2:.*]] = %[[ARG0]]) : (i1) -> i1 {
94// CHECK-NEXT:      %[[V1:.*]] = "test.make_tuple"() : () -> tuple<>
95// CHECK-NEXT:      %[[V2:.*]] = "test.make_tuple"(%[[V1]], %[[ARG2]]) : (tuple<>, i1) -> tuple<tuple<>, i1>
96// CHECK-NEXT:      %[[V3:.*]] = "test.op"(%[[V2]]) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
97// CHECK-NEXT:      %[[V4:.*]] = "test.get_tuple_element"(%[[V3]]) <{index = 0 : i32}> : (tuple<tuple<>, i1>) -> tuple<>
98// CHECK-NEXT:      %[[V5:.*]] = "test.get_tuple_element"(%[[V3]]) <{index = 1 : i32}> : (tuple<tuple<>, i1>) -> i1
99// CHECK-NEXT:      scf.condition(%[[ARG1]]) %[[V5]] : i1
100// CHECK-NEXT:    } do {
101// CHECK-NEXT:    ^bb0(%[[ARG3:.*]]: i1):
102// CHECK-NEXT:      %[[V6:.*]] = "test.source"() : () -> tuple<tuple<>, i1>
103// CHECK-NEXT:      %[[V7:.*]] = "test.get_tuple_element"(%[[V6]]) <{index = 0 : i32}> : (tuple<tuple<>, i1>) -> tuple<>
104// CHECK-NEXT:      %[[V8:.*]] = "test.get_tuple_element"(%[[V6]]) <{index = 1 : i32}> : (tuple<tuple<>, i1>) -> i1
105// CHECK-NEXT:      scf.yield %[[V8]] : i1
106// CHECK-NEXT:    }
107// CHECK-NEXT:    return %[[V0]] : i1
108func.func @while_tuple_ops(%arg0: tuple<tuple<>, i1>, %arg1: i1) -> tuple<tuple<>, i1> {
109  %0 = scf.while (%arg2 = %arg0) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1> {
110    %1 = "test.op"(%arg2) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
111    scf.condition(%arg1) %1 : tuple<tuple<>, i1>
112  } do {
113  ^bb0(%arg2: tuple<tuple<>, i1>):
114    %1 = "test.source"() : () -> tuple<tuple<>, i1>
115    scf.yield %1 : tuple<tuple<>, i1>
116  }
117  return %0 : tuple<tuple<>, i1>
118}
119
120// -----
121
122// Test case: Nested 1:N type conversion is carried through scf.for and scf.yield.
123
124// CHECK-LABEL: func.func @for_operands_results(
125// CHECK-SAME:                                    %[[ARG0:.*]]: i1,
126// CHECK-SAME:                                    %[[ARG1:.*]]: i2) -> (i1, i2) {
127// CHECK-NEXT:    %[[C0:.+]] = arith.constant 0 : index
128// CHECK-NEXT:    %[[C1:.+]] = arith.constant 1 : index
129// CHECK-NEXT:    %[[C10:.+]] = arith.constant 10 : index
130// CHECK-NEXT:    %[[OUT:.+]]:2 = scf.for %arg2 = %[[C0]] to %[[C10]] step %[[C1]] iter_args(%[[ITER0:.+]] = %[[ARG0]], %[[ITER1:.+]] = %[[ARG1]]) -> (i1, i2) {
131// CHECK-NEXT:     scf.yield %[[ITER0]], %[[ITER1]] : i1, i2
132// CHECK-NEXT:    }
133// CHECK-NEXT:    return %[[OUT]]#0, %[[OUT]]#1 : i1, i2
134
135func.func @for_operands_results(%arg0: tuple<tuple<>, i1, tuple<i2>>) -> tuple<tuple<>, i1, tuple<i2>> {
136  %c0 = arith.constant 0 : index
137  %c1 = arith.constant 1 : index
138  %c10 = arith.constant 10 : index
139
140  %0 = scf.for %i = %c0 to %c10 step %c1 iter_args(%acc = %arg0) -> tuple<tuple<>, i1, tuple<i2>> {
141    scf.yield %acc : tuple<tuple<>, i1, tuple<i2>>
142  }
143
144  return %0 : tuple<tuple<>, i1, tuple<i2>>
145}
146
147// -----
148
149// Test case: Nested 1:N type conversion is carried through scf.for and scf.yield
150
151// CHECK-LABEL: func.func @for_tuple_ops(
152// CHECK-SAME:                             %[[ARG0:.+]]: i1) -> i1 {
153// CHECK-NEXT: %[[C0:.+]] = arith.constant 0 : index
154// CHECK-NEXT: %[[C1:.+]] = arith.constant 1 : index
155// CHECK-NEXT: %[[C10:.+]] = arith.constant 10 : index
156// CHECK-NEXT: %[[FOR:.+]] = scf.for %arg1 = %[[C0]] to %[[C10]] step %[[C1]] iter_args(%[[ITER:.+]] = %[[ARG0]]) -> (i1) {
157// CHECK-NEXT:   %[[V1:.+]] = "test.make_tuple"() : () -> tuple<>
158// CHECK-NEXT:   %[[V2:.+]] = "test.make_tuple"(%[[V1]], %[[ITER]]) : (tuple<>, i1) -> tuple<tuple<>, i1>
159// CHECK-NEXT:   %[[V3:.+]] = "test.op"(%[[V2]]) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
160// CHECK-NEXT:   %[[V4:.+]] = "test.get_tuple_element"(%[[V3]]) <{index = 0 : i32}> : (tuple<tuple<>, i1>) -> tuple<>
161// CHECK-NEXT:   %[[V5:.+]] = "test.get_tuple_element"(%[[V3]]) <{index = 1 : i32}> : (tuple<tuple<>, i1>) -> i1
162// CHECK-NEXT:   scf.yield %[[V5]] : i1
163// CHECK-NEXT: }
164// CHECK-NEXT: %[[V6:.+]] = "test.make_tuple"() : () -> tuple<>
165// CHECK-NEXT: %[[V7:.+]] = "test.make_tuple"(%[[V6]], %[[FOR]]) : (tuple<>, i1) -> tuple<tuple<>, i1>
166// CHECK-NEXT: %[[V8:.+]] = "test.op"(%[[V7]]) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
167// CHECK-NEXT: %[[V9:.+]] = "test.get_tuple_element"(%[[V8]]) <{index = 0 : i32}> : (tuple<tuple<>, i1>) -> tuple<>
168// CHECK-NEXT: %[[V10:.+]] = "test.get_tuple_element"(%[[V8]]) <{index = 1 : i32}> : (tuple<tuple<>, i1>) -> i1
169// CHECK-NEXT: return %[[V10]] : i1
170
171func.func @for_tuple_ops(%arg0: tuple<tuple<>, i1>) -> tuple<tuple<>, i1> {
172  %c0 = arith.constant 0 : index
173  %c1 = arith.constant 1 : index
174  %c10 = arith.constant 10 : index
175
176  %0 = scf.for %i = %c0 to %c10 step %c1 iter_args(%acc = %arg0) -> tuple<tuple<>, i1> {
177    %1 = "test.op"(%acc) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
178    scf.yield %1 : tuple<tuple<>, i1>
179  }
180
181  %1 = "test.op"(%0) : (tuple<tuple<>, i1>) -> tuple<tuple<>, i1>
182  return %1 : tuple<tuple<>, i1>
183}
184