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