From be4a1dfbf93dcb837e06bb619b934ed8cf9fd224 Mon Sep 17 00:00:00 2001 From: Kai Nacke Date: Fri, 16 Sep 2022 23:55:53 +0000 Subject: [PATCH] [PowerPC] Extend GlobalISel implementation to emit and/or/xor. Adds some more code to GlobalISel to enable instruction selection for and/or/xor. - Makes G_IMPLICIT_DEF, G_CONSTANT, G_AND, G_OR, G_XOR legal for 64bit register size. - Implement lowerReturn in CallLowering - Provides mapping of the operands to register banks. - Adds register info to G_COPY operands. The utility functions are all only implemented so far to support this use case. Especially the functions in PPCGenRegisterBankInfo.def are too simple for general use. Reviewed By: nemanjai, shchenz, amyk Differential Revision: https://reviews.llvm.org/D127530 --- llvm/lib/Target/PowerPC/GISel/PPCCallLowering.cpp | 74 ++++++++++++++++++++-- .../PowerPC/GISel/PPCGenRegisterBankInfo.def | 62 ++++++++++++++++++ .../PowerPC/GISel/PPCInstructionSelector.cpp | 44 +++++++++++++ llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp | 9 +++ .../Target/PowerPC/GISel/PPCRegisterBankInfo.cpp | 59 +++++++++++++++++ .../lib/Target/PowerPC/GISel/PPCRegisterBankInfo.h | 34 ++++++++++ llvm/lib/Target/PowerPC/GISel/PPCRegisterBanks.td | 2 +- .../CodeGen/PowerPC/GlobalISel/ppc-isel-logical.ll | 70 ++++++++++++++++++++ 8 files changed, 347 insertions(+), 7 deletions(-) create mode 100644 llvm/lib/Target/PowerPC/GISel/PPCGenRegisterBankInfo.def create mode 100644 llvm/test/CodeGen/PowerPC/GlobalISel/ppc-isel-logical.ll diff --git a/llvm/lib/Target/PowerPC/GISel/PPCCallLowering.cpp b/llvm/lib/Target/PowerPC/GISel/PPCCallLowering.cpp index b71d59e..3913ede 100644 --- a/llvm/lib/Target/PowerPC/GISel/PPCCallLowering.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCCallLowering.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "PPCCallLowering.h" +#include "PPCCallingConv.h" #include "PPCISelLowering.h" #include "PPCSubtarget.h" #include "PPCTargetMachine.h" @@ -27,6 +28,45 @@ using namespace llvm; +namespace { + +struct OutgoingArgHandler : public CallLowering::OutgoingValueHandler { + OutgoingArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder MIB) + : OutgoingValueHandler(MIRBuilder, MRI), MIB(MIB) {} + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign VA) override; + void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy, + MachinePointerInfo &MPO, CCValAssign &VA) override; + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO, + ISD::ArgFlagsTy Flags) override; + + MachineInstrBuilder MIB; +}; +} // namespace + +void OutgoingArgHandler::assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign VA) { + MIB.addUse(PhysReg, RegState::Implicit); + Register ExtReg = extendRegister(ValVReg, VA); + MIRBuilder.buildCopy(PhysReg, ExtReg); +} + +void OutgoingArgHandler::assignValueToAddress(Register ValVReg, Register Addr, + LLT MemTy, + MachinePointerInfo &MPO, + CCValAssign &VA) { + llvm_unreachable("unimplemented"); +} + +Register OutgoingArgHandler::getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO, + ISD::ArgFlagsTy Flags) { + llvm_unreachable("unimplemented"); +} + PPCCallLowering::PPCCallLowering(const PPCTargetLowering &TLI) : CallLowering(&TLI) {} @@ -34,13 +74,35 @@ bool PPCCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, ArrayRef VRegs, FunctionLoweringInfo &FLI, Register SwiftErrorVReg) const { - assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) && - "Return value without a vreg"); - if (VRegs.size() > 0) - return false; + auto MIB = MIRBuilder.buildInstrNoInsert(PPC::BLR8); + bool Success = true; + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &DL = F.getParent()->getDataLayout(); + if (!VRegs.empty()) { + // Setup the information about the return value. + ArgInfo OrigArg{VRegs, Val->getType(), 0}; + setArgFlags(OrigArg, AttributeList::ReturnIndex, DL, F); + + // Split the return value into consecutive registers if needed. + SmallVector SplitArgs; + splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv()); - MIRBuilder.buildInstr(PPC::BLR8); - return true; + // Use the calling convention callback to determine type and location of + // return value. + OutgoingValueAssigner ArgAssigner(RetCC_PPC); + + // Handler to move the return value into the correct location. + OutgoingArgHandler ArgHandler(MIRBuilder, MRI, MIB); + + // Iterate over all return values, and move them to the assigned location. + Success = determineAndHandleAssignments(ArgHandler, ArgAssigner, SplitArgs, + MIRBuilder, F.getCallingConv(), + F.isVarArg()); + } + MIRBuilder.insertInstr(MIB); + return Success; } bool PPCCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, diff --git a/llvm/lib/Target/PowerPC/GISel/PPCGenRegisterBankInfo.def b/llvm/lib/Target/PowerPC/GISel/PPCGenRegisterBankInfo.def new file mode 100644 index 0000000..471af5d --- /dev/null +++ b/llvm/lib/Target/PowerPC/GISel/PPCGenRegisterBankInfo.def @@ -0,0 +1,62 @@ +//===- PPCGenRegisterBankInfo.def -------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines all the static objects used by PPCRegisterBankInfo. +/// \todo This should be generated by TableGen, because the logic here can be +/// derived from register bank definition. Not yet implemented. +//===----------------------------------------------------------------------===// + +namespace llvm { +RegisterBankInfo::PartialMapping PPCGenRegisterBankInfo::PartMappings[]{ + /* StartIdx, Length, RegBank */ + // 0: GPR 64-bit value. + {0, 64, PPC::GPRRegBank}, +}; + +// ValueMappings. +// Pointers to the entries in this array are returned by getValueMapping() and +// getCopyMapping(). +// +// The array has the following structure: +// - At index 0 is the invalid entry. +// - After that, the mappings for the register types from PartialMappingIdx +// follow. Each mapping consists of 3 entries, which is needed to cover +// 3-operands instructions. +// - Last, mappings for cross-register bank moves follow. Since COPY has only +// 2 operands, a mapping consists of 2 entries. +RegisterBankInfo::ValueMapping PPCGenRegisterBankInfo::ValMappings[]{ + /* BreakDown, NumBreakDowns */ + // 0: invalid + {nullptr, 0}, + // 1: GPR 64-bit value. + {&PPCGenRegisterBankInfo::PartMappings[PMI_GPR64 - PMI_Min], 1}, + {&PPCGenRegisterBankInfo::PartMappings[PMI_GPR64 - PMI_Min], 1}, + {&PPCGenRegisterBankInfo::PartMappings[PMI_GPR64 - PMI_Min], 1}, +}; + +// TODO Too simple! +const RegisterBankInfo::ValueMapping * +PPCGenRegisterBankInfo::getValueMapping(PartialMappingIdx RBIdx) { + assert(RBIdx != PartialMappingIdx::PMI_None && "No mapping needed for that"); + + unsigned ValMappingIdx = RBIdx - PMI_Min; + + return &ValMappings[1 + 3 * ValMappingIdx]; +} + +// TODO Too simple! +const RegisterBankInfo::ValueMapping * +PPCGenRegisterBankInfo::getCopyMapping(unsigned DstBankID, unsigned SrcBankID, + unsigned Size) { + assert(DstBankID < PPC::NumRegisterBanks && "Invalid bank ID"); + assert(SrcBankID < PPC::NumRegisterBanks && "Invalid bank ID"); + + return &ValMappings[1]; +} + +} // namespace llvm diff --git a/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp b/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp index 0cd8350..93bbb9c 100644 --- a/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp @@ -75,7 +75,51 @@ PPCInstructionSelector::PPCInstructionSelector(const PPCTargetMachine &TM, { } +static const TargetRegisterClass *getRegClass(LLT Ty, const RegisterBank *RB) { + if (RB->getID() == PPC::GPRRegBankID) { + if (Ty.getSizeInBits() == 64) + return &PPC::G8RCRegClass; + } + + llvm_unreachable("Unknown RegBank!"); +} + +static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII, + MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) { + Register DstReg = I.getOperand(0).getReg(); + + if (DstReg.isPhysical()) + return true; + + const RegisterBank *DstRegBank = RBI.getRegBank(DstReg, MRI, TRI); + const TargetRegisterClass *DstRC = + getRegClass(MRI.getType(DstReg), DstRegBank); + + // No need to constrain SrcReg. It will get constrained when we hit another of + // its use or its defs. + // Copies do not have constraints. + if (!RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + + return true; +} + bool PPCInstructionSelector::select(MachineInstr &I) { + auto &MBB = *I.getParent(); + auto &MF = *MBB.getParent(); + auto &MRI = MF.getRegInfo(); + + if (!isPreISelGenericOpcode(I.getOpcode())) { + if (I.isCopy()) + return selectCopy(I, TII, MRI, TRI, RBI); + + return true; + } + if (selectImpl(I, *CoverageInfo)) return true; return false; diff --git a/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp b/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp index 5d196df..be56b6f 100644 --- a/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp @@ -18,5 +18,14 @@ using namespace llvm; using namespace LegalizeActions; PPCLegalizerInfo::PPCLegalizerInfo(const PPCSubtarget &ST) { + using namespace TargetOpcode; + const LLT S64 = LLT::scalar(64); + getActionDefinitionsBuilder(G_IMPLICIT_DEF).legalFor({S64}); + getActionDefinitionsBuilder(G_CONSTANT) + .legalFor({S64}) + .clampScalar(0, S64, S64); + getActionDefinitionsBuilder({G_AND, G_OR, G_XOR}) + .legalFor({S64}) + .clampScalar(0, S64, S64); getLegacyLegalizerInfo().computeTables(); } diff --git a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp index 58165fca..2eb9ec2 100644 --- a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp @@ -21,6 +21,65 @@ #define GET_TARGET_REGBANK_IMPL #include "PPCGenRegisterBank.inc" +// This file will be TableGen'ed at some point. +#include "PPCGenRegisterBankInfo.def" + using namespace llvm; PPCRegisterBankInfo::PPCRegisterBankInfo(const TargetRegisterInfo &TRI) {} + +const RegisterBank & +PPCRegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, + LLT Ty) const { + switch (RC.getID()) { + case PPC::G8RCRegClassID: + case PPC::G8RC_NOX0RegClassID: + case PPC::G8RC_and_G8RC_NOX0RegClassID: + return getRegBank(PPC::GPRRegBankID); + default: + llvm_unreachable("Unexpected register class"); + } +} + +const RegisterBankInfo::InstructionMapping & +PPCRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { + const unsigned Opc = MI.getOpcode(); + + // Try the default logic for non-generic instructions that are either copies + // or already have some operands assigned to banks. + if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { + const RegisterBankInfo::InstructionMapping &Mapping = + getInstrMappingImpl(MI); + if (Mapping.isValid()) + return Mapping; + } + + unsigned NumOperands = MI.getNumOperands(); + const ValueMapping *OperandsMapping = nullptr; + unsigned Cost = 1; + unsigned MappingID = DefaultMappingID; + + switch (Opc) { + // Bitwise ops. + case TargetOpcode::G_AND: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: + assert(NumOperands <= 3 && + "This code is for instructions with 3 or less operands"); + OperandsMapping = getValueMapping(PMI_GPR64); + break; + case TargetOpcode::G_CONSTANT: + OperandsMapping = getOperandsMapping({getValueMapping(PMI_GPR64), nullptr}); + break; + default: + return getInvalidInstructionMapping(); + } + + return getInstructionMapping(MappingID, Cost, OperandsMapping, NumOperands); +} + +RegisterBankInfo::InstructionMappings +PPCRegisterBankInfo::getInstrAlternativeMappings(const MachineInstr &MI) const { + // TODO Implement. + return RegisterBankInfo::getInstrAlternativeMappings(MI); +} diff --git a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.h b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.h index 31a4c52..11bdd98 100644 --- a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.h +++ b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.h @@ -26,6 +26,32 @@ class TargetRegisterInfo; class PPCGenRegisterBankInfo : public RegisterBankInfo { protected: + enum PartialMappingIdx { + PMI_None = -1, + PMI_GPR64 = 1, + PMI_Min = PMI_GPR64, + }; + + static RegisterBankInfo::PartialMapping PartMappings[]; + static RegisterBankInfo::ValueMapping ValMappings[]; + static PartialMappingIdx BankIDToCopyMapIdx[]; + + /// Get the pointer to the ValueMapping representing the RegisterBank + /// at \p RBIdx. + /// + /// The returned mapping works for instructions with the same kind of + /// operands for up to 3 operands. + /// + /// \pre \p RBIdx != PartialMappingIdx::None + static const RegisterBankInfo::ValueMapping * + getValueMapping(PartialMappingIdx RBIdx); + + /// Get the pointer to the ValueMapping of the operands of a copy + /// instruction from the \p SrcBankID register bank to the \p DstBankID + /// register bank with a size of \p Size. + static const RegisterBankInfo::ValueMapping * + getCopyMapping(unsigned DstBankID, unsigned SrcBankID, unsigned Size); + #define GET_TARGET_REGBANK_CLASS #include "PPCGenRegisterBank.inc" }; @@ -33,6 +59,14 @@ protected: class PPCRegisterBankInfo final : public PPCGenRegisterBankInfo { public: PPCRegisterBankInfo(const TargetRegisterInfo &TRI); + + const RegisterBank &getRegBankFromRegClass(const TargetRegisterClass &RC, + LLT Ty) const override; + const InstructionMapping & + getInstrMapping(const MachineInstr &MI) const override; + + InstructionMappings + getInstrAlternativeMappings(const MachineInstr &MI) const override; }; } // namespace llvm diff --git a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBanks.td b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBanks.td index 0e8a4b7..771d33e 100644 --- a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBanks.td +++ b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBanks.td @@ -12,4 +12,4 @@ //===----------------------------------------------------------------------===// /// General Purpose Registers -def GPRRegBank : RegisterBank<"GPR", [G8RC]>; +def GPRRegBank : RegisterBank<"GPR", [G8RC, G8RC_NOX0]>; diff --git a/llvm/test/CodeGen/PowerPC/GlobalISel/ppc-isel-logical.ll b/llvm/test/CodeGen/PowerPC/GlobalISel/ppc-isel-logical.ll new file mode 100644 index 0000000..f133592 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/GlobalISel/ppc-isel-logical.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple ppc64le-linux -ppc-asm-full-reg-names -global-isel -o - < %s \ +; RUN: | FileCheck %s + +define i64 @test_andi64(i64 %a, i64 %b) { +; CHECK-LABEL: test_andi64: +; CHECK: # %bb.0: +; CHECK-NEXT: and r3, r3, r4 +; CHECK-NEXT: blr + %res = and i64 %a, %b + ret i64 %res +} + +define i64 @test_nandi64(i64 %a, i64 %b) { +; CHECK-LABEL: test_nandi64: +; CHECK: # %bb.0: +; CHECK-NEXT: nand r3, r3, r4 +; CHECK-NEXT: blr + %and = and i64 %a, %b + %neg = xor i64 %and, -1 + ret i64 %neg +} + +define i64 @test_andci64(i64 %a, i64 %b) { +; CHECK-LABEL: test_andci64: +; CHECK: # %bb.0: +; CHECK-NEXT: andc r3, r3, r4 +; CHECK-NEXT: blr + %neg = xor i64 %b, -1 + %and = and i64 %a, %neg + ret i64 %and +} + +define i64 @test_ori64(i64 %a, i64 %b) { +; CHECK-LABEL: test_ori64: +; CHECK: # %bb.0: +; CHECK-NEXT: or r3, r3, r4 +; CHECK-NEXT: blr + %res = or i64 %a, %b + ret i64 %res +} + +define i64 @test_orci64(i64 %a, i64 %b) { +; CHECK-LABEL: test_orci64: +; CHECK: # %bb.0: +; CHECK-NEXT: orc r3, r3, r4 +; CHECK-NEXT: blr + %neg = xor i64 %b, -1 + %or = or i64 %a, %neg + ret i64 %or +} + +define i64 @test_nori64(i64 %a, i64 %b) { +; CHECK-LABEL: test_nori64: +; CHECK: # %bb.0: +; CHECK-NEXT: nor r3, r3, r4 +; CHECK-NEXT: blr + %or = or i64 %a, %b + %neg = xor i64 %or, -1 + ret i64 %neg +} + +define i64 @test_xori64(i64 %a, i64 %b) { +; CHECK-LABEL: test_xori64: +; CHECK: # %bb.0: +; CHECK-NEXT: xor r3, r3, r4 +; CHECK-NEXT: blr + %res = xor i64 %a, %b + ret i64 %res +} -- 2.7.4