1; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(simplify-cfg))' -S < %s | FileCheck %s 2 3declare void @readnone() readnone 4declare void @unknown() 5declare 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() { 13define 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 { 22define void @test1_b1() { 23 call void @readnone() 24 ret void 25} 26 27; CHECK: define void @test1_b2() #0 { 28define void @test1_b2() { 29 call void @readnone() 30 br i1 false, label %dead, label %exit 31 32dead: 33 call void @test1_a() 34 br label %exit 35 36exit: 37 ret void 38} 39 40; CHECK: define void @test1_b3() { 41define void @test1_b3() { 42 call void @unknown() 43 br i1 false, label %dead, label %exit 44 45dead: 46 call void @test1_a() 47 br label %exit 48 49exit: 50 ret void 51} 52 53; CHECK: define void @test1_b4() #0 { 54define void @test1_b4() { 55 call void @readnone() 56 br i1 false, label %dead, label %exit 57 58dead: 59 call void @test1_a() 60 br label %exit 61 62exit: 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() { 72define 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 { 81define void @test2_b1() { 82 call void @readnone() 83 ret void 84} 85 86; CHECK: define void @test2_b2() #0 { 87define void @test2_b2() { 88 call void @reference_function_pointer(void()* @test2_a) 89 br i1 false, label %dead, label %exit 90 91dead: 92 call void @test2_a() 93 br label %exit 94 95exit: 96 ret void 97} 98 99; CHECK: define void @test2_b3() { 100define 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 105dead: 106 call void @test2_a() 107 br label %exit 108 109exit: 110 ret void 111} 112 113; CHECK: define void @test2_b4() #0 { 114define void @test2_b4() { 115 call void @reference_function_pointer(void()* @test2_a) 116 br i1 false, label %dead, label %exit 117 118dead: 119 call void @test2_a() 120 br label %exit 121 122exit: 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() { 132define 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 { 141define void @test3_b11() { 142 call void @test3_b12() 143 ret void 144} 145 146; CHECK: define void @test3_b12() #0 { 147define void @test3_b12() { 148 call void @test3_b13() 149 ret void 150} 151 152; CHECK: define void @test3_b13() #0 { 153define void @test3_b13() { 154 call void @readnone() 155 ret void 156} 157 158; CHECK: define void @test3_b21() #0 { 159define void @test3_b21() { 160 call void @test3_b22() 161 ret void 162} 163 164; CHECK: define void @test3_b22() #0 { 165define void @test3_b22() { 166 call void @test3_b23() 167 ret void 168} 169 170; CHECK: define void @test3_b23() #0 { 171define void @test3_b23() { 172 call void @readnone() 173 br i1 false, label %dead, label %exit 174 175dead: 176 call void @test3_a() 177 br label %exit 178 179exit: 180 ret void 181} 182 183; CHECK: define void @test3_b31() { 184define void @test3_b31() { 185 call void @test3_b32() 186 ret void 187} 188 189; CHECK: define void @test3_b32() { 190define void @test3_b32() { 191 call void @test3_b33() 192 ret void 193} 194 195; CHECK: define void @test3_b33() { 196define void @test3_b33() { 197 call void @unknown() 198 br i1 false, label %dead, label %exit 199 200dead: 201 call void @test3_a() 202 br label %exit 203 204exit: 205 ret void 206} 207 208; CHECK: define void @test3_b41() #0 { 209define void @test3_b41() { 210 call void @test3_b42() 211 ret void 212} 213 214; CHECK: define void @test3_b42() #0 { 215define void @test3_b42() { 216 call void @test3_b43() 217 ret void 218} 219 220; CHECK: define void @test3_b43() #0 { 221define void @test3_b43() { 222 call void @readnone() 223 br i1 false, label %dead, label %exit 224 225dead: 226 call void @test3_a() 227 br label %exit 228 229exit: 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() { 239define 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 { 248define void @test4_b11() { 249 call void @test4_b12() 250 ret void 251} 252 253; CHECK: define void @test4_b12() #0 { 254define void @test4_b12() { 255 call void @test4_b13() 256 ret void 257} 258 259; CHECK: define void @test4_b13() #0 { 260define void @test4_b13() { 261 call void @readnone() 262 ret void 263} 264 265; CHECK: define void @test4_b21() #0 { 266define void @test4_b21() { 267 call void @test4_b22() 268 ret void 269} 270 271; CHECK: define void @test4_b22() #0 { 272define void @test4_b22() { 273 call void @test4_b23() 274 ret void 275} 276 277; CHECK: define void @test4_b23() #0 { 278define void @test4_b23() { 279 call void @reference_function_pointer(void()* @test4_a) 280 br i1 false, label %dead, label %exit 281 282dead: 283 call void @test4_a() 284 br label %exit 285 286exit: 287 ret void 288} 289 290; CHECK: define void @test4_b31() { 291define void @test4_b31() { 292 call void @test4_b32() 293 ret void 294} 295 296; CHECK: define void @test4_b32() { 297define void @test4_b32() { 298 call void @test4_b33() 299 ret void 300} 301 302; CHECK: define void @test4_b33() { 303define 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 308dead: 309 call void @test4_a() 310 br label %exit 311 312exit: 313 ret void 314} 315 316; CHECK: define void @test4_b41() #0 { 317define void @test4_b41() { 318 call void @test4_b42() 319 ret void 320} 321 322; CHECK: define void @test4_b42() #0 { 323define void @test4_b42() { 324 call void @test4_b43() 325 ret void 326} 327 328; CHECK: define void @test4_b43() #0 { 329define void @test4_b43() { 330 call void @reference_function_pointer(void()* @test4_a) 331 br i1 false, label %dead, label %exit 332 333dead: 334 call void @test4_a() 335 br label %exit 336 337exit: 338 ret void 339} 340 341; CHECK: attributes #0 = { readnone } 342