| //===- X86RegisterBankInfo.cpp -----------------------------------*- 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 implements the targeting of the RegisterBankInfo class for X86. |
| /// \todo This should be generated by TableGen. |
| //===----------------------------------------------------------------------===// |
| |
| #include "X86RegisterBankInfo.h" |
| #include "X86InstrInfo.h" |
| #include "X86Subtarget.h" |
| #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" |
| #include "llvm/CodeGen/GlobalISel/Utils.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/RegisterBank.h" |
| #include "llvm/CodeGen/RegisterBankInfo.h" |
| #include "llvm/CodeGen/TargetRegisterInfo.h" |
| #include "llvm/IR/IntrinsicsX86.h" |
| |
| #define GET_TARGET_REGBANK_IMPL |
| #include "X86GenRegisterBank.inc" |
| |
| using namespace llvm; |
| // This file will be TableGen'ed at some point. |
| #define GET_TARGET_REGBANK_INFO_IMPL |
| #include "X86GenRegisterBankInfo.def" |
| |
| X86RegisterBankInfo::X86RegisterBankInfo(const TargetRegisterInfo &TRI) { |
| |
| // validate RegBank initialization. |
| const RegisterBank &RBGPR = getRegBank(X86::GPRRegBankID); |
| (void)RBGPR; |
| assert(&X86::GPRRegBank == &RBGPR && "Incorrect RegBanks inizalization."); |
| |
| // The GPR register bank is fully defined by all the registers in |
| // GR64 + its subclasses. |
| assert(RBGPR.covers(*TRI.getRegClass(X86::GR64RegClassID)) && |
| "Subclass not added?"); |
| assert(getMaximumSize(RBGPR.getID()) == 64 && |
| "GPRs should hold up to 64-bit"); |
| } |
| |
| const RegisterBank & |
| X86RegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, |
| LLT) const { |
| |
| if (X86::GR8RegClass.hasSubClassEq(&RC) || |
| X86::GR16RegClass.hasSubClassEq(&RC) || |
| X86::GR32RegClass.hasSubClassEq(&RC) || |
| X86::GR64RegClass.hasSubClassEq(&RC) || |
| X86::LOW32_ADDR_ACCESSRegClass.hasSubClassEq(&RC) || |
| X86::LOW32_ADDR_ACCESS_RBPRegClass.hasSubClassEq(&RC)) |
| return getRegBank(X86::GPRRegBankID); |
| |
| if (X86::FR32XRegClass.hasSubClassEq(&RC) || |
| X86::FR64XRegClass.hasSubClassEq(&RC) || |
| X86::VR128XRegClass.hasSubClassEq(&RC) || |
| X86::VR256XRegClass.hasSubClassEq(&RC) || |
| X86::VR512RegClass.hasSubClassEq(&RC)) |
| return getRegBank(X86::VECRRegBankID); |
| |
| if (X86::RFP80RegClass.hasSubClassEq(&RC) || |
| X86::RFP32RegClass.hasSubClassEq(&RC) || |
| X86::RFP64RegClass.hasSubClassEq(&RC)) |
| return getRegBank(X86::PSRRegBankID); |
| |
| llvm_unreachable("Unsupported register kind yet."); |
| } |
| |
| // \returns true if a given intrinsic only uses and defines FPRs. |
| static bool isFPIntrinsic(const MachineRegisterInfo &MRI, |
| const MachineInstr &MI) { |
| // TODO: Add more intrinsics. |
| switch (cast<GIntrinsic>(MI).getIntrinsicID()) { |
| default: |
| return false; |
| // SSE1 |
| case Intrinsic::x86_sse_rcp_ss: |
| case Intrinsic::x86_sse_rcp_ps: |
| case Intrinsic::x86_sse_rsqrt_ss: |
| case Intrinsic::x86_sse_rsqrt_ps: |
| case Intrinsic::x86_sse_min_ss: |
| case Intrinsic::x86_sse_min_ps: |
| case Intrinsic::x86_sse_max_ss: |
| case Intrinsic::x86_sse_max_ps: |
| return true; |
| } |
| return false; |
| } |
| |
| bool X86RegisterBankInfo::hasFPConstraints(const MachineInstr &MI, |
| const MachineRegisterInfo &MRI, |
| const TargetRegisterInfo &TRI, |
| unsigned Depth) const { |
| unsigned Op = MI.getOpcode(); |
| if (Op == TargetOpcode::G_INTRINSIC && isFPIntrinsic(MRI, MI)) |
| 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 == &getRegBank(X86::PSRRegBankID)) |
| return true; |
| if (RB == &getRegBank(X86::GPRRegBankID)) |
| return false; |
| |
| // We don't know anything. |
| // |
| // If we have a phi, we may be able to infer that it will be assigned a fp |
| // type 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); |
| }); |
| } |
| |
| bool X86RegisterBankInfo::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: |
| case TargetOpcode::G_INTRINSIC_TRUNC: |
| case TargetOpcode::G_INTRINSIC_ROUND: |
| return true; |
| default: |
| break; |
| } |
| return hasFPConstraints(MI, MRI, TRI, Depth); |
| } |
| |
| bool X86RegisterBankInfo::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); |
| } |
| |
| X86GenRegisterBankInfo::PartialMappingIdx |
| X86GenRegisterBankInfo::getPartialMappingIdx(const MachineInstr &MI, |
| const LLT &Ty, bool isFP) { |
| const MachineFunction *MF = MI.getMF(); |
| const X86Subtarget *ST = &MF->getSubtarget<X86Subtarget>(); |
| bool HasSSE1 = ST->hasSSE1(); |
| bool HasSSE2 = ST->hasSSE2(); |
| // 80 bits is only generated for X87 floating points. |
| if (Ty.getSizeInBits() == 80) |
| isFP = true; |
| if ((Ty.isScalar() && !isFP) || Ty.isPointer()) { |
| switch (Ty.getSizeInBits()) { |
| case 1: |
| case 8: |
| return PMI_GPR8; |
| case 16: |
| return PMI_GPR16; |
| case 32: |
| return PMI_GPR32; |
| case 64: |
| return PMI_GPR64; |
| case 128: |
| return PMI_VEC128; |
| break; |
| default: |
| llvm_unreachable("Unsupported register size."); |
| } |
| } else if (Ty.isScalar()) { |
| switch (Ty.getSizeInBits()) { |
| case 32: |
| return HasSSE1 ? PMI_FP32 : PMI_PSR32; |
| case 64: |
| return HasSSE2 ? PMI_FP64 : PMI_PSR64; |
| case 128: |
| return PMI_VEC128; |
| case 80: |
| return PMI_PSR80; |
| default: |
| llvm_unreachable("Unsupported register size."); |
| } |
| } else { |
| switch (Ty.getSizeInBits()) { |
| case 128: |
| return PMI_VEC128; |
| case 256: |
| return PMI_VEC256; |
| case 512: |
| return PMI_VEC512; |
| default: |
| llvm_unreachable("Unsupported register size."); |
| } |
| } |
| |
| return PMI_None; |
| } |
| |
| void X86RegisterBankInfo::getInstrPartialMappingIdxs( |
| const MachineInstr &MI, const MachineRegisterInfo &MRI, const bool isFP, |
| SmallVectorImpl<PartialMappingIdx> &OpRegBankIdx) { |
| |
| unsigned NumOperands = MI.getNumOperands(); |
| for (unsigned Idx = 0; Idx < NumOperands; ++Idx) { |
| auto &MO = MI.getOperand(Idx); |
| if (!MO.isReg() || !MO.getReg()) |
| OpRegBankIdx[Idx] = PMI_None; |
| else |
| OpRegBankIdx[Idx] = |
| getPartialMappingIdx(MI, MRI.getType(MO.getReg()), isFP); |
| } |
| } |
| |
| bool X86RegisterBankInfo::getInstrValueMapping( |
| const MachineInstr &MI, |
| const SmallVectorImpl<PartialMappingIdx> &OpRegBankIdx, |
| SmallVectorImpl<const ValueMapping *> &OpdsMapping) { |
| |
| unsigned NumOperands = MI.getNumOperands(); |
| for (unsigned Idx = 0; Idx < NumOperands; ++Idx) { |
| if (!MI.getOperand(Idx).isReg()) |
| continue; |
| if (!MI.getOperand(Idx).getReg()) |
| continue; |
| |
| auto Mapping = getValueMapping(OpRegBankIdx[Idx], 1); |
| if (!Mapping->isValid()) |
| return false; |
| |
| OpdsMapping[Idx] = Mapping; |
| } |
| return true; |
| } |
| |
| const RegisterBankInfo::InstructionMapping & |
| X86RegisterBankInfo::getSameOperandsMapping(const MachineInstr &MI, |
| bool isFP) const { |
| const MachineFunction &MF = *MI.getParent()->getParent(); |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| |
| unsigned NumOperands = MI.getNumOperands(); |
| LLT Ty = MRI.getType(MI.getOperand(0).getReg()); |
| |
| if (NumOperands != 3 || (Ty != MRI.getType(MI.getOperand(1).getReg())) || |
| (Ty != MRI.getType(MI.getOperand(2).getReg()))) |
| llvm_unreachable("Unsupported operand mapping yet."); |
| |
| auto Mapping = getValueMapping(getPartialMappingIdx(MI, Ty, isFP), 3); |
| return getInstructionMapping(DefaultMappingID, 1, Mapping, NumOperands); |
| } |
| |
| const RegisterBankInfo::InstructionMapping & |
| X86RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { |
| const MachineFunction &MF = *MI.getParent()->getParent(); |
| const TargetSubtargetInfo &STI = MF.getSubtarget(); |
| const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| 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 InstructionMapping &Mapping = getInstrMappingImpl(MI); |
| if (Mapping.isValid()) |
| return Mapping; |
| } |
| |
| switch (Opc) { |
| case TargetOpcode::G_ADD: |
| case TargetOpcode::G_SUB: |
| case TargetOpcode::G_MUL: |
| return getSameOperandsMapping(MI, false); |
| case TargetOpcode::G_FADD: |
| case TargetOpcode::G_FSUB: |
| case TargetOpcode::G_FMUL: |
| case TargetOpcode::G_FDIV: |
| return getSameOperandsMapping(MI, true); |
| case TargetOpcode::G_SHL: |
| case TargetOpcode::G_LSHR: |
| case TargetOpcode::G_ASHR: { |
| unsigned NumOperands = MI.getNumOperands(); |
| LLT Ty = MRI.getType(MI.getOperand(0).getReg()); |
| |
| auto Mapping = getValueMapping(getPartialMappingIdx(MI, Ty, false), 3); |
| return getInstructionMapping(DefaultMappingID, 1, Mapping, NumOperands); |
| } |
| default: |
| break; |
| } |
| |
| unsigned NumOperands = MI.getNumOperands(); |
| SmallVector<PartialMappingIdx, 4> OpRegBankIdx(NumOperands); |
| |
| switch (Opc) { |
| case TargetOpcode::G_FPEXT: |
| case TargetOpcode::G_FPTRUNC: |
| case TargetOpcode::G_FCONSTANT: |
| // Instruction having only floating-point operands (all scalars in |
| // VECRReg) |
| getInstrPartialMappingIdxs(MI, MRI, /* isFP= */ true, OpRegBankIdx); |
| break; |
| case TargetOpcode::G_SITOFP: |
| case TargetOpcode::G_FPTOSI: { |
| // Some of the floating-point instructions have mixed GPR and FP |
| // operands: fine-tune the computed mapping. |
| auto &Op0 = MI.getOperand(0); |
| auto &Op1 = MI.getOperand(1); |
| const LLT Ty0 = MRI.getType(Op0.getReg()); |
| const LLT Ty1 = MRI.getType(Op1.getReg()); |
| |
| bool FirstArgIsFP = Opc == TargetOpcode::G_SITOFP; |
| bool SecondArgIsFP = Opc == TargetOpcode::G_FPTOSI; |
| OpRegBankIdx[0] = getPartialMappingIdx(MI, Ty0, /* isFP= */ FirstArgIsFP); |
| OpRegBankIdx[1] = getPartialMappingIdx(MI, Ty1, /* isFP= */ SecondArgIsFP); |
| break; |
| } |
| case TargetOpcode::G_FCMP: { |
| LLT Ty1 = MRI.getType(MI.getOperand(2).getReg()); |
| LLT Ty2 = MRI.getType(MI.getOperand(3).getReg()); |
| (void)Ty2; |
| assert(Ty1.getSizeInBits() == Ty2.getSizeInBits() && |
| "Mismatched operand sizes for G_FCMP"); |
| |
| unsigned Size = Ty1.getSizeInBits(); |
| (void)Size; |
| assert((Size == 32 || Size == 64) && "Unsupported size for G_FCMP"); |
| |
| auto FpRegBank = getPartialMappingIdx(MI, Ty1, /* isFP= */ true); |
| OpRegBankIdx = {PMI_GPR8, |
| /* Predicate */ PMI_None, FpRegBank, FpRegBank}; |
| break; |
| } |
| case TargetOpcode::G_TRUNC: |
| case TargetOpcode::G_ANYEXT: { |
| auto &Op0 = MI.getOperand(0); |
| auto &Op1 = MI.getOperand(1); |
| const LLT Ty0 = MRI.getType(Op0.getReg()); |
| const LLT Ty1 = MRI.getType(Op1.getReg()); |
| |
| bool isFPTrunc = (Ty0.getSizeInBits() == 32 || Ty0.getSizeInBits() == 64) && |
| Ty1.getSizeInBits() == 128 && Opc == TargetOpcode::G_TRUNC; |
| bool isFPAnyExt = |
| Ty0.getSizeInBits() == 128 && |
| (Ty1.getSizeInBits() == 32 || Ty1.getSizeInBits() == 64) && |
| Opc == TargetOpcode::G_ANYEXT; |
| |
| getInstrPartialMappingIdxs(MI, MRI, /* isFP= */ isFPTrunc || isFPAnyExt, |
| OpRegBankIdx); |
| break; |
| } |
| case TargetOpcode::G_LOAD: { |
| // Check if that load feeds fp instructions. |
| // In that case, we want the default mapping to be on FPR |
| // instead of blind map every scalar to GPR. |
| bool IsFP = any_of(MRI.use_nodbg_instructions(cast<GLoad>(MI).getDstReg()), |
| [&](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. |
| return onlyUsesFP(UseMI, MRI, TRI); |
| }); |
| getInstrPartialMappingIdxs(MI, MRI, IsFP, OpRegBankIdx); |
| break; |
| } |
| case TargetOpcode::G_STORE: { |
| // Check if that store is fed by fp instructions. |
| Register VReg = cast<GStore>(MI).getValueReg(); |
| if (!VReg) |
| break; |
| MachineInstr *DefMI = MRI.getVRegDef(VReg); |
| bool IsFP = onlyDefinesFP(*DefMI, MRI, TRI); |
| getInstrPartialMappingIdxs(MI, MRI, IsFP, OpRegBankIdx); |
| break; |
| } |
| default: |
| // Track the bank of each register, use NotFP mapping (all scalars in |
| // GPRs) |
| getInstrPartialMappingIdxs(MI, MRI, /* isFP= */ false, OpRegBankIdx); |
| break; |
| } |
| |
| // Finally construct the computed mapping. |
| SmallVector<const ValueMapping *, 8> OpdsMapping(NumOperands); |
| if (!getInstrValueMapping(MI, OpRegBankIdx, OpdsMapping)) |
| return getInvalidInstructionMapping(); |
| |
| return getInstructionMapping(DefaultMappingID, /* Cost */ 1, |
| getOperandsMapping(OpdsMapping), NumOperands); |
| } |
| |
| void X86RegisterBankInfo::applyMappingImpl( |
| MachineIRBuilder &Builder, const OperandsMapper &OpdMapper) const { |
| return applyDefaultMapping(OpdMapper); |
| } |
| |
| RegisterBankInfo::InstructionMappings |
| X86RegisterBankInfo::getInstrAlternativeMappings(const MachineInstr &MI) const { |
| |
| const MachineFunction &MF = *MI.getParent()->getParent(); |
| const TargetSubtargetInfo &STI = MF.getSubtarget(); |
| const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| |
| switch (MI.getOpcode()) { |
| case TargetOpcode::G_LOAD: |
| case TargetOpcode::G_STORE: |
| case TargetOpcode::G_IMPLICIT_DEF: { |
| // we going to try to map 32/64/80 bit to PMI_FP32/PMI_FP64/PMI_FP80 |
| unsigned Size = getSizeInBits(MI.getOperand(0).getReg(), MRI, TRI); |
| if (Size != 32 && Size != 64 && Size != 80) |
| break; |
| |
| unsigned NumOperands = MI.getNumOperands(); |
| |
| // Track the bank of each register, use FP mapping (all scalars in VEC) |
| SmallVector<PartialMappingIdx, 4> OpRegBankIdx(NumOperands); |
| getInstrPartialMappingIdxs(MI, MRI, /* isFP= */ true, OpRegBankIdx); |
| |
| // Finally construct the computed mapping. |
| SmallVector<const ValueMapping *, 8> OpdsMapping(NumOperands); |
| if (!getInstrValueMapping(MI, OpRegBankIdx, OpdsMapping)) |
| break; |
| |
| const RegisterBankInfo::InstructionMapping &Mapping = getInstructionMapping( |
| /*ID*/ 1, /*Cost*/ 1, getOperandsMapping(OpdsMapping), NumOperands); |
| InstructionMappings AltMappings; |
| AltMappings.push_back(&Mapping); |
| return AltMappings; |
| } |
| default: |
| break; |
| } |
| return RegisterBankInfo::getInstrAlternativeMappings(MI); |
| } |