; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 ; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -aarch64-new-sme-abi -verify-machineinstrs < %s | FileCheck %s ; A simple EH test case that corresponds to the following C++ source: ; ; struct ZAResource { ; ~ZAResource() __arm_inout("za") { ; shared_za_call(); // simulate cleanup in destructor ; } ; }; ; ; void za_with_raii(bool fail) __arm_inout("za") { ; ZAResource r; ; if (fail) ; throw "Unwinding needs ZA state reload"; ; } ; ; Here if an exception is thrown we must call the ~ZAResource destructor while ; unwinding the stack. That requires us to restore ZA state before the ; shared_za_call in the cleanup block. @.str = private unnamed_addr constant [32 x i8] c"Unwinding needs ZA state reload\00", align 1 @typeinfo_for_char_const_ptr = external constant ptr define void @za_with_raii(i1 %fail) "aarch64_inout_za" personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: za_with_raii: ; CHECK: .Lfunc_begin0: ; CHECK-NEXT: .cfi_startproc ; CHECK-NEXT: .cfi_personality 156, DW.ref.__gxx_personality_v0 ; CHECK-NEXT: .cfi_lsda 28, .Lexception0 ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: stp x29, x30, [sp, #-32]! // 16-byte Folded Spill ; CHECK-NEXT: str x19, [sp, #16] // 8-byte Folded Spill ; CHECK-NEXT: mov x29, sp ; CHECK-NEXT: sub sp, sp, #16 ; CHECK-NEXT: .cfi_def_cfa w29, 32 ; CHECK-NEXT: .cfi_offset w19, -16 ; CHECK-NEXT: .cfi_offset w30, -24 ; CHECK-NEXT: .cfi_offset w29, -32 ; CHECK-NEXT: rdsvl x8, #1 ; CHECK-NEXT: mov x9, sp ; CHECK-NEXT: msub x9, x8, x8, x9 ; CHECK-NEXT: mov sp, x9 ; CHECK-NEXT: stp x9, x8, [x29, #-16] ; CHECK-NEXT: tbnz w0, #0, .LBB0_2 ; CHECK-NEXT: // %bb.1: // %return_normally ; CHECK-NEXT: mov sp, x29 ; CHECK-NEXT: ldr x19, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x29, x30, [sp], #32 // 16-byte Folded Reload ; CHECK-NEXT: b shared_za_call ; CHECK-NEXT: .LBB0_2: // %throw_exception ; CHECK-NEXT: sub x8, x29, #16 ; CHECK-NEXT: mov w0, #8 // =0x8 ; CHECK-NEXT: msr TPIDR2_EL0, x8 ; CHECK-NEXT: bl __cxa_allocate_exception ; CHECK-NEXT: adrp x8, .L.str ; CHECK-NEXT: add x8, x8, :lo12:.L.str ; CHECK-NEXT: str x8, [x0] ; CHECK-NEXT: .Ltmp0: // EH_LABEL ; CHECK-NEXT: adrp x1, :got:typeinfo_for_char_const_ptr ; CHECK-NEXT: mov x2, xzr ; CHECK-NEXT: ldr x1, [x1, :got_lo12:typeinfo_for_char_const_ptr] ; CHECK-NEXT: bl __cxa_throw ; CHECK-NEXT: .Ltmp1: // EH_LABEL ; CHECK-NEXT: smstart za ; CHECK-NEXT: mrs x8, TPIDR2_EL0 ; CHECK-NEXT: sub x0, x29, #16 ; CHECK-NEXT: cbnz x8, .LBB0_4 ; CHECK-NEXT: // %bb.3: // %throw_exception ; CHECK-NEXT: bl __arm_tpidr2_restore ; CHECK-NEXT: .LBB0_4: // %throw_exception ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: // %bb.5: // %throw_fail ; CHECK-NEXT: .LBB0_6: // %unwind_dtors ; CHECK-NEXT: .Ltmp2: // EH_LABEL ; CHECK-NEXT: mov x19, x0 ; CHECK-NEXT: smstart za ; CHECK-NEXT: mrs x8, TPIDR2_EL0 ; CHECK-NEXT: sub x0, x29, #16 ; CHECK-NEXT: cbnz x8, .LBB0_8 ; CHECK-NEXT: // %bb.7: // %unwind_dtors ; CHECK-NEXT: bl __arm_tpidr2_restore ; CHECK-NEXT: .LBB0_8: // %unwind_dtors ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: bl shared_za_call ; CHECK-NEXT: sub x8, x29, #16 ; CHECK-NEXT: mov x0, x19 ; CHECK-NEXT: msr TPIDR2_EL0, x8 ; CHECK-NEXT: bl _Unwind_Resume br i1 %fail, label %throw_exception, label %return_normally throw_exception: %exception_ptr = tail call ptr @__cxa_allocate_exception(i64 8) #3 store ptr @.str, ptr %exception_ptr, align 16 invoke void @__cxa_throw(ptr nonnull %exception_ptr, ptr nonnull @typeinfo_for_char_const_ptr, ptr null) to label %throw_fail unwind label %unwind_dtors unwind_dtors: %5 = landingpad { ptr, i32 } cleanup tail call void @shared_za_call() resume { ptr, i32 } %5 return_normally: tail call void @shared_za_call() ret void throw_fail: unreachable } ; Another simple exception handling example. Here we need to restore ZA in two ; places. After the may_throw() call to handle the case it does not throw, and ; within the catch block for the shared_za_call(). We also need to setup the ; lazy save around C++ exception ABI routines (to handle the _very_ unlikely ; case they use ZA state). ; ; void za_try_catch() __arm_inout("za") { ; try { ; may_throw(); ; } catch (...) { ; shared_za_call(); ; } ; shared_za_call(); ; } define dso_local void @try_catch() "aarch64_inout_za" personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: try_catch: ; CHECK: .Lfunc_begin1: ; CHECK-NEXT: .cfi_startproc ; CHECK-NEXT: .cfi_personality 156, DW.ref.__gxx_personality_v0 ; CHECK-NEXT: .cfi_lsda 28, .Lexception1 ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: stp x29, x30, [sp, #-16]! // 16-byte Folded Spill ; CHECK-NEXT: mov x29, sp ; CHECK-NEXT: sub sp, sp, #16 ; CHECK-NEXT: .cfi_def_cfa w29, 16 ; CHECK-NEXT: .cfi_offset w30, -8 ; CHECK-NEXT: .cfi_offset w29, -16 ; CHECK-NEXT: rdsvl x8, #1 ; CHECK-NEXT: mov x9, sp ; CHECK-NEXT: msub x9, x8, x8, x9 ; CHECK-NEXT: mov sp, x9 ; CHECK-NEXT: stp x9, x8, [x29, #-16] ; CHECK-NEXT: .Ltmp3: ; CHECK-NEXT: sub x8, x29, #16 ; CHECK-NEXT: msr TPIDR2_EL0, x8 ; CHECK-NEXT: bl may_throw ; CHECK-NEXT: .Ltmp4: ; CHECK-NEXT: .LBB1_1: // %after_catch ; CHECK-NEXT: smstart za ; CHECK-NEXT: mrs x8, TPIDR2_EL0 ; CHECK-NEXT: sub x0, x29, #16 ; CHECK-NEXT: cbnz x8, .LBB1_3 ; CHECK-NEXT: // %bb.2: // %after_catch ; CHECK-NEXT: bl __arm_tpidr2_restore ; CHECK-NEXT: .LBB1_3: // %after_catch ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: mov sp, x29 ; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload ; CHECK-NEXT: b shared_za_call ; CHECK-NEXT: .LBB1_4: // %catch ; CHECK-NEXT: .Ltmp5: ; CHECK-NEXT: bl __cxa_begin_catch ; CHECK-NEXT: smstart za ; CHECK-NEXT: mrs x8, TPIDR2_EL0 ; CHECK-NEXT: sub x0, x29, #16 ; CHECK-NEXT: cbnz x8, .LBB1_6 ; CHECK-NEXT: // %bb.5: // %catch ; CHECK-NEXT: bl __arm_tpidr2_restore ; CHECK-NEXT: .LBB1_6: // %catch ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: bl shared_za_call ; CHECK-NEXT: sub x8, x29, #16 ; CHECK-NEXT: msr TPIDR2_EL0, x8 ; CHECK-NEXT: bl __cxa_end_catch ; CHECK-NEXT: b .LBB1_1 invoke void @may_throw() to label %after_catch unwind label %catch catch: ; preds = %0 %eh_info = landingpad { ptr, i32 } catch ptr null %exception_ptr = extractvalue { ptr, i32 } %eh_info, 0 tail call ptr @__cxa_begin_catch(ptr %exception_ptr) tail call void @shared_za_call() tail call void @__cxa_end_catch() br label %after_catch after_catch: tail call void @shared_za_call() ret void } ; This example corresponds to: ; ; __arm_new("za") void try_catch_shared_za_callee() ; { ; try { ; shared_za_call(); ; } catch(...) { ; noexcept_shared_za_call(); ; } ; } ; ; In this example we don't setup a lazy save before shared_za_call(), however, ; we still enter the catch block in a ZA off state. This leads to us emitting a ; restore of a uninitialized save buffer in the catch block. This is not ideal ; but is valid in the SME ABI. Ideally, we would omit the save buffer and ; restore and simply set ZA to "on" in the catch block. define void @try_catch_shared_za_callee() "aarch64_new_za" personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: try_catch_shared_za_callee: ; CHECK: .Lfunc_begin2: ; CHECK-NEXT: .cfi_startproc ; CHECK-NEXT: .cfi_personality 156, DW.ref.__gxx_personality_v0 ; CHECK-NEXT: .cfi_lsda 28, .Lexception2 ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: stp x29, x30, [sp, #-16]! // 16-byte Folded Spill ; CHECK-NEXT: mov x29, sp ; CHECK-NEXT: sub sp, sp, #16 ; CHECK-NEXT: .cfi_def_cfa w29, 16 ; CHECK-NEXT: .cfi_offset w30, -8 ; CHECK-NEXT: .cfi_offset w29, -16 ; CHECK-NEXT: rdsvl x8, #1 ; CHECK-NEXT: mov x9, sp ; CHECK-NEXT: msub x9, x8, x8, x9 ; CHECK-NEXT: mov sp, x9 ; CHECK-NEXT: stp x9, x8, [x29, #-16] ; CHECK-NEXT: mrs x8, TPIDR2_EL0 ; CHECK-NEXT: cbz x8, .LBB2_2 ; CHECK-NEXT: // %bb.1: ; CHECK-NEXT: bl __arm_tpidr2_save ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: zero {za} ; CHECK-NEXT: .LBB2_2: ; CHECK-NEXT: smstart za ; CHECK-NEXT: .Ltmp6: ; CHECK-NEXT: bl shared_za_call ; CHECK-NEXT: .Ltmp7: ; CHECK-NEXT: .LBB2_3: // %exit ; CHECK-NEXT: smstop za ; CHECK-NEXT: mov sp, x29 ; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload ; CHECK-NEXT: ret ; CHECK-NEXT: .LBB2_4: // %catch ; CHECK-NEXT: .Ltmp8: ; CHECK-NEXT: bl __cxa_begin_catch ; CHECK-NEXT: smstart za ; CHECK-NEXT: mrs x8, TPIDR2_EL0 ; CHECK-NEXT: sub x0, x29, #16 ; CHECK-NEXT: cbnz x8, .LBB2_6 ; CHECK-NEXT: // %bb.5: // %catch ; CHECK-NEXT: bl __arm_tpidr2_restore ; CHECK-NEXT: .LBB2_6: // %catch ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: bl noexcept_shared_za_call ; CHECK-NEXT: sub x8, x29, #16 ; CHECK-NEXT: msr TPIDR2_EL0, x8 ; CHECK-NEXT: bl __cxa_end_catch ; CHECK-NEXT: msr TPIDR2_EL0, xzr ; CHECK-NEXT: b .LBB2_3 invoke void @shared_za_call() #4 to label %exit unwind label %catch catch: %eh_info = landingpad { ptr, i32 } catch ptr null %exception_ptr = extractvalue { ptr, i32 } %eh_info, 0 tail call ptr @__cxa_begin_catch(ptr %exception_ptr) tail call void @noexcept_shared_za_call() tail call void @__cxa_end_catch() br label %exit exit: ret void } declare ptr @__cxa_allocate_exception(i64) declare void @__cxa_throw(ptr, ptr, ptr) declare ptr @__cxa_begin_catch(ptr) declare void @__cxa_end_catch() declare i32 @__gxx_personality_v0(...) declare void @may_throw() declare void @shared_za_call() "aarch64_inout_za" declare void @noexcept_shared_za_call() "aarch64_inout_za"