Files
UnrealEngine/Engine/Source/Developer/ShaderCompilerCommon/Private/ISAParser/QuadModeDetect.cpp
2025-05-18 13:04:45 +08:00

790 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ISAParser/ISAParser.h"
#include "RDNA1_ISA.h"
#include "RDNA2_ISA.h"
struct FDebugParserRDNA1
{
uint32 AdjustAdvance(EInstructionType InstructionType, const uint32* InstPtr)
{
uint32 AdvanceAmount = 0u;
if (InstructionType == EInstructionType::VOP2)
{
const FInstVOP2& Inst = *reinterpret_cast<const FInstVOP2*>(InstPtr);
const RDNA1::EVOP2Ops Op = (const RDNA1::EVOP2Ops)Inst.OP;
// These instructions always have a literal constant that follows
if (RDNA1::HasTrailingLiteral(Op))
{
AdvanceAmount++;
}
}
return AdvanceAmount;
}
bool ParseSOP1(const FInstSOP1& Inst)
{
RDNA1::PrintSOP1(Inst);
return true;
}
bool ParseSOP2(const FInstSOP2& Inst)
{
RDNA1::PrintSOP2(Inst);
return true;
}
bool ParseSOPP(const FInstSOPP& Inst)
{
RDNA1::PrintSOPP(Inst);
const RDNA1::ESOPPOps Op = (const RDNA1::ESOPPOps)Inst.OP;
return (Op != RDNA1::ESOPPOps::s_endpgm);
}
bool ParseSOPK(const FInstSOPK& Inst)
{
RDNA1::PrintSOPK(Inst);
return true;
}
bool ParseSOPC(const FInstSOPC& Inst)
{
RDNA1::PrintSOPC(Inst);
return true;
}
bool ParseSMEM(const FInstSMEM& Inst)
{
RDNA1::PrintSMEM(Inst);
return true;
}
bool ParseVOP1(const FInstVOP1& Inst)
{
RDNA1::PrintVOP1(Inst);
return true;
}
bool ParseVOP1_DPP16(const FInstVOP1& Inst, const FDPP16& DPP16)
{
RDNA1::PrintVOP1(Inst);
return true;
}
bool ParseVOP2(const FInstVOP2& Inst)
{
RDNA1::PrintVOP2(Inst);
return true;
}
bool ParseVOP2_DPP16(const FInstVOP2& Inst, const FDPP16& DPP16)
{
RDNA1::PrintVOP2(Inst);
if (DPP16.DPPCTRL <= 0x0FF)
{
// DPP_QUAD_PERM
}
return true;
}
bool ParseVOP3(const FInstVOP3& Inst)
{
if (RDNA1::IsVOP3BEncoding((RDNA1::EVOP3ABOps)Inst.Encoding.VOP3B.OP))
{
RDNA1::PrintVOP3B(Inst.Encoding.VOP3B);
}
else
{
RDNA1::PrintVOP3A(Inst.Encoding.VOP3A);
}
return true;
}
bool ParseVOPC(const FInstVOPC& Inst)
{
RDNA1::PrintVOPC(Inst);
return true;
}
bool ParseVOPC_DPP16(const FInstVOPC& Inst, const FDPP16& DPP16)
{
RDNA1::PrintVOPC(Inst);
return true;
}
bool ParseVOP3P(const FInstVOP3P& Inst)
{
RDNA1::PrintVOP3P(Inst);
return true;
}
bool ParseVINTERP(const FInstVINTERP& Inst)
{
RDNA1::PrintVINTERP(Inst);
return true;
}
bool ParseLDSGDS(const FInstLDSGDS& Inst)
{
RDNA1::PrintLDSGDS(Inst);
return true;
}
bool ParseMUBUF(const FInstMUBUF& Inst)
{
RDNA1::PrintMUBUF(Inst);
return true;
}
bool ParseMTBUF(const FInstMTBUF& Inst)
{
RDNA1::PrintMTBUF(Inst);
return true;
}
bool ParseEXPORT(const FInstEXPORT& Inst)
{
RDNA1::PrintEXPORT(Inst);
return true;
}
bool ParseFSG(const FInstFSG& Inst)
{
RDNA1::PrintFSG(Inst);
return true;
}
bool ParseMIMG(const FInstMIMG& Inst)
{
RDNA1::PrintMIMG(Inst);
return true;
}
};
struct FDebugParserRDNA2
{
uint32 AdjustAdvance(EInstructionType InstructionType, const uint32* InstPtr)
{
uint32 AdvanceAmount = 0u;
if (InstructionType == EInstructionType::VOP2)
{
const FInstVOP2& Inst = *reinterpret_cast<const FInstVOP2*>(InstPtr);
const RDNA2::EVOP2Ops Op = (const RDNA2::EVOP2Ops)Inst.OP;
// These instructions always have a literal constant that follows
if (RDNA2::HasTrailingLiteral(Op))
{
AdvanceAmount++;
}
}
return AdvanceAmount;
}
bool ParseSOP1(const FInstSOP1& Inst)
{
RDNA2::PrintSOP1(Inst);
return true;
}
bool ParseSOP2(const FInstSOP2& Inst)
{
RDNA2::PrintSOP2(Inst);
return true;
}
bool ParseSOPP(const FInstSOPP& Inst)
{
RDNA2::PrintSOPP(Inst);
const RDNA2::ESOPPOps Op = (const RDNA2::ESOPPOps)Inst.OP;
return (Op != RDNA2::ESOPPOps::s_endpgm);
}
bool ParseSOPK(const FInstSOPK& Inst)
{
RDNA2::PrintSOPK(Inst);
return true;
}
bool ParseSOPC(const FInstSOPC& Inst)
{
RDNA2::PrintSOPC(Inst);
return true;
}
bool ParseSMEM(const FInstSMEM& Inst)
{
RDNA2::PrintSMEM(Inst);
return true;
}
bool ParseVOP1(const FInstVOP1& Inst)
{
RDNA2::PrintVOP1(Inst);
return true;
}
bool ParseVOP1_DPP16(const FInstVOP1& Inst, const FDPP16& DPP16)
{
RDNA2::PrintVOP1(Inst);
return true;
}
bool ParseVOP2(const FInstVOP2& Inst)
{
RDNA2::PrintVOP2(Inst);
return true;
}
bool ParseVOP2_DPP16(const FInstVOP2& Inst, const FDPP16& DPP16)
{
RDNA2::PrintVOP2(Inst);
if (DPP16.DPPCTRL <= 0x0FF)
{
// DPP_QUAD_PERM
}
return true;
}
bool ParseVOP3(const FInstVOP3& Inst)
{
if (RDNA2::IsVOP3BEncoding((RDNA2::EVOP3ABOps)Inst.Encoding.VOP3B.OP))
{
RDNA2::PrintVOP3B(Inst.Encoding.VOP3B);
}
else
{
RDNA2::PrintVOP3A(Inst.Encoding.VOP3A);
}
return true;
}
bool ParseVOPC(const FInstVOPC& Inst)
{
RDNA2::PrintVOPC(Inst);
return true;
}
bool ParseVOPC_DPP16(const FInstVOPC& Inst, const FDPP16& DPP16)
{
RDNA2::PrintVOPC(Inst);
return true;
}
bool ParseVOP3P(const FInstVOP3P& Inst)
{
RDNA2::PrintVOP3P(Inst);
return true;
}
bool ParseVINTERP(const FInstVINTERP& Inst)
{
RDNA2::PrintVINTERP(Inst);
return true;
}
bool ParseLDSGDS(const FInstLDSGDS& Inst)
{
RDNA2::PrintLDSGDS(Inst);
return true;
}
bool ParseMUBUF(const FInstMUBUF& Inst)
{
RDNA2::PrintMUBUF(Inst);
return true;
}
bool ParseMTBUF(const FInstMTBUF& Inst)
{
RDNA2::PrintMTBUF(Inst);
return true;
}
bool ParseEXPORT(const FInstEXPORT& Inst)
{
RDNA2::PrintEXPORT(Inst);
return true;
}
bool ParseFSG(const FInstFSG& Inst)
{
RDNA2::PrintFSG(Inst);
return true;
}
bool ParseMIMG(const FInstMIMG& Inst)
{
RDNA2::PrintMIMG(Inst);
return true;
}
};
struct FQuadModeParserRDNA1
{
bool bQuadMode = false;
bool bDebug = false;
uint32 AdjustAdvance(EInstructionType InstructionType, const uint32* InstPtr)
{
uint32 AdvanceAmount = 0u;
if (InstructionType == EInstructionType::VOP2)
{
const FInstVOP2& Inst = *reinterpret_cast<const FInstVOP2*>(InstPtr);
const RDNA1::EVOP2Ops Op = (const RDNA1::EVOP2Ops)Inst.OP;
// These instructions always have a literal constant that follows
if (RDNA1::HasTrailingLiteral(Op))
{
AdvanceAmount++;
}
}
return AdvanceAmount;
}
inline bool KeepParsing() const
{
// If we detected the need for quad mode, and are not debugging, stop parsing early
return !bQuadMode || bDebug;
}
bool IsDPPQuadPerm(const FDPP16& DPP16)
{
return (DPP16.DPPCTRL <= 0x0FF); // DPP_QUAD_PERM
}
bool ParseSOPP(const FInstSOPP& Inst)
{
const RDNA1::ESOPPOps Op = (const RDNA1::ESOPPOps)Inst.OP;
return (Op != RDNA1::ESOPPOps::s_endpgm) && KeepParsing();
}
bool ParseSOP1(const FInstSOP1& Inst) { return KeepParsing(); }
bool ParseSOP2(const FInstSOP2& Inst) { return KeepParsing(); }
bool ParseSOPK(const FInstSOPK& Inst) { return KeepParsing(); }
bool ParseSOPC(const FInstSOPC& Inst) { return KeepParsing(); }
bool ParseSMEM(const FInstSMEM& Inst) { return KeepParsing(); }
bool ParseVOP1(const FInstVOP1& Inst) { return KeepParsing(); }
bool ParseVOP2(const FInstVOP2& Inst) { return KeepParsing(); }
bool ParseVOP3(const FInstVOP3& Inst) { return KeepParsing(); }
bool ParseVOPC(const FInstVOPC& Inst) { return KeepParsing(); }
bool ParseVOP3P(const FInstVOP3P& Inst) { return KeepParsing(); }
bool ParseVINTERP(const FInstVINTERP& Inst) { return KeepParsing(); }
bool ParseLDSGDS(const FInstLDSGDS& Inst) { return KeepParsing(); }
bool ParseMUBUF(const FInstMUBUF& Inst) { return KeepParsing(); }
bool ParseMTBUF(const FInstMTBUF& Inst) { return KeepParsing(); }
bool ParseEXPORT(const FInstEXPORT& Inst) { return KeepParsing(); }
bool ParseFSG(const FInstFSG& Inst) { return KeepParsing(); }
bool ParseVOP1_DPP16(const FInstVOP1& Inst, const FDPP16& DPP16)
{
if (IsDPPQuadPerm(DPP16))
{
bQuadMode = true;
if (bDebug)
{
RDNA1::PrintVOP1(Inst);
}
}
return KeepParsing();
}
bool ParseVOP2_DPP16(const FInstVOP2& Inst, const FDPP16& DPP16)
{
if (IsDPPQuadPerm(DPP16))
{
bQuadMode = true;
if (bDebug)
{
RDNA1::PrintVOP2(Inst);
}
}
return KeepParsing();
}
bool ParseVOPC_DPP16(const FInstVOPC& Inst, const FDPP16& DPP16)
{
if (IsDPPQuadPerm(DPP16))
{
bQuadMode = true;
if (bDebug)
{
RDNA1::PrintVOPC(Inst);
}
}
return KeepParsing();
}
bool ParseMIMG(const FInstMIMG& Inst)
{
const RDNA1::EMIMGOps Op = (const RDNA1::EMIMGOps)Inst.OP;
switch (Op)
{
case RDNA1::EMIMGOps::image_sample:
case RDNA1::EMIMGOps::image_sample_cl:
case RDNA1::EMIMGOps::image_sample_b:
case RDNA1::EMIMGOps::image_sample_b_cl:
case RDNA1::EMIMGOps::image_sample_c:
case RDNA1::EMIMGOps::image_sample_c_cl:
case RDNA1::EMIMGOps::image_sample_c_b:
case RDNA1::EMIMGOps::image_sample_c_b_cl:
case RDNA1::EMIMGOps::image_sample_o:
case RDNA1::EMIMGOps::image_sample_cl_o:
case RDNA1::EMIMGOps::image_sample_b_o:
case RDNA1::EMIMGOps::image_sample_b_cl_o:
case RDNA1::EMIMGOps::image_sample_c_o:
case RDNA1::EMIMGOps::image_sample_c_cl_o:
case RDNA1::EMIMGOps::image_sample_c_b_o:
case RDNA1::EMIMGOps::image_sample_c_b_cl_o:
case RDNA1::EMIMGOps::image_gather4:
case RDNA1::EMIMGOps::image_gather4_cl:
case RDNA1::EMIMGOps::image_gather4_b:
case RDNA1::EMIMGOps::image_gather4_b_cl:
case RDNA1::EMIMGOps::image_gather4_c:
case RDNA1::EMIMGOps::image_gather4_c_cl:
case RDNA1::EMIMGOps::image_gather4_c_b:
case RDNA1::EMIMGOps::image_gather4_c_b_cl:
case RDNA1::EMIMGOps::image_gather4_o:
case RDNA1::EMIMGOps::image_gather4_cl_o:
case RDNA1::EMIMGOps::image_gather4_b_o:
case RDNA1::EMIMGOps::image_gather4_b_cl_o:
case RDNA1::EMIMGOps::image_gather4_c_o:
case RDNA1::EMIMGOps::image_gather4_c_cl_o:
case RDNA1::EMIMGOps::image_gather4_c_b_o:
case RDNA1::EMIMGOps::image_gather4_c_b_cl_o:
case RDNA1::EMIMGOps::image_get_lod:
case RDNA1::EMIMGOps::image_gather4h:
{
if (bDebug)
{
RDNA1::PrintMIMG(Inst);
}
bQuadMode = true;
break;
}
default:
break;
}
return KeepParsing();
}
};
struct FQuadModeParserRDNA2
{
bool bQuadMode = false;
bool bDebug = false;
uint32 AdjustAdvance(EInstructionType InstructionType, const uint32* InstPtr)
{
uint32 AdvanceAmount = 0u;
if (InstructionType == EInstructionType::VOP2)
{
const FInstVOP2& Inst = *reinterpret_cast<const FInstVOP2*>(InstPtr);
const RDNA2::EVOP2Ops Op = (const RDNA2::EVOP2Ops)Inst.OP;
// These instructions always have a literal constant that follows
if (RDNA2::HasTrailingLiteral(Op))
{
AdvanceAmount++;
}
}
return AdvanceAmount;
}
inline bool KeepParsing() const
{
// If we detected the need for quad mode, and are not debugging, stop parsing early
return !bQuadMode || bDebug;
}
bool IsDPPQuadPerm(const FDPP16& DPP16)
{
return (DPP16.DPPCTRL <= 0x0FF); // DPP_QUAD_PERM
}
bool ParseSOPP(const FInstSOPP& Inst)
{
const RDNA2::ESOPPOps Op = (const RDNA2::ESOPPOps)Inst.OP;
return (Op != RDNA2::ESOPPOps::s_endpgm) && KeepParsing();
}
bool ParseSOP1(const FInstSOP1& Inst) { return KeepParsing(); }
bool ParseSOP2(const FInstSOP2& Inst) { return KeepParsing(); }
bool ParseSOPK(const FInstSOPK& Inst) { return KeepParsing(); }
bool ParseSOPC(const FInstSOPC& Inst) { return KeepParsing(); }
bool ParseSMEM(const FInstSMEM& Inst) { return KeepParsing(); }
bool ParseVOP1(const FInstVOP1& Inst) { return KeepParsing(); }
bool ParseVOP2(const FInstVOP2& Inst) { return KeepParsing(); }
bool ParseVOP3(const FInstVOP3& Inst) { return KeepParsing(); }
bool ParseVOPC(const FInstVOPC& Inst) { return KeepParsing(); }
bool ParseVOP3P(const FInstVOP3P& Inst) { return KeepParsing(); }
bool ParseVINTERP(const FInstVINTERP& Inst) { return KeepParsing(); }
bool ParseLDSGDS(const FInstLDSGDS& Inst) { return KeepParsing(); }
bool ParseMUBUF(const FInstMUBUF& Inst) { return KeepParsing(); }
bool ParseMTBUF(const FInstMTBUF& Inst) { return KeepParsing(); }
bool ParseEXPORT(const FInstEXPORT& Inst) { return KeepParsing(); }
bool ParseFSG(const FInstFSG& Inst) { return KeepParsing(); }
bool ParseVOP1_DPP16(const FInstVOP1& Inst, const FDPP16& DPP16)
{
if (IsDPPQuadPerm(DPP16))
{
bQuadMode = true;
if (bDebug)
{
RDNA2::PrintVOP1(Inst);
}
}
return KeepParsing();
}
bool ParseVOP2_DPP16(const FInstVOP2& Inst, const FDPP16& DPP16)
{
if (IsDPPQuadPerm(DPP16))
{
bQuadMode = true;
if (bDebug)
{
RDNA2::PrintVOP2(Inst);
}
}
return KeepParsing();
}
bool ParseVOPC_DPP16(const FInstVOPC& Inst, const FDPP16& DPP16)
{
if (IsDPPQuadPerm(DPP16))
{
bQuadMode = true;
if (bDebug)
{
RDNA2::PrintVOPC(Inst);
}
}
return KeepParsing();
}
bool ParseMIMG(const FInstMIMG& Inst)
{
const RDNA2::EMIMGOps Op = (const RDNA2::EMIMGOps)Inst.OP;
switch (Op)
{
case RDNA2::EMIMGOps::image_sample:
case RDNA2::EMIMGOps::image_sample_cl:
case RDNA2::EMIMGOps::image_sample_b:
case RDNA2::EMIMGOps::image_sample_b_cl:
case RDNA2::EMIMGOps::image_sample_c:
case RDNA2::EMIMGOps::image_sample_c_cl:
case RDNA2::EMIMGOps::image_sample_c_b:
case RDNA2::EMIMGOps::image_sample_c_b_cl:
case RDNA2::EMIMGOps::image_sample_o:
case RDNA2::EMIMGOps::image_sample_cl_o:
case RDNA2::EMIMGOps::image_sample_b_o:
case RDNA2::EMIMGOps::image_sample_b_cl_o:
case RDNA2::EMIMGOps::image_sample_c_o:
case RDNA2::EMIMGOps::image_sample_c_cl_o:
case RDNA2::EMIMGOps::image_sample_c_b_o:
case RDNA2::EMIMGOps::image_sample_c_b_cl_o:
case RDNA2::EMIMGOps::image_gather4:
case RDNA2::EMIMGOps::image_gather4_cl:
case RDNA2::EMIMGOps::image_gather4_b:
case RDNA2::EMIMGOps::image_gather4_b_cl:
case RDNA2::EMIMGOps::image_gather4_c:
case RDNA2::EMIMGOps::image_gather4_c_cl:
case RDNA2::EMIMGOps::image_gather4_c_b:
case RDNA2::EMIMGOps::image_gather4_c_b_cl:
case RDNA2::EMIMGOps::image_gather4_o:
case RDNA2::EMIMGOps::image_gather4_cl_o:
case RDNA2::EMIMGOps::image_gather4_b_o:
case RDNA2::EMIMGOps::image_gather4_b_cl_o:
case RDNA2::EMIMGOps::image_gather4_c_o:
case RDNA2::EMIMGOps::image_gather4_c_cl_o:
case RDNA2::EMIMGOps::image_gather4_c_b_o:
case RDNA2::EMIMGOps::image_gather4_c_b_cl_o:
case RDNA2::EMIMGOps::image_get_lod:
case RDNA2::EMIMGOps::image_gather4h:
case RDNA2::EMIMGOps::image_gather4h_pck:
case RDNA2::EMIMGOps::image_gather8h_pck:
{
if (bDebug)
{
RDNA2::PrintMIMG(Inst);
}
bQuadMode = true;
break;
}
default:
break;
}
return KeepParsing();
}
};
template <class PARSER>
bool PerformParsing(PARSER& Parser, const uint32* ReadPtr, const uint32* EndPtr)
{
bool bSuccess = true;
bool bParsing = true;
while (ReadPtr < EndPtr && bParsing)
{
uint32_t AdvanceAmount = uint32_t(EndPtr - ReadPtr);
EInstructionType InstructionType = DecodeInstructionType(ReadPtr, AdvanceAmount);
AdvanceAmount += Parser.AdjustAdvance(InstructionType, ReadPtr);
if (InstructionType == EInstructionType::UNKNOWN)
{
//UE_LOG(LogTemp, Error, TEXT("Failed ISA Parse - Unknown Instruction"));
bSuccess = false;
break;
}
else if (ReadPtr + AdvanceAmount >= EndPtr)
{
//UE_LOG(LogTemp, Error, TEXT("Failed ISA Parse - Overfetch"));
bSuccess = false;
break;
}
else if (InstructionType == EInstructionType::VOP1)
{
const FInstVOP1& Inst = *reinterpret_cast<const FInstVOP1*>(ReadPtr);
if (Inst.SRC0 == Operand_DPP16)
{
const FDPP16& DPP16 = *reinterpret_cast<const FDPP16*>(ReadPtr + 1);
bParsing = Parser.ParseVOP1_DPP16(Inst, DPP16);
}
else
{
bParsing = Parser.ParseVOP1(Inst);
}
}
else if (InstructionType == EInstructionType::VOP2)
{
const FInstVOP2& Inst = *reinterpret_cast<const FInstVOP2*>(ReadPtr);
if (Inst.SRC0 == Operand_DPP16)
{
const FDPP16& DPP16 = *reinterpret_cast<const FDPP16*>(ReadPtr + GetAdvanceAmount(EInstructionType::VOP2));
bParsing = Parser.ParseVOP2_DPP16(Inst, DPP16);
}
else
{
bParsing = Parser.ParseVOP2(Inst);
}
}
else if (InstructionType == EInstructionType::VOPC)
{
const FInstVOPC& Inst = *reinterpret_cast<const FInstVOPC*>(ReadPtr);
if (Inst.SRC0 == Operand_DPP16)
{
const FDPP16& DPP16 = *reinterpret_cast<const FDPP16*>(ReadPtr + 1);
bParsing = Parser.ParseVOPC_DPP16(Inst, DPP16);
}
else
{
bParsing = Parser.ParseVOPC(Inst);
}
}
#define PARSE_MICROCODE_FORMAT(FORMAT) \
else if (InstructionType == EInstructionType::FORMAT) \
{ \
bParsing = Parser.Parse##FORMAT(*reinterpret_cast<const FInst##FORMAT*>(ReadPtr)); \
}
PARSE_MICROCODE_FORMAT(SOP1)
PARSE_MICROCODE_FORMAT(SOP2)
PARSE_MICROCODE_FORMAT(SOPP)
PARSE_MICROCODE_FORMAT(SOPK)
PARSE_MICROCODE_FORMAT(SOPC)
PARSE_MICROCODE_FORMAT(SMEM)
PARSE_MICROCODE_FORMAT(VOP3)
PARSE_MICROCODE_FORMAT(VOP3P)
PARSE_MICROCODE_FORMAT(VINTERP)
PARSE_MICROCODE_FORMAT(LDSGDS)
PARSE_MICROCODE_FORMAT(MUBUF)
PARSE_MICROCODE_FORMAT(MTBUF)
PARSE_MICROCODE_FORMAT(EXPORT)
PARSE_MICROCODE_FORMAT(FSG)
PARSE_MICROCODE_FORMAT(MIMG)
#undef PARSE_MICROCODE_FORMAT
ReadPtr += AdvanceAmount;
}
return bSuccess;
}
#define DEBUG_DUMP_FAILURES 0
bool ISAParser::HasDerivativeOps(bool& bHasDerivativeOps, const char* Code, uint32 CodeLength, EInstructionSet InstructionSet)
{
const uint32* ReadPtr = reinterpret_cast<const uint32*>(Code);
const uint32* EndPtr = reinterpret_cast<const uint32*>(Code + CodeLength);
// Safe default, any error and behavior is unchanged
bHasDerivativeOps = true;
if (InstructionSet == EInstructionSet::RDNA1)
{
FQuadModeParserRDNA1 Parser;
if (PerformParsing(Parser, ReadPtr, EndPtr))
{
bHasDerivativeOps = Parser.bQuadMode;
}
else
{
#if DEBUG_DUMP_FAILURES
FDebugParserRDNA1 PrintParser;
PerformParsing(PrintParser, ReadPtr, EndPtr);
#endif
return false;
}
}
else if (InstructionSet == EInstructionSet::RDNA2)
{
FQuadModeParserRDNA2 Parser;
if (PerformParsing(Parser, ReadPtr, EndPtr))
{
bHasDerivativeOps = Parser.bQuadMode;
}
else
{
#if DEBUG_DUMP_FAILURES
FDebugParserRDNA2 PrintParser;
PerformParsing(PrintParser, ReadPtr, EndPtr);
#endif
return false;
}
}
else
{
checkNoEntry();
return false;
}
return true;
}