1 ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(simplify-cfg))' -S < %s | FileCheck %s 2 3 declare void @readnone() readnone 4 declare void @unknown() 5 declare void @reference_function_pointer(void()*) readnone 6 7 ; The @test1_* set of functions checks that when we mutate functions with 8 ; simplify-cfg to delete call edges and this ends up splitting both the SCCs 9 ; and the RefSCCs that those functions are in, we re-run the CGSCC passes to 10 ; observe the refined call graph structure. 11 12 ; CHECK: define void @test1_a() { 13 define void @test1_a() { 14 call void @test1_b1() 15 call void @test1_b2() 16 call void @test1_b3() 17 call void @test1_b4() 18 ret void 19 } 20 21 ; CHECK: define void @test1_b1() #0 { 22 define void @test1_b1() { 23 call void @readnone() 24 ret void 25 } 26 27 ; CHECK: define void @test1_b2() #0 { 28 define void @test1_b2() { 29 call void @readnone() 30 br i1 false, label %dead, label %exit 31 32 dead: 33 call void @test1_a() 34 br label %exit 35 36 exit: 37 ret void 38 } 39 40 ; CHECK: define void @test1_b3() { 41 define void @test1_b3() { 42 call void @unknown() 43 br i1 false, label %dead, label %exit 44 45 dead: 46 call void @test1_a() 47 br label %exit 48 49 exit: 50 ret void 51 } 52 53 ; CHECK: define void @test1_b4() #0 { 54 define void @test1_b4() { 55 call void @readnone() 56 br i1 false, label %dead, label %exit 57 58 dead: 59 call void @test1_a() 60 br label %exit 61 62 exit: 63 ret void 64 } 65 66 67 ; The @test2_* set of functions provide similar checks to @test1_* but only 68 ; splitting the SCCs while leaving the RefSCC intact. This is accomplished by 69 ; having dummy ref edges to the root function. 70 71 ; CHECK: define void @test2_a() { 72 define void @test2_a() { 73 call void @test2_b1() 74 call void @test2_b2() 75 call void @test2_b3() 76 call void @test2_b4() 77 ret void 78 } 79 80 ; CHECK: define void @test2_b1() #0 { 81 define void @test2_b1() { 82 call void @readnone() 83 ret void 84 } 85 86 ; CHECK: define void @test2_b2() #0 { 87 define void @test2_b2() { 88 call void @reference_function_pointer(void()* @test2_a) 89 br i1 false, label %dead, label %exit 90 91 dead: 92 call void @test2_a() 93 br label %exit 94 95 exit: 96 ret void 97 } 98 99 ; CHECK: define void @test2_b3() { 100 define void @test2_b3() { 101 call void @reference_function_pointer(void()* @test2_a) 102 call void @unknown() 103 br i1 false, label %dead, label %exit 104 105 dead: 106 call void @test2_a() 107 br label %exit 108 109 exit: 110 ret void 111 } 112 113 ; CHECK: define void @test2_b4() #0 { 114 define void @test2_b4() { 115 call void @reference_function_pointer(void()* @test2_a) 116 br i1 false, label %dead, label %exit 117 118 dead: 119 call void @test2_a() 120 br label %exit 121 122 exit: 123 ret void 124 } 125 126 127 ; The @test3_* set of functions are the same challenge as @test1_* but with 128 ; multiple layers that have to be traversed in the correct order instead of 129 ; a single node. 130 131 ; CHECK: define void @test3_a() { 132 define void @test3_a() { 133 call void @test3_b11() 134 call void @test3_b21() 135 call void @test3_b31() 136 call void @test3_b41() 137 ret void 138 } 139 140 ; CHECK: define void @test3_b11() #0 { 141 define void @test3_b11() { 142 call void @test3_b12() 143 ret void 144 } 145 146 ; CHECK: define void @test3_b12() #0 { 147 define void @test3_b12() { 148 call void @test3_b13() 149 ret void 150 } 151 152 ; CHECK: define void @test3_b13() #0 { 153 define void @test3_b13() { 154 call void @readnone() 155 ret void 156 } 157 158 ; CHECK: define void @test3_b21() #0 { 159 define void @test3_b21() { 160 call void @test3_b22() 161 ret void 162 } 163 164 ; CHECK: define void @test3_b22() #0 { 165 define void @test3_b22() { 166 call void @test3_b23() 167 ret void 168 } 169 170 ; CHECK: define void @test3_b23() #0 { 171 define void @test3_b23() { 172 call void @readnone() 173 br i1 false, label %dead, label %exit 174 175 dead: 176 call void @test3_a() 177 br label %exit 178 179 exit: 180 ret void 181 } 182 183 ; CHECK: define void @test3_b31() { 184 define void @test3_b31() { 185 call void @test3_b32() 186 ret void 187 } 188 189 ; CHECK: define void @test3_b32() { 190 define void @test3_b32() { 191 call void @test3_b33() 192 ret void 193 } 194 195 ; CHECK: define void @test3_b33() { 196 define void @test3_b33() { 197 call void @unknown() 198 br i1 false, label %dead, label %exit 199 200 dead: 201 call void @test3_a() 202 br label %exit 203 204 exit: 205 ret void 206 } 207 208 ; CHECK: define void @test3_b41() #0 { 209 define void @test3_b41() { 210 call void @test3_b42() 211 ret void 212 } 213 214 ; CHECK: define void @test3_b42() #0 { 215 define void @test3_b42() { 216 call void @test3_b43() 217 ret void 218 } 219 220 ; CHECK: define void @test3_b43() #0 { 221 define void @test3_b43() { 222 call void @readnone() 223 br i1 false, label %dead, label %exit 224 225 dead: 226 call void @test3_a() 227 br label %exit 228 229 exit: 230 ret void 231 } 232 233 234 ; The @test4_* functions exercise the same core challenge as the @test2_* 235 ; functions, but again include long chains instead of single nodes and ensure 236 ; we traverse the chains in the correct order. 237 238 ; CHECK: define void @test4_a() { 239 define void @test4_a() { 240 call void @test4_b11() 241 call void @test4_b21() 242 call void @test4_b31() 243 call void @test4_b41() 244 ret void 245 } 246 247 ; CHECK: define void @test4_b11() #0 { 248 define void @test4_b11() { 249 call void @test4_b12() 250 ret void 251 } 252 253 ; CHECK: define void @test4_b12() #0 { 254 define void @test4_b12() { 255 call void @test4_b13() 256 ret void 257 } 258 259 ; CHECK: define void @test4_b13() #0 { 260 define void @test4_b13() { 261 call void @readnone() 262 ret void 263 } 264 265 ; CHECK: define void @test4_b21() #0 { 266 define void @test4_b21() { 267 call void @test4_b22() 268 ret void 269 } 270 271 ; CHECK: define void @test4_b22() #0 { 272 define void @test4_b22() { 273 call void @test4_b23() 274 ret void 275 } 276 277 ; CHECK: define void @test4_b23() #0 { 278 define void @test4_b23() { 279 call void @reference_function_pointer(void()* @test4_a) 280 br i1 false, label %dead, label %exit 281 282 dead: 283 call void @test4_a() 284 br label %exit 285 286 exit: 287 ret void 288 } 289 290 ; CHECK: define void @test4_b31() { 291 define void @test4_b31() { 292 call void @test4_b32() 293 ret void 294 } 295 296 ; CHECK: define void @test4_b32() { 297 define void @test4_b32() { 298 call void @test4_b33() 299 ret void 300 } 301 302 ; CHECK: define void @test4_b33() { 303 define void @test4_b33() { 304 call void @reference_function_pointer(void()* @test4_a) 305 call void @unknown() 306 br i1 false, label %dead, label %exit 307 308 dead: 309 call void @test4_a() 310 br label %exit 311 312 exit: 313 ret void 314 } 315 316 ; CHECK: define void @test4_b41() #0 { 317 define void @test4_b41() { 318 call void @test4_b42() 319 ret void 320 } 321 322 ; CHECK: define void @test4_b42() #0 { 323 define void @test4_b42() { 324 call void @test4_b43() 325 ret void 326 } 327 328 ; CHECK: define void @test4_b43() #0 { 329 define void @test4_b43() { 330 call void @reference_function_pointer(void()* @test4_a) 331 br i1 false, label %dead, label %exit 332 333 dead: 334 call void @test4_a() 335 br label %exit 336 337 exit: 338 ret void 339 } 340 341 ; CHECK: attributes #0 = { readnone } 342