AronaCore/third_party/HLSLcc/src/Instruction.cpp

350 lines
11 KiB
C++

#include "internal_includes/Instruction.h"
#include "internal_includes/debug.h"
#include "include/ShaderInfo.h"
// Returns the result swizzle operand for an instruction, or NULL if all src operands have swizzles
static Operand *GetSrcSwizzleOperand(Instruction *psInst)
{
switch (psInst->eOpcode)
{
case OPCODE_DP2:
case OPCODE_DP3:
case OPCODE_DP4:
case OPCODE_NOP:
case OPCODE_SWAPC:
case OPCODE_SAMPLE_C:
case OPCODE_SAMPLE_C_LZ:
ASSERT(0);
return NULL;
// Normal arithmetics, all srcs have swizzles
case OPCODE_ADD:
case OPCODE_AND:
case OPCODE_DERIV_RTX:
case OPCODE_DERIV_RTX_COARSE:
case OPCODE_DERIV_RTX_FINE:
case OPCODE_DERIV_RTY:
case OPCODE_DERIV_RTY_COARSE:
case OPCODE_DERIV_RTY_FINE:
case OPCODE_DIV:
case OPCODE_EQ:
case OPCODE_EXP:
case OPCODE_FRC:
case OPCODE_FTOI:
case OPCODE_FTOU:
case OPCODE_GE:
case OPCODE_IADD:
case OPCODE_IEQ:
case OPCODE_IGE:
case OPCODE_ILT:
case OPCODE_IMAD:
case OPCODE_IMAX:
case OPCODE_IMIN:
case OPCODE_IMUL:
case OPCODE_INE:
case OPCODE_INEG:
case OPCODE_ITOF:
case OPCODE_LOG:
case OPCODE_LT:
case OPCODE_MAD:
case OPCODE_MAX:
case OPCODE_MIN:
case OPCODE_MOV:
case OPCODE_MUL:
case OPCODE_NE:
case OPCODE_NOT:
case OPCODE_OR:
case OPCODE_ROUND_NE:
case OPCODE_ROUND_NI:
case OPCODE_ROUND_PI:
case OPCODE_ROUND_Z:
case OPCODE_RSQ:
case OPCODE_SINCOS:
case OPCODE_SQRT:
case OPCODE_UDIV:
case OPCODE_UGE:
case OPCODE_ULT:
case OPCODE_UMAD:
case OPCODE_UMAX:
case OPCODE_UMIN:
case OPCODE_UMUL:
case OPCODE_UTOF:
case OPCODE_XOR:
case OPCODE_BFI:
case OPCODE_BFREV:
case OPCODE_COUNTBITS:
case OPCODE_DADD:
case OPCODE_DDIV:
case OPCODE_DEQ:
case OPCODE_DFMA:
case OPCODE_DGE:
case OPCODE_DLT:
case OPCODE_DMAX:
case OPCODE_DMIN:
case OPCODE_DMUL:
case OPCODE_DMOV:
case OPCODE_DNE:
case OPCODE_DRCP:
case OPCODE_DTOF:
case OPCODE_F16TOF32:
case OPCODE_F32TOF16:
case OPCODE_FIRSTBIT_HI:
case OPCODE_FIRSTBIT_LO:
case OPCODE_FIRSTBIT_SHI:
case OPCODE_FTOD:
case OPCODE_IBFE:
case OPCODE_RCP:
case OPCODE_UADDC:
case OPCODE_UBFE:
case OPCODE_USUBB:
case OPCODE_MOVC:
case OPCODE_DMOVC:
return NULL;
// Special cases:
case OPCODE_GATHER4:
case OPCODE_GATHER4_C:
case OPCODE_LD:
case OPCODE_LD_MS:
case OPCODE_LOD:
case OPCODE_LD_UAV_TYPED:
case OPCODE_LD_RAW:
case OPCODE_SAMPLE:
case OPCODE_SAMPLE_B:
case OPCODE_SAMPLE_L:
case OPCODE_SAMPLE_D:
case OPCODE_RESINFO:
return &psInst->asOperands[2];
case OPCODE_GATHER4_PO:
case OPCODE_GATHER4_PO_C:
case OPCODE_LD_STRUCTURED:
return &psInst->asOperands[3];
case OPCODE_SAMPLE_INFO:
return &psInst->asOperands[1];
case OPCODE_ISHL:
case OPCODE_ISHR:
case OPCODE_USHR:
// sm4 variant has single component selection on src1 -> only src0 has swizzle
if (psInst->asOperands[2].eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE)
return &psInst->asOperands[1];
else // whereas sm5 variant has swizzle also on src1
return NULL;
default:
ASSERT(0);
return NULL;
}
}
// Tweak the source operands of an instruction so that the rebased write mask will still work
static void DoSrcOperandRebase(Operand *psOperand, uint32_t rebase)
{
uint32_t i;
switch (psOperand->eSelMode)
{
default:
case OPERAND_4_COMPONENT_MASK_MODE:
ASSERT(psOperand->ui32CompMask == 0 || psOperand->ui32CompMask == OPERAND_4_COMPONENT_MASK_ALL);
// Special case for immediates, they do not have swizzles
if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32)
{
if (psOperand->iNumComponents > 1)
std::copy(&psOperand->afImmediates[rebase], &psOperand->afImmediates[4], &psOperand->afImmediates[0]);
return;
}
if (psOperand->eType == OPERAND_TYPE_IMMEDIATE64)
{
if (psOperand->iNumComponents > 1)
std::copy(&psOperand->adImmediates[rebase], &psOperand->adImmediates[4], &psOperand->adImmediates[0]);
return;
}
// Need to change this to swizzle
psOperand->eSelMode = OPERAND_4_COMPONENT_SWIZZLE_MODE;
psOperand->ui32Swizzle = 0;
for (i = 0; i < 4 - rebase; i++)
psOperand->aui32Swizzle[i] = i + rebase;
for (; i < 4; i++)
psOperand->aui32Swizzle[i] = rebase; // The first actual input.
break;
case OPERAND_4_COMPONENT_SELECT_1_MODE:
// Nothing to do
break;
case OPERAND_4_COMPONENT_SWIZZLE_MODE:
for (i = rebase; i < 4; i++)
psOperand->aui32Swizzle[i - rebase] = psOperand->aui32Swizzle[i];
break;
}
}
void Instruction::ChangeOperandTempRegister(Operand *psOperand, uint32_t oldReg, uint32_t newReg, uint32_t compMask, uint32_t flags, uint32_t rebase)
{
uint32_t i = 0;
uint32_t accessMask = 0;
int isDestination = 0;
Operand *psSwizzleOperand = NULL;
if (flags & UD_CHANGE_SUBOPERANDS)
{
for (i = 0; i < MAX_SUB_OPERANDS; i++)
{
if (psOperand->m_SubOperands[i].get())
ChangeOperandTempRegister(psOperand->m_SubOperands[i].get(), oldReg, newReg, compMask, UD_CHANGE_ALL, rebase);
}
}
if ((flags & UD_CHANGE_MAIN_OPERAND) == 0)
return;
if (psOperand->eType != OPERAND_TYPE_TEMP)
return;
if (psOperand->ui32RegisterNumber != oldReg)
return;
accessMask = psOperand->GetAccessMask();
// If this operation touches other components than the one(s) we're splitting, skip it
if ((accessMask & (~compMask)) != 0)
{
// Verify that we've not messed up in reachability analysis.
// This would mean that we've encountered an instruction that accesses
// a component in multi-component mode and we're supposed to treat it as single-use only.
// Now that we track operands we can bring this back
ASSERT((accessMask & compMask) == 0);
return;
}
#if 0
printf("Updating operand %d with access mask %X\n", (int)psOperand->id, accessMask);
#endif
psOperand->ui32RegisterNumber = newReg;
if (rebase == 0)
return;
// Update component mask. Note that we don't need to do anything to the suboperands. They do not affect destination writemask.
switch (psOperand->eSelMode)
{
case OPERAND_4_COMPONENT_MASK_MODE:
{
uint32_t oldMask = psOperand->ui32CompMask;
if (oldMask == 0)
oldMask = OPERAND_4_COMPONENT_MASK_ALL;
// Check that we're not losing any information
ASSERT((oldMask >> rebase) << rebase == oldMask);
psOperand->ui32CompMask = (oldMask >> rebase);
break;
}
case OPERAND_4_COMPONENT_SELECT_1_MODE:
ASSERT(psOperand->aui32Swizzle[0] >= rebase);
psOperand->aui32Swizzle[0] -= rebase;
break;
case OPERAND_4_COMPONENT_SWIZZLE_MODE:
{
for (i = 0; i < 4; i++)
{
// Note that this rebase is different from the one done for source operands
ASSERT(psOperand->aui32Swizzle[i] >= rebase);
psOperand->aui32Swizzle[i] -= rebase;
}
break;
}
default:
ASSERT(0);
}
// Tweak operand datatypes
std::copy(&psOperand->aeDataType[rebase], &psOperand->aeDataType[4], &psOperand->aeDataType[0]);
// If this operand is a destination, we'll need to tweak sources as well
for (i = 0; i < ui32FirstSrc; i++)
{
if (psOperand == &asOperands[i])
{
isDestination = 1;
break;
}
}
if (isDestination == 0)
return;
// Nasty corner case of 2 destinations, not supported if both targets are written
ASSERT((ui32FirstSrc < 2) || (asOperands[0].eType == OPERAND_TYPE_NULL) || (asOperands[1].eType == OPERAND_TYPE_NULL));
// If we made it this far, we're rebasing a destination temp (and the only destination), need to tweak sources depending on the instruction
switch (eOpcode)
{
// The opcodes that do not need tweaking:
case OPCODE_DP2:
case OPCODE_DP3:
case OPCODE_DP4:
case OPCODE_BUFINFO:
case OPCODE_SAMPLE_C:
case OPCODE_SAMPLE_C_LZ:
return;
default:
psSwizzleOperand = GetSrcSwizzleOperand(this); // Null means tweak all source operands
if (psSwizzleOperand)
{
DoSrcOperandRebase(psSwizzleOperand, rebase);
return;
}
else
{
for (i = ui32FirstSrc; i < ui32NumOperands; i++)
{
DoSrcOperandRebase(&asOperands[i], rebase);
}
}
return;
}
}
// Returns nonzero if psInst is a sample instruction and the sampler has medium or low precision
bool Instruction::IsPartialPrecisionSamplerInstruction(const ShaderInfo &info, OPERAND_MIN_PRECISION *pType) const
{
const Operand *op;
const ResourceBinding *psBinding = NULL;
OPERAND_MIN_PRECISION sType = OPERAND_MIN_PRECISION_DEFAULT;
switch (eOpcode)
{
default:
return false;
case OPCODE_SAMPLE:
case OPCODE_SAMPLE_B:
case OPCODE_SAMPLE_L:
case OPCODE_SAMPLE_D:
case OPCODE_SAMPLE_C:
case OPCODE_SAMPLE_C_LZ:
break;
}
op = &asOperands[3];
ASSERT(op->eType == OPERAND_TYPE_SAMPLER);
info.GetResourceFromBindingPoint(RGROUP_SAMPLER, op->ui32RegisterNumber, &psBinding);
if (!psBinding)
{
/* Try to look from texture group */
info.GetResourceFromBindingPoint(RGROUP_TEXTURE, op->ui32RegisterNumber, &psBinding);
}
sType = Operand::ResourcePrecisionToOperandPrecision(psBinding ? psBinding->ePrecision : REFLECT_RESOURCE_PRECISION_UNKNOWN);
if (sType == OPERAND_MIN_PRECISION_DEFAULT)
return false;
if (pType)
*pType = sType;
return true;
}