1//===-- AArch64SMEAttributes.cpp - Helper for interpreting SME attributes -===//
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "AArch64SMEAttributes.h"
10#include "AArch64ISelLowering.h"
11#include "llvm/ADT/StringSwitch.h"
12#include "llvm/IR/InstrTypes.h"
13#include "llvm/IR/RuntimeLibcalls.h"
14#include <cassert>
15
16using namespace llvm;
17
18void SMEAttrs::validate() const {
19 // Streaming Mode Attrs
20 assert(!(hasStreamingInterface() && hasStreamingCompatibleInterface()) &&
21 "SM_Enabled and SM_Compatible are mutually exclusive");
22
23 // ZA Attrs
24 assert(!(isNewZA() && (Bitmask & SME_ABI_Routine)) &&
25 "ZA_New and SME_ABI_Routine are mutually exclusive");
26
27 assert(
28 (isNewZA() + isInZA() + isOutZA() + isInOutZA() + isPreservesZA()) <= 1 &&
29 "Attributes 'aarch64_new_za', 'aarch64_in_za', 'aarch64_out_za', "
30 "'aarch64_inout_za' and 'aarch64_preserves_za' are mutually exclusive");
31
32 // ZT0 Attrs
33 assert(
34 (isNewZT0() + isInZT0() + isOutZT0() + isInOutZT0() + isPreservesZT0()) <=
35 1 &&
36 "Attributes 'aarch64_new_zt0', 'aarch64_in_zt0', 'aarch64_out_zt0', "
37 "'aarch64_inout_zt0' and 'aarch64_preserves_zt0' are mutually exclusive");
38
39 assert(!(hasAgnosticZAInterface() && hasSharedZAInterface()) &&
40 "Function cannot have a shared-ZA interface and an agnostic-ZA "
41 "interface");
42}
43
44SMEAttrs::SMEAttrs(const AttributeList &Attrs) {
45 // Note: 'aarch64_zt0_undef' was previously used (and subsequently removed).
46 // To avoid introducing any compatibility issues don't reuse
47 // 'aarch64_zt0_undef' for another purpose.
48 Bitmask = 0;
49 for (Attribute Attr : Attrs.getFnAttrs()) {
50 if (!Attr.isStringAttribute())
51 continue;
52
53 StringRef Kind = Attr.getKindAsString();
54 if (!Kind.consume_front(Prefix: "aarch64_"))
55 continue;
56
57 Bitmask |= StringSwitch<unsigned>(Kind)
58 .Case(S: "pstate_sm_enabled", Value: SM_Enabled)
59 .Case(S: "pstate_sm_compatible", Value: SM_Compatible)
60 .Case(S: "pstate_sm_body", Value: SM_Body)
61 .Case(S: "za_state_agnostic", Value: ZA_State_Agnostic)
62 .Case(S: "in_za", Value: encodeZAState(S: StateValue::In))
63 .Case(S: "out_za", Value: encodeZAState(S: StateValue::Out))
64 .Case(S: "inout_za", Value: encodeZAState(S: StateValue::InOut))
65 .Case(S: "preserves_za", Value: encodeZAState(S: StateValue::Preserved))
66 .Case(S: "new_za", Value: encodeZAState(S: StateValue::New))
67 .Case(S: "in_zt0", Value: encodeZT0State(S: StateValue::In))
68 .Case(S: "out_zt0", Value: encodeZT0State(S: StateValue::Out))
69 .Case(S: "inout_zt0", Value: encodeZT0State(S: StateValue::InOut))
70 .Case(S: "preserves_zt0", Value: encodeZT0State(S: StateValue::Preserved))
71 .Case(S: "new_zt0", Value: encodeZT0State(S: StateValue::New))
72 .Default(Value: Normal);
73 }
74}
75
76void SMEAttrs::addKnownFunctionAttrs(StringRef FuncName,
77 const RTLIB::RuntimeLibcallsInfo &RTLCI) {
78 RTLIB::LibcallImpl Impl = RTLCI.getSupportedLibcallImpl(FuncName);
79 if (Impl == RTLIB::Unsupported)
80 return;
81 unsigned KnownAttrs = SMEAttrs::Normal;
82 RTLIB::Libcall LC = RTLIB::RuntimeLibcallsInfo::getLibcallFromImpl(Impl);
83 switch (LC) {
84 case RTLIB::SMEABI_SME_STATE:
85 case RTLIB::SMEABI_TPIDR2_SAVE:
86 case RTLIB::SMEABI_GET_CURRENT_VG:
87 case RTLIB::SMEABI_SME_STATE_SIZE:
88 case RTLIB::SMEABI_SME_SAVE:
89 case RTLIB::SMEABI_SME_RESTORE:
90 KnownAttrs |= SMEAttrs::SM_Compatible | SMEAttrs::SME_ABI_Routine;
91 break;
92 case RTLIB::SMEABI_ZA_DISABLE:
93 case RTLIB::SMEABI_TPIDR2_RESTORE:
94 KnownAttrs |= SMEAttrs::SM_Compatible | encodeZAState(S: StateValue::In) |
95 SMEAttrs::SME_ABI_Routine;
96 break;
97 case RTLIB::SC_MEMCPY:
98 case RTLIB::SC_MEMMOVE:
99 case RTLIB::SC_MEMSET:
100 case RTLIB::SC_MEMCHR:
101 KnownAttrs |= SMEAttrs::SM_Compatible;
102 break;
103 default:
104 break;
105 }
106 set(M: KnownAttrs);
107}
108
109bool SMECallAttrs::requiresSMChange() const {
110 if (callee().hasStreamingCompatibleInterface())
111 return false;
112
113 // Both non-streaming
114 if (caller().hasNonStreamingInterfaceAndBody() &&
115 callee().hasNonStreamingInterface())
116 return false;
117
118 // Both streaming
119 if (caller().hasStreamingInterfaceOrBody() &&
120 callee().hasStreamingInterface())
121 return false;
122
123 return true;
124}
125
126SMECallAttrs::SMECallAttrs(const CallBase &CB,
127 const RTLIB::RuntimeLibcallsInfo *RTLCI)
128 : CallerFn(*CB.getFunction()), CalledFn(SMEAttrs::Normal),
129 Callsite(CB.getAttributes()), IsIndirect(CB.isIndirectCall()) {
130 if (auto *CalledFunction = CB.getCalledFunction())
131 CalledFn = SMEAttrs(*CalledFunction, RTLCI);
132
133 // FIXME: We probably should not allow SME attributes on direct calls but
134 // clang duplicates streaming mode attributes at each callsite.
135 assert((IsIndirect || ((Callsite | CalledFn) == CalledFn)) &&
136 "SME attributes at callsite do not match declaration");
137
138 // An `invoke` of an agnostic ZA function may not return normally (it may
139 // resume in an exception block). In this case, it acts like a private ZA
140 // callee and may require a ZA save to be set up before it is called.
141 if (isa<InvokeInst>(Val: CB))
142 CalledFn.set(M: SMEAttrs::ZA_State_Agnostic, /*Enable=*/false);
143}
144