Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/PrologEpilogInserter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,10 @@ PEIImpl::planClearRegisters(MachineFunction &MF,
continue;

MCRegister Reg = MO.getReg();
// TODO: Mark allocatable subregisters used as well. ARM pair operands
// such as R0_R1 must mark R0 and R1 so used-gpr can select the scalar
// components without classifying GPRPair as a general-purpose class.
// Add coverage for pair-only uses before enabling ARM register clearing.
if (AllocatableSet[Reg.id()])
UsedRegs.set(Reg.id());
}
Expand Down
59 changes: 59 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
#include "ARMBaseRegisterInfo.h"
#include "ARM.h"
#include "ARMBaseInstrInfo.h"
#include "ARMCallingConv.h"
#include "ARMFrameLowering.h"
#include "ARMISelLowering.h"
#include "ARMMachineFunctionInfo.h"
#include "ARMSubtarget.h"
#include "MCTargetDesc/ARMAddressingModes.h"
Expand Down Expand Up @@ -54,6 +56,9 @@

using namespace llvm;

#define GET_CC_REGISTER_LISTS
#include "ARMGenCallingConv.inc"

ARMBaseRegisterInfo::ARMBaseRegisterInfo()
: ARMGenRegisterInfo(ARM::LR, 0, 0, ARM::PC) {
ARM_MC::initLLVMToCVRegMapping(this);
Expand Down Expand Up @@ -279,6 +284,60 @@ bool ARMBaseRegisterInfo::isInlineAsmReadOnlyReg(const MachineFunction &MF,
return Reserved.test(PhysReg.id());
}

bool ARMBaseRegisterInfo::isArgumentRegister(const MachineFunction &MF,
MCRegister PhysReg) const {
// Ask the lowering which convention this function was assigned rather than
// deriving it again here. Which one a calling convention resolves to depends
// on the ABI, on whether the subtarget has floating-point registers, on
// Thumb-1, and on varargs, and a second copy of that reasoning would be one
// that can disagree with the one the arguments were actually assigned by.
const Function &F = MF.getFunction();
const ARMTargetLowering *TLI =
MF.getSubtarget<ARMSubtarget>().getTargetLowering();
CCAssignFn *Fn = TLI->CCAssignFnForCall(F.getCallingConv(), F.isVarArg());

auto HasReg = [PhysReg](ArrayRef<MCRegister> RegList) {
return llvm::is_contained(RegList, PhysReg);
};

// Swift argument registers are emitted as a separate list. The attributes
// can also select these registers under non-Swift calling conventions.
CallingConv::ID CC = F.getCallingConv();
const bool IsSwiftArgument =
CC == CallingConv::Swift || CC == CallingConv::SwiftTail ||
(PhysReg == ARM::R10 &&
F.getAttributes().hasAttrSomewhere(Attribute::SwiftSelf)) ||
(PhysReg == ARM::R8 &&
F.getAttributes().hasAttrSomewhere(Attribute::SwiftError));

// Generated argument lists currently omit registers from delegated
// conventions, so explicitly include those lists here. For example,
// CC_ARM_AAPCS_ArgRegs contains only R12; CC_ARM_AAPCS_Common_ArgRegs
// supplies R0-R3. Removing the union would lose ordinary AAPCS arguments.
if (Fn == CC_ARM_APCS)
return HasReg(CC_ARM_APCS_ArgRegs) ||
(IsSwiftArgument && HasReg(CC_ARM_APCS_Swift_ArgRegs));
if (Fn == FastCC_ARM_APCS)
return HasReg(FastCC_ARM_APCS_ArgRegs) || HasReg(CC_ARM_APCS_ArgRegs) ||
(IsSwiftArgument && HasReg(CC_ARM_APCS_Swift_ArgRegs));
if (Fn == CC_ARM_APCS_GHC)
return HasReg(CC_ARM_APCS_GHC_ArgRegs);
if (Fn == CC_ARM_AAPCS)
return HasReg(CC_ARM_AAPCS_ArgRegs) ||
HasReg(CC_ARM_AAPCS_Common_ArgRegs) ||
(IsSwiftArgument && HasReg(CC_ARM_AAPCS_Swift_ArgRegs));
if (Fn == CC_ARM_AAPCS_VFP)
return HasReg(CC_ARM_AAPCS_VFP_ArgRegs) ||
HasReg(CC_ARM_AAPCS_Common_ArgRegs) ||
(IsSwiftArgument && HasReg(CC_ARM_AAPCS_VFP_Swift_ArgRegs));
if (Fn == CC_ARM_Win32_CFGuard_Check)
return HasReg(CC_ARM_Win32_CFGuard_Check_ArgRegs);

// The selector returns one of the handled functions or terminates with an
// unsupported-convention error.
llvm_unreachable("unhandled ARM calling convention");
}

const TargetRegisterClass *
ARMBaseRegisterInfo::getLargestLegalSuperClass(const TargetRegisterClass *RC,
const MachineFunction &MF) const {
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseRegisterInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class ARMBaseRegisterInfo : public ARMGenRegisterInfo {
MCRegister PhysReg) const override;
bool isInlineAsmReadOnlyReg(const MachineFunction &MF,
MCRegister PhysReg) const override;
bool isArgumentRegister(const MachineFunction &MF,
MCRegister PhysReg) const override;

const TargetRegisterClass *
getPointerRegClass(unsigned Kind = 0) const override;
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/ARM/ARMRegisterInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,20 @@ def DQuadSpc : RegisterClass<"ARM", [v4i64], 64, (add Tuples3DSpc)>;

// FP context payload
def FPCXTRegs : RegisterClass<"ARM", [i32], 32, (add FPCXTNS)>;

//===----------------------------------------------------------------------===//
// Register categories.
//

// GPR spans R0-R12, SP, LR and PC. PEI selects register-clearing candidates
// from the allocatable set, which excludes the reserved SP and PC registers.
def GeneralPurposeRegisters : RegisterCategory<[GPR]>;

// Status and control registers are excluded from call-used register clearing.
// Clearing condition flags requires separate handling.
// FPCXTNS holds the floating-point context the security extension saves
// and restores across a non-secure call; overwriting it would destroy state
// the caller is entitled to get back, which is not what clearing a call-used
// register means. VPR is deliberately absent: an MVE predicate is data.
def FixedRegisters : RegisterCategory<[CCR, cl_FPSCR_NZCV, FP_STATUS_REGS,
FPCXTRegs]>;
15 changes: 15 additions & 0 deletions llvm/test/CodeGen/ARM/zero-call-used-regs-unsupported.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
; RUN: not llc -mtriple=armv7-unknown-linux-gnueabi < %s -o /dev/null 2>&1 | FileCheck %s
; RUN: not llc -mtriple=thumbv6m-none-eabi < %s -o /dev/null 2>&1 | FileCheck %s
; RUN: not llc -mtriple=thumbv7m-none-eabi < %s -o /dev/null 2>&1 | FileCheck %s
; RUN: not llc -mtriple=thumbv8.1m.main-none-eabi -mattr=+mve < %s -o /dev/null 2>&1 | FileCheck %s

; ARM does not implement emitZeroCallUsedRegs, so supportsZeroCallUsedRegs is
; false and the request is reported. Before the query it was dropped silently.
Expand All @@ -8,6 +11,18 @@ define i32 @used_gpr(i32 %x) "zero-call-used-regs"="used-gpr" {
ret i32 %x
}

; Register classification must not enable clearing on a target that has no
; emitter, even when it can identify the requested GPR or argument registers.
; CHECK: error: {{.*}}in function all_gpr i32 (i32): "zero-call-used-regs" is not supported by this target
define i32 @all_gpr(i32 %x) "zero-call-used-regs"="all-gpr" {
ret i32 %x
}

; CHECK: error: {{.*}}in function all_arg i32 (i32): "zero-call-used-regs" is not supported by this target
define i32 @all_arg(i32 %x) "zero-call-used-regs"="all-arg" {
ret i32 %x
}

; CHECK: error: {{.*}}in function all i32 (i32): "zero-call-used-regs" is not supported by this target
define i32 @all(i32 %x) "zero-call-used-regs"="all" {
ret i32 %x
Expand Down
75 changes: 75 additions & 0 deletions llvm/unittests/Target/ARM/ARMRegisterInfoTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
// 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
//
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/ARMMCTargetDesc.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "gtest/gtest.h"

using namespace llvm;

TEST(ARMRegisterInfoTest, SwiftArgumentAttributes) {
LLVMInitializeARMTargetInfo();
LLVMInitializeARMTarget();
LLVMInitializeARMTargetMC();

// Exercise APCS, AAPCS and AAPCS-VFP, including FastCC's APCS delegation.
for (const char *TripleName :
{"armv7-unknown-linux-gnueabi", "armv7-unknown-linux-gnueabihf",
"armv7-apple-ios"}) {
Triple TT(TripleName);
std::string Error;
const Target *T = TargetRegistry::lookupTarget(TT, Error);
ASSERT_NE(T, nullptr) << Error;
TargetOptions Options;
std::unique_ptr<TargetMachine> TM(T->createTargetMachine(
TT, "cortex-a9", "+neon", Options, std::nullopt));
ASSERT_NE(TM, nullptr);

for (CallingConv::ID CC : {CallingConv::C, CallingConv::Fast,
CallingConv::Swift, CallingConv::SwiftTail}) {
for (bool HasSelf : {false, true}) {
for (bool HasError : {false, true}) {
SCOPED_TRACE(testing::Message()
<< TripleName << " CC=" << CC << " self=" << HasSelf
<< " error=" << HasError);
LLVMContext Context;
Module M("test", Context);
M.setTargetTriple(TT);
M.setDataLayout(TM->createDataLayout());
Type *PtrTy = PointerType::getUnqual(Context);
Function *F =
Function::Create(FunctionType::get(Type::getVoidTy(Context),
{PtrTy, PtrTy}, false),
GlobalValue::ExternalLinkage, "f", M);
F->setCallingConv(CC);
if (HasSelf)
F->addParamAttr(0, Attribute::SwiftSelf);
if (HasError)
F->addParamAttr(1, Attribute::SwiftError);

MachineModuleInfo MMI(TM.get());
MachineFunction &MF = MMI.getOrCreateMachineFunction(*F);
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
bool IsSwift =
CC == CallingConv::Swift || CC == CallingConv::SwiftTail;
EXPECT_EQ(TRI.isArgumentRegister(MF, ARM::R10), IsSwift || HasSelf);
EXPECT_EQ(TRI.isArgumentRegister(MF, ARM::R8), IsSwift || HasError);
EXPECT_TRUE(TRI.isArgumentRegister(MF, ARM::R0));
EXPECT_FALSE(TRI.isArgumentRegister(MF, ARM::R9));
}
}
}
}
}
1 change: 1 addition & 0 deletions llvm/unittests/Target/ARM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(LLVM_LINK_COMPONENTS
)

add_llvm_target_unittest(ARMTests
ARMRegisterInfoTest.cpp
MachineInstrTest.cpp
InstSizes.cpp
ARMSelectionDAGTest.cpp
Expand Down