
#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;
}


