1 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s 2 // RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s 3 4 void may_throw(); 5 void dont_throw() noexcept; 6 7 struct Cleanup { 8 ~Cleanup() { dont_throw(); } 9 }; 10 11 // Multiple catch clauses w/o catch-all 12 void test0() { 13 try { 14 may_throw(); 15 } catch (int) { 16 dont_throw(); 17 } catch (double) { 18 dont_throw(); 19 } 20 } 21 22 // CHECK-LABEL: define void @_Z5test0v() {{.*}} personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) 23 24 // CHECK: %[[INT_ALLOCA:.*]] = alloca i32 25 // CHECK: invoke void @_Z9may_throwv() 26 // CHECK-NEXT: to label %[[NORMAL_BB:.*]] unwind label %[[CATCHDISPATCH_BB:.*]] 27 28 // CHECK: [[CATCHDISPATCH_BB]]: 29 // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller 30 31 // CHECK: [[CATCHSTART_BB]]: 32 // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] 33 // CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CATCHPAD]]) 34 // CHECK-NEXT: store i8* %[[EXN]], i8** %exn.slot 35 // CHECK-NEXT: %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector(token %[[CATCHPAD]]) 36 // CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #2 37 // CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]] 38 // CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_INT_BB:.*]], label %[[CATCH_FALLTHROUGH_BB:.*]] 39 40 // CHECK: [[CATCH_INT_BB]]: 41 // CHECK-NEXT: %[[EXN:.*]] = load i8*, i8** %exn.slot 42 // CHECK-NEXT: %[[ADDR:.*]] = call i8* @__cxa_begin_catch(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 43 // CHECK-NEXT: %[[ADDR_CAST:.*]] = bitcast i8* %[[ADDR]] to i32* 44 // CHECK-NEXT: %[[INT_VAL:.*]] = load i32, i32* %[[ADDR_CAST]] 45 // CHECK-NEXT: store i32 %[[INT_VAL]], i32* %[[INT_ALLOCA]] 46 // CHECK-NEXT: call void @_Z10dont_throwv() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 47 // CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 48 // CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB0:.*]] 49 50 // CHECK: [[CATCHRET_DEST_BB0]]: 51 // CHECK-NEXT: br label %[[TRY_CONT_BB:.*]] 52 53 // CHECK: [[CATCH_FALLTHROUGH_BB]] 54 // CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) #2 55 // CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]] 56 // CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_FLOAT_BB:.*]], label %[[RETHROW_BB:.*]] 57 58 // CHECK: [[CATCH_FLOAT_BB]]: 59 // CHECK: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB1:.*]] 60 61 // CHECK: [[CATCHRET_DEST_BB1]]: 62 // CHECK-NEXT: br label %[[TRY_CONT_BB]] 63 64 // CHECK: [[RETHROW_BB]]: 65 // CHECK-NEXT: call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 66 // CHECK-NEXT: unreachable 67 68 // Single catch-all 69 void test1() { 70 try { 71 may_throw(); 72 } catch (...) { 73 dont_throw(); 74 } 75 } 76 77 // CATCH-LABEL: @_Z5test1v() 78 79 // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller 80 81 // CHECK: [[CATCHSTART_BB]]: 82 // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null] 83 // CHECK: br label %[[CATCH_ALL_BB:.*]] 84 85 // CHECK: [[CATCH_ALL_BB]]: 86 // CHECK: catchret from %[[CATCHPAD]] to label 87 88 // Multiple catch clauses w/ catch-all 89 void test2() { 90 try { 91 may_throw(); 92 } catch (int) { 93 dont_throw(); 94 } catch (...) { 95 dont_throw(); 96 } 97 } 98 99 // CHECK-LABEL: @_Z5test2v() 100 101 // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller 102 103 // CHECK: [[CATCHSTART_BB]]: 104 // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] 105 // CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[CATCH_ALL_BB:.*]] 106 107 // CHECK: [[CATCH_INT_BB]]: 108 // CHECK: catchret from %[[CATCHPAD]] to label 109 110 // CHECK: [[CATCH_ALL_BB]]: 111 // CHECK: catchret from %[[CATCHPAD]] to label 112 113 // Cleanup 114 void test3() { 115 Cleanup c; 116 may_throw(); 117 } 118 119 // CHECK-LABEL: @_Z5test3v() 120 121 // CHECK: invoke void @_Z9may_throwv() 122 // CHECK-NEXT: to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]] 123 124 // CHECK: [[EHCLEANUP_BB]]: 125 // CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within none [] 126 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ] 127 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller 128 129 // Possibly throwing function call within a catch 130 void test4() { 131 try { 132 may_throw(); 133 } catch (int) { 134 may_throw(); 135 } 136 } 137 138 // CHECK-LABEL: @_Z5test4v() 139 140 // CHECK: %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller 141 142 // CHECK: [[CATCHSTART_BB]]: 143 // CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] 144 145 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] 146 // CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB:.*]] 147 148 // CHECK: [[INVOKE_CONT_BB]]: 149 // CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 150 // CHECK-NEXT: catchret from %[[CATCHPAD]] to label 151 152 // CHECK: [[EHCLEANUP_BB]]: 153 // CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within %[[CATCHPAD]] [] 154 // CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ] 155 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller 156 157 // Possibly throwing function call within a catch-all 158 void test5() { 159 try { 160 may_throw(); 161 } catch (...) { 162 may_throw(); 163 } 164 } 165 166 // CHECK-LABEL: @_Z5test5v() 167 168 // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller 169 170 // CHECK: [[CATCHSTART_BB]]: 171 // CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null] 172 173 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] 174 // CHECK-NEXT: to label %[[INVOKE_CONT_BB0:.*]] unwind label %[[EHCLEANUP_BB:.*]] 175 176 // CHECK: [[INVOKE_CONT_BB0]]: 177 // CHECK-NEXT: call void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD]]) ] 178 // CHECK-NEXT: catchret from %[[CATCHPAD]] to label 179 180 // CHECK: [[EHCLEANUP_BB]]: 181 // CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD]] [] 182 // CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD0]]) ] 183 // CHECK-NEXT: to label %[[INVOKE_CONT_BB1:.*]] unwind label %[[TERMINATE_BB:.*]] 184 185 // CHECK: [[INVOKE_CONT_BB1]]: 186 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind to caller 187 188 // CHECK: [[TERMINATE_BB]]: 189 // CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CLEANUPPAD0]] [] 190 // CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CLEANUPPAD1]]) 191 // CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] 192 // CHECK-NEXT: unreachable 193 194 // CHECK-LABEL: define {{.*}} void @__clang_call_terminate(i8* %0) 195 // CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %{{.*}}) 196 // CHECK-NEXT: call void @_ZSt9terminatev() 197 // CHECK-NEXT: unreachable 198 199 // Try-catch with cleanups 200 void test6() { 201 Cleanup c1; 202 try { 203 Cleanup c2; 204 may_throw(); 205 } catch (int) { 206 Cleanup c3; 207 may_throw(); 208 } 209 } 210 211 // CHECK-LABEL: @_Z5test6v() 212 // CHECK: invoke void @_Z9may_throwv() 213 // CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]] 214 215 // CHECK: [[EHCLEANUP_BB0]]: 216 // CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] 217 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ] 218 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]] 219 220 // CHECK: [[CATCH_DISPATCH_BB]]: 221 // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]] 222 223 // CHECK: [[CATCHSTART_BB]]: 224 // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] 225 // CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]] 226 227 // CHECK: [[CATCH_INT_BB]]: 228 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] 229 // CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]] 230 231 // CHECK: [[INVOKE_CONT_BB]]: 232 // CHECK: catchret from %[[CATCHPAD]] to label %{{.*}} 233 234 // CHECK: [[RETHROW_BB]]: 235 // CHECK-NEXT: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 236 // CHECK-NEXT: to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]] 237 238 // CHECK: [[EHCLEANUP_BB2]]: 239 // CHECK-NEXT: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] [] 240 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ] 241 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]] 242 243 // CHECK: [[EHCLEANUP_BB3]]: 244 // CHECK-NEXT: %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] [] 245 // CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]] 246 247 // CHECK: [[EHCLEANUP_BB1]]: 248 // CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within none [] 249 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] 250 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD1]] unwind to caller 251 252 // CHECK: [[UNREACHABLE_BB]]: 253 // CHECK-NEXT: unreachable 254 255 // Nested try-catches within a try with cleanups 256 void test7() { 257 Cleanup c1; 258 may_throw(); 259 try { 260 Cleanup c2; 261 may_throw(); 262 try { 263 Cleanup c3; 264 may_throw(); 265 } catch (int) { 266 may_throw(); 267 } catch (double) { 268 may_throw(); 269 } 270 } catch (int) { 271 may_throw(); 272 } catch (...) { 273 may_throw(); 274 } 275 } 276 277 // CHECK-LABEL: @_Z5test7v() 278 // CHECK: invoke void @_Z9may_throwv() 279 280 // CHECK: invoke void @_Z9may_throwv() 281 282 // CHECK: invoke void @_Z9may_throwv() 283 284 // CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] 285 // CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label 286 287 // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none 288 289 // CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] 290 291 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] 292 293 // CHECK: catchret from %[[CATCHPAD0]] to label 294 295 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] 296 297 // CHECK: catchret from %[[CATCHPAD0]] to label 298 299 // CHECK: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] 300 301 // CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] 302 // CHECK: cleanupret from %[[CLEANUPPAD1]] unwind label 303 304 // CHECK: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] [] 305 // CHECK: cleanupret from %[[CLEANUPPAD2]] unwind label 306 307 // CHECK: %[[CLEANUPPAD3:.*]] = cleanuppad within none [] 308 // CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label 309 310 // CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within none 311 312 // CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] 313 314 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] 315 316 // CHECK: catchret from %[[CATCHPAD1]] to label 317 318 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] 319 320 // CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ] 321 322 // CHECK: catchret from %[[CATCHPAD1]] to label 323 324 // CHECK: %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] [] 325 // CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ] 326 327 // CHECK: cleanupret from %[[CLEANUPPAD4]] unwind label 328 329 // CHECK: %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] [] 330 // CHECK: cleanupret from %[[CLEANUPPAD5]] unwind label 331 332 // CHECK: %[[CLEANUPPAD6:.*]] = cleanuppad within none [] 333 // CHECK: cleanupret from %[[CLEANUPPAD6]] unwind to caller 334 335 // CHECK: unreachable 336 337 // CHECK: %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] [] 338 // CHECK: call void @__clang_call_terminate(i8* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ] 339 // CHECK: unreachable 340 341 // Nested try-catches within a catch 342 void test8() { 343 try { 344 may_throw(); 345 } catch (int) { 346 try { 347 may_throw(); 348 } catch (int) { 349 may_throw(); 350 } 351 } 352 } 353 354 // CHECK-LABEL: @_Z5test8v() 355 // CHECK: invoke void @_Z9may_throwv() 356 357 // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none 358 359 // CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*)] 360 361 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] 362 363 // CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]] 364 365 // CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*)] 366 367 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] 368 369 // CHECK: catchret from %[[CATCHPAD1]] to label 370 371 // CHECK: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ] 372 373 // CHECK: catchret from %[[CATCHPAD0]] to label 374 375 // CHECK: call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] 376 // CHECK: unreachable 377 378 // CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] [] 379 // CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label 380 381 // CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] 382 // CHECK: cleanupret from %[[CLEANUPPAD1]] unwind to caller 383 384 // CHECK: unreachable 385