mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-30 21:29:28 +00:00
type (in addition to the memory type). The *LoadExt* legalization handling used to only have one type, the memory type. This forced users to assume that as long as the extload for the memory type was declared legal, and the result type was legal, the whole extload was legal. However, this isn't always the case. For instance, on X86, with AVX, this is legal: v4i32 load, zext from v4i8 but this isn't: v4i64 load, zext from v4i8 Whereas v4i64 is (arguably) legal, even without AVX2. Note that the same thing was done a while ago for truncstores (r46140), but I assume no one needed it yet for extloads, so here we go. Calls to getLoadExtAction were changed to add the value type, found manually in the surrounding code. Calls to setLoadExtAction were mechanically changed, by wrapping the call in a loop, to match previous behavior. The loop iterates over the MVT subrange corresponding to the memory type (FP vectors, etc...). I also pulled neighboring setTruncStoreActions into some of the loops; those shouldn't make a difference, as the additional types are illegal. (e.g., i128->i1 truncstores on PPC.) No functional change intended. Differential Revision: http://reviews.llvm.org/D6532 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225421 91177308-0d34-0410-b5e6-96231b3b80d8
2299 lines
81 KiB
C++
2299 lines
81 KiB
C++
//===-- R600ISelLowering.cpp - R600 DAG Lowering Implementation -----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
/// \file
|
|
/// \brief Custom DAG lowering for R600
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "R600ISelLowering.h"
|
|
#include "AMDGPUFrameLowering.h"
|
|
#include "AMDGPUIntrinsicInfo.h"
|
|
#include "AMDGPUSubtarget.h"
|
|
#include "R600Defines.h"
|
|
#include "R600InstrInfo.h"
|
|
#include "R600MachineFunctionInfo.h"
|
|
#include "llvm/Analysis/ValueTracking.h"
|
|
#include "llvm/CodeGen/CallingConvLower.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/SelectionDAG.h"
|
|
#include "llvm/IR/Argument.h"
|
|
#include "llvm/IR/Function.h"
|
|
|
|
using namespace llvm;
|
|
|
|
R600TargetLowering::R600TargetLowering(TargetMachine &TM) :
|
|
AMDGPUTargetLowering(TM),
|
|
Gen(TM.getSubtarget<AMDGPUSubtarget>().getGeneration()) {
|
|
addRegisterClass(MVT::v4f32, &AMDGPU::R600_Reg128RegClass);
|
|
addRegisterClass(MVT::f32, &AMDGPU::R600_Reg32RegClass);
|
|
addRegisterClass(MVT::v4i32, &AMDGPU::R600_Reg128RegClass);
|
|
addRegisterClass(MVT::i32, &AMDGPU::R600_Reg32RegClass);
|
|
addRegisterClass(MVT::v2f32, &AMDGPU::R600_Reg64RegClass);
|
|
addRegisterClass(MVT::v2i32, &AMDGPU::R600_Reg64RegClass);
|
|
|
|
computeRegisterProperties();
|
|
|
|
// Set condition code actions
|
|
setCondCodeAction(ISD::SETO, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETUO, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETLT, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETLE, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETOLT, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETOLE, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETONE, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETUEQ, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETUGE, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETUGT, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETULT, MVT::f32, Expand);
|
|
setCondCodeAction(ISD::SETULE, MVT::f32, Expand);
|
|
|
|
setCondCodeAction(ISD::SETLE, MVT::i32, Expand);
|
|
setCondCodeAction(ISD::SETLT, MVT::i32, Expand);
|
|
setCondCodeAction(ISD::SETULE, MVT::i32, Expand);
|
|
setCondCodeAction(ISD::SETULT, MVT::i32, Expand);
|
|
|
|
setOperationAction(ISD::FCOS, MVT::f32, Custom);
|
|
setOperationAction(ISD::FSIN, MVT::f32, Custom);
|
|
|
|
setOperationAction(ISD::SETCC, MVT::v4i32, Expand);
|
|
setOperationAction(ISD::SETCC, MVT::v2i32, Expand);
|
|
|
|
setOperationAction(ISD::BR_CC, MVT::i32, Expand);
|
|
setOperationAction(ISD::BR_CC, MVT::f32, Expand);
|
|
setOperationAction(ISD::BRCOND, MVT::Other, Custom);
|
|
|
|
setOperationAction(ISD::FSUB, MVT::f32, Expand);
|
|
|
|
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
|
|
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
|
|
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::i1, Custom);
|
|
|
|
setOperationAction(ISD::SELECT_CC, MVT::f32, Custom);
|
|
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
|
|
|
|
setOperationAction(ISD::SETCC, MVT::i32, Expand);
|
|
setOperationAction(ISD::SETCC, MVT::f32, Expand);
|
|
setOperationAction(ISD::FP_TO_UINT, MVT::i1, Custom);
|
|
setOperationAction(ISD::FP_TO_SINT, MVT::i64, Custom);
|
|
setOperationAction(ISD::FP_TO_UINT, MVT::i64, Custom);
|
|
|
|
setOperationAction(ISD::SELECT, MVT::i32, Expand);
|
|
setOperationAction(ISD::SELECT, MVT::f32, Expand);
|
|
setOperationAction(ISD::SELECT, MVT::v2i32, Expand);
|
|
setOperationAction(ISD::SELECT, MVT::v4i32, Expand);
|
|
|
|
// Expand sign extension of vectors
|
|
if (!Subtarget->hasBFE())
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand);
|
|
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i1, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v4i1, Expand);
|
|
|
|
if (!Subtarget->hasBFE())
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i8, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v4i8, Expand);
|
|
|
|
if (!Subtarget->hasBFE())
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i16, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v4i16, Expand);
|
|
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i32, Legal);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i32, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v4i32, Expand);
|
|
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::Other, Expand);
|
|
|
|
|
|
// Legalize loads and stores to the private address space.
|
|
setOperationAction(ISD::LOAD, MVT::i32, Custom);
|
|
setOperationAction(ISD::LOAD, MVT::v2i32, Custom);
|
|
setOperationAction(ISD::LOAD, MVT::v4i32, Custom);
|
|
|
|
// EXTLOAD should be the same as ZEXTLOAD. It is legal for some address
|
|
// spaces, so it is custom lowered to handle those where it isn't.
|
|
for (MVT VT : MVT::integer_valuetypes()) {
|
|
setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote);
|
|
setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i8, Custom);
|
|
setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i16, Custom);
|
|
|
|
setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote);
|
|
setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i8, Custom);
|
|
setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i16, Custom);
|
|
|
|
setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote);
|
|
setLoadExtAction(ISD::EXTLOAD, VT, MVT::i8, Custom);
|
|
setLoadExtAction(ISD::EXTLOAD, VT, MVT::i16, Custom);
|
|
}
|
|
|
|
setOperationAction(ISD::STORE, MVT::i8, Custom);
|
|
setOperationAction(ISD::STORE, MVT::i32, Custom);
|
|
setOperationAction(ISD::STORE, MVT::v2i32, Custom);
|
|
setOperationAction(ISD::STORE, MVT::v4i32, Custom);
|
|
setTruncStoreAction(MVT::i32, MVT::i8, Custom);
|
|
setTruncStoreAction(MVT::i32, MVT::i16, Custom);
|
|
|
|
setOperationAction(ISD::LOAD, MVT::i32, Custom);
|
|
setOperationAction(ISD::LOAD, MVT::v4i32, Custom);
|
|
setOperationAction(ISD::FrameIndex, MVT::i32, Custom);
|
|
|
|
setOperationAction(ISD::EXTRACT_VECTOR_ELT, MVT::v2i32, Custom);
|
|
setOperationAction(ISD::EXTRACT_VECTOR_ELT, MVT::v2f32, Custom);
|
|
setOperationAction(ISD::EXTRACT_VECTOR_ELT, MVT::v4i32, Custom);
|
|
setOperationAction(ISD::EXTRACT_VECTOR_ELT, MVT::v4f32, Custom);
|
|
|
|
setOperationAction(ISD::INSERT_VECTOR_ELT, MVT::v2i32, Custom);
|
|
setOperationAction(ISD::INSERT_VECTOR_ELT, MVT::v2f32, Custom);
|
|
setOperationAction(ISD::INSERT_VECTOR_ELT, MVT::v4i32, Custom);
|
|
setOperationAction(ISD::INSERT_VECTOR_ELT, MVT::v4f32, Custom);
|
|
|
|
setTargetDAGCombine(ISD::FP_ROUND);
|
|
setTargetDAGCombine(ISD::FP_TO_SINT);
|
|
setTargetDAGCombine(ISD::EXTRACT_VECTOR_ELT);
|
|
setTargetDAGCombine(ISD::SELECT_CC);
|
|
setTargetDAGCombine(ISD::INSERT_VECTOR_ELT);
|
|
|
|
setOperationAction(ISD::SUB, MVT::i64, Expand);
|
|
|
|
// These should be replaced by UDVIREM, but it does not happen automatically
|
|
// during Type Legalization
|
|
setOperationAction(ISD::UDIV, MVT::i64, Custom);
|
|
setOperationAction(ISD::UREM, MVT::i64, Custom);
|
|
setOperationAction(ISD::SDIV, MVT::i64, Custom);
|
|
setOperationAction(ISD::SREM, MVT::i64, Custom);
|
|
|
|
// We don't have 64-bit shifts. Thus we need either SHX i64 or SHX_PARTS i32
|
|
// to be Legal/Custom in order to avoid library calls.
|
|
setOperationAction(ISD::SHL_PARTS, MVT::i32, Custom);
|
|
setOperationAction(ISD::SRL_PARTS, MVT::i32, Custom);
|
|
setOperationAction(ISD::SRA_PARTS, MVT::i32, Custom);
|
|
|
|
setOperationAction(ISD::GlobalAddress, MVT::i32, Custom);
|
|
|
|
const MVT ScalarIntVTs[] = { MVT::i32, MVT::i64 };
|
|
for (MVT VT : ScalarIntVTs) {
|
|
setOperationAction(ISD::ADDC, VT, Expand);
|
|
setOperationAction(ISD::SUBC, VT, Expand);
|
|
setOperationAction(ISD::ADDE, VT, Expand);
|
|
setOperationAction(ISD::SUBE, VT, Expand);
|
|
}
|
|
|
|
setSchedulingPreference(Sched::Source);
|
|
}
|
|
|
|
MachineBasicBlock * R600TargetLowering::EmitInstrWithCustomInserter(
|
|
MachineInstr * MI, MachineBasicBlock * BB) const {
|
|
MachineFunction * MF = BB->getParent();
|
|
MachineRegisterInfo &MRI = MF->getRegInfo();
|
|
MachineBasicBlock::iterator I = *MI;
|
|
const R600InstrInfo *TII =
|
|
static_cast<const R600InstrInfo *>(MF->getSubtarget().getInstrInfo());
|
|
|
|
switch (MI->getOpcode()) {
|
|
default:
|
|
// Replace LDS_*_RET instruction that don't have any uses with the
|
|
// equivalent LDS_*_NORET instruction.
|
|
if (TII->isLDSRetInstr(MI->getOpcode())) {
|
|
int DstIdx = TII->getOperandIdx(MI->getOpcode(), AMDGPU::OpName::dst);
|
|
assert(DstIdx != -1);
|
|
MachineInstrBuilder NewMI;
|
|
// FIXME: getLDSNoRetOp method only handles LDS_1A1D LDS ops. Add
|
|
// LDS_1A2D support and remove this special case.
|
|
if (!MRI.use_empty(MI->getOperand(DstIdx).getReg()) ||
|
|
MI->getOpcode() == AMDGPU::LDS_CMPST_RET)
|
|
return BB;
|
|
|
|
NewMI = BuildMI(*BB, I, BB->findDebugLoc(I),
|
|
TII->get(AMDGPU::getLDSNoRetOp(MI->getOpcode())));
|
|
for (unsigned i = 1, e = MI->getNumOperands(); i < e; ++i) {
|
|
NewMI.addOperand(MI->getOperand(i));
|
|
}
|
|
} else {
|
|
return AMDGPUTargetLowering::EmitInstrWithCustomInserter(MI, BB);
|
|
}
|
|
break;
|
|
case AMDGPU::CLAMP_R600: {
|
|
MachineInstr *NewMI = TII->buildDefaultInstruction(*BB, I,
|
|
AMDGPU::MOV,
|
|
MI->getOperand(0).getReg(),
|
|
MI->getOperand(1).getReg());
|
|
TII->addFlag(NewMI, 0, MO_FLAG_CLAMP);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::FABS_R600: {
|
|
MachineInstr *NewMI = TII->buildDefaultInstruction(*BB, I,
|
|
AMDGPU::MOV,
|
|
MI->getOperand(0).getReg(),
|
|
MI->getOperand(1).getReg());
|
|
TII->addFlag(NewMI, 0, MO_FLAG_ABS);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::FNEG_R600: {
|
|
MachineInstr *NewMI = TII->buildDefaultInstruction(*BB, I,
|
|
AMDGPU::MOV,
|
|
MI->getOperand(0).getReg(),
|
|
MI->getOperand(1).getReg());
|
|
TII->addFlag(NewMI, 0, MO_FLAG_NEG);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::MASK_WRITE: {
|
|
unsigned maskedRegister = MI->getOperand(0).getReg();
|
|
assert(TargetRegisterInfo::isVirtualRegister(maskedRegister));
|
|
MachineInstr * defInstr = MRI.getVRegDef(maskedRegister);
|
|
TII->addFlag(defInstr, 0, MO_FLAG_MASK);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::MOV_IMM_F32:
|
|
TII->buildMovImm(*BB, I, MI->getOperand(0).getReg(),
|
|
MI->getOperand(1).getFPImm()->getValueAPF()
|
|
.bitcastToAPInt().getZExtValue());
|
|
break;
|
|
case AMDGPU::MOV_IMM_I32:
|
|
TII->buildMovImm(*BB, I, MI->getOperand(0).getReg(),
|
|
MI->getOperand(1).getImm());
|
|
break;
|
|
case AMDGPU::CONST_COPY: {
|
|
MachineInstr *NewMI = TII->buildDefaultInstruction(*BB, MI, AMDGPU::MOV,
|
|
MI->getOperand(0).getReg(), AMDGPU::ALU_CONST);
|
|
TII->setImmOperand(NewMI, AMDGPU::OpName::src0_sel,
|
|
MI->getOperand(1).getImm());
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::RAT_WRITE_CACHELESS_32_eg:
|
|
case AMDGPU::RAT_WRITE_CACHELESS_64_eg:
|
|
case AMDGPU::RAT_WRITE_CACHELESS_128_eg: {
|
|
unsigned EOP = (std::next(I)->getOpcode() == AMDGPU::RETURN) ? 1 : 0;
|
|
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(MI->getOpcode()))
|
|
.addOperand(MI->getOperand(0))
|
|
.addOperand(MI->getOperand(1))
|
|
.addImm(EOP); // Set End of program bit
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::TXD: {
|
|
unsigned T0 = MRI.createVirtualRegister(&AMDGPU::R600_Reg128RegClass);
|
|
unsigned T1 = MRI.createVirtualRegister(&AMDGPU::R600_Reg128RegClass);
|
|
MachineOperand &RID = MI->getOperand(4);
|
|
MachineOperand &SID = MI->getOperand(5);
|
|
unsigned TextureId = MI->getOperand(6).getImm();
|
|
unsigned SrcX = 0, SrcY = 1, SrcZ = 2, SrcW = 3;
|
|
unsigned CTX = 1, CTY = 1, CTZ = 1, CTW = 1;
|
|
|
|
switch (TextureId) {
|
|
case 5: // Rect
|
|
CTX = CTY = 0;
|
|
break;
|
|
case 6: // Shadow1D
|
|
SrcW = SrcZ;
|
|
break;
|
|
case 7: // Shadow2D
|
|
SrcW = SrcZ;
|
|
break;
|
|
case 8: // ShadowRect
|
|
CTX = CTY = 0;
|
|
SrcW = SrcZ;
|
|
break;
|
|
case 9: // 1DArray
|
|
SrcZ = SrcY;
|
|
CTZ = 0;
|
|
break;
|
|
case 10: // 2DArray
|
|
CTZ = 0;
|
|
break;
|
|
case 11: // Shadow1DArray
|
|
SrcZ = SrcY;
|
|
CTZ = 0;
|
|
break;
|
|
case 12: // Shadow2DArray
|
|
CTZ = 0;
|
|
break;
|
|
}
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::TEX_SET_GRADIENTS_H), T0)
|
|
.addOperand(MI->getOperand(3))
|
|
.addImm(SrcX)
|
|
.addImm(SrcY)
|
|
.addImm(SrcZ)
|
|
.addImm(SrcW)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(1)
|
|
.addImm(2)
|
|
.addImm(3)
|
|
.addOperand(RID)
|
|
.addOperand(SID)
|
|
.addImm(CTX)
|
|
.addImm(CTY)
|
|
.addImm(CTZ)
|
|
.addImm(CTW);
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::TEX_SET_GRADIENTS_V), T1)
|
|
.addOperand(MI->getOperand(2))
|
|
.addImm(SrcX)
|
|
.addImm(SrcY)
|
|
.addImm(SrcZ)
|
|
.addImm(SrcW)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(1)
|
|
.addImm(2)
|
|
.addImm(3)
|
|
.addOperand(RID)
|
|
.addOperand(SID)
|
|
.addImm(CTX)
|
|
.addImm(CTY)
|
|
.addImm(CTZ)
|
|
.addImm(CTW);
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::TEX_SAMPLE_G))
|
|
.addOperand(MI->getOperand(0))
|
|
.addOperand(MI->getOperand(1))
|
|
.addImm(SrcX)
|
|
.addImm(SrcY)
|
|
.addImm(SrcZ)
|
|
.addImm(SrcW)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(1)
|
|
.addImm(2)
|
|
.addImm(3)
|
|
.addOperand(RID)
|
|
.addOperand(SID)
|
|
.addImm(CTX)
|
|
.addImm(CTY)
|
|
.addImm(CTZ)
|
|
.addImm(CTW)
|
|
.addReg(T0, RegState::Implicit)
|
|
.addReg(T1, RegState::Implicit);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::TXD_SHADOW: {
|
|
unsigned T0 = MRI.createVirtualRegister(&AMDGPU::R600_Reg128RegClass);
|
|
unsigned T1 = MRI.createVirtualRegister(&AMDGPU::R600_Reg128RegClass);
|
|
MachineOperand &RID = MI->getOperand(4);
|
|
MachineOperand &SID = MI->getOperand(5);
|
|
unsigned TextureId = MI->getOperand(6).getImm();
|
|
unsigned SrcX = 0, SrcY = 1, SrcZ = 2, SrcW = 3;
|
|
unsigned CTX = 1, CTY = 1, CTZ = 1, CTW = 1;
|
|
|
|
switch (TextureId) {
|
|
case 5: // Rect
|
|
CTX = CTY = 0;
|
|
break;
|
|
case 6: // Shadow1D
|
|
SrcW = SrcZ;
|
|
break;
|
|
case 7: // Shadow2D
|
|
SrcW = SrcZ;
|
|
break;
|
|
case 8: // ShadowRect
|
|
CTX = CTY = 0;
|
|
SrcW = SrcZ;
|
|
break;
|
|
case 9: // 1DArray
|
|
SrcZ = SrcY;
|
|
CTZ = 0;
|
|
break;
|
|
case 10: // 2DArray
|
|
CTZ = 0;
|
|
break;
|
|
case 11: // Shadow1DArray
|
|
SrcZ = SrcY;
|
|
CTZ = 0;
|
|
break;
|
|
case 12: // Shadow2DArray
|
|
CTZ = 0;
|
|
break;
|
|
}
|
|
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::TEX_SET_GRADIENTS_H), T0)
|
|
.addOperand(MI->getOperand(3))
|
|
.addImm(SrcX)
|
|
.addImm(SrcY)
|
|
.addImm(SrcZ)
|
|
.addImm(SrcW)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(1)
|
|
.addImm(2)
|
|
.addImm(3)
|
|
.addOperand(RID)
|
|
.addOperand(SID)
|
|
.addImm(CTX)
|
|
.addImm(CTY)
|
|
.addImm(CTZ)
|
|
.addImm(CTW);
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::TEX_SET_GRADIENTS_V), T1)
|
|
.addOperand(MI->getOperand(2))
|
|
.addImm(SrcX)
|
|
.addImm(SrcY)
|
|
.addImm(SrcZ)
|
|
.addImm(SrcW)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(1)
|
|
.addImm(2)
|
|
.addImm(3)
|
|
.addOperand(RID)
|
|
.addOperand(SID)
|
|
.addImm(CTX)
|
|
.addImm(CTY)
|
|
.addImm(CTZ)
|
|
.addImm(CTW);
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::TEX_SAMPLE_C_G))
|
|
.addOperand(MI->getOperand(0))
|
|
.addOperand(MI->getOperand(1))
|
|
.addImm(SrcX)
|
|
.addImm(SrcY)
|
|
.addImm(SrcZ)
|
|
.addImm(SrcW)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(0)
|
|
.addImm(1)
|
|
.addImm(2)
|
|
.addImm(3)
|
|
.addOperand(RID)
|
|
.addOperand(SID)
|
|
.addImm(CTX)
|
|
.addImm(CTY)
|
|
.addImm(CTZ)
|
|
.addImm(CTW)
|
|
.addReg(T0, RegState::Implicit)
|
|
.addReg(T1, RegState::Implicit);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::BRANCH:
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::JUMP))
|
|
.addOperand(MI->getOperand(0));
|
|
break;
|
|
|
|
case AMDGPU::BRANCH_COND_f32: {
|
|
MachineInstr *NewMI =
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::PRED_X),
|
|
AMDGPU::PREDICATE_BIT)
|
|
.addOperand(MI->getOperand(1))
|
|
.addImm(OPCODE_IS_NOT_ZERO)
|
|
.addImm(0); // Flags
|
|
TII->addFlag(NewMI, 0, MO_FLAG_PUSH);
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::JUMP_COND))
|
|
.addOperand(MI->getOperand(0))
|
|
.addReg(AMDGPU::PREDICATE_BIT, RegState::Kill);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::BRANCH_COND_i32: {
|
|
MachineInstr *NewMI =
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::PRED_X),
|
|
AMDGPU::PREDICATE_BIT)
|
|
.addOperand(MI->getOperand(1))
|
|
.addImm(OPCODE_IS_NOT_ZERO_INT)
|
|
.addImm(0); // Flags
|
|
TII->addFlag(NewMI, 0, MO_FLAG_PUSH);
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(AMDGPU::JUMP_COND))
|
|
.addOperand(MI->getOperand(0))
|
|
.addReg(AMDGPU::PREDICATE_BIT, RegState::Kill);
|
|
break;
|
|
}
|
|
|
|
case AMDGPU::EG_ExportSwz:
|
|
case AMDGPU::R600_ExportSwz: {
|
|
// Instruction is left unmodified if its not the last one of its type
|
|
bool isLastInstructionOfItsType = true;
|
|
unsigned InstExportType = MI->getOperand(1).getImm();
|
|
for (MachineBasicBlock::iterator NextExportInst = std::next(I),
|
|
EndBlock = BB->end(); NextExportInst != EndBlock;
|
|
NextExportInst = std::next(NextExportInst)) {
|
|
if (NextExportInst->getOpcode() == AMDGPU::EG_ExportSwz ||
|
|
NextExportInst->getOpcode() == AMDGPU::R600_ExportSwz) {
|
|
unsigned CurrentInstExportType = NextExportInst->getOperand(1)
|
|
.getImm();
|
|
if (CurrentInstExportType == InstExportType) {
|
|
isLastInstructionOfItsType = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
bool EOP = (std::next(I)->getOpcode() == AMDGPU::RETURN) ? 1 : 0;
|
|
if (!EOP && !isLastInstructionOfItsType)
|
|
return BB;
|
|
unsigned CfInst = (MI->getOpcode() == AMDGPU::EG_ExportSwz)? 84 : 40;
|
|
BuildMI(*BB, I, BB->findDebugLoc(I), TII->get(MI->getOpcode()))
|
|
.addOperand(MI->getOperand(0))
|
|
.addOperand(MI->getOperand(1))
|
|
.addOperand(MI->getOperand(2))
|
|
.addOperand(MI->getOperand(3))
|
|
.addOperand(MI->getOperand(4))
|
|
.addOperand(MI->getOperand(5))
|
|
.addOperand(MI->getOperand(6))
|
|
.addImm(CfInst)
|
|
.addImm(EOP);
|
|
break;
|
|
}
|
|
case AMDGPU::RETURN: {
|
|
// RETURN instructions must have the live-out registers as implicit uses,
|
|
// otherwise they appear dead.
|
|
R600MachineFunctionInfo *MFI = MF->getInfo<R600MachineFunctionInfo>();
|
|
MachineInstrBuilder MIB(*MF, MI);
|
|
for (unsigned i = 0, e = MFI->LiveOuts.size(); i != e; ++i)
|
|
MIB.addReg(MFI->LiveOuts[i], RegState::Implicit);
|
|
return BB;
|
|
}
|
|
}
|
|
|
|
MI->eraseFromParent();
|
|
return BB;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Custom DAG Lowering Operations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SDValue R600TargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
R600MachineFunctionInfo *MFI = MF.getInfo<R600MachineFunctionInfo>();
|
|
switch (Op.getOpcode()) {
|
|
default: return AMDGPUTargetLowering::LowerOperation(Op, DAG);
|
|
case ISD::EXTRACT_VECTOR_ELT: return LowerEXTRACT_VECTOR_ELT(Op, DAG);
|
|
case ISD::INSERT_VECTOR_ELT: return LowerINSERT_VECTOR_ELT(Op, DAG);
|
|
case ISD::SHL_PARTS: return LowerSHLParts(Op, DAG);
|
|
case ISD::SRA_PARTS:
|
|
case ISD::SRL_PARTS: return LowerSRXParts(Op, DAG);
|
|
case ISD::FCOS:
|
|
case ISD::FSIN: return LowerTrig(Op, DAG);
|
|
case ISD::SELECT_CC: return LowerSELECT_CC(Op, DAG);
|
|
case ISD::STORE: return LowerSTORE(Op, DAG);
|
|
case ISD::LOAD: {
|
|
SDValue Result = LowerLOAD(Op, DAG);
|
|
assert((!Result.getNode() ||
|
|
Result.getNode()->getNumValues() == 2) &&
|
|
"Load should return a value and a chain");
|
|
return Result;
|
|
}
|
|
|
|
case ISD::BRCOND: return LowerBRCOND(Op, DAG);
|
|
case ISD::GlobalAddress: return LowerGlobalAddress(MFI, Op, DAG);
|
|
case ISD::INTRINSIC_VOID: {
|
|
SDValue Chain = Op.getOperand(0);
|
|
unsigned IntrinsicID =
|
|
cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
|
|
switch (IntrinsicID) {
|
|
case AMDGPUIntrinsic::AMDGPU_store_output: {
|
|
int64_t RegIndex = cast<ConstantSDNode>(Op.getOperand(3))->getZExtValue();
|
|
unsigned Reg = AMDGPU::R600_TReg32RegClass.getRegister(RegIndex);
|
|
MFI->LiveOuts.push_back(Reg);
|
|
return DAG.getCopyToReg(Chain, SDLoc(Op), Reg, Op.getOperand(2));
|
|
}
|
|
case AMDGPUIntrinsic::R600_store_swizzle: {
|
|
const SDValue Args[8] = {
|
|
Chain,
|
|
Op.getOperand(2), // Export Value
|
|
Op.getOperand(3), // ArrayBase
|
|
Op.getOperand(4), // Type
|
|
DAG.getConstant(0, MVT::i32), // SWZ_X
|
|
DAG.getConstant(1, MVT::i32), // SWZ_Y
|
|
DAG.getConstant(2, MVT::i32), // SWZ_Z
|
|
DAG.getConstant(3, MVT::i32) // SWZ_W
|
|
};
|
|
return DAG.getNode(AMDGPUISD::EXPORT, SDLoc(Op), Op.getValueType(), Args);
|
|
}
|
|
|
|
// default for switch(IntrinsicID)
|
|
default: break;
|
|
}
|
|
// break out of case ISD::INTRINSIC_VOID in switch(Op.getOpcode())
|
|
break;
|
|
}
|
|
case ISD::INTRINSIC_WO_CHAIN: {
|
|
unsigned IntrinsicID =
|
|
cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue();
|
|
EVT VT = Op.getValueType();
|
|
SDLoc DL(Op);
|
|
switch(IntrinsicID) {
|
|
default: return AMDGPUTargetLowering::LowerOperation(Op, DAG);
|
|
case AMDGPUIntrinsic::R600_load_input: {
|
|
int64_t RegIndex = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
|
|
unsigned Reg = AMDGPU::R600_TReg32RegClass.getRegister(RegIndex);
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
MachineRegisterInfo &MRI = MF.getRegInfo();
|
|
MRI.addLiveIn(Reg);
|
|
return DAG.getCopyFromReg(DAG.getEntryNode(),
|
|
SDLoc(DAG.getEntryNode()), Reg, VT);
|
|
}
|
|
|
|
case AMDGPUIntrinsic::R600_interp_input: {
|
|
int slot = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
|
|
int ijb = cast<ConstantSDNode>(Op.getOperand(2))->getSExtValue();
|
|
MachineSDNode *interp;
|
|
if (ijb < 0) {
|
|
const MachineFunction &MF = DAG.getMachineFunction();
|
|
const R600InstrInfo *TII = static_cast<const R600InstrInfo *>(
|
|
MF.getSubtarget().getInstrInfo());
|
|
interp = DAG.getMachineNode(AMDGPU::INTERP_VEC_LOAD, DL,
|
|
MVT::v4f32, DAG.getTargetConstant(slot / 4 , MVT::i32));
|
|
return DAG.getTargetExtractSubreg(
|
|
TII->getRegisterInfo().getSubRegFromChannel(slot % 4),
|
|
DL, MVT::f32, SDValue(interp, 0));
|
|
}
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
MachineRegisterInfo &MRI = MF.getRegInfo();
|
|
unsigned RegisterI = AMDGPU::R600_TReg32RegClass.getRegister(2 * ijb);
|
|
unsigned RegisterJ = AMDGPU::R600_TReg32RegClass.getRegister(2 * ijb + 1);
|
|
MRI.addLiveIn(RegisterI);
|
|
MRI.addLiveIn(RegisterJ);
|
|
SDValue RegisterINode = DAG.getCopyFromReg(DAG.getEntryNode(),
|
|
SDLoc(DAG.getEntryNode()), RegisterI, MVT::f32);
|
|
SDValue RegisterJNode = DAG.getCopyFromReg(DAG.getEntryNode(),
|
|
SDLoc(DAG.getEntryNode()), RegisterJ, MVT::f32);
|
|
|
|
if (slot % 4 < 2)
|
|
interp = DAG.getMachineNode(AMDGPU::INTERP_PAIR_XY, DL,
|
|
MVT::f32, MVT::f32, DAG.getTargetConstant(slot / 4 , MVT::i32),
|
|
RegisterJNode, RegisterINode);
|
|
else
|
|
interp = DAG.getMachineNode(AMDGPU::INTERP_PAIR_ZW, DL,
|
|
MVT::f32, MVT::f32, DAG.getTargetConstant(slot / 4 , MVT::i32),
|
|
RegisterJNode, RegisterINode);
|
|
return SDValue(interp, slot % 2);
|
|
}
|
|
case AMDGPUIntrinsic::R600_interp_xy:
|
|
case AMDGPUIntrinsic::R600_interp_zw: {
|
|
int slot = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
|
|
MachineSDNode *interp;
|
|
SDValue RegisterINode = Op.getOperand(2);
|
|
SDValue RegisterJNode = Op.getOperand(3);
|
|
|
|
if (IntrinsicID == AMDGPUIntrinsic::R600_interp_xy)
|
|
interp = DAG.getMachineNode(AMDGPU::INTERP_PAIR_XY, DL,
|
|
MVT::f32, MVT::f32, DAG.getTargetConstant(slot, MVT::i32),
|
|
RegisterJNode, RegisterINode);
|
|
else
|
|
interp = DAG.getMachineNode(AMDGPU::INTERP_PAIR_ZW, DL,
|
|
MVT::f32, MVT::f32, DAG.getTargetConstant(slot, MVT::i32),
|
|
RegisterJNode, RegisterINode);
|
|
return DAG.getNode(ISD::BUILD_VECTOR, DL, MVT::v2f32,
|
|
SDValue(interp, 0), SDValue(interp, 1));
|
|
}
|
|
case AMDGPUIntrinsic::R600_tex:
|
|
case AMDGPUIntrinsic::R600_texc:
|
|
case AMDGPUIntrinsic::R600_txl:
|
|
case AMDGPUIntrinsic::R600_txlc:
|
|
case AMDGPUIntrinsic::R600_txb:
|
|
case AMDGPUIntrinsic::R600_txbc:
|
|
case AMDGPUIntrinsic::R600_txf:
|
|
case AMDGPUIntrinsic::R600_txq:
|
|
case AMDGPUIntrinsic::R600_ddx:
|
|
case AMDGPUIntrinsic::R600_ddy:
|
|
case AMDGPUIntrinsic::R600_ldptr: {
|
|
unsigned TextureOp;
|
|
switch (IntrinsicID) {
|
|
case AMDGPUIntrinsic::R600_tex:
|
|
TextureOp = 0;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_texc:
|
|
TextureOp = 1;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_txl:
|
|
TextureOp = 2;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_txlc:
|
|
TextureOp = 3;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_txb:
|
|
TextureOp = 4;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_txbc:
|
|
TextureOp = 5;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_txf:
|
|
TextureOp = 6;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_txq:
|
|
TextureOp = 7;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_ddx:
|
|
TextureOp = 8;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_ddy:
|
|
TextureOp = 9;
|
|
break;
|
|
case AMDGPUIntrinsic::R600_ldptr:
|
|
TextureOp = 10;
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unknow Texture Operation");
|
|
}
|
|
|
|
SDValue TexArgs[19] = {
|
|
DAG.getConstant(TextureOp, MVT::i32),
|
|
Op.getOperand(1),
|
|
DAG.getConstant(0, MVT::i32),
|
|
DAG.getConstant(1, MVT::i32),
|
|
DAG.getConstant(2, MVT::i32),
|
|
DAG.getConstant(3, MVT::i32),
|
|
Op.getOperand(2),
|
|
Op.getOperand(3),
|
|
Op.getOperand(4),
|
|
DAG.getConstant(0, MVT::i32),
|
|
DAG.getConstant(1, MVT::i32),
|
|
DAG.getConstant(2, MVT::i32),
|
|
DAG.getConstant(3, MVT::i32),
|
|
Op.getOperand(5),
|
|
Op.getOperand(6),
|
|
Op.getOperand(7),
|
|
Op.getOperand(8),
|
|
Op.getOperand(9),
|
|
Op.getOperand(10)
|
|
};
|
|
return DAG.getNode(AMDGPUISD::TEXTURE_FETCH, DL, MVT::v4f32, TexArgs);
|
|
}
|
|
case AMDGPUIntrinsic::AMDGPU_dp4: {
|
|
SDValue Args[8] = {
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(1),
|
|
DAG.getConstant(0, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(2),
|
|
DAG.getConstant(0, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(1),
|
|
DAG.getConstant(1, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(2),
|
|
DAG.getConstant(1, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(1),
|
|
DAG.getConstant(2, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(2),
|
|
DAG.getConstant(2, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(1),
|
|
DAG.getConstant(3, MVT::i32)),
|
|
DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::f32, Op.getOperand(2),
|
|
DAG.getConstant(3, MVT::i32))
|
|
};
|
|
return DAG.getNode(AMDGPUISD::DOT4, DL, MVT::f32, Args);
|
|
}
|
|
|
|
case Intrinsic::r600_read_ngroups_x:
|
|
return LowerImplicitParameter(DAG, VT, DL, 0);
|
|
case Intrinsic::r600_read_ngroups_y:
|
|
return LowerImplicitParameter(DAG, VT, DL, 1);
|
|
case Intrinsic::r600_read_ngroups_z:
|
|
return LowerImplicitParameter(DAG, VT, DL, 2);
|
|
case Intrinsic::r600_read_global_size_x:
|
|
return LowerImplicitParameter(DAG, VT, DL, 3);
|
|
case Intrinsic::r600_read_global_size_y:
|
|
return LowerImplicitParameter(DAG, VT, DL, 4);
|
|
case Intrinsic::r600_read_global_size_z:
|
|
return LowerImplicitParameter(DAG, VT, DL, 5);
|
|
case Intrinsic::r600_read_local_size_x:
|
|
return LowerImplicitParameter(DAG, VT, DL, 6);
|
|
case Intrinsic::r600_read_local_size_y:
|
|
return LowerImplicitParameter(DAG, VT, DL, 7);
|
|
case Intrinsic::r600_read_local_size_z:
|
|
return LowerImplicitParameter(DAG, VT, DL, 8);
|
|
|
|
case Intrinsic::AMDGPU_read_workdim:
|
|
return LowerImplicitParameter(DAG, VT, DL, MFI->ABIArgOffset / 4);
|
|
|
|
case Intrinsic::r600_read_tgid_x:
|
|
return CreateLiveInRegister(DAG, &AMDGPU::R600_TReg32RegClass,
|
|
AMDGPU::T1_X, VT);
|
|
case Intrinsic::r600_read_tgid_y:
|
|
return CreateLiveInRegister(DAG, &AMDGPU::R600_TReg32RegClass,
|
|
AMDGPU::T1_Y, VT);
|
|
case Intrinsic::r600_read_tgid_z:
|
|
return CreateLiveInRegister(DAG, &AMDGPU::R600_TReg32RegClass,
|
|
AMDGPU::T1_Z, VT);
|
|
case Intrinsic::r600_read_tidig_x:
|
|
return CreateLiveInRegister(DAG, &AMDGPU::R600_TReg32RegClass,
|
|
AMDGPU::T0_X, VT);
|
|
case Intrinsic::r600_read_tidig_y:
|
|
return CreateLiveInRegister(DAG, &AMDGPU::R600_TReg32RegClass,
|
|
AMDGPU::T0_Y, VT);
|
|
case Intrinsic::r600_read_tidig_z:
|
|
return CreateLiveInRegister(DAG, &AMDGPU::R600_TReg32RegClass,
|
|
AMDGPU::T0_Z, VT);
|
|
case Intrinsic::AMDGPU_rsq:
|
|
// XXX - I'm assuming SI's RSQ_LEGACY matches R600's behavior.
|
|
return DAG.getNode(AMDGPUISD::RSQ_LEGACY, DL, VT, Op.getOperand(1));
|
|
}
|
|
// break out of case ISD::INTRINSIC_WO_CHAIN in switch(Op.getOpcode())
|
|
break;
|
|
}
|
|
} // end switch(Op.getOpcode())
|
|
return SDValue();
|
|
}
|
|
|
|
void R600TargetLowering::ReplaceNodeResults(SDNode *N,
|
|
SmallVectorImpl<SDValue> &Results,
|
|
SelectionDAG &DAG) const {
|
|
switch (N->getOpcode()) {
|
|
default:
|
|
AMDGPUTargetLowering::ReplaceNodeResults(N, Results, DAG);
|
|
return;
|
|
case ISD::FP_TO_UINT:
|
|
if (N->getValueType(0) == MVT::i1) {
|
|
Results.push_back(LowerFPTOUINT(N->getOperand(0), DAG));
|
|
return;
|
|
}
|
|
// Fall-through. Since we don't care about out of bounds values
|
|
// we can use FP_TO_SINT for uints too. The DAGLegalizer code for uint
|
|
// considers some extra cases which are not necessary here.
|
|
case ISD::FP_TO_SINT: {
|
|
SDValue Result;
|
|
if (expandFP_TO_SINT(N, Result, DAG))
|
|
Results.push_back(Result);
|
|
return;
|
|
}
|
|
case ISD::UDIV: {
|
|
SDValue Op = SDValue(N, 0);
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
SDValue UDIVREM = DAG.getNode(ISD::UDIVREM, DL, DAG.getVTList(VT, VT),
|
|
N->getOperand(0), N->getOperand(1));
|
|
Results.push_back(UDIVREM);
|
|
break;
|
|
}
|
|
case ISD::UREM: {
|
|
SDValue Op = SDValue(N, 0);
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
SDValue UDIVREM = DAG.getNode(ISD::UDIVREM, DL, DAG.getVTList(VT, VT),
|
|
N->getOperand(0), N->getOperand(1));
|
|
Results.push_back(UDIVREM.getValue(1));
|
|
break;
|
|
}
|
|
case ISD::SDIV: {
|
|
SDValue Op = SDValue(N, 0);
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
SDValue SDIVREM = DAG.getNode(ISD::SDIVREM, DL, DAG.getVTList(VT, VT),
|
|
N->getOperand(0), N->getOperand(1));
|
|
Results.push_back(SDIVREM);
|
|
break;
|
|
}
|
|
case ISD::SREM: {
|
|
SDValue Op = SDValue(N, 0);
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
SDValue SDIVREM = DAG.getNode(ISD::SDIVREM, DL, DAG.getVTList(VT, VT),
|
|
N->getOperand(0), N->getOperand(1));
|
|
Results.push_back(SDIVREM.getValue(1));
|
|
break;
|
|
}
|
|
case ISD::SDIVREM: {
|
|
SDValue Op = SDValue(N, 1);
|
|
SDValue RES = LowerSDIVREM(Op, DAG);
|
|
Results.push_back(RES);
|
|
Results.push_back(RES.getValue(1));
|
|
break;
|
|
}
|
|
case ISD::UDIVREM: {
|
|
SDValue Op = SDValue(N, 0);
|
|
LowerUDIVREM64(Op, DAG, Results);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SDValue R600TargetLowering::vectorToVerticalVector(SelectionDAG &DAG,
|
|
SDValue Vector) const {
|
|
|
|
SDLoc DL(Vector);
|
|
EVT VecVT = Vector.getValueType();
|
|
EVT EltVT = VecVT.getVectorElementType();
|
|
SmallVector<SDValue, 8> Args;
|
|
|
|
for (unsigned i = 0, e = VecVT.getVectorNumElements();
|
|
i != e; ++i) {
|
|
Args.push_back(DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, EltVT,
|
|
Vector, DAG.getConstant(i, getVectorIdxTy())));
|
|
}
|
|
|
|
return DAG.getNode(AMDGPUISD::BUILD_VERTICAL_VECTOR, DL, VecVT, Args);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerEXTRACT_VECTOR_ELT(SDValue Op,
|
|
SelectionDAG &DAG) const {
|
|
|
|
SDLoc DL(Op);
|
|
SDValue Vector = Op.getOperand(0);
|
|
SDValue Index = Op.getOperand(1);
|
|
|
|
if (isa<ConstantSDNode>(Index) ||
|
|
Vector.getOpcode() == AMDGPUISD::BUILD_VERTICAL_VECTOR)
|
|
return Op;
|
|
|
|
Vector = vectorToVerticalVector(DAG, Vector);
|
|
return DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, Op.getValueType(),
|
|
Vector, Index);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerINSERT_VECTOR_ELT(SDValue Op,
|
|
SelectionDAG &DAG) const {
|
|
SDLoc DL(Op);
|
|
SDValue Vector = Op.getOperand(0);
|
|
SDValue Value = Op.getOperand(1);
|
|
SDValue Index = Op.getOperand(2);
|
|
|
|
if (isa<ConstantSDNode>(Index) ||
|
|
Vector.getOpcode() == AMDGPUISD::BUILD_VERTICAL_VECTOR)
|
|
return Op;
|
|
|
|
Vector = vectorToVerticalVector(DAG, Vector);
|
|
SDValue Insert = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, Op.getValueType(),
|
|
Vector, Value, Index);
|
|
return vectorToVerticalVector(DAG, Insert);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerTrig(SDValue Op, SelectionDAG &DAG) const {
|
|
// On hw >= R700, COS/SIN input must be between -1. and 1.
|
|
// Thus we lower them to TRIG ( FRACT ( x / 2Pi + 0.5) - 0.5)
|
|
EVT VT = Op.getValueType();
|
|
SDValue Arg = Op.getOperand(0);
|
|
SDValue FractPart = DAG.getNode(AMDGPUISD::FRACT, SDLoc(Op), VT,
|
|
DAG.getNode(ISD::FADD, SDLoc(Op), VT,
|
|
DAG.getNode(ISD::FMUL, SDLoc(Op), VT, Arg,
|
|
DAG.getConstantFP(0.15915494309, MVT::f32)),
|
|
DAG.getConstantFP(0.5, MVT::f32)));
|
|
unsigned TrigNode;
|
|
switch (Op.getOpcode()) {
|
|
case ISD::FCOS:
|
|
TrigNode = AMDGPUISD::COS_HW;
|
|
break;
|
|
case ISD::FSIN:
|
|
TrigNode = AMDGPUISD::SIN_HW;
|
|
break;
|
|
default:
|
|
llvm_unreachable("Wrong trig opcode");
|
|
}
|
|
SDValue TrigVal = DAG.getNode(TrigNode, SDLoc(Op), VT,
|
|
DAG.getNode(ISD::FADD, SDLoc(Op), VT, FractPart,
|
|
DAG.getConstantFP(-0.5, MVT::f32)));
|
|
if (Gen >= AMDGPUSubtarget::R700)
|
|
return TrigVal;
|
|
// On R600 hw, COS/SIN input must be between -Pi and Pi.
|
|
return DAG.getNode(ISD::FMUL, SDLoc(Op), VT, TrigVal,
|
|
DAG.getConstantFP(3.14159265359, MVT::f32));
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerSHLParts(SDValue Op, SelectionDAG &DAG) const {
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
|
|
SDValue Lo = Op.getOperand(0);
|
|
SDValue Hi = Op.getOperand(1);
|
|
SDValue Shift = Op.getOperand(2);
|
|
SDValue Zero = DAG.getConstant(0, VT);
|
|
SDValue One = DAG.getConstant(1, VT);
|
|
|
|
SDValue Width = DAG.getConstant(VT.getSizeInBits(), VT);
|
|
SDValue Width1 = DAG.getConstant(VT.getSizeInBits() - 1, VT);
|
|
SDValue BigShift = DAG.getNode(ISD::SUB, DL, VT, Shift, Width);
|
|
SDValue CompShift = DAG.getNode(ISD::SUB, DL, VT, Width1, Shift);
|
|
|
|
// The dance around Width1 is necessary for 0 special case.
|
|
// Without it the CompShift might be 32, producing incorrect results in
|
|
// Overflow. So we do the shift in two steps, the alternative is to
|
|
// add a conditional to filter the special case.
|
|
|
|
SDValue Overflow = DAG.getNode(ISD::SRL, DL, VT, Lo, CompShift);
|
|
Overflow = DAG.getNode(ISD::SRL, DL, VT, Overflow, One);
|
|
|
|
SDValue HiSmall = DAG.getNode(ISD::SHL, DL, VT, Hi, Shift);
|
|
HiSmall = DAG.getNode(ISD::OR, DL, VT, HiSmall, Overflow);
|
|
SDValue LoSmall = DAG.getNode(ISD::SHL, DL, VT, Lo, Shift);
|
|
|
|
SDValue HiBig = DAG.getNode(ISD::SHL, DL, VT, Lo, BigShift);
|
|
SDValue LoBig = Zero;
|
|
|
|
Hi = DAG.getSelectCC(DL, Shift, Width, HiSmall, HiBig, ISD::SETULT);
|
|
Lo = DAG.getSelectCC(DL, Shift, Width, LoSmall, LoBig, ISD::SETULT);
|
|
|
|
return DAG.getNode(ISD::MERGE_VALUES, DL, DAG.getVTList(VT,VT), Lo, Hi);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerSRXParts(SDValue Op, SelectionDAG &DAG) const {
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
|
|
SDValue Lo = Op.getOperand(0);
|
|
SDValue Hi = Op.getOperand(1);
|
|
SDValue Shift = Op.getOperand(2);
|
|
SDValue Zero = DAG.getConstant(0, VT);
|
|
SDValue One = DAG.getConstant(1, VT);
|
|
|
|
const bool SRA = Op.getOpcode() == ISD::SRA_PARTS;
|
|
|
|
SDValue Width = DAG.getConstant(VT.getSizeInBits(), VT);
|
|
SDValue Width1 = DAG.getConstant(VT.getSizeInBits() - 1, VT);
|
|
SDValue BigShift = DAG.getNode(ISD::SUB, DL, VT, Shift, Width);
|
|
SDValue CompShift = DAG.getNode(ISD::SUB, DL, VT, Width1, Shift);
|
|
|
|
// The dance around Width1 is necessary for 0 special case.
|
|
// Without it the CompShift might be 32, producing incorrect results in
|
|
// Overflow. So we do the shift in two steps, the alternative is to
|
|
// add a conditional to filter the special case.
|
|
|
|
SDValue Overflow = DAG.getNode(ISD::SHL, DL, VT, Hi, CompShift);
|
|
Overflow = DAG.getNode(ISD::SHL, DL, VT, Overflow, One);
|
|
|
|
SDValue HiSmall = DAG.getNode(SRA ? ISD::SRA : ISD::SRL, DL, VT, Hi, Shift);
|
|
SDValue LoSmall = DAG.getNode(ISD::SRL, DL, VT, Lo, Shift);
|
|
LoSmall = DAG.getNode(ISD::OR, DL, VT, LoSmall, Overflow);
|
|
|
|
SDValue LoBig = DAG.getNode(SRA ? ISD::SRA : ISD::SRL, DL, VT, Hi, BigShift);
|
|
SDValue HiBig = SRA ? DAG.getNode(ISD::SRA, DL, VT, Hi, Width1) : Zero;
|
|
|
|
Hi = DAG.getSelectCC(DL, Shift, Width, HiSmall, HiBig, ISD::SETULT);
|
|
Lo = DAG.getSelectCC(DL, Shift, Width, LoSmall, LoBig, ISD::SETULT);
|
|
|
|
return DAG.getNode(ISD::MERGE_VALUES, DL, DAG.getVTList(VT,VT), Lo, Hi);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerFPTOUINT(SDValue Op, SelectionDAG &DAG) const {
|
|
return DAG.getNode(
|
|
ISD::SETCC,
|
|
SDLoc(Op),
|
|
MVT::i1,
|
|
Op, DAG.getConstantFP(0.0f, MVT::f32),
|
|
DAG.getCondCode(ISD::SETNE)
|
|
);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerImplicitParameter(SelectionDAG &DAG, EVT VT,
|
|
SDLoc DL,
|
|
unsigned DwordOffset) const {
|
|
unsigned ByteOffset = DwordOffset * 4;
|
|
PointerType * PtrType = PointerType::get(VT.getTypeForEVT(*DAG.getContext()),
|
|
AMDGPUAS::CONSTANT_BUFFER_0);
|
|
|
|
// We shouldn't be using an offset wider than 16-bits for implicit parameters.
|
|
assert(isInt<16>(ByteOffset));
|
|
|
|
return DAG.getLoad(VT, DL, DAG.getEntryNode(),
|
|
DAG.getConstant(ByteOffset, MVT::i32), // PTR
|
|
MachinePointerInfo(ConstantPointerNull::get(PtrType)),
|
|
false, false, false, 0);
|
|
}
|
|
|
|
bool R600TargetLowering::isZero(SDValue Op) const {
|
|
if(ConstantSDNode *Cst = dyn_cast<ConstantSDNode>(Op)) {
|
|
return Cst->isNullValue();
|
|
} else if(ConstantFPSDNode *CstFP = dyn_cast<ConstantFPSDNode>(Op)){
|
|
return CstFP->isZero();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
|
|
SDLoc DL(Op);
|
|
EVT VT = Op.getValueType();
|
|
|
|
SDValue LHS = Op.getOperand(0);
|
|
SDValue RHS = Op.getOperand(1);
|
|
SDValue True = Op.getOperand(2);
|
|
SDValue False = Op.getOperand(3);
|
|
SDValue CC = Op.getOperand(4);
|
|
SDValue Temp;
|
|
|
|
if (VT == MVT::f32) {
|
|
DAGCombinerInfo DCI(DAG, AfterLegalizeVectorOps, true, nullptr);
|
|
SDValue MinMax = CombineFMinMaxLegacy(DL, VT, LHS, RHS, True, False, CC, DCI);
|
|
if (MinMax)
|
|
return MinMax;
|
|
}
|
|
|
|
// LHS and RHS are guaranteed to be the same value type
|
|
EVT CompareVT = LHS.getValueType();
|
|
|
|
// Check if we can lower this to a native operation.
|
|
|
|
// Try to lower to a SET* instruction:
|
|
//
|
|
// SET* can match the following patterns:
|
|
//
|
|
// select_cc f32, f32, -1, 0, cc_supported
|
|
// select_cc f32, f32, 1.0f, 0.0f, cc_supported
|
|
// select_cc i32, i32, -1, 0, cc_supported
|
|
//
|
|
|
|
// Move hardware True/False values to the correct operand.
|
|
ISD::CondCode CCOpcode = cast<CondCodeSDNode>(CC)->get();
|
|
ISD::CondCode InverseCC =
|
|
ISD::getSetCCInverse(CCOpcode, CompareVT == MVT::i32);
|
|
if (isHWTrueValue(False) && isHWFalseValue(True)) {
|
|
if (isCondCodeLegal(InverseCC, CompareVT.getSimpleVT())) {
|
|
std::swap(False, True);
|
|
CC = DAG.getCondCode(InverseCC);
|
|
} else {
|
|
ISD::CondCode SwapInvCC = ISD::getSetCCSwappedOperands(InverseCC);
|
|
if (isCondCodeLegal(SwapInvCC, CompareVT.getSimpleVT())) {
|
|
std::swap(False, True);
|
|
std::swap(LHS, RHS);
|
|
CC = DAG.getCondCode(SwapInvCC);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isHWTrueValue(True) && isHWFalseValue(False) &&
|
|
(CompareVT == VT || VT == MVT::i32)) {
|
|
// This can be matched by a SET* instruction.
|
|
return DAG.getNode(ISD::SELECT_CC, DL, VT, LHS, RHS, True, False, CC);
|
|
}
|
|
|
|
// Try to lower to a CND* instruction:
|
|
//
|
|
// CND* can match the following patterns:
|
|
//
|
|
// select_cc f32, 0.0, f32, f32, cc_supported
|
|
// select_cc f32, 0.0, i32, i32, cc_supported
|
|
// select_cc i32, 0, f32, f32, cc_supported
|
|
// select_cc i32, 0, i32, i32, cc_supported
|
|
//
|
|
|
|
// Try to move the zero value to the RHS
|
|
if (isZero(LHS)) {
|
|
ISD::CondCode CCOpcode = cast<CondCodeSDNode>(CC)->get();
|
|
// Try swapping the operands
|
|
ISD::CondCode CCSwapped = ISD::getSetCCSwappedOperands(CCOpcode);
|
|
if (isCondCodeLegal(CCSwapped, CompareVT.getSimpleVT())) {
|
|
std::swap(LHS, RHS);
|
|
CC = DAG.getCondCode(CCSwapped);
|
|
} else {
|
|
// Try inverting the conditon and then swapping the operands
|
|
ISD::CondCode CCInv = ISD::getSetCCInverse(CCOpcode, CompareVT.isInteger());
|
|
CCSwapped = ISD::getSetCCSwappedOperands(CCInv);
|
|
if (isCondCodeLegal(CCSwapped, CompareVT.getSimpleVT())) {
|
|
std::swap(True, False);
|
|
std::swap(LHS, RHS);
|
|
CC = DAG.getCondCode(CCSwapped);
|
|
}
|
|
}
|
|
}
|
|
if (isZero(RHS)) {
|
|
SDValue Cond = LHS;
|
|
SDValue Zero = RHS;
|
|
ISD::CondCode CCOpcode = cast<CondCodeSDNode>(CC)->get();
|
|
if (CompareVT != VT) {
|
|
// Bitcast True / False to the correct types. This will end up being
|
|
// a nop, but it allows us to define only a single pattern in the
|
|
// .TD files for each CND* instruction rather than having to have
|
|
// one pattern for integer True/False and one for fp True/False
|
|
True = DAG.getNode(ISD::BITCAST, DL, CompareVT, True);
|
|
False = DAG.getNode(ISD::BITCAST, DL, CompareVT, False);
|
|
}
|
|
|
|
switch (CCOpcode) {
|
|
case ISD::SETONE:
|
|
case ISD::SETUNE:
|
|
case ISD::SETNE:
|
|
CCOpcode = ISD::getSetCCInverse(CCOpcode, CompareVT == MVT::i32);
|
|
Temp = True;
|
|
True = False;
|
|
False = Temp;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
SDValue SelectNode = DAG.getNode(ISD::SELECT_CC, DL, CompareVT,
|
|
Cond, Zero,
|
|
True, False,
|
|
DAG.getCondCode(CCOpcode));
|
|
return DAG.getNode(ISD::BITCAST, DL, VT, SelectNode);
|
|
}
|
|
|
|
// If we make it this for it means we have no native instructions to handle
|
|
// this SELECT_CC, so we must lower it.
|
|
SDValue HWTrue, HWFalse;
|
|
|
|
if (CompareVT == MVT::f32) {
|
|
HWTrue = DAG.getConstantFP(1.0f, CompareVT);
|
|
HWFalse = DAG.getConstantFP(0.0f, CompareVT);
|
|
} else if (CompareVT == MVT::i32) {
|
|
HWTrue = DAG.getConstant(-1, CompareVT);
|
|
HWFalse = DAG.getConstant(0, CompareVT);
|
|
}
|
|
else {
|
|
llvm_unreachable("Unhandled value type in LowerSELECT_CC");
|
|
}
|
|
|
|
// Lower this unsupported SELECT_CC into a combination of two supported
|
|
// SELECT_CC operations.
|
|
SDValue Cond = DAG.getNode(ISD::SELECT_CC, DL, CompareVT, LHS, RHS, HWTrue, HWFalse, CC);
|
|
|
|
return DAG.getNode(ISD::SELECT_CC, DL, VT,
|
|
Cond, HWFalse,
|
|
True, False,
|
|
DAG.getCondCode(ISD::SETNE));
|
|
}
|
|
|
|
/// LLVM generates byte-addressed pointers. For indirect addressing, we need to
|
|
/// convert these pointers to a register index. Each register holds
|
|
/// 16 bytes, (4 x 32bit sub-register), but we need to take into account the
|
|
/// \p StackWidth, which tells us how many of the 4 sub-registrers will be used
|
|
/// for indirect addressing.
|
|
SDValue R600TargetLowering::stackPtrToRegIndex(SDValue Ptr,
|
|
unsigned StackWidth,
|
|
SelectionDAG &DAG) const {
|
|
unsigned SRLPad;
|
|
switch(StackWidth) {
|
|
case 1:
|
|
SRLPad = 2;
|
|
break;
|
|
case 2:
|
|
SRLPad = 3;
|
|
break;
|
|
case 4:
|
|
SRLPad = 4;
|
|
break;
|
|
default: llvm_unreachable("Invalid stack width");
|
|
}
|
|
|
|
return DAG.getNode(ISD::SRL, SDLoc(Ptr), Ptr.getValueType(), Ptr,
|
|
DAG.getConstant(SRLPad, MVT::i32));
|
|
}
|
|
|
|
void R600TargetLowering::getStackAddress(unsigned StackWidth,
|
|
unsigned ElemIdx,
|
|
unsigned &Channel,
|
|
unsigned &PtrIncr) const {
|
|
switch (StackWidth) {
|
|
default:
|
|
case 1:
|
|
Channel = 0;
|
|
if (ElemIdx > 0) {
|
|
PtrIncr = 1;
|
|
} else {
|
|
PtrIncr = 0;
|
|
}
|
|
break;
|
|
case 2:
|
|
Channel = ElemIdx % 2;
|
|
if (ElemIdx == 2) {
|
|
PtrIncr = 1;
|
|
} else {
|
|
PtrIncr = 0;
|
|
}
|
|
break;
|
|
case 4:
|
|
Channel = ElemIdx;
|
|
PtrIncr = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerSTORE(SDValue Op, SelectionDAG &DAG) const {
|
|
SDLoc DL(Op);
|
|
StoreSDNode *StoreNode = cast<StoreSDNode>(Op);
|
|
SDValue Chain = Op.getOperand(0);
|
|
SDValue Value = Op.getOperand(1);
|
|
SDValue Ptr = Op.getOperand(2);
|
|
|
|
SDValue Result = AMDGPUTargetLowering::LowerSTORE(Op, DAG);
|
|
if (Result.getNode()) {
|
|
return Result;
|
|
}
|
|
|
|
if (StoreNode->getAddressSpace() == AMDGPUAS::GLOBAL_ADDRESS) {
|
|
if (StoreNode->isTruncatingStore()) {
|
|
EVT VT = Value.getValueType();
|
|
assert(VT.bitsLE(MVT::i32));
|
|
EVT MemVT = StoreNode->getMemoryVT();
|
|
SDValue MaskConstant;
|
|
if (MemVT == MVT::i8) {
|
|
MaskConstant = DAG.getConstant(0xFF, MVT::i32);
|
|
} else {
|
|
assert(MemVT == MVT::i16);
|
|
MaskConstant = DAG.getConstant(0xFFFF, MVT::i32);
|
|
}
|
|
SDValue DWordAddr = DAG.getNode(ISD::SRL, DL, VT, Ptr,
|
|
DAG.getConstant(2, MVT::i32));
|
|
SDValue ByteIndex = DAG.getNode(ISD::AND, DL, Ptr.getValueType(), Ptr,
|
|
DAG.getConstant(0x00000003, VT));
|
|
SDValue TruncValue = DAG.getNode(ISD::AND, DL, VT, Value, MaskConstant);
|
|
SDValue Shift = DAG.getNode(ISD::SHL, DL, VT, ByteIndex,
|
|
DAG.getConstant(3, VT));
|
|
SDValue ShiftedValue = DAG.getNode(ISD::SHL, DL, VT, TruncValue, Shift);
|
|
SDValue Mask = DAG.getNode(ISD::SHL, DL, VT, MaskConstant, Shift);
|
|
// XXX: If we add a 64-bit ZW register class, then we could use a 2 x i32
|
|
// vector instead.
|
|
SDValue Src[4] = {
|
|
ShiftedValue,
|
|
DAG.getConstant(0, MVT::i32),
|
|
DAG.getConstant(0, MVT::i32),
|
|
Mask
|
|
};
|
|
SDValue Input = DAG.getNode(ISD::BUILD_VECTOR, DL, MVT::v4i32, Src);
|
|
SDValue Args[3] = { Chain, Input, DWordAddr };
|
|
return DAG.getMemIntrinsicNode(AMDGPUISD::STORE_MSKOR, DL,
|
|
Op->getVTList(), Args, MemVT,
|
|
StoreNode->getMemOperand());
|
|
} else if (Ptr->getOpcode() != AMDGPUISD::DWORDADDR &&
|
|
Value.getValueType().bitsGE(MVT::i32)) {
|
|
// Convert pointer from byte address to dword address.
|
|
Ptr = DAG.getNode(AMDGPUISD::DWORDADDR, DL, Ptr.getValueType(),
|
|
DAG.getNode(ISD::SRL, DL, Ptr.getValueType(),
|
|
Ptr, DAG.getConstant(2, MVT::i32)));
|
|
|
|
if (StoreNode->isTruncatingStore() || StoreNode->isIndexed()) {
|
|
llvm_unreachable("Truncated and indexed stores not supported yet");
|
|
} else {
|
|
Chain = DAG.getStore(Chain, DL, Value, Ptr, StoreNode->getMemOperand());
|
|
}
|
|
return Chain;
|
|
}
|
|
}
|
|
|
|
EVT ValueVT = Value.getValueType();
|
|
|
|
if (StoreNode->getAddressSpace() != AMDGPUAS::PRIVATE_ADDRESS) {
|
|
return SDValue();
|
|
}
|
|
|
|
SDValue Ret = AMDGPUTargetLowering::LowerSTORE(Op, DAG);
|
|
if (Ret.getNode()) {
|
|
return Ret;
|
|
}
|
|
// Lowering for indirect addressing
|
|
|
|
const MachineFunction &MF = DAG.getMachineFunction();
|
|
const AMDGPUFrameLowering *TFL = static_cast<const AMDGPUFrameLowering *>(
|
|
getTargetMachine().getSubtargetImpl()->getFrameLowering());
|
|
unsigned StackWidth = TFL->getStackWidth(MF);
|
|
|
|
Ptr = stackPtrToRegIndex(Ptr, StackWidth, DAG);
|
|
|
|
if (ValueVT.isVector()) {
|
|
unsigned NumElemVT = ValueVT.getVectorNumElements();
|
|
EVT ElemVT = ValueVT.getVectorElementType();
|
|
SmallVector<SDValue, 4> Stores(NumElemVT);
|
|
|
|
assert(NumElemVT >= StackWidth && "Stack width cannot be greater than "
|
|
"vector width in load");
|
|
|
|
for (unsigned i = 0; i < NumElemVT; ++i) {
|
|
unsigned Channel, PtrIncr;
|
|
getStackAddress(StackWidth, i, Channel, PtrIncr);
|
|
Ptr = DAG.getNode(ISD::ADD, DL, MVT::i32, Ptr,
|
|
DAG.getConstant(PtrIncr, MVT::i32));
|
|
SDValue Elem = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, ElemVT,
|
|
Value, DAG.getConstant(i, MVT::i32));
|
|
|
|
Stores[i] = DAG.getNode(AMDGPUISD::REGISTER_STORE, DL, MVT::Other,
|
|
Chain, Elem, Ptr,
|
|
DAG.getTargetConstant(Channel, MVT::i32));
|
|
}
|
|
Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Stores);
|
|
} else {
|
|
if (ValueVT == MVT::i8) {
|
|
Value = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i32, Value);
|
|
}
|
|
Chain = DAG.getNode(AMDGPUISD::REGISTER_STORE, DL, MVT::Other, Chain, Value, Ptr,
|
|
DAG.getTargetConstant(0, MVT::i32)); // Channel
|
|
}
|
|
|
|
return Chain;
|
|
}
|
|
|
|
// return (512 + (kc_bank << 12)
|
|
static int
|
|
ConstantAddressBlock(unsigned AddressSpace) {
|
|
switch (AddressSpace) {
|
|
case AMDGPUAS::CONSTANT_BUFFER_0:
|
|
return 512;
|
|
case AMDGPUAS::CONSTANT_BUFFER_1:
|
|
return 512 + 4096;
|
|
case AMDGPUAS::CONSTANT_BUFFER_2:
|
|
return 512 + 4096 * 2;
|
|
case AMDGPUAS::CONSTANT_BUFFER_3:
|
|
return 512 + 4096 * 3;
|
|
case AMDGPUAS::CONSTANT_BUFFER_4:
|
|
return 512 + 4096 * 4;
|
|
case AMDGPUAS::CONSTANT_BUFFER_5:
|
|
return 512 + 4096 * 5;
|
|
case AMDGPUAS::CONSTANT_BUFFER_6:
|
|
return 512 + 4096 * 6;
|
|
case AMDGPUAS::CONSTANT_BUFFER_7:
|
|
return 512 + 4096 * 7;
|
|
case AMDGPUAS::CONSTANT_BUFFER_8:
|
|
return 512 + 4096 * 8;
|
|
case AMDGPUAS::CONSTANT_BUFFER_9:
|
|
return 512 + 4096 * 9;
|
|
case AMDGPUAS::CONSTANT_BUFFER_10:
|
|
return 512 + 4096 * 10;
|
|
case AMDGPUAS::CONSTANT_BUFFER_11:
|
|
return 512 + 4096 * 11;
|
|
case AMDGPUAS::CONSTANT_BUFFER_12:
|
|
return 512 + 4096 * 12;
|
|
case AMDGPUAS::CONSTANT_BUFFER_13:
|
|
return 512 + 4096 * 13;
|
|
case AMDGPUAS::CONSTANT_BUFFER_14:
|
|
return 512 + 4096 * 14;
|
|
case AMDGPUAS::CONSTANT_BUFFER_15:
|
|
return 512 + 4096 * 15;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerLOAD(SDValue Op, SelectionDAG &DAG) const
|
|
{
|
|
EVT VT = Op.getValueType();
|
|
SDLoc DL(Op);
|
|
LoadSDNode *LoadNode = cast<LoadSDNode>(Op);
|
|
SDValue Chain = Op.getOperand(0);
|
|
SDValue Ptr = Op.getOperand(1);
|
|
SDValue LoweredLoad;
|
|
|
|
SDValue Ret = AMDGPUTargetLowering::LowerLOAD(Op, DAG);
|
|
if (Ret.getNode()) {
|
|
SDValue Ops[2] = {
|
|
Ret,
|
|
Chain
|
|
};
|
|
return DAG.getMergeValues(Ops, DL);
|
|
}
|
|
|
|
// Lower loads constant address space global variable loads
|
|
if (LoadNode->getAddressSpace() == AMDGPUAS::CONSTANT_ADDRESS &&
|
|
isa<GlobalVariable>(
|
|
GetUnderlyingObject(LoadNode->getMemOperand()->getValue()))) {
|
|
|
|
SDValue Ptr = DAG.getZExtOrTrunc(LoadNode->getBasePtr(), DL,
|
|
getPointerTy(AMDGPUAS::PRIVATE_ADDRESS));
|
|
Ptr = DAG.getNode(ISD::SRL, DL, MVT::i32, Ptr,
|
|
DAG.getConstant(2, MVT::i32));
|
|
return DAG.getNode(AMDGPUISD::REGISTER_LOAD, DL, Op->getVTList(),
|
|
LoadNode->getChain(), Ptr,
|
|
DAG.getTargetConstant(0, MVT::i32), Op.getOperand(2));
|
|
}
|
|
|
|
if (LoadNode->getAddressSpace() == AMDGPUAS::LOCAL_ADDRESS && VT.isVector()) {
|
|
SDValue MergedValues[2] = {
|
|
ScalarizeVectorLoad(Op, DAG),
|
|
Chain
|
|
};
|
|
return DAG.getMergeValues(MergedValues, DL);
|
|
}
|
|
|
|
int ConstantBlock = ConstantAddressBlock(LoadNode->getAddressSpace());
|
|
if (ConstantBlock > -1 &&
|
|
((LoadNode->getExtensionType() == ISD::NON_EXTLOAD) ||
|
|
(LoadNode->getExtensionType() == ISD::ZEXTLOAD))) {
|
|
SDValue Result;
|
|
if (isa<ConstantExpr>(LoadNode->getMemOperand()->getValue()) ||
|
|
isa<Constant>(LoadNode->getMemOperand()->getValue()) ||
|
|
isa<ConstantSDNode>(Ptr)) {
|
|
SDValue Slots[4];
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
// We want Const position encoded with the following formula :
|
|
// (((512 + (kc_bank << 12) + const_index) << 2) + chan)
|
|
// const_index is Ptr computed by llvm using an alignment of 16.
|
|
// Thus we add (((512 + (kc_bank << 12)) + chan ) * 4 here and
|
|
// then div by 4 at the ISel step
|
|
SDValue NewPtr = DAG.getNode(ISD::ADD, DL, Ptr.getValueType(), Ptr,
|
|
DAG.getConstant(4 * i + ConstantBlock * 16, MVT::i32));
|
|
Slots[i] = DAG.getNode(AMDGPUISD::CONST_ADDRESS, DL, MVT::i32, NewPtr);
|
|
}
|
|
EVT NewVT = MVT::v4i32;
|
|
unsigned NumElements = 4;
|
|
if (VT.isVector()) {
|
|
NewVT = VT;
|
|
NumElements = VT.getVectorNumElements();
|
|
}
|
|
Result = DAG.getNode(ISD::BUILD_VECTOR, DL, NewVT,
|
|
makeArrayRef(Slots, NumElements));
|
|
} else {
|
|
// non-constant ptr can't be folded, keeps it as a v4f32 load
|
|
Result = DAG.getNode(AMDGPUISD::CONST_ADDRESS, DL, MVT::v4i32,
|
|
DAG.getNode(ISD::SRL, DL, MVT::i32, Ptr, DAG.getConstant(4, MVT::i32)),
|
|
DAG.getConstant(LoadNode->getAddressSpace() -
|
|
AMDGPUAS::CONSTANT_BUFFER_0, MVT::i32)
|
|
);
|
|
}
|
|
|
|
if (!VT.isVector()) {
|
|
Result = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::i32, Result,
|
|
DAG.getConstant(0, MVT::i32));
|
|
}
|
|
|
|
SDValue MergedValues[2] = {
|
|
Result,
|
|
Chain
|
|
};
|
|
return DAG.getMergeValues(MergedValues, DL);
|
|
}
|
|
|
|
// For most operations returning SDValue() will result in the node being
|
|
// expanded by the DAG Legalizer. This is not the case for ISD::LOAD, so we
|
|
// need to manually expand loads that may be legal in some address spaces and
|
|
// illegal in others. SEXT loads from CONSTANT_BUFFER_0 are supported for
|
|
// compute shaders, since the data is sign extended when it is uploaded to the
|
|
// buffer. However SEXT loads from other address spaces are not supported, so
|
|
// we need to expand them here.
|
|
if (LoadNode->getExtensionType() == ISD::SEXTLOAD) {
|
|
EVT MemVT = LoadNode->getMemoryVT();
|
|
assert(!MemVT.isVector() && (MemVT == MVT::i16 || MemVT == MVT::i8));
|
|
SDValue ShiftAmount =
|
|
DAG.getConstant(VT.getSizeInBits() - MemVT.getSizeInBits(), MVT::i32);
|
|
SDValue NewLoad = DAG.getExtLoad(ISD::EXTLOAD, DL, VT, Chain, Ptr,
|
|
LoadNode->getPointerInfo(), MemVT,
|
|
LoadNode->isVolatile(),
|
|
LoadNode->isNonTemporal(),
|
|
LoadNode->isInvariant(),
|
|
LoadNode->getAlignment());
|
|
SDValue Shl = DAG.getNode(ISD::SHL, DL, VT, NewLoad, ShiftAmount);
|
|
SDValue Sra = DAG.getNode(ISD::SRA, DL, VT, Shl, ShiftAmount);
|
|
|
|
SDValue MergedValues[2] = { Sra, Chain };
|
|
return DAG.getMergeValues(MergedValues, DL);
|
|
}
|
|
|
|
if (LoadNode->getAddressSpace() != AMDGPUAS::PRIVATE_ADDRESS) {
|
|
return SDValue();
|
|
}
|
|
|
|
// Lowering for indirect addressing
|
|
const MachineFunction &MF = DAG.getMachineFunction();
|
|
const AMDGPUFrameLowering *TFL = static_cast<const AMDGPUFrameLowering *>(
|
|
getTargetMachine().getSubtargetImpl()->getFrameLowering());
|
|
unsigned StackWidth = TFL->getStackWidth(MF);
|
|
|
|
Ptr = stackPtrToRegIndex(Ptr, StackWidth, DAG);
|
|
|
|
if (VT.isVector()) {
|
|
unsigned NumElemVT = VT.getVectorNumElements();
|
|
EVT ElemVT = VT.getVectorElementType();
|
|
SDValue Loads[4];
|
|
|
|
assert(NumElemVT >= StackWidth && "Stack width cannot be greater than "
|
|
"vector width in load");
|
|
|
|
for (unsigned i = 0; i < NumElemVT; ++i) {
|
|
unsigned Channel, PtrIncr;
|
|
getStackAddress(StackWidth, i, Channel, PtrIncr);
|
|
Ptr = DAG.getNode(ISD::ADD, DL, MVT::i32, Ptr,
|
|
DAG.getConstant(PtrIncr, MVT::i32));
|
|
Loads[i] = DAG.getNode(AMDGPUISD::REGISTER_LOAD, DL, ElemVT,
|
|
Chain, Ptr,
|
|
DAG.getTargetConstant(Channel, MVT::i32),
|
|
Op.getOperand(2));
|
|
}
|
|
for (unsigned i = NumElemVT; i < 4; ++i) {
|
|
Loads[i] = DAG.getUNDEF(ElemVT);
|
|
}
|
|
EVT TargetVT = EVT::getVectorVT(*DAG.getContext(), ElemVT, 4);
|
|
LoweredLoad = DAG.getNode(ISD::BUILD_VECTOR, DL, TargetVT, Loads);
|
|
} else {
|
|
LoweredLoad = DAG.getNode(AMDGPUISD::REGISTER_LOAD, DL, VT,
|
|
Chain, Ptr,
|
|
DAG.getTargetConstant(0, MVT::i32), // Channel
|
|
Op.getOperand(2));
|
|
}
|
|
|
|
SDValue Ops[2] = {
|
|
LoweredLoad,
|
|
Chain
|
|
};
|
|
|
|
return DAG.getMergeValues(Ops, DL);
|
|
}
|
|
|
|
SDValue R600TargetLowering::LowerBRCOND(SDValue Op, SelectionDAG &DAG) const {
|
|
SDValue Chain = Op.getOperand(0);
|
|
SDValue Cond = Op.getOperand(1);
|
|
SDValue Jump = Op.getOperand(2);
|
|
|
|
return DAG.getNode(AMDGPUISD::BRANCH_COND, SDLoc(Op), Op.getValueType(),
|
|
Chain, Jump, Cond);
|
|
}
|
|
|
|
/// XXX Only kernel functions are supported, so we can assume for now that
|
|
/// every function is a kernel function, but in the future we should use
|
|
/// separate calling conventions for kernel and non-kernel functions.
|
|
SDValue R600TargetLowering::LowerFormalArguments(
|
|
SDValue Chain,
|
|
CallingConv::ID CallConv,
|
|
bool isVarArg,
|
|
const SmallVectorImpl<ISD::InputArg> &Ins,
|
|
SDLoc DL, SelectionDAG &DAG,
|
|
SmallVectorImpl<SDValue> &InVals) const {
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), ArgLocs,
|
|
*DAG.getContext());
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
R600MachineFunctionInfo *MFI = MF.getInfo<R600MachineFunctionInfo>();
|
|
|
|
SmallVector<ISD::InputArg, 8> LocalIns;
|
|
|
|
getOriginalFunctionArgs(DAG, MF.getFunction(), Ins, LocalIns);
|
|
|
|
AnalyzeFormalArguments(CCInfo, LocalIns);
|
|
|
|
for (unsigned i = 0, e = Ins.size(); i < e; ++i) {
|
|
CCValAssign &VA = ArgLocs[i];
|
|
const ISD::InputArg &In = Ins[i];
|
|
EVT VT = In.VT;
|
|
EVT MemVT = VA.getLocVT();
|
|
if (!VT.isVector() && MemVT.isVector()) {
|
|
// Get load source type if scalarized.
|
|
MemVT = MemVT.getVectorElementType();
|
|
}
|
|
|
|
if (MFI->getShaderType() != ShaderType::COMPUTE) {
|
|
unsigned Reg = MF.addLiveIn(VA.getLocReg(), &AMDGPU::R600_Reg128RegClass);
|
|
SDValue Register = DAG.getCopyFromReg(Chain, DL, Reg, VT);
|
|
InVals.push_back(Register);
|
|
continue;
|
|
}
|
|
|
|
PointerType *PtrTy = PointerType::get(VT.getTypeForEVT(*DAG.getContext()),
|
|
AMDGPUAS::CONSTANT_BUFFER_0);
|
|
|
|
// i64 isn't a legal type, so the register type used ends up as i32, which
|
|
// isn't expected here. It attempts to create this sextload, but it ends up
|
|
// being invalid. Somehow this seems to work with i64 arguments, but breaks
|
|
// for <1 x i64>.
|
|
|
|
// The first 36 bytes of the input buffer contains information about
|
|
// thread group and global sizes.
|
|
ISD::LoadExtType Ext = ISD::NON_EXTLOAD;
|
|
if (MemVT.getScalarSizeInBits() != VT.getScalarSizeInBits()) {
|
|
// FIXME: This should really check the extload type, but the handling of
|
|
// extload vector parameters seems to be broken.
|
|
|
|
// Ext = In.Flags.isSExt() ? ISD::SEXTLOAD : ISD::ZEXTLOAD;
|
|
Ext = ISD::SEXTLOAD;
|
|
}
|
|
|
|
// Compute the offset from the value.
|
|
// XXX - I think PartOffset should give you this, but it seems to give the
|
|
// size of the register which isn't useful.
|
|
|
|
unsigned ValBase = ArgLocs[In.OrigArgIndex].getLocMemOffset();
|
|
unsigned PartOffset = VA.getLocMemOffset();
|
|
unsigned Offset = 36 + VA.getLocMemOffset();
|
|
|
|
MachinePointerInfo PtrInfo(UndefValue::get(PtrTy), PartOffset - ValBase);
|
|
SDValue Arg = DAG.getLoad(ISD::UNINDEXED, Ext, VT, DL, Chain,
|
|
DAG.getConstant(Offset, MVT::i32),
|
|
DAG.getUNDEF(MVT::i32),
|
|
PtrInfo,
|
|
MemVT, false, true, true, 4);
|
|
|
|
// 4 is the preferred alignment for the CONSTANT memory space.
|
|
InVals.push_back(Arg);
|
|
MFI->ABIArgOffset = Offset + MemVT.getStoreSize();
|
|
}
|
|
return Chain;
|
|
}
|
|
|
|
EVT R600TargetLowering::getSetCCResultType(LLVMContext &, EVT VT) const {
|
|
if (!VT.isVector())
|
|
return MVT::i32;
|
|
return VT.changeVectorElementTypeToInteger();
|
|
}
|
|
|
|
static SDValue CompactSwizzlableVector(
|
|
SelectionDAG &DAG, SDValue VectorEntry,
|
|
DenseMap<unsigned, unsigned> &RemapSwizzle) {
|
|
assert(VectorEntry.getOpcode() == ISD::BUILD_VECTOR);
|
|
assert(RemapSwizzle.empty());
|
|
SDValue NewBldVec[4] = {
|
|
VectorEntry.getOperand(0),
|
|
VectorEntry.getOperand(1),
|
|
VectorEntry.getOperand(2),
|
|
VectorEntry.getOperand(3)
|
|
};
|
|
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
if (NewBldVec[i].getOpcode() == ISD::UNDEF)
|
|
// We mask write here to teach later passes that the ith element of this
|
|
// vector is undef. Thus we can use it to reduce 128 bits reg usage,
|
|
// break false dependencies and additionnaly make assembly easier to read.
|
|
RemapSwizzle[i] = 7; // SEL_MASK_WRITE
|
|
if (ConstantFPSDNode *C = dyn_cast<ConstantFPSDNode>(NewBldVec[i])) {
|
|
if (C->isZero()) {
|
|
RemapSwizzle[i] = 4; // SEL_0
|
|
NewBldVec[i] = DAG.getUNDEF(MVT::f32);
|
|
} else if (C->isExactlyValue(1.0)) {
|
|
RemapSwizzle[i] = 5; // SEL_1
|
|
NewBldVec[i] = DAG.getUNDEF(MVT::f32);
|
|
}
|
|
}
|
|
|
|
if (NewBldVec[i].getOpcode() == ISD::UNDEF)
|
|
continue;
|
|
for (unsigned j = 0; j < i; j++) {
|
|
if (NewBldVec[i] == NewBldVec[j]) {
|
|
NewBldVec[i] = DAG.getUNDEF(NewBldVec[i].getValueType());
|
|
RemapSwizzle[i] = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return DAG.getNode(ISD::BUILD_VECTOR, SDLoc(VectorEntry),
|
|
VectorEntry.getValueType(), NewBldVec);
|
|
}
|
|
|
|
static SDValue ReorganizeVector(SelectionDAG &DAG, SDValue VectorEntry,
|
|
DenseMap<unsigned, unsigned> &RemapSwizzle) {
|
|
assert(VectorEntry.getOpcode() == ISD::BUILD_VECTOR);
|
|
assert(RemapSwizzle.empty());
|
|
SDValue NewBldVec[4] = {
|
|
VectorEntry.getOperand(0),
|
|
VectorEntry.getOperand(1),
|
|
VectorEntry.getOperand(2),
|
|
VectorEntry.getOperand(3)
|
|
};
|
|
bool isUnmovable[4] = { false, false, false, false };
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
RemapSwizzle[i] = i;
|
|
if (NewBldVec[i].getOpcode() == ISD::EXTRACT_VECTOR_ELT) {
|
|
unsigned Idx = dyn_cast<ConstantSDNode>(NewBldVec[i].getOperand(1))
|
|
->getZExtValue();
|
|
if (i == Idx)
|
|
isUnmovable[Idx] = true;
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
if (NewBldVec[i].getOpcode() == ISD::EXTRACT_VECTOR_ELT) {
|
|
unsigned Idx = dyn_cast<ConstantSDNode>(NewBldVec[i].getOperand(1))
|
|
->getZExtValue();
|
|
if (isUnmovable[Idx])
|
|
continue;
|
|
// Swap i and Idx
|
|
std::swap(NewBldVec[Idx], NewBldVec[i]);
|
|
std::swap(RemapSwizzle[i], RemapSwizzle[Idx]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return DAG.getNode(ISD::BUILD_VECTOR, SDLoc(VectorEntry),
|
|
VectorEntry.getValueType(), NewBldVec);
|
|
}
|
|
|
|
|
|
SDValue R600TargetLowering::OptimizeSwizzle(SDValue BuildVector,
|
|
SDValue Swz[4], SelectionDAG &DAG) const {
|
|
assert(BuildVector.getOpcode() == ISD::BUILD_VECTOR);
|
|
// Old -> New swizzle values
|
|
DenseMap<unsigned, unsigned> SwizzleRemap;
|
|
|
|
BuildVector = CompactSwizzlableVector(DAG, BuildVector, SwizzleRemap);
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
unsigned Idx = dyn_cast<ConstantSDNode>(Swz[i])->getZExtValue();
|
|
if (SwizzleRemap.find(Idx) != SwizzleRemap.end())
|
|
Swz[i] = DAG.getConstant(SwizzleRemap[Idx], MVT::i32);
|
|
}
|
|
|
|
SwizzleRemap.clear();
|
|
BuildVector = ReorganizeVector(DAG, BuildVector, SwizzleRemap);
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
unsigned Idx = dyn_cast<ConstantSDNode>(Swz[i])->getZExtValue();
|
|
if (SwizzleRemap.find(Idx) != SwizzleRemap.end())
|
|
Swz[i] = DAG.getConstant(SwizzleRemap[Idx], MVT::i32);
|
|
}
|
|
|
|
return BuildVector;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Custom DAG Optimizations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SDValue R600TargetLowering::PerformDAGCombine(SDNode *N,
|
|
DAGCombinerInfo &DCI) const {
|
|
SelectionDAG &DAG = DCI.DAG;
|
|
|
|
switch (N->getOpcode()) {
|
|
default: return AMDGPUTargetLowering::PerformDAGCombine(N, DCI);
|
|
// (f32 fp_round (f64 uint_to_fp a)) -> (f32 uint_to_fp a)
|
|
case ISD::FP_ROUND: {
|
|
SDValue Arg = N->getOperand(0);
|
|
if (Arg.getOpcode() == ISD::UINT_TO_FP && Arg.getValueType() == MVT::f64) {
|
|
return DAG.getNode(ISD::UINT_TO_FP, SDLoc(N), N->getValueType(0),
|
|
Arg.getOperand(0));
|
|
}
|
|
break;
|
|
}
|
|
|
|
// (i32 fp_to_sint (fneg (select_cc f32, f32, 1.0, 0.0 cc))) ->
|
|
// (i32 select_cc f32, f32, -1, 0 cc)
|
|
//
|
|
// Mesa's GLSL frontend generates the above pattern a lot and we can lower
|
|
// this to one of the SET*_DX10 instructions.
|
|
case ISD::FP_TO_SINT: {
|
|
SDValue FNeg = N->getOperand(0);
|
|
if (FNeg.getOpcode() != ISD::FNEG) {
|
|
return SDValue();
|
|
}
|
|
SDValue SelectCC = FNeg.getOperand(0);
|
|
if (SelectCC.getOpcode() != ISD::SELECT_CC ||
|
|
SelectCC.getOperand(0).getValueType() != MVT::f32 || // LHS
|
|
SelectCC.getOperand(2).getValueType() != MVT::f32 || // True
|
|
!isHWTrueValue(SelectCC.getOperand(2)) ||
|
|
!isHWFalseValue(SelectCC.getOperand(3))) {
|
|
return SDValue();
|
|
}
|
|
|
|
return DAG.getNode(ISD::SELECT_CC, SDLoc(N), N->getValueType(0),
|
|
SelectCC.getOperand(0), // LHS
|
|
SelectCC.getOperand(1), // RHS
|
|
DAG.getConstant(-1, MVT::i32), // True
|
|
DAG.getConstant(0, MVT::i32), // Flase
|
|
SelectCC.getOperand(4)); // CC
|
|
|
|
break;
|
|
}
|
|
|
|
// insert_vector_elt (build_vector elt0, ... , eltN), NewEltIdx, idx
|
|
// => build_vector elt0, ... , NewEltIdx, ... , eltN
|
|
case ISD::INSERT_VECTOR_ELT: {
|
|
SDValue InVec = N->getOperand(0);
|
|
SDValue InVal = N->getOperand(1);
|
|
SDValue EltNo = N->getOperand(2);
|
|
SDLoc dl(N);
|
|
|
|
// If the inserted element is an UNDEF, just use the input vector.
|
|
if (InVal.getOpcode() == ISD::UNDEF)
|
|
return InVec;
|
|
|
|
EVT VT = InVec.getValueType();
|
|
|
|
// If we can't generate a legal BUILD_VECTOR, exit
|
|
if (!isOperationLegal(ISD::BUILD_VECTOR, VT))
|
|
return SDValue();
|
|
|
|
// Check that we know which element is being inserted
|
|
if (!isa<ConstantSDNode>(EltNo))
|
|
return SDValue();
|
|
unsigned Elt = cast<ConstantSDNode>(EltNo)->getZExtValue();
|
|
|
|
// Check that the operand is a BUILD_VECTOR (or UNDEF, which can essentially
|
|
// be converted to a BUILD_VECTOR). Fill in the Ops vector with the
|
|
// vector elements.
|
|
SmallVector<SDValue, 8> Ops;
|
|
if (InVec.getOpcode() == ISD::BUILD_VECTOR) {
|
|
Ops.append(InVec.getNode()->op_begin(),
|
|
InVec.getNode()->op_end());
|
|
} else if (InVec.getOpcode() == ISD::UNDEF) {
|
|
unsigned NElts = VT.getVectorNumElements();
|
|
Ops.append(NElts, DAG.getUNDEF(InVal.getValueType()));
|
|
} else {
|
|
return SDValue();
|
|
}
|
|
|
|
// Insert the element
|
|
if (Elt < Ops.size()) {
|
|
// All the operands of BUILD_VECTOR must have the same type;
|
|
// we enforce that here.
|
|
EVT OpVT = Ops[0].getValueType();
|
|
if (InVal.getValueType() != OpVT)
|
|
InVal = OpVT.bitsGT(InVal.getValueType()) ?
|
|
DAG.getNode(ISD::ANY_EXTEND, dl, OpVT, InVal) :
|
|
DAG.getNode(ISD::TRUNCATE, dl, OpVT, InVal);
|
|
Ops[Elt] = InVal;
|
|
}
|
|
|
|
// Return the new vector
|
|
return DAG.getNode(ISD::BUILD_VECTOR, dl, VT, Ops);
|
|
}
|
|
|
|
// Extract_vec (Build_vector) generated by custom lowering
|
|
// also needs to be customly combined
|
|
case ISD::EXTRACT_VECTOR_ELT: {
|
|
SDValue Arg = N->getOperand(0);
|
|
if (Arg.getOpcode() == ISD::BUILD_VECTOR) {
|
|
if (ConstantSDNode *Const = dyn_cast<ConstantSDNode>(N->getOperand(1))) {
|
|
unsigned Element = Const->getZExtValue();
|
|
return Arg->getOperand(Element);
|
|
}
|
|
}
|
|
if (Arg.getOpcode() == ISD::BITCAST &&
|
|
Arg.getOperand(0).getOpcode() == ISD::BUILD_VECTOR) {
|
|
if (ConstantSDNode *Const = dyn_cast<ConstantSDNode>(N->getOperand(1))) {
|
|
unsigned Element = Const->getZExtValue();
|
|
return DAG.getNode(ISD::BITCAST, SDLoc(N), N->getVTList(),
|
|
Arg->getOperand(0).getOperand(Element));
|
|
}
|
|
}
|
|
}
|
|
|
|
case ISD::SELECT_CC: {
|
|
// Try common optimizations
|
|
SDValue Ret = AMDGPUTargetLowering::PerformDAGCombine(N, DCI);
|
|
if (Ret.getNode())
|
|
return Ret;
|
|
|
|
// fold selectcc (selectcc x, y, a, b, cc), b, a, b, seteq ->
|
|
// selectcc x, y, a, b, inv(cc)
|
|
//
|
|
// fold selectcc (selectcc x, y, a, b, cc), b, a, b, setne ->
|
|
// selectcc x, y, a, b, cc
|
|
SDValue LHS = N->getOperand(0);
|
|
if (LHS.getOpcode() != ISD::SELECT_CC) {
|
|
return SDValue();
|
|
}
|
|
|
|
SDValue RHS = N->getOperand(1);
|
|
SDValue True = N->getOperand(2);
|
|
SDValue False = N->getOperand(3);
|
|
ISD::CondCode NCC = cast<CondCodeSDNode>(N->getOperand(4))->get();
|
|
|
|
if (LHS.getOperand(2).getNode() != True.getNode() ||
|
|
LHS.getOperand(3).getNode() != False.getNode() ||
|
|
RHS.getNode() != False.getNode()) {
|
|
return SDValue();
|
|
}
|
|
|
|
switch (NCC) {
|
|
default: return SDValue();
|
|
case ISD::SETNE: return LHS;
|
|
case ISD::SETEQ: {
|
|
ISD::CondCode LHSCC = cast<CondCodeSDNode>(LHS.getOperand(4))->get();
|
|
LHSCC = ISD::getSetCCInverse(LHSCC,
|
|
LHS.getOperand(0).getValueType().isInteger());
|
|
if (DCI.isBeforeLegalizeOps() ||
|
|
isCondCodeLegal(LHSCC, LHS.getOperand(0).getSimpleValueType()))
|
|
return DAG.getSelectCC(SDLoc(N),
|
|
LHS.getOperand(0),
|
|
LHS.getOperand(1),
|
|
LHS.getOperand(2),
|
|
LHS.getOperand(3),
|
|
LHSCC);
|
|
break;
|
|
}
|
|
}
|
|
return SDValue();
|
|
}
|
|
|
|
case AMDGPUISD::EXPORT: {
|
|
SDValue Arg = N->getOperand(1);
|
|
if (Arg.getOpcode() != ISD::BUILD_VECTOR)
|
|
break;
|
|
|
|
SDValue NewArgs[8] = {
|
|
N->getOperand(0), // Chain
|
|
SDValue(),
|
|
N->getOperand(2), // ArrayBase
|
|
N->getOperand(3), // Type
|
|
N->getOperand(4), // SWZ_X
|
|
N->getOperand(5), // SWZ_Y
|
|
N->getOperand(6), // SWZ_Z
|
|
N->getOperand(7) // SWZ_W
|
|
};
|
|
SDLoc DL(N);
|
|
NewArgs[1] = OptimizeSwizzle(N->getOperand(1), &NewArgs[4], DAG);
|
|
return DAG.getNode(AMDGPUISD::EXPORT, DL, N->getVTList(), NewArgs);
|
|
}
|
|
case AMDGPUISD::TEXTURE_FETCH: {
|
|
SDValue Arg = N->getOperand(1);
|
|
if (Arg.getOpcode() != ISD::BUILD_VECTOR)
|
|
break;
|
|
|
|
SDValue NewArgs[19] = {
|
|
N->getOperand(0),
|
|
N->getOperand(1),
|
|
N->getOperand(2),
|
|
N->getOperand(3),
|
|
N->getOperand(4),
|
|
N->getOperand(5),
|
|
N->getOperand(6),
|
|
N->getOperand(7),
|
|
N->getOperand(8),
|
|
N->getOperand(9),
|
|
N->getOperand(10),
|
|
N->getOperand(11),
|
|
N->getOperand(12),
|
|
N->getOperand(13),
|
|
N->getOperand(14),
|
|
N->getOperand(15),
|
|
N->getOperand(16),
|
|
N->getOperand(17),
|
|
N->getOperand(18),
|
|
};
|
|
NewArgs[1] = OptimizeSwizzle(N->getOperand(1), &NewArgs[2], DAG);
|
|
return DAG.getNode(AMDGPUISD::TEXTURE_FETCH, SDLoc(N), N->getVTList(),
|
|
NewArgs);
|
|
}
|
|
}
|
|
|
|
return AMDGPUTargetLowering::PerformDAGCombine(N, DCI);
|
|
}
|
|
|
|
static bool
|
|
FoldOperand(SDNode *ParentNode, unsigned SrcIdx, SDValue &Src, SDValue &Neg,
|
|
SDValue &Abs, SDValue &Sel, SDValue &Imm, SelectionDAG &DAG) {
|
|
const R600InstrInfo *TII =
|
|
static_cast<const R600InstrInfo *>(DAG.getSubtarget().getInstrInfo());
|
|
if (!Src.isMachineOpcode())
|
|
return false;
|
|
switch (Src.getMachineOpcode()) {
|
|
case AMDGPU::FNEG_R600:
|
|
if (!Neg.getNode())
|
|
return false;
|
|
Src = Src.getOperand(0);
|
|
Neg = DAG.getTargetConstant(1, MVT::i32);
|
|
return true;
|
|
case AMDGPU::FABS_R600:
|
|
if (!Abs.getNode())
|
|
return false;
|
|
Src = Src.getOperand(0);
|
|
Abs = DAG.getTargetConstant(1, MVT::i32);
|
|
return true;
|
|
case AMDGPU::CONST_COPY: {
|
|
unsigned Opcode = ParentNode->getMachineOpcode();
|
|
bool HasDst = TII->getOperandIdx(Opcode, AMDGPU::OpName::dst) > -1;
|
|
|
|
if (!Sel.getNode())
|
|
return false;
|
|
|
|
SDValue CstOffset = Src.getOperand(0);
|
|
if (ParentNode->getValueType(0).isVector())
|
|
return false;
|
|
|
|
// Gather constants values
|
|
int SrcIndices[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src2),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_W),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_W)
|
|
};
|
|
std::vector<unsigned> Consts;
|
|
for (int OtherSrcIdx : SrcIndices) {
|
|
int OtherSelIdx = TII->getSelIdx(Opcode, OtherSrcIdx);
|
|
if (OtherSrcIdx < 0 || OtherSelIdx < 0)
|
|
continue;
|
|
if (HasDst) {
|
|
OtherSrcIdx--;
|
|
OtherSelIdx--;
|
|
}
|
|
if (RegisterSDNode *Reg =
|
|
dyn_cast<RegisterSDNode>(ParentNode->getOperand(OtherSrcIdx))) {
|
|
if (Reg->getReg() == AMDGPU::ALU_CONST) {
|
|
ConstantSDNode *Cst
|
|
= cast<ConstantSDNode>(ParentNode->getOperand(OtherSelIdx));
|
|
Consts.push_back(Cst->getZExtValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstantSDNode *Cst = cast<ConstantSDNode>(CstOffset);
|
|
Consts.push_back(Cst->getZExtValue());
|
|
if (!TII->fitsConstReadLimitations(Consts)) {
|
|
return false;
|
|
}
|
|
|
|
Sel = CstOffset;
|
|
Src = DAG.getRegister(AMDGPU::ALU_CONST, MVT::f32);
|
|
return true;
|
|
}
|
|
case AMDGPU::MOV_IMM_I32:
|
|
case AMDGPU::MOV_IMM_F32: {
|
|
unsigned ImmReg = AMDGPU::ALU_LITERAL_X;
|
|
uint64_t ImmValue = 0;
|
|
|
|
|
|
if (Src.getMachineOpcode() == AMDGPU::MOV_IMM_F32) {
|
|
ConstantFPSDNode *FPC = dyn_cast<ConstantFPSDNode>(Src.getOperand(0));
|
|
float FloatValue = FPC->getValueAPF().convertToFloat();
|
|
if (FloatValue == 0.0) {
|
|
ImmReg = AMDGPU::ZERO;
|
|
} else if (FloatValue == 0.5) {
|
|
ImmReg = AMDGPU::HALF;
|
|
} else if (FloatValue == 1.0) {
|
|
ImmReg = AMDGPU::ONE;
|
|
} else {
|
|
ImmValue = FPC->getValueAPF().bitcastToAPInt().getZExtValue();
|
|
}
|
|
} else {
|
|
ConstantSDNode *C = dyn_cast<ConstantSDNode>(Src.getOperand(0));
|
|
uint64_t Value = C->getZExtValue();
|
|
if (Value == 0) {
|
|
ImmReg = AMDGPU::ZERO;
|
|
} else if (Value == 1) {
|
|
ImmReg = AMDGPU::ONE_INT;
|
|
} else {
|
|
ImmValue = Value;
|
|
}
|
|
}
|
|
|
|
// Check that we aren't already using an immediate.
|
|
// XXX: It's possible for an instruction to have more than one
|
|
// immediate operand, but this is not supported yet.
|
|
if (ImmReg == AMDGPU::ALU_LITERAL_X) {
|
|
if (!Imm.getNode())
|
|
return false;
|
|
ConstantSDNode *C = dyn_cast<ConstantSDNode>(Imm);
|
|
assert(C);
|
|
if (C->getZExtValue())
|
|
return false;
|
|
Imm = DAG.getTargetConstant(ImmValue, MVT::i32);
|
|
}
|
|
Src = DAG.getRegister(ImmReg, MVT::i32);
|
|
return true;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/// \brief Fold the instructions after selecting them
|
|
SDNode *R600TargetLowering::PostISelFolding(MachineSDNode *Node,
|
|
SelectionDAG &DAG) const {
|
|
const R600InstrInfo *TII =
|
|
static_cast<const R600InstrInfo *>(DAG.getSubtarget().getInstrInfo());
|
|
if (!Node->isMachineOpcode())
|
|
return Node;
|
|
unsigned Opcode = Node->getMachineOpcode();
|
|
SDValue FakeOp;
|
|
|
|
std::vector<SDValue> Ops;
|
|
for (const SDUse &I : Node->ops())
|
|
Ops.push_back(I);
|
|
|
|
if (Opcode == AMDGPU::DOT_4) {
|
|
int OperandIdx[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_W),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_W)
|
|
};
|
|
int NegIdx[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_neg_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_neg_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_neg_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_neg_W),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_neg_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_neg_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_neg_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_neg_W)
|
|
};
|
|
int AbsIdx[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_abs_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_abs_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_abs_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_abs_W),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_abs_X),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_abs_Y),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_abs_Z),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_abs_W)
|
|
};
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
if (OperandIdx[i] < 0)
|
|
return Node;
|
|
SDValue &Src = Ops[OperandIdx[i] - 1];
|
|
SDValue &Neg = Ops[NegIdx[i] - 1];
|
|
SDValue &Abs = Ops[AbsIdx[i] - 1];
|
|
bool HasDst = TII->getOperandIdx(Opcode, AMDGPU::OpName::dst) > -1;
|
|
int SelIdx = TII->getSelIdx(Opcode, OperandIdx[i]);
|
|
if (HasDst)
|
|
SelIdx--;
|
|
SDValue &Sel = (SelIdx > -1) ? Ops[SelIdx] : FakeOp;
|
|
if (FoldOperand(Node, i, Src, Neg, Abs, Sel, FakeOp, DAG))
|
|
return DAG.getMachineNode(Opcode, SDLoc(Node), Node->getVTList(), Ops);
|
|
}
|
|
} else if (Opcode == AMDGPU::REG_SEQUENCE) {
|
|
for (unsigned i = 1, e = Node->getNumOperands(); i < e; i += 2) {
|
|
SDValue &Src = Ops[i];
|
|
if (FoldOperand(Node, i, Src, FakeOp, FakeOp, FakeOp, FakeOp, DAG))
|
|
return DAG.getMachineNode(Opcode, SDLoc(Node), Node->getVTList(), Ops);
|
|
}
|
|
} else if (Opcode == AMDGPU::CLAMP_R600) {
|
|
SDValue Src = Node->getOperand(0);
|
|
if (!Src.isMachineOpcode() ||
|
|
!TII->hasInstrModifiers(Src.getMachineOpcode()))
|
|
return Node;
|
|
int ClampIdx = TII->getOperandIdx(Src.getMachineOpcode(),
|
|
AMDGPU::OpName::clamp);
|
|
if (ClampIdx < 0)
|
|
return Node;
|
|
std::vector<SDValue> Ops;
|
|
unsigned NumOp = Src.getNumOperands();
|
|
for(unsigned i = 0; i < NumOp; ++i)
|
|
Ops.push_back(Src.getOperand(i));
|
|
Ops[ClampIdx - 1] = DAG.getTargetConstant(1, MVT::i32);
|
|
return DAG.getMachineNode(Src.getMachineOpcode(), SDLoc(Node),
|
|
Node->getVTList(), Ops);
|
|
} else {
|
|
if (!TII->hasInstrModifiers(Opcode))
|
|
return Node;
|
|
int OperandIdx[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src2)
|
|
};
|
|
int NegIdx[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_neg),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_neg),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src2_neg)
|
|
};
|
|
int AbsIdx[] = {
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src0_abs),
|
|
TII->getOperandIdx(Opcode, AMDGPU::OpName::src1_abs),
|
|
-1
|
|
};
|
|
for (unsigned i = 0; i < 3; i++) {
|
|
if (OperandIdx[i] < 0)
|
|
return Node;
|
|
SDValue &Src = Ops[OperandIdx[i] - 1];
|
|
SDValue &Neg = Ops[NegIdx[i] - 1];
|
|
SDValue FakeAbs;
|
|
SDValue &Abs = (AbsIdx[i] > -1) ? Ops[AbsIdx[i] - 1] : FakeAbs;
|
|
bool HasDst = TII->getOperandIdx(Opcode, AMDGPU::OpName::dst) > -1;
|
|
int SelIdx = TII->getSelIdx(Opcode, OperandIdx[i]);
|
|
int ImmIdx = TII->getOperandIdx(Opcode, AMDGPU::OpName::literal);
|
|
if (HasDst) {
|
|
SelIdx--;
|
|
ImmIdx--;
|
|
}
|
|
SDValue &Sel = (SelIdx > -1) ? Ops[SelIdx] : FakeOp;
|
|
SDValue &Imm = Ops[ImmIdx];
|
|
if (FoldOperand(Node, i, Src, Neg, Abs, Sel, Imm, DAG))
|
|
return DAG.getMachineNode(Opcode, SDLoc(Node), Node->getVTList(), Ops);
|
|
}
|
|
}
|
|
|
|
return Node;
|
|
}
|