LLVM 22.0.0git
SMEABIPass.cpp
Go to the documentation of this file.
1//===--------- SMEABI - SME ABI-------------------------------------------===//
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// This pass implements parts of the the SME ABI, such as:
10// * Using the lazy-save mechanism before enabling the use of ZA.
11// * Setting up the lazy-save mechanism around invokes.
12//
13//===----------------------------------------------------------------------===//
14
15#include "AArch64.h"
17#include "llvm/ADT/StringRef.h"
21#include "llvm/IR/IRBuilder.h"
23#include "llvm/IR/IntrinsicsAArch64.h"
24#include "llvm/IR/LLVMContext.h"
25#include "llvm/IR/Module.h"
29
30using namespace llvm;
31
32#define DEBUG_TYPE "aarch64-sme-abi"
33
34namespace {
35struct SMEABI : public FunctionPass {
36 static char ID; // Pass identification, replacement for typeid
37 SMEABI() : FunctionPass(ID) {}
38
39 bool runOnFunction(Function &F) override;
40
41 void getAnalysisUsage(AnalysisUsage &AU) const override {
43 }
44
45private:
46 bool updateNewStateFunctions(Module *M, Function *F, IRBuilder<> &Builder,
47 SMEAttrs FnAttrs, const TargetLowering &TLI);
48};
49} // end anonymous namespace
50
51char SMEABI::ID = 0;
52static const char *name = "SME ABI Pass";
53INITIALIZE_PASS_BEGIN(SMEABI, DEBUG_TYPE, name, false, false)
55
56FunctionPass *llvm::createSMEABIPass() { return new SMEABI(); }
57
58//===----------------------------------------------------------------------===//
59// Utility functions
60//===----------------------------------------------------------------------===//
61
62// Utility function to emit a call to __arm_tpidr2_save and clear TPIDR2_EL0.
63void emitTPIDR2Save(Module *M, IRBuilder<> &Builder, const TargetLowering &TLI,
64 bool ZT0IsUndef = false) {
65 auto &Ctx = M->getContext();
66 auto *TPIDR2SaveTy =
67 FunctionType::get(Builder.getVoidTy(), {}, /*IsVarArgs=*/false);
68 auto Attrs =
69 AttributeList().addFnAttribute(Ctx, "aarch64_pstate_sm_compatible");
70 RTLIB::Libcall LC = RTLIB::SMEABI_TPIDR2_SAVE;
71 FunctionCallee Callee =
72 M->getOrInsertFunction(TLI.getLibcallName(LC), TPIDR2SaveTy, Attrs);
73 CallInst *Call = Builder.CreateCall(Callee);
74
75 // If ZT0 is undefined (i.e. we're at the entry of a "new_zt0" function), mark
76 // that on the __arm_tpidr2_save call. This prevents an unnecessary spill of
77 // ZT0 that can occur before ZA is enabled.
78 if (ZT0IsUndef)
79 Call->addFnAttr(Attribute::get(Ctx, "aarch64_zt0_undef"));
80
81 Call->setCallingConv(TLI.getLibcallCallingConv(LC));
82
83 // A save to TPIDR2 should be followed by clearing TPIDR2_EL0.
84 Function *WriteIntr =
85 Intrinsic::getOrInsertDeclaration(M, Intrinsic::aarch64_sme_set_tpidr2);
86 Builder.CreateCall(WriteIntr->getFunctionType(), WriteIntr,
87 Builder.getInt64(0));
88}
89
90/// This function generates code at the beginning and end of a function marked
91/// with either `aarch64_new_za` or `aarch64_new_zt0`.
92/// At the beginning of the function, the following code is generated:
93/// - Commit lazy-save if active [Private-ZA Interface*]
94/// - Enable PSTATE.ZA [Private-ZA Interface]
95/// - Zero ZA [Has New ZA State]
96/// - Zero ZT0 [Has New ZT0 State]
97///
98/// * A function with new ZT0 state will not change ZA, so committing the
99/// lazy-save is not strictly necessary. However, the lazy-save mechanism
100/// may be active on entry to the function, with PSTATE.ZA set to 1. If
101/// the new ZT0 function calls a function that does not share ZT0, we will
102/// need to conditionally SMSTOP ZA before the call, setting PSTATE.ZA to 0.
103/// For this reason, it's easier to always commit the lazy-save at the
104/// beginning of the function regardless of whether it has ZA state.
105///
106/// At the end of the function, PSTATE.ZA is disabled if the function has a
107/// Private-ZA Interface. A function is considered to have a Private-ZA
108/// interface if it does not share ZA or ZT0.
109///
110bool SMEABI::updateNewStateFunctions(Module *M, Function *F,
111 IRBuilder<> &Builder, SMEAttrs FnAttrs,
112 const TargetLowering &TLI) {
113 LLVMContext &Context = F->getContext();
114 BasicBlock *OrigBB = &F->getEntryBlock();
115 Builder.SetInsertPoint(&OrigBB->front());
116
117 // Commit any active lazy-saves if this is a Private-ZA function. If the
118 // value read from TPIDR2_EL0 is not null on entry to the function then
119 // the lazy-saving scheme is active and we should call __arm_tpidr2_save
120 // to commit the lazy save.
121 if (FnAttrs.hasPrivateZAInterface()) {
122 // Create the new blocks for reading TPIDR2_EL0 & enabling ZA state.
123 auto *SaveBB = OrigBB->splitBasicBlock(OrigBB->begin(), "save.za", true);
124 auto *PreludeBB = BasicBlock::Create(Context, "prelude", F, SaveBB);
125
126 // Read TPIDR2_EL0 in PreludeBB & branch to SaveBB if not 0.
127 Builder.SetInsertPoint(PreludeBB);
128 Function *TPIDR2Intr =
129 Intrinsic::getOrInsertDeclaration(M, Intrinsic::aarch64_sme_get_tpidr2);
130 auto *TPIDR2 = Builder.CreateCall(TPIDR2Intr->getFunctionType(), TPIDR2Intr,
131 {}, "tpidr2");
132 auto *Cmp = Builder.CreateCmp(ICmpInst::ICMP_NE, TPIDR2,
133 Builder.getInt64(0), "cmp");
134 Builder.CreateCondBr(Cmp, SaveBB, OrigBB);
135
136 // Create a call __arm_tpidr2_save, which commits the lazy save.
137 Builder.SetInsertPoint(&SaveBB->back());
138 emitTPIDR2Save(M, Builder, TLI, /*ZT0IsUndef=*/FnAttrs.isNewZT0());
139
140 // Enable pstate.za at the start of the function.
141 Builder.SetInsertPoint(&OrigBB->front());
142 Function *EnableZAIntr =
143 Intrinsic::getOrInsertDeclaration(M, Intrinsic::aarch64_sme_za_enable);
144 Builder.CreateCall(EnableZAIntr->getFunctionType(), EnableZAIntr);
145 }
146
147 if (FnAttrs.isNewZA()) {
148 Function *ZeroIntr =
149 Intrinsic::getOrInsertDeclaration(M, Intrinsic::aarch64_sme_zero);
150 Builder.CreateCall(ZeroIntr->getFunctionType(), ZeroIntr,
151 Builder.getInt32(0xff));
152 }
153
154 if (FnAttrs.isNewZT0()) {
155 Function *ClearZT0Intr =
156 Intrinsic::getOrInsertDeclaration(M, Intrinsic::aarch64_sme_zero_zt);
157 Builder.CreateCall(ClearZT0Intr->getFunctionType(), ClearZT0Intr,
158 {Builder.getInt32(0)});
159 }
160
161 if (FnAttrs.hasPrivateZAInterface()) {
162 // Before returning, disable pstate.za
163 for (BasicBlock &BB : *F) {
164 Instruction *T = BB.getTerminator();
165 if (!T || !isa<ReturnInst>(T))
166 continue;
167 Builder.SetInsertPoint(T);
169 M, Intrinsic::aarch64_sme_za_disable);
170 Builder.CreateCall(DisableZAIntr->getFunctionType(), DisableZAIntr);
171 }
172 }
173
174 F->addFnAttr("aarch64_expanded_pstate_za");
175 return true;
176}
177
178bool SMEABI::runOnFunction(Function &F) {
179 Module *M = F.getParent();
180 LLVMContext &Context = F.getContext();
181 IRBuilder<> Builder(Context);
182
183 if (F.isDeclaration() || F.hasFnAttribute("aarch64_expanded_pstate_za"))
184 return false;
185
186 const TargetMachine &TM =
187 getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
188 const TargetLowering &TLI = *TM.getSubtargetImpl(F)->getTargetLowering();
189
190 bool Changed = false;
191 SMEAttrs FnAttrs(F);
192 if (FnAttrs.isNewZA() || FnAttrs.isNewZT0())
193 Changed |= updateNewStateFunctions(M, &F, Builder, FnAttrs, TLI);
194
195 return Changed;
196}
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition: MD5.cpp:55
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:39
void emitTPIDR2Save(Module *M, IRBuilder<> &Builder, const TargetLowering &TLI, bool ZT0IsUndef=false)
Definition: SMEABIPass.cpp:63
static const char * name
Definition: SMEABIPass.cpp:52
#define DEBUG_TYPE
Definition: SMEABIPass.cpp:32
This file describes how to lower LLVM code to machine code.
Target-Independent Code Generator Pass Configuration Options pass.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AttributeList addFnAttribute(LLVMContext &C, Attribute::AttrKind Kind) const
Add a function attribute to the list.
Definition: Attributes.h:593
static LLVM_ABI Attribute get(LLVMContext &Context, AttrKind Kind, uint64_t Val=0)
Return a uniquified Attribute object.
Definition: Attributes.cpp:95
LLVM Basic Block Representation.
Definition: BasicBlock.h:62
iterator begin()
Instruction iterator methods.
Definition: BasicBlock.h:459
const Instruction & front() const
Definition: BasicBlock.h:482
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition: BasicBlock.h:206
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="", bool Before=false)
Split the basic block into two basic blocks at the specified instruction.
Definition: BasicBlock.cpp:555
This class represents a function call, abstracting a target machine's calling convention.
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Definition: DerivedTypes.h:170
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:314
virtual bool runOnFunction(Function &F)=0
runOnFunction - Virtual method overriden by subclasses to do the per-function processing of the pass.
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition: Function.h:209
ConstantInt * getInt64(uint64_t C)
Get a constant 64-bit value.
Definition: IRBuilder.h:527
ConstantInt * getInt32(uint32_t C)
Get a constant 32-bit value.
Definition: IRBuilder.h:522
Value * CreateCmp(CmpInst::Predicate Pred, Value *LHS, Value *RHS, const Twine &Name="", MDNode *FPMathTag=nullptr)
Definition: IRBuilder.h:2463
BranchInst * CreateCondBr(Value *Cond, BasicBlock *True, BasicBlock *False, MDNode *BranchWeights=nullptr, MDNode *Unpredictable=nullptr)
Create a conditional 'br Cond, TrueDest, FalseDest' instruction.
Definition: IRBuilder.h:1197
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args={}, const Twine &Name="", MDNode *FPMathTag=nullptr)
Definition: IRBuilder.h:2508
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Definition: IRBuilder.h:207
Type * getVoidTy()
Fetch the type representing void.
Definition: IRBuilder.h:600
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2780
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:68
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:67
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
Definition: Pass.cpp:112
SMEAttrs is a utility class to parse the SME ACLE attributes on functions.
bool hasPrivateZAInterface() const
bool isNewZA() const
CallingConv::ID getLibcallCallingConv(RTLIB::Libcall Call) const
Get the CallingConv that should be used for the specified libcall.
const char * getLibcallName(RTLIB::Libcall Call) const
Get the libcall routine name for the specified libcall.
This class defines information used to lower LLVM code to legal SelectionDAG operators that the targe...
Primary interface to the complete machine description for the target machine.
Definition: TargetMachine.h:83
Target-Independent Code Generator Pass Configuration Options.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > Tys={})
Look up the Function declaration of the intrinsic id in the Module M.
Definition: Intrinsics.cpp:751
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
FunctionPass * createSMEABIPass()
Definition: SMEABIPass.cpp:56