429 lines · cpp
1// REQUIRES: webassembly-registered-target2 3// RUN: %clang -E -dM %s -target wasm32-unknown-unknown -fwasm-exceptions | FileCheck %s -check-prefix PREPROCESSOR4// PREPROCESSOR: #define __WASM_EXCEPTIONS__ 15 6// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -mllvm -wasm-enable-eh -exception-model=wasm -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s7// RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -mllvm -wasm-enable-eh -exception-model=wasm -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s8 9// Test code generation for Wasm EH using WebAssembly EH proposal.10// (https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md)11 12void may_throw();13void dont_throw() noexcept;14 15struct Cleanup {16 ~Cleanup() { dont_throw(); }17};18 19// Multiple catch clauses w/o catch-all20void multiple_catches_wo_catch_all() {21 try {22 may_throw();23 } catch (int) {24 dont_throw();25 } catch (double) {26 dont_throw();27 }28}29 30// CHECK-LABEL: define void @_Z29multiple_catches_wo_catch_allv() {{.*}} personality ptr @__gxx_wasm_personality_v031 32// CHECK: %[[INT_ALLOCA:.*]] = alloca i3233// CHECK: invoke void @_Z9may_throwv()34// CHECK-NEXT: to label %[[NORMAL_BB:.*]] unwind label %[[CATCHDISPATCH_BB:.*]]35 36// CHECK: [[CATCHDISPATCH_BB]]:37// CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller38 39// CHECK: [[CATCHSTART_BB]]:40// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [ptr @_ZTIi, ptr @_ZTId]41// CHECK-NEXT: %[[EXN:.*]] = call ptr @llvm.wasm.get.exception(token %[[CATCHPAD]])42// CHECK-NEXT: store ptr %[[EXN]], ptr %exn.slot43// CHECK-NEXT: %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector(token %[[CATCHPAD]])44// CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi) #745// CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]]46// CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_INT_BB:.*]], label %[[CATCH_FALLTHROUGH_BB:.*]]47 48// CHECK: [[CATCH_INT_BB]]:49// CHECK-NEXT: %[[EXN:.*]] = load ptr, ptr %exn.slot50// CHECK-NEXT: %[[ADDR:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]51// CHECK-NEXT: %[[INT_VAL:.*]] = load i32, ptr %[[ADDR]]52// CHECK-NEXT: store i32 %[[INT_VAL]], ptr %[[INT_ALLOCA]]53// CHECK-NEXT: call void @_Z10dont_throwv() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]54// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]55// CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB0:.*]]56 57// CHECK: [[CATCHRET_DEST_BB0]]:58// CHECK-NEXT: br label %[[TRY_CONT_BB:.*]]59 60// CHECK: [[CATCH_FALLTHROUGH_BB]]61// CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTId) #762// CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]]63// CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_FLOAT_BB:.*]], label %[[RETHROW_BB:.*]]64 65// CHECK: [[CATCH_FLOAT_BB]]:66// CHECK: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB1:.*]]67 68// CHECK: [[CATCHRET_DEST_BB1]]:69// CHECK-NEXT: br label %[[TRY_CONT_BB]]70 71// CHECK: [[RETHROW_BB]]:72// CHECK-NEXT: call void @llvm.wasm.rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]73// CHECK-NEXT: unreachable74 75// Single catch-all76void single_catch_all() {77 try {78 may_throw();79 } catch (...) {80 dont_throw();81 }82}83 84// CATCH-LABEL: @_Z16single_catch_allv()85 86// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller87 88// CHECK: [[CATCHSTART_BB]]:89// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [ptr null]90// CHECK: br label %[[CATCH_ALL_BB:.*]]91 92// CHECK: [[CATCH_ALL_BB]]:93// CHECK: catchret from %[[CATCHPAD]] to label94 95// Multiple catch clauses w/ catch-all96void multiple_catches_w_catch_all() {97 try {98 may_throw();99 } catch (int) {100 dont_throw();101 } catch (...) {102 dont_throw();103 }104}105 106// CHECK-LABEL: @_Z28multiple_catches_w_catch_allv()107 108// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller109 110// CHECK: [[CATCHSTART_BB]]:111// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [ptr @_ZTIi, ptr null]112// CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[CATCH_ALL_BB:.*]]113 114// CHECK: [[CATCH_INT_BB]]:115// CHECK: catchret from %[[CATCHPAD]] to label116 117// CHECK: [[CATCH_ALL_BB]]:118// CHECK: catchret from %[[CATCHPAD]] to label119 120// Cleanup121void cleanup() {122 Cleanup c;123 may_throw();124}125 126// CHECK-LABEL: @_Z7cleanupv()127 128// CHECK: invoke void @_Z9may_throwv()129// CHECK-NEXT: to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]]130 131// CHECK: [[EHCLEANUP_BB]]:132// CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within none []133// CHECK-NEXT: call noundef ptr @_ZN7CleanupD1Ev(ptr {{[^,]*}} %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ]134// CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller135 136// Possibly throwing function call within a catch137void catch_int() {138 try {139 may_throw();140 } catch (int) {141 may_throw();142 }143}144 145// CHECK-LABEL: @_Z9catch_intv()146 147// CHECK: %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller148 149// CHECK: [[CATCHSTART_BB]]:150// CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [ptr @_ZTIi]151 152// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]153// CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB:.*]]154 155// CHECK: [[INVOKE_CONT_BB]]:156// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]157// CHECK-NEXT: catchret from %[[CATCHPAD]] to label158 159// CHECK: [[EHCLEANUP_BB]]:160// CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within %[[CATCHPAD]] []161// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ]162// CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller163 164// Possibly throwing function call within a catch-all165void catch_all() {166 try {167 may_throw();168 } catch (...) {169 may_throw();170 }171}172 173// CHECK-LABEL: @_Z9catch_allv()174 175// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller176 177// CHECK: [[CATCHSTART_BB]]:178// CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [ptr null]179 180// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]181// CHECK-NEXT: to label %[[INVOKE_CONT_BB0:.*]] unwind label %[[EHCLEANUP_BB:.*]]182 183// CHECK: [[INVOKE_CONT_BB0]]:184// CHECK-NEXT: call void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD]]) ]185// CHECK-NEXT: catchret from %[[CATCHPAD]] to label186 187// CHECK: [[EHCLEANUP_BB]]:188// CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD]] []189// CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD0]]) ]190// CHECK-NEXT: to label %[[INVOKE_CONT_BB1:.*]] unwind label %[[TERMINATE_BB:.*]]191 192// CHECK: [[INVOKE_CONT_BB1]]:193// CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind to caller194 195// CHECK: [[TERMINATE_BB]]:196// CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CLEANUPPAD0]] []197// CHECK-NEXT: call void @_ZSt9terminatev() {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ]198// CHECK-NEXT: unreachable199 200// Try-catch with cleanups201void try_catch_w_cleanups() {202 Cleanup c1;203 try {204 Cleanup c2;205 may_throw();206 } catch (int) {207 Cleanup c3;208 may_throw();209 }210}211 212// CHECK-LABEL: @_Z20try_catch_w_cleanupsv()213// CHECK: invoke void @_Z9may_throwv()214// CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]]215 216// CHECK: [[EHCLEANUP_BB0]]:217// CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within none []218// CHECK-NEXT: call noundef ptr @_ZN7CleanupD1Ev(ptr {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ]219// CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]]220 221// CHECK: [[CATCH_DISPATCH_BB]]:222// CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]]223 224// CHECK: [[CATCHSTART_BB]]:225// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [ptr @_ZTIi]226// CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]]227 228// CHECK: [[CATCH_INT_BB]]:229// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]230// CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]]231 232// CHECK: [[INVOKE_CONT_BB]]:233// CHECK: catchret from %[[CATCHPAD]] to label %{{.*}}234 235// CHECK: [[RETHROW_BB]]:236// CHECK-NEXT: invoke void @llvm.wasm.rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]237// CHECK-NEXT: to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]]238 239// CHECK: [[EHCLEANUP_BB2]]:240// CHECK-NEXT: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] []241// CHECK-NEXT: call noundef ptr @_ZN7CleanupD1Ev(ptr {{[^,]*}} %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ]242// CHECK-NEXT: cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]]243 244// CHECK: [[EHCLEANUP_BB3]]:245// CHECK-NEXT: %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] []246// CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]]247 248// CHECK: [[EHCLEANUP_BB1]]:249// CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within none []250// CHECK-NEXT: call noundef ptr @_ZN7CleanupD1Ev(ptr {{[^,]*}} %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ]251// CHECK-NEXT: cleanupret from %[[CLEANUPPAD1]] unwind to caller252 253// CHECK: [[UNREACHABLE_BB]]:254// CHECK-NEXT: unreachable255 256// Nested try-catches within a try with cleanups257void nested_try_catches_with_cleanups() {258 Cleanup c1;259 may_throw();260 try {261 Cleanup c2;262 may_throw();263 try {264 Cleanup c3;265 may_throw();266 } catch (int) {267 may_throw();268 } catch (double) {269 may_throw();270 }271 } catch (int) {272 may_throw();273 } catch (...) {274 may_throw();275 }276}277 278// CHECK-LABEL: @_Z32nested_try_catches_with_cleanupsv()279// CHECK: invoke void @_Z9may_throwv()280 281// CHECK: invoke void @_Z9may_throwv()282 283// CHECK: invoke void @_Z9may_throwv()284 285// CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within none []286// CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label287 288// CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none289 290// CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [ptr @_ZTIi, ptr @_ZTId]291 292// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]293 294// CHECK: catchret from %[[CATCHPAD0]] to label295 296// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]297 298// CHECK: catchret from %[[CATCHPAD0]] to label299 300// CHECK: invoke void @llvm.wasm.rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ]301 302// CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] []303// CHECK: cleanupret from %[[CLEANUPPAD1]] unwind label304 305// CHECK: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] []306// CHECK: cleanupret from %[[CLEANUPPAD2]] unwind label307 308// CHECK: %[[CLEANUPPAD3:.*]] = cleanuppad within none []309// CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label310 311// CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within none312 313// CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [ptr @_ZTIi, ptr null]314 315// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]316 317// CHECK: catchret from %[[CATCHPAD1]] to label318 319// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]320 321// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ]322 323// CHECK: catchret from %[[CATCHPAD1]] to label324 325// CHECK: %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] []326// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ]327 328// CHECK: cleanupret from %[[CLEANUPPAD4]] unwind label329 330// CHECK: %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] []331// CHECK: cleanupret from %[[CLEANUPPAD5]] unwind label332 333// CHECK: %[[CLEANUPPAD6:.*]] = cleanuppad within none []334// CHECK: cleanupret from %[[CLEANUPPAD6]] unwind to caller335 336// CHECK: unreachable337 338// CHECK: %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] []339// CHECK: call void @_ZSt9terminatev() {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ]340// CHECK: unreachable341 342// Nested try-catches within a catch343void nested_try_catch_within_catch() {344 try {345 may_throw();346 } catch (int) {347 try {348 may_throw();349 } catch (int) {350 may_throw();351 }352 }353}354 355// CHECK-LABEL: @_Z29nested_try_catch_within_catchv()356// CHECK: invoke void @_Z9may_throwv()357 358// CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none359 360// CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [ptr @_ZTIi]361 362// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]363 364// CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]]365 366// CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [ptr @_ZTIi]367 368// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]369 370// CHECK: catchret from %[[CATCHPAD1]] to label371 372// CHECK: invoke void @llvm.wasm.rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ]373 374// CHECK: catchret from %[[CATCHPAD0]] to label375 376// CHECK: call void @llvm.wasm.rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ]377// CHECK: unreachable378 379// CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] []380// CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label381 382// CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] []383// CHECK: cleanupret from %[[CLEANUPPAD1]] unwind to caller384 385// CHECK: unreachable386 387void noexcept_throw() noexcept {388 throw 3;389}390 391// CATCH-LABEL: define void @_Z14noexcept_throwv()392// CHECK: %{{.*}} = cleanuppad within none []393// CHECK-NEXT: call void @_ZSt9terminatev()394 395 396// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -mllvm -wasm-enable-eh -exception-model=wasm -target-feature +exception-handling -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=WARNING-DEFAULT397// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -mllvm -wasm-enable-eh -exception-model=wasm -target-feature +exception-handling -Wwasm-exception-spec -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=WARNING-ON398// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -mllvm -wasm-enable-eh -exception-model=wasm -target-feature +exception-handling -Wno-wasm-exception-spec -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=WARNING-OFF399// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fexceptions -fcxx-exceptions -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=EM-EH-WARNING400 401// Wasm EH ignores dynamic exception specifications with types at the moment.402// This is controlled by -Wwasm-exception-spec, which is on by default. This403// warning can be suppressed with -Wno-wasm-exception-spec. Checks if a warning404// message is correctly printed or not printed depending on the options.405void exception_spec_warning() throw(int) {406}407// WARNING-DEFAULT: warning: dynamic exception specifications with types are currently ignored in wasm408// WARNING-ON: warning: dynamic exception specifications with types are currently ignored in wasm409// WARNING-OFF-NOT: warning: dynamic exception specifications with types are currently ignored in wasm410// EM-EH-WARNING: warning: dynamic exception specifications with types are currently ignored in wasm411 412// Wasm currently treats 'throw()' in the same way as 'noexcept'. Check if the413// same warning message is printed as if when a 'noexcept' function throws.414void exception_spec_throw_empty() throw() {415 throw 3;416}417// WARNING-DEFAULT: warning: 'exception_spec_throw_empty' has a non-throwing exception specification but can still throw418// WARNING-DEFAULT: function declared non-throwing here419 420// Here we only check if the command enables wasm exception handling in the421// backend so that exception handling instructions can be generated in .s file.422 423// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -mllvm -wasm-enable-eh -exception-model=wasm -target-feature +exception-handling -S -o - -std=c++11 | FileCheck %s --check-prefix=ASSEMBLY424 425// ASSEMBLY: try426// ASSEMBLY: catch427// ASSEMBLY: rethrow428// ASSEMBLY: end_try429