It turns that the issue was unrelated to the code-changes, but only triggered
by one of the tests. The SMEABI pass incorrectly marked the CFG as preserved,
even though it modified the CFG.
This reverts commit 8bcf5df304.
145 lines
5.3 KiB
C++
145 lines
5.3 KiB
C++
//===--------- SMEABI - SME ABI-------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This pass implements parts of the the SME ABI, such as:
|
|
// * Using the lazy-save mechanism before enabling the use of ZA.
|
|
// * Setting up the lazy-save mechanism around invokes.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AArch64.h"
|
|
#include "Utils/AArch64BaseInfo.h"
|
|
#include "Utils/AArch64SMEAttributes.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/IntrinsicsAArch64.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Transforms/Utils/Cloning.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "aarch64-sme-abi"
|
|
|
|
namespace {
|
|
struct SMEABI : public FunctionPass {
|
|
static char ID; // Pass identification, replacement for typeid
|
|
SMEABI() : FunctionPass(ID) {
|
|
initializeSMEABIPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
bool runOnFunction(Function &F) override;
|
|
|
|
private:
|
|
bool updateNewZAFunctions(Module *M, Function *F, IRBuilder<> &Builder);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
char SMEABI::ID = 0;
|
|
static const char *name = "SME ABI Pass";
|
|
INITIALIZE_PASS_BEGIN(SMEABI, DEBUG_TYPE, name, false, false)
|
|
INITIALIZE_PASS_END(SMEABI, DEBUG_TYPE, name, false, false)
|
|
|
|
FunctionPass *llvm::createSMEABIPass() { return new SMEABI(); }
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Utility functions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Utility function to emit a call to __arm_tpidr2_save and clear TPIDR2_EL0.
|
|
void emitTPIDR2Save(Module *M, IRBuilder<> &Builder) {
|
|
auto *TPIDR2SaveTy =
|
|
FunctionType::get(Builder.getVoidTy(), {}, /*IsVarArgs=*/false);
|
|
auto Attrs =
|
|
AttributeList()
|
|
.addFnAttribute(M->getContext(), "aarch64_pstate_sm_compatible")
|
|
.addFnAttribute(M->getContext(), "aarch64_pstate_za_preserved");
|
|
FunctionCallee Callee =
|
|
M->getOrInsertFunction("__arm_tpidr2_save", TPIDR2SaveTy, Attrs);
|
|
CallInst *Call = Builder.CreateCall(Callee);
|
|
Call->setCallingConv(
|
|
CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0);
|
|
|
|
// A save to TPIDR2 should be followed by clearing TPIDR2_EL0.
|
|
Function *WriteIntr =
|
|
Intrinsic::getDeclaration(M, Intrinsic::aarch64_sme_set_tpidr2);
|
|
Builder.CreateCall(WriteIntr->getFunctionType(), WriteIntr,
|
|
Builder.getInt64(0));
|
|
}
|
|
|
|
/// This function generates code to commit a lazy save at the beginning of a
|
|
/// function marked with `aarch64_pstate_za_new`. If the value read from
|
|
/// TPIDR2_EL0 is not null on entry to the function then the lazy-saving scheme
|
|
/// is active and we should call __arm_tpidr2_save to commit the lazy save.
|
|
/// Additionally, PSTATE.ZA should be enabled at the beginning of the function
|
|
/// and disabled before returning.
|
|
bool SMEABI::updateNewZAFunctions(Module *M, Function *F,
|
|
IRBuilder<> &Builder) {
|
|
LLVMContext &Context = F->getContext();
|
|
BasicBlock *OrigBB = &F->getEntryBlock();
|
|
|
|
// Create the new blocks for reading TPIDR2_EL0 & enabling ZA state.
|
|
auto *SaveBB = OrigBB->splitBasicBlock(OrigBB->begin(), "save.za", true);
|
|
auto *PreludeBB = BasicBlock::Create(Context, "prelude", F, SaveBB);
|
|
|
|
// Read TPIDR2_EL0 in PreludeBB & branch to SaveBB if not 0.
|
|
Builder.SetInsertPoint(PreludeBB);
|
|
Function *TPIDR2Intr =
|
|
Intrinsic::getDeclaration(M, Intrinsic::aarch64_sme_get_tpidr2);
|
|
auto *TPIDR2 = Builder.CreateCall(TPIDR2Intr->getFunctionType(), TPIDR2Intr,
|
|
{}, "tpidr2");
|
|
auto *Cmp =
|
|
Builder.CreateCmp(ICmpInst::ICMP_NE, TPIDR2, Builder.getInt64(0), "cmp");
|
|
Builder.CreateCondBr(Cmp, SaveBB, OrigBB);
|
|
|
|
// Create a call __arm_tpidr2_save, which commits the lazy save.
|
|
Builder.SetInsertPoint(&SaveBB->back());
|
|
emitTPIDR2Save(M, Builder);
|
|
|
|
// Enable pstate.za at the start of the function.
|
|
Builder.SetInsertPoint(&OrigBB->front());
|
|
Function *EnableZAIntr =
|
|
Intrinsic::getDeclaration(M, Intrinsic::aarch64_sme_za_enable);
|
|
Builder.CreateCall(EnableZAIntr->getFunctionType(), EnableZAIntr);
|
|
|
|
// Before returning, disable pstate.za
|
|
for (BasicBlock &BB : F->getBasicBlockList()) {
|
|
Instruction *T = BB.getTerminator();
|
|
if (!T || !isa<ReturnInst>(T))
|
|
continue;
|
|
Builder.SetInsertPoint(T);
|
|
Function *DisableZAIntr =
|
|
Intrinsic::getDeclaration(M, Intrinsic::aarch64_sme_za_disable);
|
|
Builder.CreateCall(DisableZAIntr->getFunctionType(), DisableZAIntr);
|
|
}
|
|
|
|
F->addFnAttr("aarch64_expanded_pstate_za");
|
|
return true;
|
|
}
|
|
|
|
bool SMEABI::runOnFunction(Function &F) {
|
|
Module *M = F.getParent();
|
|
LLVMContext &Context = F.getContext();
|
|
IRBuilder<> Builder(Context);
|
|
|
|
if (F.isDeclaration() || F.hasFnAttribute("aarch64_expanded_pstate_za"))
|
|
return false;
|
|
|
|
bool Changed = false;
|
|
SMEAttrs FnAttrs(F);
|
|
if (FnAttrs.hasNewZAInterface())
|
|
Changed |= updateNewZAFunctions(M, &F, Builder);
|
|
|
|
return Changed;
|
|
}
|