| //===-- R600InstrInfo.cpp - R600 Instruction Information ------------------===// |
| // |
| // 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 |
| /// R600 Implementation of TargetInstrInfo. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "R600InstrInfo.h" |
| #include "AMDGPU.h" |
| #include "AMDGPUInstrInfo.h" |
| #include "AMDGPUSubtarget.h" |
| #include "R600Defines.h" |
| #include "R600FrameLowering.h" |
| #include "R600RegisterInfo.h" |
| #include "MCTargetDesc/AMDGPUMCTargetDesc.h" |
| #include "Utils/AMDGPUBaseInfo.h" |
| #include "llvm/ADT/BitVector.h" |
| #include "llvm/ADT/SmallSet.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/CodeGen/MachineBasicBlock.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineFunction.h" |
| #include "llvm/CodeGen/MachineInstr.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineOperand.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/TargetRegisterInfo.h" |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include <algorithm> |
| #include <cassert> |
| #include <cstdint> |
| #include <cstring> |
| #include <iterator> |
| #include <utility> |
| #include <vector> |
| |
| using namespace llvm; |
| |
| #define GET_INSTRINFO_CTOR_DTOR |
| #include "R600GenDFAPacketizer.inc" |
| |
| #define GET_INSTRINFO_CTOR_DTOR |
| #define GET_INSTRMAP_INFO |
| #define GET_INSTRINFO_NAMED_OPS |
| #include "R600GenInstrInfo.inc" |
| |
| R600InstrInfo::R600InstrInfo(const R600Subtarget &ST) |
| : R600GenInstrInfo(-1, -1), RI(), ST(ST) {} |
| |
| bool R600InstrInfo::isVector(const MachineInstr &MI) const { |
| return get(MI.getOpcode()).TSFlags & R600_InstFlag::VECTOR; |
| } |
| |
| void R600InstrInfo::copyPhysReg(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MI, |
| const DebugLoc &DL, MCRegister DestReg, |
| MCRegister SrcReg, bool KillSrc) const { |
| unsigned VectorComponents = 0; |
| if ((R600::R600_Reg128RegClass.contains(DestReg) || |
| R600::R600_Reg128VerticalRegClass.contains(DestReg)) && |
| (R600::R600_Reg128RegClass.contains(SrcReg) || |
| R600::R600_Reg128VerticalRegClass.contains(SrcReg))) { |
| VectorComponents = 4; |
| } else if((R600::R600_Reg64RegClass.contains(DestReg) || |
| R600::R600_Reg64VerticalRegClass.contains(DestReg)) && |
| (R600::R600_Reg64RegClass.contains(SrcReg) || |
| R600::R600_Reg64VerticalRegClass.contains(SrcReg))) { |
| VectorComponents = 2; |
| } |
| |
| if (VectorComponents > 0) { |
| for (unsigned I = 0; I < VectorComponents; I++) { |
| unsigned SubRegIndex = AMDGPURegisterInfo::getSubRegFromChannel(I); |
| buildDefaultInstruction(MBB, MI, R600::MOV, |
| RI.getSubReg(DestReg, SubRegIndex), |
| RI.getSubReg(SrcReg, SubRegIndex)) |
| .addReg(DestReg, |
| RegState::Define | RegState::Implicit); |
| } |
| } else { |
| MachineInstr *NewMI = buildDefaultInstruction(MBB, MI, R600::MOV, |
| DestReg, SrcReg); |
| NewMI->getOperand(getOperandIdx(*NewMI, R600::OpName::src0)) |
| .setIsKill(KillSrc); |
| } |
| } |
| |
| /// \returns true if \p MBBI can be moved into a new basic. |
| bool R600InstrInfo::isLegalToSplitMBBAt(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI) const { |
| for (MachineInstr::const_mop_iterator I = MBBI->operands_begin(), |
| E = MBBI->operands_end(); I != E; ++I) { |
| if (I->isReg() && !Register::isVirtualRegister(I->getReg()) && I->isUse() && |
| RI.isPhysRegLiveAcrossClauses(I->getReg())) |
| return false; |
| } |
| return true; |
| } |
| |
| bool R600InstrInfo::isMov(unsigned Opcode) const { |
| switch(Opcode) { |
| default: |
| return false; |
| case R600::MOV: |
| case R600::MOV_IMM_F32: |
| case R600::MOV_IMM_I32: |
| return true; |
| } |
| } |
| |
| bool R600InstrInfo::isReductionOp(unsigned Opcode) const { |
| return false; |
| } |
| |
| bool R600InstrInfo::isCubeOp(unsigned Opcode) const { |
| switch(Opcode) { |
| default: return false; |
| case R600::CUBE_r600_pseudo: |
| case R600::CUBE_r600_real: |
| case R600::CUBE_eg_pseudo: |
| case R600::CUBE_eg_real: |
| return true; |
| } |
| } |
| |
| bool R600InstrInfo::isALUInstr(unsigned Opcode) const { |
| unsigned TargetFlags = get(Opcode).TSFlags; |
| |
| return (TargetFlags & R600_InstFlag::ALU_INST); |
| } |
| |
| bool R600InstrInfo::hasInstrModifiers(unsigned Opcode) const { |
| unsigned TargetFlags = get(Opcode).TSFlags; |
| |
| return ((TargetFlags & R600_InstFlag::OP1) | |
| (TargetFlags & R600_InstFlag::OP2) | |
| (TargetFlags & R600_InstFlag::OP3)); |
| } |
| |
| bool R600InstrInfo::isLDSInstr(unsigned Opcode) const { |
| unsigned TargetFlags = get(Opcode).TSFlags; |
| |
| return ((TargetFlags & R600_InstFlag::LDS_1A) | |
| (TargetFlags & R600_InstFlag::LDS_1A1D) | |
| (TargetFlags & R600_InstFlag::LDS_1A2D)); |
| } |
| |
| bool R600InstrInfo::isLDSRetInstr(unsigned Opcode) const { |
| return isLDSInstr(Opcode) && getOperandIdx(Opcode, R600::OpName::dst) != -1; |
| } |
| |
| bool R600InstrInfo::canBeConsideredALU(const MachineInstr &MI) const { |
| if (isALUInstr(MI.getOpcode())) |
| return true; |
| if (isVector(MI) || isCubeOp(MI.getOpcode())) |
| return true; |
| switch (MI.getOpcode()) { |
| case R600::PRED_X: |
| case R600::INTERP_PAIR_XY: |
| case R600::INTERP_PAIR_ZW: |
| case R600::INTERP_VEC_LOAD: |
| case R600::COPY: |
| case R600::DOT_4: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool R600InstrInfo::isTransOnly(unsigned Opcode) const { |
| if (ST.hasCaymanISA()) |
| return false; |
| return (get(Opcode).getSchedClass() == R600::Sched::TransALU); |
| } |
| |
| bool R600InstrInfo::isTransOnly(const MachineInstr &MI) const { |
| return isTransOnly(MI.getOpcode()); |
| } |
| |
| bool R600InstrInfo::isVectorOnly(unsigned Opcode) const { |
| return (get(Opcode).getSchedClass() == R600::Sched::VecALU); |
| } |
| |
| bool R600InstrInfo::isVectorOnly(const MachineInstr &MI) const { |
| return isVectorOnly(MI.getOpcode()); |
| } |
| |
| bool R600InstrInfo::isExport(unsigned Opcode) const { |
| return (get(Opcode).TSFlags & R600_InstFlag::IS_EXPORT); |
| } |
| |
| bool R600InstrInfo::usesVertexCache(unsigned Opcode) const { |
| return ST.hasVertexCache() && IS_VTX(get(Opcode)); |
| } |
| |
| bool R600InstrInfo::usesVertexCache(const MachineInstr &MI) const { |
| const MachineFunction *MF = MI.getParent()->getParent(); |
| return !AMDGPU::isCompute(MF->getFunction().getCallingConv()) && |
| usesVertexCache(MI.getOpcode()); |
| } |
| |
| bool R600InstrInfo::usesTextureCache(unsigned Opcode) const { |
| return (!ST.hasVertexCache() && IS_VTX(get(Opcode))) || IS_TEX(get(Opcode)); |
| } |
| |
| bool R600InstrInfo::usesTextureCache(const MachineInstr &MI) const { |
| const MachineFunction *MF = MI.getParent()->getParent(); |
| return (AMDGPU::isCompute(MF->getFunction().getCallingConv()) && |
| usesVertexCache(MI.getOpcode())) || |
| usesTextureCache(MI.getOpcode()); |
| } |
| |
| bool R600InstrInfo::mustBeLastInClause(unsigned Opcode) const { |
| switch (Opcode) { |
| case R600::KILLGT: |
| case R600::GROUP_BARRIER: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool R600InstrInfo::usesAddressRegister(MachineInstr &MI) const { |
| return MI.findRegisterUseOperandIdx(R600::AR_X, false, &RI) != -1; |
| } |
| |
| bool R600InstrInfo::definesAddressRegister(MachineInstr &MI) const { |
| return MI.findRegisterDefOperandIdx(R600::AR_X, false, false, &RI) != -1; |
| } |
| |
| bool R600InstrInfo::readsLDSSrcReg(const MachineInstr &MI) const { |
| if (!isALUInstr(MI.getOpcode())) { |
| return false; |
| } |
| for (MachineInstr::const_mop_iterator I = MI.operands_begin(), |
| E = MI.operands_end(); |
| I != E; ++I) { |
| if (!I->isReg() || !I->isUse() || Register::isVirtualRegister(I->getReg())) |
| continue; |
| |
| if (R600::R600_LDS_SRC_REGRegClass.contains(I->getReg())) |
| return true; |
| } |
| return false; |
| } |
| |
| int R600InstrInfo::getSelIdx(unsigned Opcode, unsigned SrcIdx) const { |
| static const unsigned SrcSelTable[][2] = { |
| {R600::OpName::src0, R600::OpName::src0_sel}, |
| {R600::OpName::src1, R600::OpName::src1_sel}, |
| {R600::OpName::src2, R600::OpName::src2_sel}, |
| {R600::OpName::src0_X, R600::OpName::src0_sel_X}, |
| {R600::OpName::src0_Y, R600::OpName::src0_sel_Y}, |
| {R600::OpName::src0_Z, R600::OpName::src0_sel_Z}, |
| {R600::OpName::src0_W, R600::OpName::src0_sel_W}, |
| {R600::OpName::src1_X, R600::OpName::src1_sel_X}, |
| {R600::OpName::src1_Y, R600::OpName::src1_sel_Y}, |
| {R600::OpName::src1_Z, R600::OpName::src1_sel_Z}, |
| {R600::OpName::src1_W, R600::OpName::src1_sel_W} |
| }; |
| |
| for (const auto &Row : SrcSelTable) { |
| if (getOperandIdx(Opcode, Row[0]) == (int)SrcIdx) { |
| return getOperandIdx(Opcode, Row[1]); |
| } |
| } |
| return -1; |
| } |
| |
| SmallVector<std::pair<MachineOperand *, int64_t>, 3> |
| R600InstrInfo::getSrcs(MachineInstr &MI) const { |
| SmallVector<std::pair<MachineOperand *, int64_t>, 3> Result; |
| |
| if (MI.getOpcode() == R600::DOT_4) { |
| static const unsigned OpTable[8][2] = { |
| {R600::OpName::src0_X, R600::OpName::src0_sel_X}, |
| {R600::OpName::src0_Y, R600::OpName::src0_sel_Y}, |
| {R600::OpName::src0_Z, R600::OpName::src0_sel_Z}, |
| {R600::OpName::src0_W, R600::OpName::src0_sel_W}, |
| {R600::OpName::src1_X, R600::OpName::src1_sel_X}, |
| {R600::OpName::src1_Y, R600::OpName::src1_sel_Y}, |
| {R600::OpName::src1_Z, R600::OpName::src1_sel_Z}, |
| {R600::OpName::src1_W, R600::OpName::src1_sel_W}, |
| }; |
| |
| for (unsigned j = 0; j < 8; j++) { |
| MachineOperand &MO = |
| MI.getOperand(getOperandIdx(MI.getOpcode(), OpTable[j][0])); |
| Register Reg = MO.getReg(); |
| if (Reg == R600::ALU_CONST) { |
| MachineOperand &Sel = |
| MI.getOperand(getOperandIdx(MI.getOpcode(), OpTable[j][1])); |
| Result.push_back(std::make_pair(&MO, Sel.getImm())); |
| continue; |
| } |
| |
| } |
| return Result; |
| } |
| |
| static const unsigned OpTable[3][2] = { |
| {R600::OpName::src0, R600::OpName::src0_sel}, |
| {R600::OpName::src1, R600::OpName::src1_sel}, |
| {R600::OpName::src2, R600::OpName::src2_sel}, |
| }; |
| |
| for (unsigned j = 0; j < 3; j++) { |
| int SrcIdx = getOperandIdx(MI.getOpcode(), OpTable[j][0]); |
| if (SrcIdx < 0) |
| break; |
| MachineOperand &MO = MI.getOperand(SrcIdx); |
| Register Reg = MO.getReg(); |
| if (Reg == R600::ALU_CONST) { |
| MachineOperand &Sel = |
| MI.getOperand(getOperandIdx(MI.getOpcode(), OpTable[j][1])); |
| Result.push_back(std::make_pair(&MO, Sel.getImm())); |
| continue; |
| } |
| if (Reg == R600::ALU_LITERAL_X) { |
| MachineOperand &Operand = |
| MI.getOperand(getOperandIdx(MI.getOpcode(), R600::OpName::literal)); |
| if (Operand.isImm()) { |
| Result.push_back(std::make_pair(&MO, Operand.getImm())); |
| continue; |
| } |
| assert(Operand.isGlobal()); |
| } |
| Result.push_back(std::make_pair(&MO, 0)); |
| } |
| return Result; |
| } |
| |
| std::vector<std::pair<int, unsigned>> |
| R600InstrInfo::ExtractSrcs(MachineInstr &MI, |
| const DenseMap<unsigned, unsigned> &PV, |
| unsigned &ConstCount) const { |
| ConstCount = 0; |
| const std::pair<int, unsigned> DummyPair(-1, 0); |
| std::vector<std::pair<int, unsigned>> Result; |
| unsigned i = 0; |
| for (const auto &Src : getSrcs(MI)) { |
| ++i; |
| Register Reg = Src.first->getReg(); |
| int Index = RI.getEncodingValue(Reg) & 0xff; |
| if (Reg == R600::OQAP) { |
| Result.push_back(std::make_pair(Index, 0U)); |
| } |
| if (PV.find(Reg) != PV.end()) { |
| // 255 is used to tells its a PS/PV reg |
| Result.push_back(std::make_pair(255, 0U)); |
| continue; |
| } |
| if (Index > 127) { |
| ConstCount++; |
| Result.push_back(DummyPair); |
| continue; |
| } |
| unsigned Chan = RI.getHWRegChan(Reg); |
| Result.push_back(std::make_pair(Index, Chan)); |
| } |
| for (; i < 3; ++i) |
| Result.push_back(DummyPair); |
| return Result; |
| } |
| |
| static std::vector<std::pair<int, unsigned>> |
| Swizzle(std::vector<std::pair<int, unsigned>> Src, |
| R600InstrInfo::BankSwizzle Swz) { |
| if (Src[0] == Src[1]) |
| Src[1].first = -1; |
| switch (Swz) { |
| case R600InstrInfo::ALU_VEC_012_SCL_210: |
| break; |
| case R600InstrInfo::ALU_VEC_021_SCL_122: |
| std::swap(Src[1], Src[2]); |
| break; |
| case R600InstrInfo::ALU_VEC_102_SCL_221: |
| std::swap(Src[0], Src[1]); |
| break; |
| case R600InstrInfo::ALU_VEC_120_SCL_212: |
| std::swap(Src[0], Src[1]); |
| std::swap(Src[0], Src[2]); |
| break; |
| case R600InstrInfo::ALU_VEC_201: |
| std::swap(Src[0], Src[2]); |
| std::swap(Src[0], Src[1]); |
| break; |
| case R600InstrInfo::ALU_VEC_210: |
| std::swap(Src[0], Src[2]); |
| break; |
| } |
| return Src; |
| } |
| |
| static unsigned getTransSwizzle(R600InstrInfo::BankSwizzle Swz, unsigned Op) { |
| assert(Op < 3 && "Out of range swizzle index"); |
| switch (Swz) { |
| case R600InstrInfo::ALU_VEC_012_SCL_210: { |
| unsigned Cycles[3] = { 2, 1, 0}; |
| return Cycles[Op]; |
| } |
| case R600InstrInfo::ALU_VEC_021_SCL_122: { |
| unsigned Cycles[3] = { 1, 2, 2}; |
| return Cycles[Op]; |
| } |
| case R600InstrInfo::ALU_VEC_120_SCL_212: { |
| unsigned Cycles[3] = { 2, 1, 2}; |
| return Cycles[Op]; |
| } |
| case R600InstrInfo::ALU_VEC_102_SCL_221: { |
| unsigned Cycles[3] = { 2, 2, 1}; |
| return Cycles[Op]; |
| } |
| default: |
| llvm_unreachable("Wrong Swizzle for Trans Slot"); |
| } |
| } |
| |
| /// returns how many MIs (whose inputs are represented by IGSrcs) can be packed |
| /// in the same Instruction Group while meeting read port limitations given a |
| /// Swz swizzle sequence. |
| unsigned R600InstrInfo::isLegalUpTo( |
| const std::vector<std::vector<std::pair<int, unsigned>>> &IGSrcs, |
| const std::vector<R600InstrInfo::BankSwizzle> &Swz, |
| const std::vector<std::pair<int, unsigned>> &TransSrcs, |
| R600InstrInfo::BankSwizzle TransSwz) const { |
| int Vector[4][3]; |
| memset(Vector, -1, sizeof(Vector)); |
| for (unsigned i = 0, e = IGSrcs.size(); i < e; i++) { |
| const std::vector<std::pair<int, unsigned>> &Srcs = |
| Swizzle(IGSrcs[i], Swz[i]); |
| for (unsigned j = 0; j < 3; j++) { |
| const std::pair<int, unsigned> &Src = Srcs[j]; |
| if (Src.first < 0 || Src.first == 255) |
| continue; |
| if (Src.first == GET_REG_INDEX(RI.getEncodingValue(R600::OQAP))) { |
| if (Swz[i] != R600InstrInfo::ALU_VEC_012_SCL_210 && |
| Swz[i] != R600InstrInfo::ALU_VEC_021_SCL_122) { |
| // The value from output queue A (denoted by register OQAP) can |
| // only be fetched during the first cycle. |
| return false; |
| } |
| // OQAP does not count towards the normal read port restrictions |
| continue; |
| } |
| if (Vector[Src.second][j] < 0) |
| Vector[Src.second][j] = Src.first; |
| if (Vector[Src.second][j] != Src.first) |
| return i; |
| } |
| } |
| // Now check Trans Alu |
| for (unsigned i = 0, e = TransSrcs.size(); i < e; ++i) { |
| const std::pair<int, unsigned> &Src = TransSrcs[i]; |
| unsigned Cycle = getTransSwizzle(TransSwz, i); |
| if (Src.first < 0) |
| continue; |
| if (Src.first == 255) |
| continue; |
| if (Vector[Src.second][Cycle] < 0) |
| Vector[Src.second][Cycle] = Src.first; |
| if (Vector[Src.second][Cycle] != Src.first) |
| return IGSrcs.size() - 1; |
| } |
| return IGSrcs.size(); |
| } |
| |
| /// Given a swizzle sequence SwzCandidate and an index Idx, returns the next |
| /// (in lexicographic term) swizzle sequence assuming that all swizzles after |
| /// Idx can be skipped |
| static bool |
| NextPossibleSolution( |
| std::vector<R600InstrInfo::BankSwizzle> &SwzCandidate, |
| unsigned Idx) { |
| assert(Idx < SwzCandidate.size()); |
| int ResetIdx = Idx; |
| while (ResetIdx > -1 && SwzCandidate[ResetIdx] == R600InstrInfo::ALU_VEC_210) |
| ResetIdx --; |
| for (unsigned i = ResetIdx + 1, e = SwzCandidate.size(); i < e; i++) { |
| SwzCandidate[i] = R600InstrInfo::ALU_VEC_012_SCL_210; |
| } |
| if (ResetIdx == -1) |
| return false; |
| int NextSwizzle = SwzCandidate[ResetIdx] + 1; |
| SwzCandidate[ResetIdx] = (R600InstrInfo::BankSwizzle)NextSwizzle; |
| return true; |
| } |
| |
| /// Enumerate all possible Swizzle sequence to find one that can meet all |
| /// read port requirements. |
| bool R600InstrInfo::FindSwizzleForVectorSlot( |
| const std::vector<std::vector<std::pair<int, unsigned>>> &IGSrcs, |
| std::vector<R600InstrInfo::BankSwizzle> &SwzCandidate, |
| const std::vector<std::pair<int, unsigned>> &TransSrcs, |
| R600InstrInfo::BankSwizzle TransSwz) const { |
| unsigned ValidUpTo = 0; |
| do { |
| ValidUpTo = isLegalUpTo(IGSrcs, SwzCandidate, TransSrcs, TransSwz); |
| if (ValidUpTo == IGSrcs.size()) |
| return true; |
| } while (NextPossibleSolution(SwzCandidate, ValidUpTo)); |
| return false; |
| } |
| |
| /// Instructions in Trans slot can't read gpr at cycle 0 if they also read |
| /// a const, and can't read a gpr at cycle 1 if they read 2 const. |
| static bool |
| isConstCompatible(R600InstrInfo::BankSwizzle TransSwz, |
| const std::vector<std::pair<int, unsigned>> &TransOps, |
| unsigned ConstCount) { |
| // TransALU can't read 3 constants |
| if (ConstCount > 2) |
| return false; |
| for (unsigned i = 0, e = TransOps.size(); i < e; ++i) { |
| const std::pair<int, unsigned> &Src = TransOps[i]; |
| unsigned Cycle = getTransSwizzle(TransSwz, i); |
| if (Src.first < 0) |
| continue; |
| if (ConstCount > 0 && Cycle == 0) |
| return false; |
| if (ConstCount > 1 && Cycle == 1) |
| return false; |
| } |
| return true; |
| } |
| |
| bool |
| R600InstrInfo::fitsReadPortLimitations(const std::vector<MachineInstr *> &IG, |
| const DenseMap<unsigned, unsigned> &PV, |
| std::vector<BankSwizzle> &ValidSwizzle, |
| bool isLastAluTrans) |
| const { |
| //Todo : support shared src0 - src1 operand |
| |
| std::vector<std::vector<std::pair<int, unsigned>>> IGSrcs; |
| ValidSwizzle.clear(); |
| unsigned ConstCount = 0; |
| BankSwizzle TransBS = ALU_VEC_012_SCL_210; |
| for (unsigned i = 0, e = IG.size(); i < e; ++i) { |
| IGSrcs.push_back(ExtractSrcs(*IG[i], PV, ConstCount)); |
| unsigned Op = getOperandIdx(IG[i]->getOpcode(), |
| R600::OpName::bank_swizzle); |
| ValidSwizzle.push_back( (R600InstrInfo::BankSwizzle) |
| IG[i]->getOperand(Op).getImm()); |
| } |
| std::vector<std::pair<int, unsigned>> TransOps; |
| if (!isLastAluTrans) |
| return FindSwizzleForVectorSlot(IGSrcs, ValidSwizzle, TransOps, TransBS); |
| |
| TransOps = std::move(IGSrcs.back()); |
| IGSrcs.pop_back(); |
| ValidSwizzle.pop_back(); |
| |
| static const R600InstrInfo::BankSwizzle TransSwz[] = { |
| ALU_VEC_012_SCL_210, |
| ALU_VEC_021_SCL_122, |
| ALU_VEC_120_SCL_212, |
| ALU_VEC_102_SCL_221 |
| }; |
| for (unsigned i = 0; i < 4; i++) { |
| TransBS = TransSwz[i]; |
| if (!isConstCompatible(TransBS, TransOps, ConstCount)) |
| continue; |
| bool Result = FindSwizzleForVectorSlot(IGSrcs, ValidSwizzle, TransOps, |
| TransBS); |
| if (Result) { |
| ValidSwizzle.push_back(TransBS); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool |
| R600InstrInfo::fitsConstReadLimitations(const std::vector<unsigned> &Consts) |
| const { |
| assert (Consts.size() <= 12 && "Too many operands in instructions group"); |
| unsigned Pair1 = 0, Pair2 = 0; |
| for (unsigned i = 0, n = Consts.size(); i < n; ++i) { |
| unsigned ReadConstHalf = Consts[i] & 2; |
| unsigned ReadConstIndex = Consts[i] & (~3); |
| unsigned ReadHalfConst = ReadConstIndex | ReadConstHalf; |
| if (!Pair1) { |
| Pair1 = ReadHalfConst; |
| continue; |
| } |
| if (Pair1 == ReadHalfConst) |
| continue; |
| if (!Pair2) { |
| Pair2 = ReadHalfConst; |
| continue; |
| } |
| if (Pair2 != ReadHalfConst) |
| return false; |
| } |
| return true; |
| } |
| |
| bool |
| R600InstrInfo::fitsConstReadLimitations(const std::vector<MachineInstr *> &MIs) |
| const { |
| std::vector<unsigned> Consts; |
| SmallSet<int64_t, 4> Literals; |
| for (unsigned i = 0, n = MIs.size(); i < n; i++) { |
| MachineInstr &MI = *MIs[i]; |
| if (!isALUInstr(MI.getOpcode())) |
| continue; |
| |
| for (const auto &Src : getSrcs(MI)) { |
| if (Src.first->getReg() == R600::ALU_LITERAL_X) |
| Literals.insert(Src.second); |
| if (Literals.size() > 4) |
| return false; |
| if (Src.first->getReg() == R600::ALU_CONST) |
| Consts.push_back(Src.second); |
| if (R600::R600_KC0RegClass.contains(Src.first->getReg()) || |
| R600::R600_KC1RegClass.contains(Src.first->getReg())) { |
| unsigned Index = RI.getEncodingValue(Src.first->getReg()) & 0xff; |
| unsigned Chan = RI.getHWRegChan(Src.first->getReg()); |
| Consts.push_back((Index << 2) | Chan); |
| } |
| } |
| } |
| return fitsConstReadLimitations(Consts); |
| } |
| |
| DFAPacketizer * |
| R600InstrInfo::CreateTargetScheduleState(const TargetSubtargetInfo &STI) const { |
| const InstrItineraryData *II = STI.getInstrItineraryData(); |
| return static_cast<const R600Subtarget &>(STI).createDFAPacketizer(II); |
| } |
| |
| static bool |
| isPredicateSetter(unsigned Opcode) { |
| switch (Opcode) { |
| case R600::PRED_X: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static MachineInstr * |
| findFirstPredicateSetterFrom(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator I) { |
| while (I != MBB.begin()) { |
| --I; |
| MachineInstr &MI = *I; |
| if (isPredicateSetter(MI.getOpcode())) |
| return &MI; |
| } |
| |
| return nullptr; |
| } |
| |
| static |
| bool isJump(unsigned Opcode) { |
| return Opcode == R600::JUMP || Opcode == R600::JUMP_COND; |
| } |
| |
| static bool isBranch(unsigned Opcode) { |
| return Opcode == R600::BRANCH || Opcode == R600::BRANCH_COND_i32 || |
| Opcode == R600::BRANCH_COND_f32; |
| } |
| |
| bool R600InstrInfo::analyzeBranch(MachineBasicBlock &MBB, |
| MachineBasicBlock *&TBB, |
| MachineBasicBlock *&FBB, |
| SmallVectorImpl<MachineOperand> &Cond, |
| bool AllowModify) const { |
| // Most of the following comes from the ARM implementation of AnalyzeBranch |
| |
| // If the block has no terminators, it just falls into the block after it. |
| MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); |
| if (I == MBB.end()) |
| return false; |
| |
| // R600::BRANCH* instructions are only available after isel and are not |
| // handled |
| if (isBranch(I->getOpcode())) |
| return true; |
| if (!isJump(I->getOpcode())) { |
| return false; |
| } |
| |
| // Remove successive JUMP |
| while (I != MBB.begin() && std::prev(I)->getOpcode() == R600::JUMP) { |
| MachineBasicBlock::iterator PriorI = std::prev(I); |
| if (AllowModify) |
| I->removeFromParent(); |
| I = PriorI; |
| } |
| MachineInstr &LastInst = *I; |
| |
| // If there is only one terminator instruction, process it. |
| unsigned LastOpc = LastInst.getOpcode(); |
| if (I == MBB.begin() || !isJump((--I)->getOpcode())) { |
| if (LastOpc == R600::JUMP) { |
| TBB = LastInst.getOperand(0).getMBB(); |
| return false; |
| } else if (LastOpc == R600::JUMP_COND) { |
| auto predSet = I; |
| while (!isPredicateSetter(predSet->getOpcode())) { |
| predSet = --I; |
| } |
| TBB = LastInst.getOperand(0).getMBB(); |
| Cond.push_back(predSet->getOperand(1)); |
| Cond.push_back(predSet->getOperand(2)); |
| Cond.push_back(MachineOperand::CreateReg(R600::PRED_SEL_ONE, false)); |
| return false; |
| } |
| return true; // Can't handle indirect branch. |
| } |
| |
| // Get the instruction before it if it is a terminator. |
| MachineInstr &SecondLastInst = *I; |
| unsigned SecondLastOpc = SecondLastInst.getOpcode(); |
| |
| // If the block ends with a B and a Bcc, handle it. |
| if (SecondLastOpc == R600::JUMP_COND && LastOpc == R600::JUMP) { |
| auto predSet = --I; |
| while (!isPredicateSetter(predSet->getOpcode())) { |
| predSet = --I; |
| } |
| TBB = SecondLastInst.getOperand(0).getMBB(); |
| FBB = LastInst.getOperand(0).getMBB(); |
| Cond.push_back(predSet->getOperand(1)); |
| Cond.push_back(predSet->getOperand(2)); |
| Cond.push_back(MachineOperand::CreateReg(R600::PRED_SEL_ONE, false)); |
| return false; |
| } |
| |
| // Otherwise, can't handle this. |
| return true; |
| } |
| |
| static |
| MachineBasicBlock::iterator FindLastAluClause(MachineBasicBlock &MBB) { |
| for (MachineBasicBlock::reverse_iterator It = MBB.rbegin(), E = MBB.rend(); |
| It != E; ++It) { |
| if (It->getOpcode() == R600::CF_ALU || |
| It->getOpcode() == R600::CF_ALU_PUSH_BEFORE) |
| return It.getReverse(); |
| } |
| return MBB.end(); |
| } |
| |
| unsigned R600InstrInfo::insertBranch(MachineBasicBlock &MBB, |
| MachineBasicBlock *TBB, |
| MachineBasicBlock *FBB, |
| ArrayRef<MachineOperand> Cond, |
| const DebugLoc &DL, |
| int *BytesAdded) const { |
| assert(TBB && "insertBranch must not be told to insert a fallthrough"); |
| assert(!BytesAdded && "code size not handled"); |
| |
| if (!FBB) { |
| if (Cond.empty()) { |
| BuildMI(&MBB, DL, get(R600::JUMP)).addMBB(TBB); |
| return 1; |
| } else { |
| MachineInstr *PredSet = findFirstPredicateSetterFrom(MBB, MBB.end()); |
| assert(PredSet && "No previous predicate !"); |
| addFlag(*PredSet, 0, MO_FLAG_PUSH); |
| PredSet->getOperand(2).setImm(Cond[1].getImm()); |
| |
| BuildMI(&MBB, DL, get(R600::JUMP_COND)) |
| .addMBB(TBB) |
| .addReg(R600::PREDICATE_BIT, RegState::Kill); |
| MachineBasicBlock::iterator CfAlu = FindLastAluClause(MBB); |
| if (CfAlu == MBB.end()) |
| return 1; |
| assert (CfAlu->getOpcode() == R600::CF_ALU); |
| CfAlu->setDesc(get(R600::CF_ALU_PUSH_BEFORE)); |
| return 1; |
| } |
| } else { |
| MachineInstr *PredSet = findFirstPredicateSetterFrom(MBB, MBB.end()); |
| assert(PredSet && "No previous predicate !"); |
| addFlag(*PredSet, 0, MO_FLAG_PUSH); |
| PredSet->getOperand(2).setImm(Cond[1].getImm()); |
| BuildMI(&MBB, DL, get(R600::JUMP_COND)) |
| .addMBB(TBB) |
| .addReg(R600::PREDICATE_BIT, RegState::Kill); |
| BuildMI(&MBB, DL, get(R600::JUMP)).addMBB(FBB); |
| MachineBasicBlock::iterator CfAlu = FindLastAluClause(MBB); |
| if (CfAlu == MBB.end()) |
| return 2; |
| assert (CfAlu->getOpcode() == R600::CF_ALU); |
| CfAlu->setDesc(get(R600::CF_ALU_PUSH_BEFORE)); |
| return 2; |
| } |
| } |
| |
| unsigned R600InstrInfo::removeBranch(MachineBasicBlock &MBB, |
| int *BytesRemoved) const { |
| assert(!BytesRemoved && "code size not handled"); |
| |
| // Note : we leave PRED* instructions there. |
| // They may be needed when predicating instructions. |
| |
| MachineBasicBlock::iterator I = MBB.end(); |
| |
| if (I == MBB.begin()) { |
| return 0; |
| } |
| --I; |
| switch (I->getOpcode()) { |
| default: |
| return 0; |
| case R600::JUMP_COND: { |
| MachineInstr *predSet = findFirstPredicateSetterFrom(MBB, I); |
| clearFlag(*predSet, 0, MO_FLAG_PUSH); |
| I->eraseFromParent(); |
| MachineBasicBlock::iterator CfAlu = FindLastAluClause(MBB); |
| if (CfAlu == MBB.end()) |
| break; |
| assert (CfAlu->getOpcode() == R600::CF_ALU_PUSH_BEFORE); |
| CfAlu->setDesc(get(R600::CF_ALU)); |
| break; |
| } |
| case R600::JUMP: |
| I->eraseFromParent(); |
| break; |
| } |
| I = MBB.end(); |
| |
| if (I == MBB.begin()) { |
| return 1; |
| } |
| --I; |
| switch (I->getOpcode()) { |
| // FIXME: only one case?? |
| default: |
| return 1; |
| case R600::JUMP_COND: { |
| MachineInstr *predSet = findFirstPredicateSetterFrom(MBB, I); |
| clearFlag(*predSet, 0, MO_FLAG_PUSH); |
| I->eraseFromParent(); |
| MachineBasicBlock::iterator CfAlu = FindLastAluClause(MBB); |
| if (CfAlu == MBB.end()) |
| break; |
| assert (CfAlu->getOpcode() == R600::CF_ALU_PUSH_BEFORE); |
| CfAlu->setDesc(get(R600::CF_ALU)); |
| break; |
| } |
| case R600::JUMP: |
| I->eraseFromParent(); |
| break; |
| } |
| return 2; |
| } |
| |
| bool R600InstrInfo::isPredicated(const MachineInstr &MI) const { |
| int idx = MI.findFirstPredOperandIdx(); |
| if (idx < 0) |
| return false; |
| |
| Register Reg = MI.getOperand(idx).getReg(); |
| switch (Reg) { |
| default: return false; |
| case R600::PRED_SEL_ONE: |
| case R600::PRED_SEL_ZERO: |
| case R600::PREDICATE_BIT: |
| return true; |
| } |
| } |
| |
| bool R600InstrInfo::isPredicable(const MachineInstr &MI) const { |
| // XXX: KILL* instructions can be predicated, but they must be the last |
| // instruction in a clause, so this means any instructions after them cannot |
| // be predicated. Until we have proper support for instruction clauses in the |
| // backend, we will mark KILL* instructions as unpredicable. |
| |
| if (MI.getOpcode() == R600::KILLGT) { |
| return false; |
| } else if (MI.getOpcode() == R600::CF_ALU) { |
| // If the clause start in the middle of MBB then the MBB has more |
| // than a single clause, unable to predicate several clauses. |
| if (MI.getParent()->begin() != MachineBasicBlock::const_iterator(MI)) |
| return false; |
| // TODO: We don't support KC merging atm |
| return MI.getOperand(3).getImm() == 0 && MI.getOperand(4).getImm() == 0; |
| } else if (isVector(MI)) { |
| return false; |
| } else { |
| return TargetInstrInfo::isPredicable(MI); |
| } |
| } |
| |
| bool |
| R600InstrInfo::isProfitableToIfCvt(MachineBasicBlock &MBB, |
| unsigned NumCycles, |
| unsigned ExtraPredCycles, |
| BranchProbability Probability) const{ |
| return true; |
| } |
| |
| bool |
| R600InstrInfo::isProfitableToIfCvt(MachineBasicBlock &TMBB, |
| unsigned NumTCycles, |
| unsigned ExtraTCycles, |
| MachineBasicBlock &FMBB, |
| unsigned NumFCycles, |
| unsigned ExtraFCycles, |
| BranchProbability Probability) const { |
| return true; |
| } |
| |
| bool |
| R600InstrInfo::isProfitableToDupForIfCvt(MachineBasicBlock &MBB, |
| unsigned NumCycles, |
| BranchProbability Probability) |
| const { |
| return true; |
| } |
| |
| bool |
| R600InstrInfo::isProfitableToUnpredicate(MachineBasicBlock &TMBB, |
| MachineBasicBlock &FMBB) const { |
| return false; |
| } |
| |
| bool |
| R600InstrInfo::reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const { |
| MachineOperand &MO = Cond[1]; |
| switch (MO.getImm()) { |
| case R600::PRED_SETE_INT: |
| MO.setImm(R600::PRED_SETNE_INT); |
| break; |
| case R600::PRED_SETNE_INT: |
| MO.setImm(R600::PRED_SETE_INT); |
| break; |
| case R600::PRED_SETE: |
| MO.setImm(R600::PRED_SETNE); |
| break; |
| case R600::PRED_SETNE: |
| MO.setImm(R600::PRED_SETE); |
| break; |
| default: |
| return true; |
| } |
| |
| MachineOperand &MO2 = Cond[2]; |
| switch (MO2.getReg()) { |
| case R600::PRED_SEL_ZERO: |
| MO2.setReg(R600::PRED_SEL_ONE); |
| break; |
| case R600::PRED_SEL_ONE: |
| MO2.setReg(R600::PRED_SEL_ZERO); |
| break; |
| default: |
| return true; |
| } |
| return false; |
| } |
| |
| bool R600InstrInfo::DefinesPredicate(MachineInstr &MI, |
| std::vector<MachineOperand> &Pred) const { |
| return isPredicateSetter(MI.getOpcode()); |
| } |
| |
| bool R600InstrInfo::PredicateInstruction(MachineInstr &MI, |
| ArrayRef<MachineOperand> Pred) const { |
| int PIdx = MI.findFirstPredOperandIdx(); |
| |
| if (MI.getOpcode() == R600::CF_ALU) { |
| MI.getOperand(8).setImm(0); |
| return true; |
| } |
| |
| if (MI.getOpcode() == R600::DOT_4) { |
| MI.getOperand(getOperandIdx(MI, R600::OpName::pred_sel_X)) |
| .setReg(Pred[2].getReg()); |
| MI.getOperand(getOperandIdx(MI, R600::OpName::pred_sel_Y)) |
| .setReg(Pred[2].getReg()); |
| MI.getOperand(getOperandIdx(MI, R600::OpName::pred_sel_Z)) |
| .setReg(Pred[2].getReg()); |
| MI.getOperand(getOperandIdx(MI, R600::OpName::pred_sel_W)) |
| .setReg(Pred[2].getReg()); |
| MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI); |
| MIB.addReg(R600::PREDICATE_BIT, RegState::Implicit); |
| return true; |
| } |
| |
| if (PIdx != -1) { |
| MachineOperand &PMO = MI.getOperand(PIdx); |
| PMO.setReg(Pred[2].getReg()); |
| MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI); |
| MIB.addReg(R600::PREDICATE_BIT, RegState::Implicit); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| unsigned int R600InstrInfo::getPredicationCost(const MachineInstr &) const { |
| return 2; |
| } |
| |
| unsigned int R600InstrInfo::getInstrLatency(const InstrItineraryData *ItinData, |
| const MachineInstr &, |
| unsigned *PredCost) const { |
| if (PredCost) |
| *PredCost = 2; |
| return 2; |
| } |
| |
| unsigned R600InstrInfo::calculateIndirectAddress(unsigned RegIndex, |
| unsigned Channel) const { |
| assert(Channel == 0); |
| return RegIndex; |
| } |
| |
| bool R600InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { |
| switch (MI.getOpcode()) { |
| default: { |
| MachineBasicBlock *MBB = MI.getParent(); |
| int OffsetOpIdx = |
| R600::getNamedOperandIdx(MI.getOpcode(), R600::OpName::addr); |
| // addr is a custom operand with multiple MI operands, and only the |
| // first MI operand is given a name. |
| int RegOpIdx = OffsetOpIdx + 1; |
| int ChanOpIdx = |
| R600::getNamedOperandIdx(MI.getOpcode(), R600::OpName::chan); |
| if (isRegisterLoad(MI)) { |
| int DstOpIdx = |
| R600::getNamedOperandIdx(MI.getOpcode(), R600::OpName::dst); |
| unsigned RegIndex = MI.getOperand(RegOpIdx).getImm(); |
| unsigned Channel = MI.getOperand(ChanOpIdx).getImm(); |
| unsigned Address = calculateIndirectAddress(RegIndex, Channel); |
| Register OffsetReg = MI.getOperand(OffsetOpIdx).getReg(); |
| if (OffsetReg == R600::INDIRECT_BASE_ADDR) { |
| buildMovInstr(MBB, MI, MI.getOperand(DstOpIdx).getReg(), |
| getIndirectAddrRegClass()->getRegister(Address)); |
| } else { |
| buildIndirectRead(MBB, MI, MI.getOperand(DstOpIdx).getReg(), Address, |
| OffsetReg); |
| } |
| } else if (isRegisterStore(MI)) { |
| int ValOpIdx = |
| R600::getNamedOperandIdx(MI.getOpcode(), R600::OpName::val); |
| unsigned RegIndex = MI.getOperand(RegOpIdx).getImm(); |
| unsigned Channel = MI.getOperand(ChanOpIdx).getImm(); |
| unsigned Address = calculateIndirectAddress(RegIndex, Channel); |
| Register OffsetReg = MI.getOperand(OffsetOpIdx).getReg(); |
| if (OffsetReg == R600::INDIRECT_BASE_ADDR) { |
| buildMovInstr(MBB, MI, getIndirectAddrRegClass()->getRegister(Address), |
| MI.getOperand(ValOpIdx).getReg()); |
| } else { |
| buildIndirectWrite(MBB, MI, MI.getOperand(ValOpIdx).getReg(), |
| calculateIndirectAddress(RegIndex, Channel), |
| OffsetReg); |
| } |
| } else { |
| return false; |
| } |
| |
| MBB->erase(MI); |
| return true; |
| } |
| case R600::R600_EXTRACT_ELT_V2: |
| case R600::R600_EXTRACT_ELT_V4: |
| buildIndirectRead(MI.getParent(), MI, MI.getOperand(0).getReg(), |
| RI.getHWRegIndex(MI.getOperand(1).getReg()), // Address |
| MI.getOperand(2).getReg(), |
| RI.getHWRegChan(MI.getOperand(1).getReg())); |
| break; |
| case R600::R600_INSERT_ELT_V2: |
| case R600::R600_INSERT_ELT_V4: |
| buildIndirectWrite(MI.getParent(), MI, MI.getOperand(2).getReg(), // Value |
| RI.getHWRegIndex(MI.getOperand(1).getReg()), // Address |
| MI.getOperand(3).getReg(), // Offset |
| RI.getHWRegChan(MI.getOperand(1).getReg())); // Channel |
| break; |
| } |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| void R600InstrInfo::reserveIndirectRegisters(BitVector &Reserved, |
| const MachineFunction &MF, |
| const R600RegisterInfo &TRI) const { |
| const R600Subtarget &ST = MF.getSubtarget<R600Subtarget>(); |
| const R600FrameLowering *TFL = ST.getFrameLowering(); |
| |
| unsigned StackWidth = TFL->getStackWidth(MF); |
| int End = getIndirectIndexEnd(MF); |
| |
| if (End == -1) |
| return; |
| |
| for (int Index = getIndirectIndexBegin(MF); Index <= End; ++Index) { |
| for (unsigned Chan = 0; Chan < StackWidth; ++Chan) { |
| unsigned Reg = R600::R600_TReg32RegClass.getRegister((4 * Index) + Chan); |
| TRI.reserveRegisterTuples(Reserved, Reg); |
| } |
| } |
| } |
| |
| const TargetRegisterClass *R600InstrInfo::getIndirectAddrRegClass() const { |
| return &R600::R600_TReg32_XRegClass; |
| } |
| |
| MachineInstrBuilder R600InstrInfo::buildIndirectWrite(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator I, |
| unsigned ValueReg, unsigned Address, |
| unsigned OffsetReg) const { |
| return buildIndirectWrite(MBB, I, ValueReg, Address, OffsetReg, 0); |
| } |
| |
| MachineInstrBuilder R600InstrInfo::buildIndirectWrite(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator I, |
| unsigned ValueReg, unsigned Address, |
| unsigned OffsetReg, |
| unsigned AddrChan) const { |
| unsigned AddrReg; |
| switch (AddrChan) { |
| default: llvm_unreachable("Invalid Channel"); |
| case 0: AddrReg = R600::R600_AddrRegClass.getRegister(Address); break; |
| case 1: AddrReg = R600::R600_Addr_YRegClass.getRegister(Address); break; |
| case 2: AddrReg = R600::R600_Addr_ZRegClass.getRegister(Address); break; |
| case 3: AddrReg = R600::R600_Addr_WRegClass.getRegister(Address); break; |
| } |
| MachineInstr *MOVA = buildDefaultInstruction(*MBB, I, R600::MOVA_INT_eg, |
| R600::AR_X, OffsetReg); |
| setImmOperand(*MOVA, R600::OpName::write, 0); |
| |
| MachineInstrBuilder Mov = buildDefaultInstruction(*MBB, I, R600::MOV, |
| AddrReg, ValueReg) |
| .addReg(R600::AR_X, |
| RegState::Implicit | RegState::Kill); |
| setImmOperand(*Mov, R600::OpName::dst_rel, 1); |
| return Mov; |
| } |
| |
| MachineInstrBuilder R600InstrInfo::buildIndirectRead(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator I, |
| unsigned ValueReg, unsigned Address, |
| unsigned OffsetReg) const { |
| return buildIndirectRead(MBB, I, ValueReg, Address, OffsetReg, 0); |
| } |
| |
| MachineInstrBuilder R600InstrInfo::buildIndirectRead(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator I, |
| unsigned ValueReg, unsigned Address, |
| unsigned OffsetReg, |
| unsigned AddrChan) const { |
| unsigned AddrReg; |
| switch (AddrChan) { |
| default: llvm_unreachable("Invalid Channel"); |
| case 0: AddrReg = R600::R600_AddrRegClass.getRegister(Address); break; |
| case 1: AddrReg = R600::R600_Addr_YRegClass.getRegister(Address); break; |
| case 2: AddrReg = R600::R600_Addr_ZRegClass.getRegister(Address); break; |
| case 3: AddrReg = R600::R600_Addr_WRegClass.getRegister(Address); break; |
| } |
| MachineInstr *MOVA = buildDefaultInstruction(*MBB, I, R600::MOVA_INT_eg, |
| R600::AR_X, |
| OffsetReg); |
| setImmOperand(*MOVA, R600::OpName::write, 0); |
| MachineInstrBuilder Mov = buildDefaultInstruction(*MBB, I, R600::MOV, |
| ValueReg, |
| AddrReg) |
| .addReg(R600::AR_X, |
| RegState::Implicit | RegState::Kill); |
| setImmOperand(*Mov, R600::OpName::src0_rel, 1); |
| |
| return Mov; |
| } |
| |
| int R600InstrInfo::getIndirectIndexBegin(const MachineFunction &MF) const { |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); |
| int Offset = -1; |
| |
| if (MFI.getNumObjects() == 0) { |
| return -1; |
| } |
| |
| if (MRI.livein_empty()) { |
| return 0; |
| } |
| |
| const TargetRegisterClass *IndirectRC = getIndirectAddrRegClass(); |
| for (std::pair<unsigned, unsigned> LI : MRI.liveins()) { |
| unsigned Reg = LI.first; |
| if (Register::isVirtualRegister(Reg) || !IndirectRC->contains(Reg)) |
| continue; |
| |
| unsigned RegIndex; |
| unsigned RegEnd; |
| for (RegIndex = 0, RegEnd = IndirectRC->getNumRegs(); RegIndex != RegEnd; |
| ++RegIndex) { |
| if (IndirectRC->getRegister(RegIndex) == Reg) |
| break; |
| } |
| Offset = std::max(Offset, (int)RegIndex); |
| } |
| |
| return Offset + 1; |
| } |
| |
| int R600InstrInfo::getIndirectIndexEnd(const MachineFunction &MF) const { |
| int Offset = 0; |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); |
| |
| // Variable sized objects are not supported |
| if (MFI.hasVarSizedObjects()) { |
| return -1; |
| } |
| |
| if (MFI.getNumObjects() == 0) { |
| return -1; |
| } |
| |
| const R600Subtarget &ST = MF.getSubtarget<R600Subtarget>(); |
| const R600FrameLowering *TFL = ST.getFrameLowering(); |
| |
| unsigned IgnoredFrameReg; |
| Offset = TFL->getFrameIndexReference(MF, -1, IgnoredFrameReg); |
| |
| return getIndirectIndexBegin(MF) + Offset; |
| } |
| |
| unsigned R600InstrInfo::getMaxAlusPerClause() const { |
| return 115; |
| } |
| |
| MachineInstrBuilder R600InstrInfo::buildDefaultInstruction(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator I, |
| unsigned Opcode, |
| unsigned DstReg, |
| unsigned Src0Reg, |
| unsigned Src1Reg) const { |
| MachineInstrBuilder MIB = BuildMI(MBB, I, MBB.findDebugLoc(I), get(Opcode), |
| DstReg); // $dst |
| |
| if (Src1Reg) { |
| MIB.addImm(0) // $update_exec_mask |
| .addImm(0); // $update_predicate |
| } |
| MIB.addImm(1) // $write |
| .addImm(0) // $omod |
| .addImm(0) // $dst_rel |
| .addImm(0) // $dst_clamp |
| .addReg(Src0Reg) // $src0 |
| .addImm(0) // $src0_neg |
| .addImm(0) // $src0_rel |
| .addImm(0) // $src0_abs |
| .addImm(-1); // $src0_sel |
| |
| if (Src1Reg) { |
| MIB.addReg(Src1Reg) // $src1 |
| .addImm(0) // $src1_neg |
| .addImm(0) // $src1_rel |
| .addImm(0) // $src1_abs |
| .addImm(-1); // $src1_sel |
| } |
| |
| //XXX: The r600g finalizer expects this to be 1, once we've moved the |
| //scheduling to the backend, we can change the default to 0. |
| MIB.addImm(1) // $last |
| .addReg(R600::PRED_SEL_OFF) // $pred_sel |
| .addImm(0) // $literal |
| .addImm(0); // $bank_swizzle |
| |
| return MIB; |
| } |
| |
| #define OPERAND_CASE(Label) \ |
| case Label: { \ |
| static const unsigned Ops[] = \ |
| { \ |
| Label##_X, \ |
| Label##_Y, \ |
| Label##_Z, \ |
| Label##_W \ |
| }; \ |
| return Ops[Slot]; \ |
| } |
| |
| static unsigned getSlotedOps(unsigned Op, unsigned Slot) { |
| switch (Op) { |
| OPERAND_CASE(R600::OpName::update_exec_mask) |
| OPERAND_CASE(R600::OpName::update_pred) |
| OPERAND_CASE(R600::OpName::write) |
| OPERAND_CASE(R600::OpName::omod) |
| OPERAND_CASE(R600::OpName::dst_rel) |
| OPERAND_CASE(R600::OpName::clamp) |
| OPERAND_CASE(R600::OpName::src0) |
| OPERAND_CASE(R600::OpName::src0_neg) |
| OPERAND_CASE(R600::OpName::src0_rel) |
| OPERAND_CASE(R600::OpName::src0_abs) |
| OPERAND_CASE(R600::OpName::src0_sel) |
| OPERAND_CASE(R600::OpName::src1) |
| OPERAND_CASE(R600::OpName::src1_neg) |
| OPERAND_CASE(R600::OpName::src1_rel) |
| OPERAND_CASE(R600::OpName::src1_abs) |
| OPERAND_CASE(R600::OpName::src1_sel) |
| OPERAND_CASE(R600::OpName::pred_sel) |
| default: |
| llvm_unreachable("Wrong Operand"); |
| } |
| } |
| |
| #undef OPERAND_CASE |
| |
| MachineInstr *R600InstrInfo::buildSlotOfVectorInstruction( |
| MachineBasicBlock &MBB, MachineInstr *MI, unsigned Slot, unsigned DstReg) |
| const { |
| assert (MI->getOpcode() == R600::DOT_4 && "Not Implemented"); |
| unsigned Opcode; |
| if (ST.getGeneration() <= AMDGPUSubtarget::R700) |
| Opcode = R600::DOT4_r600; |
| else |
| Opcode = R600::DOT4_eg; |
| MachineBasicBlock::iterator I = MI; |
| MachineOperand &Src0 = MI->getOperand( |
| getOperandIdx(MI->getOpcode(), getSlotedOps(R600::OpName::src0, Slot))); |
| MachineOperand &Src1 = MI->getOperand( |
| getOperandIdx(MI->getOpcode(), getSlotedOps(R600::OpName::src1, Slot))); |
| MachineInstr *MIB = buildDefaultInstruction( |
| MBB, I, Opcode, DstReg, Src0.getReg(), Src1.getReg()); |
| static const unsigned Operands[14] = { |
| R600::OpName::update_exec_mask, |
| R600::OpName::update_pred, |
| R600::OpName::write, |
| R600::OpName::omod, |
| R600::OpName::dst_rel, |
| R600::OpName::clamp, |
| R600::OpName::src0_neg, |
| R600::OpName::src0_rel, |
| R600::OpName::src0_abs, |
| R600::OpName::src0_sel, |
| R600::OpName::src1_neg, |
| R600::OpName::src1_rel, |
| R600::OpName::src1_abs, |
| R600::OpName::src1_sel, |
| }; |
| |
| MachineOperand &MO = MI->getOperand(getOperandIdx(MI->getOpcode(), |
| getSlotedOps(R600::OpName::pred_sel, Slot))); |
| MIB->getOperand(getOperandIdx(Opcode, R600::OpName::pred_sel)) |
| .setReg(MO.getReg()); |
| |
| for (unsigned i = 0; i < 14; i++) { |
| MachineOperand &MO = MI->getOperand( |
| getOperandIdx(MI->getOpcode(), getSlotedOps(Operands[i], Slot))); |
| assert (MO.isImm()); |
| setImmOperand(*MIB, Operands[i], MO.getImm()); |
| } |
| MIB->getOperand(20).setImm(0); |
| return MIB; |
| } |
| |
| MachineInstr *R600InstrInfo::buildMovImm(MachineBasicBlock &BB, |
| MachineBasicBlock::iterator I, |
| unsigned DstReg, |
| uint64_t Imm) const { |
| MachineInstr *MovImm = buildDefaultInstruction(BB, I, R600::MOV, DstReg, |
| R600::ALU_LITERAL_X); |
| setImmOperand(*MovImm, R600::OpName::literal, Imm); |
| return MovImm; |
| } |
| |
| MachineInstr *R600InstrInfo::buildMovInstr(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator I, |
| unsigned DstReg, unsigned SrcReg) const { |
| return buildDefaultInstruction(*MBB, I, R600::MOV, DstReg, SrcReg); |
| } |
| |
| int R600InstrInfo::getOperandIdx(const MachineInstr &MI, unsigned Op) const { |
| return getOperandIdx(MI.getOpcode(), Op); |
| } |
| |
| int R600InstrInfo::getOperandIdx(unsigned Opcode, unsigned Op) const { |
| return R600::getNamedOperandIdx(Opcode, Op); |
| } |
| |
| void R600InstrInfo::setImmOperand(MachineInstr &MI, unsigned Op, |
| int64_t Imm) const { |
| int Idx = getOperandIdx(MI, Op); |
| assert(Idx != -1 && "Operand not supported for this instruction."); |
| assert(MI.getOperand(Idx).isImm()); |
| MI.getOperand(Idx).setImm(Imm); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Instruction flag getters/setters |
| //===----------------------------------------------------------------------===// |
| |
| MachineOperand &R600InstrInfo::getFlagOp(MachineInstr &MI, unsigned SrcIdx, |
| unsigned Flag) const { |
| unsigned TargetFlags = get(MI.getOpcode()).TSFlags; |
| int FlagIndex = 0; |
| if (Flag != 0) { |
| // If we pass something other than the default value of Flag to this |
| // function, it means we are want to set a flag on an instruction |
| // that uses native encoding. |
| assert(HAS_NATIVE_OPERANDS(TargetFlags)); |
| bool IsOP3 = (TargetFlags & R600_InstFlag::OP3) == R600_InstFlag::OP3; |
| switch (Flag) { |
| case MO_FLAG_CLAMP: |
| FlagIndex = getOperandIdx(MI, R600::OpName::clamp); |
| break; |
| case MO_FLAG_MASK: |
| FlagIndex = getOperandIdx(MI, R600::OpName::write); |
| break; |
| case MO_FLAG_NOT_LAST: |
| case MO_FLAG_LAST: |
| FlagIndex = getOperandIdx(MI, R600::OpName::last); |
| break; |
| case MO_FLAG_NEG: |
| switch (SrcIdx) { |
| case 0: |
| FlagIndex = getOperandIdx(MI, R600::OpName::src0_neg); |
| break; |
| case 1: |
| FlagIndex = getOperandIdx(MI, R600::OpName::src1_neg); |
| break; |
| case 2: |
| FlagIndex = getOperandIdx(MI, R600::OpName::src2_neg); |
| break; |
| } |
| break; |
| |
| case MO_FLAG_ABS: |
| assert(!IsOP3 && "Cannot set absolute value modifier for OP3 " |
| "instructions."); |
| (void)IsOP3; |
| switch (SrcIdx) { |
| case 0: |
| FlagIndex = getOperandIdx(MI, R600::OpName::src0_abs); |
| break; |
| case 1: |
| FlagIndex = getOperandIdx(MI, R600::OpName::src1_abs); |
| break; |
| } |
| break; |
| |
| default: |
| FlagIndex = -1; |
| break; |
| } |
| assert(FlagIndex != -1 && "Flag not supported for this instruction"); |
| } else { |
| FlagIndex = GET_FLAG_OPERAND_IDX(TargetFlags); |
| assert(FlagIndex != 0 && |
| "Instruction flags not supported for this instruction"); |
| } |
| |
| MachineOperand &FlagOp = MI.getOperand(FlagIndex); |
| assert(FlagOp.isImm()); |
| return FlagOp; |
| } |
| |
| void R600InstrInfo::addFlag(MachineInstr &MI, unsigned Operand, |
| unsigned Flag) const { |
| unsigned TargetFlags = get(MI.getOpcode()).TSFlags; |
| if (Flag == 0) { |
| return; |
| } |
| if (HAS_NATIVE_OPERANDS(TargetFlags)) { |
| MachineOperand &FlagOp = getFlagOp(MI, Operand, Flag); |
| if (Flag == MO_FLAG_NOT_LAST) { |
| clearFlag(MI, Operand, MO_FLAG_LAST); |
| } else if (Flag == MO_FLAG_MASK) { |
| clearFlag(MI, Operand, Flag); |
| } else { |
| FlagOp.setImm(1); |
| } |
| } else { |
| MachineOperand &FlagOp = getFlagOp(MI, Operand); |
| FlagOp.setImm(FlagOp.getImm() | (Flag << (NUM_MO_FLAGS * Operand))); |
| } |
| } |
| |
| void R600InstrInfo::clearFlag(MachineInstr &MI, unsigned Operand, |
| unsigned Flag) const { |
| unsigned TargetFlags = get(MI.getOpcode()).TSFlags; |
| if (HAS_NATIVE_OPERANDS(TargetFlags)) { |
| MachineOperand &FlagOp = getFlagOp(MI, Operand, Flag); |
| FlagOp.setImm(0); |
| } else { |
| MachineOperand &FlagOp = getFlagOp(MI); |
| unsigned InstFlags = FlagOp.getImm(); |
| InstFlags &= ~(Flag << (NUM_MO_FLAGS * Operand)); |
| FlagOp.setImm(InstFlags); |
| } |
| } |
| |
| unsigned R600InstrInfo::getAddressSpaceForPseudoSourceKind( |
| unsigned Kind) const { |
| switch (Kind) { |
| case PseudoSourceValue::Stack: |
| case PseudoSourceValue::FixedStack: |
| return AMDGPUAS::PRIVATE_ADDRESS; |
| case PseudoSourceValue::ConstantPool: |
| case PseudoSourceValue::GOT: |
| case PseudoSourceValue::JumpTable: |
| case PseudoSourceValue::GlobalValueCallEntry: |
| case PseudoSourceValue::ExternalSymbolCallEntry: |
| case PseudoSourceValue::TargetCustom: |
| return AMDGPUAS::CONSTANT_ADDRESS; |
| } |
| |
| llvm_unreachable("Invalid pseudo source kind"); |
| } |