119 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// REQUIRES: target={{aarch64-.+}}10// UNSUPPORTED: target={{.*-windows.*}}11 12#include <libunwind.h>13#include <stdint.h>14#include <stdio.h>15#include <stdlib.h>16#include <string.h>17#include <sys/auxv.h>18 19// Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA20// (and commits a lazy save of ZA) before resuming from unwinding.21 22// Note: This test requires SME (and is setup to pass on targets without SME).23 24static bool checkHasSME() {25 constexpr int hwcap2_sme = (1 << 23);26 unsigned long hwcap2 = getauxval(AT_HWCAP2);27 return (hwcap2 & hwcap2_sme) != 0;28}29 30struct TPIDR2Block {31 void *za_save_buffer;32 uint64_t num_save_slices;33};34 35__attribute__((noinline)) void private_za() {36 // Note: Lazy save active on entry to function.37 unw_context_t context;38 unw_cursor_t cursor;39 40 unw_getcontext(&context);41 unw_init_local(&cursor, &context);42 unw_step(&cursor);43 unw_resume(&cursor);44}45 46bool isZAOn() {47 register uint64_t svcr asm("x20");48 asm(".inst 0xd53b4254" : "=r"(svcr));49 return (svcr & 0b10) != 0;50}51 52__attribute__((noinline)) void za_function_with_lazy_save() {53 register uint64_t tmp asm("x8");54 55 // SMSTART ZA (should zero ZA)56 asm(".inst 0xd503457f");57 58 // RDSVL x8, #1 (read streaming vector length)59 asm(".inst 0x04bf5828" : "=r"(tmp));60 61 // Allocate and fill ZA save buffer with 0xAA.62 size_t buffer_size = tmp * tmp;63 uint8_t *za_save_buffer = (uint8_t *)alloca(buffer_size);64 memset(za_save_buffer, 0xAA, buffer_size);65 66 TPIDR2Block block = {za_save_buffer, tmp};67 tmp = reinterpret_cast<uint64_t>(&block);68 69 // MRS TPIDR2_EL0, x8 (setup lazy save of ZA)70 asm(".inst 0xd51bd0a8" ::"r"(tmp));71 72 // ZA should be on before unwinding.73 if (!isZAOn()) {74 fprintf(stderr, __FILE__ ": fail (ZA not on before call)\n");75 abort();76 } else {77 fprintf(stderr, __FILE__ ": pass (ZA on before call)\n");78 }79 80 private_za();81 82 // ZA should be off after unwinding.83 if (isZAOn()) {84 fprintf(stderr, __FILE__ ": fail (ZA on after unwinding)\n");85 abort();86 } else {87 fprintf(stderr, __FILE__ ": pass (ZA off after unwinding)\n");88 }89 90 // MRS x8, TPIDR2_EL0 (read TPIDR2_EL0)91 asm(".inst 0xd53bd0a8" : "=r"(tmp));92 // ZA should have been saved (TPIDR2_EL0 zero).93 if (tmp != 0) {94 fprintf(stderr, __FILE__ ": fail (TPIDR2_EL0 non-null after unwinding)\n");95 abort();96 } else {97 fprintf(stderr, __FILE__ ": pass (TPIDR2_EL0 null after unwinding)\n");98 }99 100 // ZA (all zero) should have been saved to the buffer.101 for (unsigned i = 0; i < buffer_size; ++i) {102 if (za_save_buffer[i] != 0) {103 fprintf(stderr,104 __FILE__ ": fail (za_save_buffer non-zero after unwinding)\n");105 abort();106 }107 }108 fprintf(stderr, __FILE__ ": pass (za_save_buffer zero'd after unwinding)\n");109}110 111int main(int, char **) {112 if (!checkHasSME()) {113 fprintf(stderr, __FILE__ ": pass (no SME support)\n");114 return 0; // Pass (SME is required for this test to run).115 }116 za_function_with_lazy_save();117 return 0;118}119