/*===================== begin_copyright_notice ==================================

Copyright (c) 2017 Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


======================= end_copyright_notice ==================================*/
#include "Compiler/CISACodeGen/helper.h"
#include "Compiler/CISACodeGen/ShaderUnits.hpp"
#include "Compiler/CISACodeGen/ShaderCodeGen.hpp"
#include "Compiler/CISACodeGen/DomainShaderLowering.hpp"
#include "DomainShaderLowering.hpp"
#include "Compiler/IGCPassSupport.h"
#include "common/IGCIRBuilder.h"

using namespace IGC;
using namespace IGC::IGCMD;
using namespace llvm;

namespace IGC
{

class DomainShaderLowering : public llvm::FunctionPass
{
public:
    DomainShaderLowering();

    virtual bool runOnFunction(llvm::Function &F) override;

    virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override
    {
        AU.setPreservesCFG();
        AU.addRequired<MetaDataUtilsWrapper>();
        AU.addRequired<CollectDomainShaderProperties>();
        AU.addRequired<CodeGenContextWrapper>();
    }
    static char ID;
private:
    void LowerIntrinsicInputOutput(llvm::Function &F);

    void CalculateVertexHeaderSize(Function& F);

    void AddURBRead(Value* index, Value* offset, Instruction* prev);

    /// Replaces calls to OUTPUT with appropriately computed URBWrites.
    void lowerOutput(GenIntrinsicInst* inst, std::vector<llvm::Instruction*>& offsetInst);

    /// Returns the offset in URB of the attribute with attribute index 
    /// and output type defined by the parameters.
    QuadEltUnit GetURBOffset(ShaderOutputType outputType, uint attrIdx);


    /// Inserts new URBWrite instruction with given mask and arguments before 
    /// instuction 'prev'. 
    /// TODO: This should be a common function for all Lowering passes.
    void LowerToInputVecOrURBReadIntrinsic(Value* index, Value* offset, Instruction* prev);
    void AddInputVec(Instruction* prev, uint32_t currentElementIndex);

    void AddURBWrite(
        Value* offset, 
        unsigned int mask, 
        Value* data[8], 
        Instruction* prev);
    /// Sets up mask and data values depending on the instruction's output type.
    /// TODO: This should be a common function for all Lowering passes.
    void SetMaskAndData( 
        llvm::Instruction* inst, 
        unsigned int & mask, 
        llvm::Value* data[8]);

    static const unsigned int m_maxNumOfOutput; 
    llvm::Module*  m_pModule;    //< remembers the llvm module of this program
    QuadEltUnit    m_headerSize; //< size of the vertex header, always 2 for DS
    const uint32_t m_pMaxNumOfPushedInputs;
    CollectDomainShaderProperties* m_dsPropsPass;
};

#define PASS_FLAG "igc-collect-domain-shader-properties"
#define PASS_DESCRIPTION "Collect information related to domain shader"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS true
IGC_INITIALIZE_PASS_BEGIN(CollectDomainShaderProperties, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_END(CollectDomainShaderProperties, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)

//undef marcros to avoid redefinition warnings
#undef PASS_FLAG
#undef PASS_DESCRIPTION
#undef PASS_ANALYSIS

#define PASS_FLAG "igc-domain-shader-lowering"
#define PASS_DESCRIPTION "Lower inputs outputs for domain shader"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
IGC_INITIALIZE_PASS_BEGIN(DomainShaderLowering, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_DEPENDENCY(CollectDomainShaderProperties)
IGC_INITIALIZE_PASS_END(DomainShaderLowering, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)

DomainShaderLowering::DomainShaderLowering()
: FunctionPass(ID)
, m_headerSize(QuadEltUnit(2))
, m_pMaxNumOfPushedInputs(384)  // No. of dword's that can be pushed (about 48 GRF's worth of input data)
{
    initializeDomainShaderLoweringPass(*PassRegistry::getPassRegistry());
}

CollectDomainShaderProperties::CollectDomainShaderProperties() : ImmutablePass(ID) 
{
    initializeCollectDomainShaderPropertiesPass(*PassRegistry::getPassRegistry());
}

char DomainShaderLowering::ID = 0;
const unsigned int DomainShaderLowering::m_maxNumOfOutput = 32;

bool DomainShaderLowering::runOnFunction(llvm::Function &F)
{
    MetaDataUtils *pMdUtils = getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils();
    if (!isEntryFunc(pMdUtils, &F))
    {
        return false;
    }
    m_dsPropsPass = &getAnalysis<CollectDomainShaderProperties>();
    m_pModule = F.getParent();
    LowerIntrinsicInputOutput(F);
    return true;
}

void DomainShaderLowering::LowerIntrinsicInputOutput(Function& F)
{
    CalculateVertexHeaderSize(F);
    llvm::IRBuilder<> builder(F.getParent()->getContext());
    SmallVector<Instruction*, 10> instructionToRemove;

    Value* zero = builder.getInt32(0);

    llvm::GlobalVariable* pGlobal = m_pModule->getGlobalVariable("MaxNumOfInputSignatureEntries");
    auto pCtx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
    uint maxInputSignatureCount = 
        int_cast<uint>(llvm::cast<llvm::ConstantInt>(pGlobal->getInitializer())->getZExtValue());
    pGlobal = m_pModule->getGlobalVariable("MaxNumOfPatchConstantSignatureEntries");
    uint m_pMaxPatchConstantSignatureDeclarations = int_cast<uint>(llvm::cast<llvm::ConstantInt>(pGlobal->getInitializer())->getZExtValue());

    std::vector<llvm::Instruction*> offsetInst(m_maxNumOfOutput + m_headerSize.Count(), nullptr);
    BasicBlock* retBlock = nullptr;
    for(auto BI = F.begin(), BE = F.end(); BI != BE; ++BI)
    {
        for (auto II = BI->begin(), IE = BI->end(); II != IE; ++II)
        {
            if(isa<ReturnInst>(BI->getTerminator()))
            {
                retBlock = &(*BI);
            }
            if (GenIntrinsicInst* inst = dyn_cast<GenIntrinsicInst>(II))
            {
                GenISAIntrinsic::ID IID = inst->getIntrinsicID();
                if (IID == GenISAIntrinsic::GenISA_DCL_DSCntrlPtInputVec)
                {
                    builder.SetInsertPoint(inst);
                    Value* inputSignatureCount = builder.getInt32(maxInputSignatureCount);
                    Value* newEleId1 = builder.CreateMul(
                        inst->getOperand(0),
                        inputSignatureCount,
                        VALUE_NAME("DSnewEleId1"));
                    Value* newEleId2 = builder.CreateAdd(
                        newEleId1,
                        inst->getOperand(1),
                        VALUE_NAME("DSnewEleId2"));

                    // Add the offset to the patch constant outputs and tessellation header here
                    newEleId2 = builder.CreateAdd(
                        newEleId2,
                        builder.getInt32( ((m_pMaxPatchConstantSignatureDeclarations % 2 == 0) ? 
                                                m_pMaxPatchConstantSignatureDeclarations : 
                                                m_pMaxPatchConstantSignatureDeclarations + 1 ) 
                            + 2), // Max Patch constant signatures does not include the tessellation header
                        VALUE_NAME("DSnewEleId2"));

                    LowerToInputVecOrURBReadIntrinsic(zero, newEleId2, inst);
                }
                else if (IID == GenISAIntrinsic::GenISA_DCL_DSPatchConstInputVec)
                {
                    builder.SetInsertPoint(inst);

                    // Add the offset to  tessellation header here
                    Value* newEleId = builder.CreateAdd(
                        inst->getOperand(0),
                        builder.getInt32(2),// the tessellation header occupies 1GRF
                        VALUE_NAME("DSnewEleId2"));
                    LowerToInputVecOrURBReadIntrinsic(zero, newEleId, inst);
                }
                else if (IID == GenISAIntrinsic::GenISA_DCL_DSInputTessFactor)
                {
                    if (llvm::ConstantInt* pIndex = llvm::dyn_cast<llvm::ConstantInt>(inst->getOperand(0)))
                    {
                        uint32_t index = 0;
                        ShaderOutputType tessFactor =
                            (enum ShaderOutputType)pIndex->getZExtValue();

                        switch (tessFactor)
                        {
                        case SHADER_OUTPUT_TYPE_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR:
                        case SHADER_OUTPUT_TYPE_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR:
                        case SHADER_OUTPUT_TYPE_FINAL_LINE_DETAIL_TESSFACTOR:
                            index = 7;
                            break;

                        case SHADER_OUTPUT_TYPE_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR:
                        case SHADER_OUTPUT_TYPE_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR:
                        case SHADER_OUTPUT_TYPE_FINAL_LINE_DENSITY_TESSFACTOR:
                            index = 6;
                            break;

                        case SHADER_OUTPUT_TYPE_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR:
                        case SHADER_OUTPUT_TYPE_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR:
                            index = 5;
                            break;

                        case SHADER_OUTPUT_TYPE_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR:
                        case SHADER_OUTPUT_TYPE_FINAL_TRI_INSIDE_TESSFACTOR:
                            index = 4;
                            break;

                        case SHADER_OUTPUT_TYPE_FINAL_QUAD_U_INSIDE_TESSFACTOR:
                            index = 3;
                            break;

                        case SHADER_OUTPUT_TYPE_FINAL_QUAD_V_INSIDE_TESSFACTOR:
                            index = 2;
                            break;
                        default:
                            break;
                        }
                        e_interpolation interpolant = DSDualPatchEnabled(pCtx) ? EINTERPOLATION_UNDEFINED: EINTERPOLATION_CONSTANT;
                        Value* arguments[] =
                        {
                            builder.getInt32(index),
                            builder.getInt32(interpolant) // arg2 - interpolation mode
                        };

                        Instruction* shaderInputVec = GenIntrinsicInst::Create(
                            GenISAIntrinsic::getDeclaration(
                            m_pModule,
                            GenISAIntrinsic::GenISA_DCL_inputVec,
                            Type::getFloatTy(m_pModule->getContext())),
                            arguments,
                            "",
                            inst);
                        inst->replaceAllUsesWith(shaderInputVec);
                    }
                }
                else if (IID == GenISAIntrinsic::GenISA_OUTPUT)
                {
                    lowerOutput(inst, offsetInst);
                    instructionToRemove.push_back(inst);
                }
            }
        }
    }
    Value* undef = llvm::UndefValue::get(Type::getFloatTy(F.getContext()));
    if(getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->platform.WaForceDSToWriteURB())
    {
        unsigned int numPhaseWritten = 0;
        for(unsigned int i = 0; i < m_maxNumOfOutput + m_headerSize.Count(); i+=2)
        {
            if(offsetInst[i] != nullptr || offsetInst[i + 1] != nullptr)
            {
                numPhaseWritten++;
            }
        }
        if(numPhaseWritten < 2)
        {
            for(unsigned int i = 0; i < 4; i+=2)
            {
                // In case shader doesn't write in t eh URB header we force a dummy write to avoid HW hang
                if(offsetInst[i] == nullptr && offsetInst[i+1] == nullptr)
                {
                    Value* data[8] =
                    {
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                    };
                    // write 32bytes of random data in the vertex header
                    Value* offset = ConstantInt::get(Type::getInt32Ty(m_pModule->getContext()), i);
                    m_dsPropsPass->DeclareOutput(QuadEltUnit(i+1));
                    m_dsPropsPass->DeclareOutput(QuadEltUnit(i+2));
                    AddURBWrite(offset, 0xFF, data, retBlock->getTerminator());
                }
            }
        }
    }
    //URB padding to 32Byte offsets
    for (unsigned int i = 0; i < m_maxNumOfOutput + m_headerSize.Count(); i++)
    {
        //If not aligned to 32Byte offset and has valid data
        if (offsetInst[i])
        {
            unsigned int adjacentSlot = i + (i % 2 == 0 ? 1 : -1);
            if(offsetInst[adjacentSlot])
            {
                //Valid data present
                continue;
            }
            else
            {
                //No need to pad URB at offset position 0 for OGL since it is always padded with 0
                if (i == 1 && pCtx->m_DriverInfo.NeedClearVertexHeader())
                {
                    break;
                }
                else
                {

                    Value* data[8] =
                    {
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                        undef,
                    };

                    Value* offset = ConstantInt::get(Type::getInt32Ty(m_pModule->getContext()), adjacentSlot);
                    AddURBWrite(offset, 0xF, data, offsetInst[i]);
                }
            }
        }
    }

    for (auto & inst : instructionToRemove)
    {
        inst->eraseFromParent();
    }
}

void DomainShaderLowering::CalculateVertexHeaderSize(Function& F)
{
    IGC::CodeGenContext* context = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
    if (context->getModuleMetaData()->URBInfo.hasVertexHeader)
    {
        if (context->getModuleMetaData()->URBInfo.has64BVertexHeaderOutput)
        {
            m_headerSize = QuadEltUnit(4);
            m_dsPropsPass->DeclareClipDistance();
        }
        else
        {
            m_headerSize = QuadEltUnit(2);
        }
    }
    else
    {
        m_headerSize = QuadEltUnit(0);
    }

    for (auto I = F.begin(), E = F.end(); I != E; ++I)
    {
        llvm::BasicBlock *pLLVMBB = &(*I);
        llvm::BasicBlock::InstListType &instructionList = pLLVMBB->getInstList();
        for (auto I = instructionList.begin(), E = instructionList.end(); I != E; ++I)
        {
            if (GenIntrinsicInst* inst = dyn_cast<GenIntrinsicInst>(I))
            {
                if (inst->getIntrinsicID() == GenISAIntrinsic::GenISA_OUTPUT)
                {
                    const ShaderOutputType usage = static_cast<ShaderOutputType>(
                        llvm::cast<llvm::ConstantInt>(inst->getOperand(4))->getZExtValue());
                    if (usage == SHADER_OUTPUT_TYPE_CLIPDISTANCE_LO ||
                        usage == SHADER_OUTPUT_TYPE_CLIPDISTANCE_HI)
                    {
                        m_headerSize = QuadEltUnit(4);
                        m_dsPropsPass->DeclareClipDistance();
                    }
                }
            }
        }
    }
}


void DomainShaderLowering::LowerToInputVecOrURBReadIntrinsic(Value* index, Value* offset, Instruction* prev)
{
    llvm::IRBuilder<> builder(prev);
    bool loweredToInputVec = false;

    if (llvm::ConstantInt* pElementIndex = llvm::dyn_cast<llvm::ConstantInt>(offset))
    {
        uint elementIndex = static_cast<uint>(pElementIndex->getZExtValue());
        uint currentElementIndex = elementIndex * 4;

        bool pushCondition = (currentElementIndex < m_pMaxNumOfPushedInputs);

        if (pushCondition)
        {
            loweredToInputVec = true;
            AddInputVec(prev, currentElementIndex);
        }
    }
    if (!loweredToInputVec)
    {
        // Else we need to lower this instruction to URBRead
        AddURBRead(index, offset, prev);
    }
}

void DomainShaderLowering::AddInputVec(Instruction* prev, uint32_t currentElementIndex)
{
    llvm::IGCIRBuilder<> builder(prev);
    Value* channels[4] = { 0 };
    
    Function* shaderInputVecFn = GenISAIntrinsic::getDeclaration(
        m_pModule,
        GenISAIntrinsic::GenISA_DCL_inputVec,
        builder.getFloatTy());
    for(unsigned int i = 0; i < 4; i++)
    {
        Value* index = builder.getInt32(currentElementIndex + i);
        auto pCtx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
        e_interpolation interpolant = 
            DSDualPatchEnabled(pCtx) ? EINTERPOLATION_UNDEFINED : EINTERPOLATION_CONSTANT;
        Value* interpolation = builder.getInt32(interpolant);
        channels[i] = builder.CreateCall2(shaderInputVecFn, index, interpolation);
    }
    for(auto useIt = prev->user_begin(); useIt != prev->user_end();)
    {
        Value* I = *useIt++;
        if(ExtractElementInst* elem = dyn_cast<ExtractElementInst>(I))
        {
            if(ConstantInt* index = dyn_cast<ConstantInt>(elem->getIndexOperand()))
            {
                unsigned int channelIndex = static_cast<unsigned int>(index->getZExtValue());
                elem->replaceAllUsesWith(channels[channelIndex]);
                elem->eraseFromParent();
            }
        }
    }
    if(!prev->use_empty())
    {
        Value* vec = UndefValue::get(prev->getType());
        for(unsigned int i = 0; i < 4; i++)
        {
            vec = builder.CreateInsertElement(vec, channels[i], builder.getInt32(i));
        }
        prev->replaceAllUsesWith(vec);
    }
}

void DomainShaderLowering::AddURBRead(Value* index, Value* offset, Instruction* prev)
{
    Value* arguments[] =
    {
        index,
        offset
    };

    Instruction* urbRead = GenIntrinsicInst::Create(
        GenISAIntrinsic::getDeclaration(m_pModule, GenISAIntrinsic::GenISA_URBRead),
        arguments,
        "",
        prev);
    Value* vec4 = nullptr;
    while (!prev->use_empty())
    {
        auto I = prev->user_begin();
        if (ExtractElementInst* elem = dyn_cast<ExtractElementInst>(*I))
        {
            Instruction* newExt =
                ExtractElementInst::Create(urbRead, elem->getIndexOperand(), "", elem);
            elem->replaceAllUsesWith(newExt);
            elem->eraseFromParent();
        }
        else
        {
            // the vector is used directly, extract the first 4 elements and recreate a vec4
            if (vec4 == nullptr)
            {
                Value *data[4] = { nullptr, nullptr, nullptr, nullptr };
                Type *int32Ty = Type::getInt32Ty(m_pModule->getContext());

                VectorToElement(urbRead, data, int32Ty, prev, 4);
                vec4 = ElementToVector(data, int32Ty, prev, 4);
            }

            (*I)->replaceUsesOfWith(prev, vec4);
        }
    }
}

void DomainShaderLowering::lowerOutput(GenIntrinsicInst* inst, std::vector<llvm::Instruction*>& offsetInst)
{
    llvm::IRBuilder<> builder(inst);

    const ShaderOutputType usage = static_cast<ShaderOutputType>(
        llvm::cast<llvm::ConstantInt>(inst->getOperand(4))->getZExtValue());
    const uint attributeIndex = static_cast<uint>(
        llvm::cast<llvm::ConstantInt>(inst->getOperand(5))->getZExtValue());

    Value* data[8];
    unsigned int mask = 0;
    SetMaskAndData(inst, mask, data);


    // Compute the offset for the given attribute and usage.
    QuadEltUnit offset = GetURBOffset(usage, attributeIndex);
    assert(offset.Count() < (m_maxNumOfOutput + m_headerSize.Count()) && "Index is out of bound");
    offsetInst[offset.Count()] = inst;

    uint offsetWritten = int_cast<unsigned int>(offset.Count() + (mask <= 0xF ? 1 : 2));
    m_dsPropsPass->DeclareOutput(QuadEltUnit(offsetWritten));
    // Create urb write instruction.
    Value* offsetVal = builder.getInt32(offset.Count());
    AddURBWrite(offsetVal, mask, data, inst);
}

QuadEltUnit DomainShaderLowering::GetURBOffset(
    ShaderOutputType outputType, 
    uint attrIdx)
{
    switch(outputType)
    {
    case SHADER_OUTPUT_TYPE_POINTWIDTH:
        return QuadEltUnit(0);

    case SHADER_OUTPUT_TYPE_POSITION:
        return QuadEltUnit(1);

    case SHADER_OUTPUT_TYPE_DEFAULT:
        return QuadEltUnit(attrIdx) + m_headerSize;

    case SHADER_OUTPUT_TYPE_CLIPDISTANCE_LO:
        return QuadEltUnit((m_headerSize.Count() - 2));

    case SHADER_OUTPUT_TYPE_CLIPDISTANCE_HI:
        return QuadEltUnit((m_headerSize.Count() - 1));

    case SHADER_OUTPUT_TYPE_VIEWPORT_ARRAY_INDEX:
        m_dsPropsPass->SetVPAIndexDeclared(true);
        return QuadEltUnit(0);
        break;

    case SHADER_OUTPUT_TYPE_RENDER_TARGET_ARRAY_INDEX:
        m_dsPropsPass->SetRTAIndexDeclared(true);
        return QuadEltUnit(0);
        break;
    default:
        assert(!"unknown DS output type");
        break;
    }
    return QuadEltUnit(0);
}

/// Inserts new URBWrite instruction with given mask and arguments before 
/// instuction 'prev'. 
/// TODO: This should be a common function for all Lowering passes.
void DomainShaderLowering::AddURBWrite(
    llvm::Value* offset, 
    unsigned int mask, 
    llvm::Value* data[8], 
    llvm::Instruction* prev)
{
    assert(mask < 256 && "mask is an 8-bit bitmask and has to be in range 0..255");
    Value* arguments[] = 
    {
        offset,
        ConstantInt::get(Type::getInt32Ty(m_pModule->getContext()), mask), 
        data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]
    };

    GenIntrinsicInst::Create(
        GenISAIntrinsic::getDeclaration(m_pModule, GenISAIntrinsic::GenISA_URBWrite), 
        arguments, 
        "",
        prev);
}

// TODO: think about moving this to a common base class for all lowerings
void DomainShaderLowering::SetMaskAndData(
    llvm::Instruction* inst, 
    unsigned int & mask, 
    llvm::Value* data[8])
{
    // argument positions in the argument list
    const uint usageArgPos = 4;

    Value* fpzero = llvm::ConstantFP::get(Type::getFloatTy(m_pModule->getContext()), 0.0f);
    for(unsigned int i=0; i < 8; ++i)
    {
        data[i] = fpzero;
    }
    // handle cases of writes of single dwords that may happen in vertex header
    /// The first four dwords of VUE are as follows:
    /// +------------+
    /// |  reserved  |  <--offset = 0
    /// +------------+
    /// |            |  <--offset = 1 
    /// +------------+
    /// |            |  <--offset = 2 
    /// +------------+
    /// | POINTWIDTH |  <--offset = 3 
    /// +------------+

    const ShaderOutputType usage = static_cast<ShaderOutputType>(
        llvm::cast<llvm::ConstantInt>(inst->getOperand(usageArgPos))->getZExtValue());
    switch(usage)
    {
    case SHADER_OUTPUT_TYPE_POINTWIDTH:
        mask = 0x08;
        data[3] = inst->getOperand(0);
        break;
    case SHADER_OUTPUT_TYPE_RENDER_TARGET_ARRAY_INDEX:
         mask = 0x02;
         data[1] = inst->getOperand(0);
         break;
    case SHADER_OUTPUT_TYPE_VIEWPORT_ARRAY_INDEX:
          mask = 0x04;
          data[2] = inst->getOperand(0);
          break;
    default:
        {
            const uint numDataArgs = 4;
            mask = (1u << numDataArgs) - 1u; // bitmask of 4 consecutive ones
            for(unsigned int i=0; i < numDataArgs; ++i)
            {
                data[i] = inst->getOperand(i);
            }
        }
        break;
    }
}

char CollectDomainShaderProperties::ID = 0;

void CollectDomainShaderProperties::DeclareClipDistance()
{
    m_dsProps.m_hasClipDistance = true;
}

void CollectDomainShaderProperties::DeclareOutput(QuadEltUnit newOffset)
{
    if(m_dsProps.m_URBOutputLength < newOffset)
    {
        m_dsProps.m_URBOutputLength = newOffset;
    }
}

void CollectDomainShaderProperties::SetDomainPointUArgu(llvm::Value* Arg)
{
    m_dsProps.m_UArg = Arg;
}

void CollectDomainShaderProperties::SetDomainPointVArgu(llvm::Value* Arg)
{
    m_dsProps.m_VArg = Arg;
}

void CollectDomainShaderProperties::SetDomainPointWArgu(llvm::Value* Arg)
{
    m_dsProps.m_WArg = Arg;
}

llvm::Value* CollectDomainShaderProperties::GetDomainPointUArgu()
{
    return (m_dsProps.m_UArg);
}

llvm::Value* CollectDomainShaderProperties::GetDomainPointVArgu()
{
    return (m_dsProps.m_VArg);
}

llvm::Value* CollectDomainShaderProperties::GetDomainPointWArgu()
{
    return (m_dsProps.m_WArg);
}

llvm::FunctionPass* createDomainShaderLoweringPass()
{
    return new DomainShaderLowering();
}

void CollectDomainShaderProperties::SetRTAIndexDeclared(bool rtaiDeclared)
{
     m_dsProps.m_isRTAIndexDeclared = rtaiDeclared;
}

void CollectDomainShaderProperties::SetVPAIndexDeclared(bool vpaiDeclared)
{
     m_dsProps.m_isVPAIndexDeclared = vpaiDeclared;
}

}
