1 ; RUN: opt < %s -simplifycfg -S | FileCheck %s 2 3 ; ModuleID = 'cppeh-simplify.cpp' 4 target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" 5 target triple = "x86_64-pc-windows-msvc18.0.0" 6 7 8 ; This case arises when two objects with empty destructors are cleaned up. 9 ; 10 ; void f1() { 11 ; S a; 12 ; S b; 13 ; g(); 14 ; } 15 ; 16 ; In this case, both cleanup pads can be eliminated and the invoke can be 17 ; converted to a call. 18 ; 19 ; CHECK: define void @f1() 20 ; CHECK: entry: 21 ; CHECK: call void @g() 22 ; CHECK: ret void 23 ; CHECK-NOT: cleanuppad 24 ; CHECK: } 25 ; 26 define void @f1() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 27 entry: 28 invoke void @g() to label %invoke.cont unwind label %ehcleanup 29 30 invoke.cont: ; preds = %entry 31 ret void 32 33 ehcleanup: ; preds = %entry 34 %0 = cleanuppad within none [] 35 cleanupret from %0 unwind label %ehcleanup.1 36 37 ehcleanup.1: ; preds = %ehcleanup 38 %1 = cleanuppad within none [] 39 cleanupret from %1 unwind to caller 40 } 41 42 43 ; This case arises when an object with an empty destructor must be cleaned up 44 ; outside of a try-block and an object with a non-empty destructor must be 45 ; cleaned up within the try-block. 46 ; 47 ; void f2() { 48 ; S a; 49 ; try { 50 ; S2 b; 51 ; g(); 52 ; } catch (...) {} 53 ; } 54 ; 55 ; In this case, the outermost cleanup pad can be eliminated and the catch block 56 ; should unwind to the caller (that is, exception handling continues with the 57 ; parent frame of the caller). 58 ; 59 ; CHECK: define void @f2() 60 ; CHECK: entry: 61 ; CHECK: invoke void @g() 62 ; CHECK: ehcleanup: 63 ; CHECK: cleanuppad within none 64 ; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) 65 ; CHECK: cleanupret from %0 unwind label %catch.dispatch 66 ; CHECK: catch.dispatch: 67 ; CHECK: catchswitch within none [label %catch] unwind to caller 68 ; CHECK: catch: 69 ; CHECK: catchpad 70 ; CHECK: catchret 71 ; CHECK-NOT: cleanuppad 72 ; CHECK: } 73 ; 74 define void @f2() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 75 entry: 76 %b = alloca %struct.S2, align 1 77 invoke void @g() to label %invoke.cont unwind label %ehcleanup 78 79 invoke.cont: ; preds = %entry 80 br label %try.cont 81 82 ehcleanup: ; preds = %entry 83 %0 = cleanuppad within none [] 84 call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) 85 cleanupret from %0 unwind label %catch.dispatch 86 87 catch.dispatch: ; preds = %ehcleanup 88 %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 89 90 catch: ; preds = %catch.dispatch 91 %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] 92 catchret from %1 to label %catchret.dest 93 94 catchret.dest: ; preds = %catch 95 br label %try.cont 96 97 try.cont: ; preds = %catchret.dest, %invoke.cont 98 ret void 99 100 ehcleanup.1: 101 %2 = cleanuppad within none [] 102 cleanupret from %2 unwind to caller 103 } 104 105 106 ; This case arises when an object with a non-empty destructor must be cleaned up 107 ; outside of a try-block and an object with an empty destructor must be cleaned 108 ; within the try-block. 109 ; 110 ; void f3() { 111 ; S2 a; 112 ; try { 113 ; S b; 114 ; g(); 115 ; } catch (...) {} 116 ; } 117 ; 118 ; In this case the inner cleanup pad should be eliminated and the invoke of g() 119 ; should unwind directly to the catchpad. 120 ; 121 ; CHECK-LABEL: define void @f3() 122 ; CHECK: entry: 123 ; CHECK: invoke void @g() 124 ; CHECK: to label %try.cont unwind label %catch.dispatch 125 ; CHECK: catch.dispatch: 126 ; CHECK-NEXT: catchswitch within none [label %catch] unwind label %ehcleanup.1 127 ; CHECK: catch: 128 ; CHECK: catchpad within %cs1 [i8* null, i32 64, i8* null] 129 ; CHECK: catchret 130 ; CHECK: ehcleanup.1: 131 ; CHECK: cleanuppad 132 ; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) 133 ; CHECK: cleanupret from %cp3 unwind to caller 134 ; CHECK: } 135 ; 136 define void @f3() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 137 entry: 138 %a = alloca %struct.S2, align 1 139 invoke void @g() to label %invoke.cont unwind label %ehcleanup 140 141 invoke.cont: ; preds = %entry 142 br label %try.cont 143 144 ehcleanup: ; preds = %entry 145 %0 = cleanuppad within none [] 146 cleanupret from %0 unwind label %catch.dispatch 147 148 catch.dispatch: ; preds = %ehcleanup 149 %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 150 151 catch: ; preds = %catch.dispatch 152 %cp2 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] 153 catchret from %cp2 to label %catchret.dest 154 155 catchret.dest: ; preds = %catch 156 br label %try.cont 157 158 try.cont: ; preds = %catchret.dest, %invoke.cont 159 ret void 160 161 ehcleanup.1: 162 %cp3 = cleanuppad within none [] 163 call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) 164 cleanupret from %cp3 unwind to caller 165 } 166 167 168 ; This case arises when an object with an empty destructor may require cleanup 169 ; from either inside or outside of a try-block. 170 ; 171 ; void f4() { 172 ; S a; 173 ; g(); 174 ; try { 175 ; g(); 176 ; } catch (...) {} 177 ; } 178 ; 179 ; In this case, the cleanuppad should be eliminated, the invoke outside of the 180 ; catch block should be converted to a call (that is, that is, exception 181 ; handling continues with the parent frame of the caller).) 182 ; 183 ; CHECK-LABEL: define void @f4() 184 ; CHECK: entry: 185 ; CHECK: call void @g 186 ; Note: The cleanuppad simplification will insert an unconditional branch here 187 ; but it will be eliminated, placing the following invoke in the entry BB. 188 ; CHECK: invoke void @g() 189 ; CHECK: to label %try.cont unwind label %catch.dispatch 190 ; CHECK: catch.dispatch: 191 ; CHECK: catchswitch within none [label %catch] unwind to caller 192 ; CHECK: catch: 193 ; CHECK: catchpad 194 ; CHECK: catchret 195 ; CHECK-NOT: cleanuppad 196 ; CHECK: } 197 ; 198 define void @f4() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 199 entry: 200 invoke void @g() 201 to label %invoke.cont unwind label %ehcleanup 202 203 invoke.cont: ; preds = %entry 204 invoke void @g() 205 to label %try.cont unwind label %catch.dispatch 206 207 catch.dispatch: ; preds = %invoke.cont 208 %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup 209 210 catch: ; preds = %catch.dispatch 211 %0 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] 212 catchret from %0 to label %try.cont 213 214 try.cont: ; preds = %catch, %invoke.cont 215 ret void 216 217 ehcleanup: 218 %cp2 = cleanuppad within none [] 219 cleanupret from %cp2 unwind to caller 220 } 221 222 ; This case tests simplification of an otherwise empty cleanup pad that contains 223 ; a PHI node. 224 ; 225 ; int f6() { 226 ; int state = 1; 227 ; try { 228 ; S a; 229 ; g(); 230 ; state = 2; 231 ; g(); 232 ; } catch (...) { 233 ; return state; 234 ; } 235 ; return 0; 236 ; } 237 ; 238 ; In this case, the cleanup pad should be eliminated and the PHI node in the 239 ; cleanup pad should be sunk into the catch dispatch block. 240 ; 241 ; CHECK-LABEL: define i32 @f6() 242 ; CHECK: entry: 243 ; CHECK: invoke void @g() 244 ; CHECK: invoke.cont: 245 ; CHECK: invoke void @g() 246 ; CHECK-NOT: ehcleanup: 247 ; CHECK-NOT: cleanuppad 248 ; CHECK: catch.dispatch: 249 ; CHECK: %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] 250 ; CHECK: } 251 define i32 @f6() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 252 entry: 253 invoke void @g() 254 to label %invoke.cont unwind label %ehcleanup 255 256 invoke.cont: ; preds = %entry 257 invoke void @g() 258 to label %return unwind label %ehcleanup 259 260 ehcleanup: ; preds = %invoke.cont, %entry 261 %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] 262 %0 = cleanuppad within none [] 263 cleanupret from %0 unwind label %catch.dispatch 264 265 catch.dispatch: ; preds = %ehcleanup 266 %cs1 = catchswitch within none [label %catch] unwind to caller 267 268 catch: ; preds = %catch.dispatch 269 %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] 270 catchret from %1 to label %return 271 272 return: ; preds = %invoke.cont, %catch 273 %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ] 274 ret i32 %retval.0 275 } 276 277 ; This case tests another variation of simplification of an otherwise empty 278 ; cleanup pad that contains a PHI node. 279 ; 280 ; int f7() { 281 ; int state = 1; 282 ; try { 283 ; g(); 284 ; state = 2; 285 ; S a; 286 ; g(); 287 ; state = 3; 288 ; g(); 289 ; } catch (...) { 290 ; return state; 291 ; } 292 ; return 0; 293 ; } 294 ; 295 ; In this case, the cleanup pad should be eliminated and the PHI node in the 296 ; cleanup pad should be merged with the PHI node in the catch dispatch block. 297 ; 298 ; CHECK-LABEL: define i32 @f7() 299 ; CHECK: entry: 300 ; CHECK: invoke void @g() 301 ; CHECK: invoke.cont: 302 ; CHECK: invoke void @g() 303 ; CHECK: invoke.cont.1: 304 ; CHECK: invoke void @g() 305 ; CHECK-NOT: ehcleanup: 306 ; CHECK-NOT: cleanuppad 307 ; CHECK: catch.dispatch: 308 ; CHECK: %state.1 = phi i32 [ 1, %entry ], [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] 309 ; CHECK: } 310 define i32 @f7() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 311 entry: 312 invoke void @g() 313 to label %invoke.cont unwind label %catch.dispatch 314 315 invoke.cont: ; preds = %entry 316 invoke void @g() 317 to label %invoke.cont.1 unwind label %ehcleanup 318 319 invoke.cont.1: ; preds = %invoke.cont 320 invoke void @g() 321 to label %return unwind label %ehcleanup 322 323 ehcleanup: ; preds = %invoke.cont.1, %invoke.cont 324 %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] 325 %0 = cleanuppad within none [] 326 cleanupret from %0 unwind label %catch.dispatch 327 328 catch.dispatch: ; preds = %ehcleanup, %entry 329 %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ] 330 %cs1 = catchswitch within none [label %catch] unwind to caller 331 332 catch: ; preds = %catch.dispatch 333 %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] 334 catchret from %1 to label %return 335 336 return: ; preds = %invoke.cont.1, %catch 337 %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ] 338 ret i32 %retval.0 339 } 340 341 ; This case tests a scenario where an empty cleanup pad is not dominated by all 342 ; of the predecessors of its successor, but the successor references a PHI node 343 ; in the empty cleanup pad. 344 ; 345 ; Conceptually, the case being modeled is something like this: 346 ; 347 ; int f8() { 348 ; int x = 1; 349 ; try { 350 ; S a; 351 ; g(); 352 ; x = 2; 353 ; retry: 354 ; g(); 355 ; return 356 ; } catch (...) { 357 ; use_x(x); 358 ; } 359 ; goto retry; 360 ; } 361 ; 362 ; While that C++ syntax isn't legal, the IR below is. 363 ; 364 ; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch 365 ; should have an incoming value entry for path from 'foo' that references the 366 ; PHI node itself. 367 ; 368 ; CHECK-LABEL: define void @f8() 369 ; CHECK: entry: 370 ; CHECK: invoke void @g() 371 ; CHECK: invoke.cont: 372 ; CHECK: invoke void @g() 373 ; CHECK-NOT: ehcleanup: 374 ; CHECK-NOT: cleanuppad 375 ; CHECK: catch.dispatch: 376 ; CHECK: %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ], [ %x, %catch.cont ] 377 ; CHECK: } 378 define void @f8() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 379 entry: 380 invoke void @g() 381 to label %invoke.cont unwind label %ehcleanup 382 383 invoke.cont: ; preds = %entry 384 invoke void @g() 385 to label %return unwind label %ehcleanup 386 387 ehcleanup: ; preds = %invoke.cont, %entry 388 %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] 389 %0 = cleanuppad within none [] 390 cleanupret from %0 unwind label %catch.dispatch 391 392 catch.dispatch: ; preds = %ehcleanup, %catch.cont 393 %cs1 = catchswitch within none [label %catch] unwind to caller 394 395 catch: ; preds = %catch.dispatch 396 %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] 397 call void @use_x(i32 %x) 398 catchret from %1 to label %catch.cont 399 400 catch.cont: ; preds = %catch 401 invoke void @g() 402 to label %return unwind label %catch.dispatch 403 404 return: ; preds = %invoke.cont, %catch.cont 405 ret void 406 } 407 408 %struct.S = type { i8 } 409 %struct.S2 = type { i8 } 410 declare void @"\01??1S2@@QEAA@XZ"(%struct.S2*) 411 declare void @g() 412 declare void @use_x(i32 %x) 413 414 declare i32 @__CxxFrameHandler3(...) 415 416