| //===- PPCRegisterBankInfo.cpp --------------------------------------------===// |
| // |
| // 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 implements the targeting of the RegisterBankInfo class for |
| /// PowerPC. |
| //===----------------------------------------------------------------------===// |
| |
| #include "PPCRegisterBankInfo.h" |
| #include "PPCRegisterInfo.h" |
| #include "llvm/CodeGen/MachineFunction.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/Support/Debug.h" |
| |
| #define DEBUG_TYPE "ppc-reg-bank-info" |
| |
| #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: |
| case PPC::GPRCRegClassID: |
| case PPC::GPRC_NOR0RegClassID: |
| case PPC::GPRC_and_GPRC_NOR0RegClassID: |
| return getRegBank(PPC::GPRRegBankID); |
| case PPC::VSFRCRegClassID: |
| case PPC::SPILLTOVSRRC_and_VSFRCRegClassID: |
| case PPC::SPILLTOVSRRC_and_VFRCRegClassID: |
| case PPC::SPILLTOVSRRC_and_F4RCRegClassID: |
| case PPC::F8RCRegClassID: |
| case PPC::VFRCRegClassID: |
| case PPC::VSSRCRegClassID: |
| case PPC::F4RCRegClassID: |
| return getRegBank(PPC::FPRRegBankID); |
| case PPC::CRRCRegClassID: |
| case PPC::CRBITRCRegClassID: |
| return getRegBank(PPC::CRRegBankID); |
| 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; |
| } |
| |
| const MachineFunction &MF = *MI.getParent()->getParent(); |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| const TargetSubtargetInfo &STI = MF.getSubtarget(); |
| const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); |
| |
| unsigned NumOperands = MI.getNumOperands(); |
| const ValueMapping *OperandsMapping = nullptr; |
| unsigned Cost = 1; |
| unsigned MappingID = DefaultMappingID; |
| |
| switch (Opc) { |
| // Arithmetic ops. |
| case TargetOpcode::G_ADD: |
| case TargetOpcode::G_SUB: |
| // Bitwise ops. |
| case TargetOpcode::G_AND: |
| case TargetOpcode::G_OR: |
| case TargetOpcode::G_XOR: |
| // Extension ops. |
| case TargetOpcode::G_SEXT: |
| case TargetOpcode::G_ZEXT: |
| case TargetOpcode::G_ANYEXT: |
| assert(NumOperands <= 3 && |
| "This code is for instructions with 3 or less operands"); |
| OperandsMapping = getValueMapping(PMI_GPR64); |
| break; |
| case TargetOpcode::G_FADD: |
| case TargetOpcode::G_FSUB: |
| case TargetOpcode::G_FMUL: |
| case TargetOpcode::G_FDIV: { |
| Register SrcReg = MI.getOperand(1).getReg(); |
| unsigned Size = getSizeInBits(SrcReg, MRI, TRI); |
| |
| assert((Size == 32 || Size == 64) && "Unsupported floating point types!\n"); |
| OperandsMapping = getValueMapping(Size == 32 ? PMI_FPR32 : PMI_FPR64); |
| break; |
| } |
| case TargetOpcode::G_FCMP: { |
| unsigned CmpSize = MRI.getType(MI.getOperand(2).getReg()).getSizeInBits(); |
| |
| OperandsMapping = getOperandsMapping( |
| {getValueMapping(PMI_CR), nullptr, |
| getValueMapping(CmpSize == 32 ? PMI_FPR32 : PMI_FPR64), |
| getValueMapping(CmpSize == 32 ? PMI_FPR32 : PMI_FPR64)}); |
| break; |
| } |
| case TargetOpcode::G_CONSTANT: |
| OperandsMapping = getOperandsMapping({getValueMapping(PMI_GPR64), nullptr}); |
| break; |
| case TargetOpcode::G_FPTOUI: |
| case TargetOpcode::G_FPTOSI: { |
| Register SrcReg = MI.getOperand(1).getReg(); |
| unsigned Size = getSizeInBits(SrcReg, MRI, TRI); |
| |
| OperandsMapping = getOperandsMapping( |
| {getValueMapping(PMI_GPR64), |
| getValueMapping(Size == 32 ? PMI_FPR32 : PMI_FPR64)}); |
| break; |
| } |
| case TargetOpcode::G_UITOFP: |
| case TargetOpcode::G_SITOFP: { |
| Register SrcReg = MI.getOperand(0).getReg(); |
| unsigned Size = getSizeInBits(SrcReg, MRI, TRI); |
| |
| OperandsMapping = |
| getOperandsMapping({getValueMapping(Size == 32 ? PMI_FPR32 : PMI_FPR64), |
| getValueMapping(PMI_GPR64)}); |
| break; |
| } |
| case TargetOpcode::G_LOAD: { |
| unsigned Size = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); |
| // Check if that load feeds fp instructions. |
| if (any_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()), |
| [&](const MachineInstr &UseMI) { |
| // If we have at least one direct use in a FP instruction, |
| // assume this was a floating point load in the IR. If it was |
| // not, we would have had a bitcast before reaching that |
| // instruction. |
| // |
| // Int->FP conversion operations are also captured in |
| // onlyDefinesFP(). |
| return onlyUsesFP(UseMI, MRI, TRI); |
| })) |
| OperandsMapping = getOperandsMapping( |
| {getValueMapping(Size == 64 ? PMI_FPR64 : PMI_FPR32), |
| getValueMapping(PMI_GPR64)}); |
| else |
| OperandsMapping = getOperandsMapping( |
| {getValueMapping(Size == 64 ? PMI_GPR64 : PMI_GPR32), |
| getValueMapping(PMI_GPR64)}); |
| break; |
| } |
| case TargetOpcode::G_STORE: { |
| // Check if the store is fed by fp instructions. |
| MachineInstr *DefMI = MRI.getVRegDef(MI.getOperand(0).getReg()); |
| unsigned Size = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); |
| if (onlyDefinesFP(*DefMI, MRI, TRI)) |
| OperandsMapping = getOperandsMapping( |
| {getValueMapping(Size == 64 ? PMI_FPR64 : PMI_FPR32), |
| getValueMapping(PMI_GPR64)}); |
| else |
| OperandsMapping = getOperandsMapping( |
| {getValueMapping(Size == 64 ? PMI_GPR64 : PMI_GPR32), |
| getValueMapping(PMI_GPR64)}); |
| break; |
| } |
| case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: { |
| // FIXME: We have to check every operand in this MI and compute value |
| // mapping accordingly. |
| SmallVector<const ValueMapping *, 8> OpdsMapping(NumOperands); |
| OperandsMapping = getOperandsMapping(OpdsMapping); |
| break; |
| } |
| default: |
| return getInvalidInstructionMapping(); |
| } |
| |
| return getInstructionMapping(MappingID, Cost, OperandsMapping, NumOperands); |
| } |
| |
| /// Returns whether opcode \p Opc is a pre-isel generic floating-point opcode, |
| /// having only floating-point operands. |
| /// FIXME: this is copied from target AArch64. Needs some code refactor here to |
| /// put this function in GlobalISel/Utils.cpp. |
| static bool isPreISelGenericFloatingPointOpcode(unsigned Opc) { |
| switch (Opc) { |
| case TargetOpcode::G_FADD: |
| case TargetOpcode::G_FSUB: |
| case TargetOpcode::G_FMUL: |
| case TargetOpcode::G_FMA: |
| case TargetOpcode::G_FDIV: |
| case TargetOpcode::G_FCONSTANT: |
| case TargetOpcode::G_FPEXT: |
| case TargetOpcode::G_FPTRUNC: |
| case TargetOpcode::G_FCEIL: |
| case TargetOpcode::G_FFLOOR: |
| case TargetOpcode::G_FNEARBYINT: |
| case TargetOpcode::G_FNEG: |
| case TargetOpcode::G_FCOS: |
| case TargetOpcode::G_FSIN: |
| case TargetOpcode::G_FLOG10: |
| case TargetOpcode::G_FLOG: |
| case TargetOpcode::G_FLOG2: |
| case TargetOpcode::G_FSQRT: |
| case TargetOpcode::G_FABS: |
| case TargetOpcode::G_FEXP: |
| case TargetOpcode::G_FRINT: |
| case TargetOpcode::G_INTRINSIC_TRUNC: |
| case TargetOpcode::G_INTRINSIC_ROUND: |
| case TargetOpcode::G_FMAXNUM: |
| case TargetOpcode::G_FMINNUM: |
| case TargetOpcode::G_FMAXIMUM: |
| case TargetOpcode::G_FMINIMUM: |
| return true; |
| } |
| return false; |
| } |
| |
| /// \returns true if a given intrinsic \p ID only uses and defines FPRs. |
| static bool isFPIntrinsic(unsigned ID) { |
| // TODO: Add more intrinsics. |
| return false; |
| } |
| |
| /// FIXME: this is copied from target AArch64. Needs some code refactor here to |
| /// put this function in class RegisterBankInfo. |
| bool PPCRegisterBankInfo::hasFPConstraints(const MachineInstr &MI, |
| const MachineRegisterInfo &MRI, |
| const TargetRegisterInfo &TRI, |
| unsigned Depth) const { |
| unsigned Op = MI.getOpcode(); |
| if (Op == TargetOpcode::G_INTRINSIC && isFPIntrinsic(MI.getIntrinsicID())) |
| return true; |
| |
| // Do we have an explicit floating point instruction? |
| if (isPreISelGenericFloatingPointOpcode(Op)) |
| return true; |
| |
| // No. Check if we have a copy-like instruction. If we do, then we could |
| // still be fed by floating point instructions. |
| if (Op != TargetOpcode::COPY && !MI.isPHI() && |
| !isPreISelGenericOptimizationHint(Op)) |
| return false; |
| |
| // Check if we already know the register bank. |
| auto *RB = getRegBank(MI.getOperand(0).getReg(), MRI, TRI); |
| if (RB == &PPC::FPRRegBank) |
| return true; |
| if (RB == &PPC::GPRRegBank) |
| return false; |
| |
| // We don't know anything. |
| // |
| // If we have a phi, we may be able to infer that it will be assigned a FPR |
| // based off of its inputs. |
| if (!MI.isPHI() || Depth > MaxFPRSearchDepth) |
| return false; |
| |
| return any_of(MI.explicit_uses(), [&](const MachineOperand &Op) { |
| return Op.isReg() && |
| onlyDefinesFP(*MRI.getVRegDef(Op.getReg()), MRI, TRI, Depth + 1); |
| }); |
| } |
| |
| /// FIXME: this is copied from target AArch64. Needs some code refactor here to |
| /// put this function in class RegisterBankInfo. |
| bool PPCRegisterBankInfo::onlyUsesFP(const MachineInstr &MI, |
| const MachineRegisterInfo &MRI, |
| const TargetRegisterInfo &TRI, |
| unsigned Depth) const { |
| switch (MI.getOpcode()) { |
| case TargetOpcode::G_FPTOSI: |
| case TargetOpcode::G_FPTOUI: |
| case TargetOpcode::G_FCMP: |
| case TargetOpcode::G_LROUND: |
| case TargetOpcode::G_LLROUND: |
| return true; |
| default: |
| break; |
| } |
| return hasFPConstraints(MI, MRI, TRI, Depth); |
| } |
| |
| /// FIXME: this is copied from target AArch64. Needs some code refactor here to |
| /// put this function in class RegisterBankInfo. |
| bool PPCRegisterBankInfo::onlyDefinesFP(const MachineInstr &MI, |
| const MachineRegisterInfo &MRI, |
| const TargetRegisterInfo &TRI, |
| unsigned Depth) const { |
| switch (MI.getOpcode()) { |
| case TargetOpcode::G_SITOFP: |
| case TargetOpcode::G_UITOFP: |
| return true; |
| default: |
| break; |
| } |
| return hasFPConstraints(MI, MRI, TRI, Depth); |
| } |
| |
| RegisterBankInfo::InstructionMappings |
| PPCRegisterBankInfo::getInstrAlternativeMappings(const MachineInstr &MI) const { |
| // TODO Implement. |
| return RegisterBankInfo::getInstrAlternativeMappings(MI); |
| } |