[dxbc] Implemented basic control flow instuctions

This commit is contained in:
Philip Rebohle 2017-12-18 11:53:28 +01:00
parent 858913ec0c
commit 1e08c0744f
9 changed files with 430 additions and 95 deletions

View File

@ -48,84 +48,37 @@ namespace dxvk {
void DxbcCompiler::processInstruction(const DxbcShaderInstruction& ins) {
switch (ins.op) {
case DxbcOpcode::DclGlobalFlags:
return this->emitDclGlobalFlags(ins);
switch (ins.opClass) {
case DxbcInstClass::Declaration:
return this->emitDcl(ins);
case DxbcOpcode::DclTemps:
return this->emitDclTemps(ins);
case DxbcInstClass::ControlFlow:
return this->emitControlFlow(ins);
case DxbcOpcode::DclInput:
case DxbcOpcode::DclInputSgv:
case DxbcOpcode::DclInputSiv:
case DxbcOpcode::DclInputPs:
case DxbcOpcode::DclInputPsSgv:
case DxbcOpcode::DclInputPsSiv:
case DxbcOpcode::DclOutput:
case DxbcOpcode::DclOutputSgv:
case DxbcOpcode::DclOutputSiv:
return this->emitDclInterfaceReg(ins);
case DxbcOpcode::DclConstantBuffer:
return this->emitDclConstantBuffer(ins);
case DxbcOpcode::DclSampler:
return this->emitDclSampler(ins);
case DxbcOpcode::DclResource:
return this->emitDclResource(ins);
case DxbcOpcode::Add:
case DxbcOpcode::Div:
case DxbcOpcode::Exp:
case DxbcOpcode::Log:
case DxbcOpcode::Mad:
case DxbcOpcode::Max:
case DxbcOpcode::Min:
case DxbcOpcode::Mul:
case DxbcOpcode::Mov:
case DxbcOpcode::Rsq:
case DxbcOpcode::Sqrt:
case DxbcOpcode::IAdd:
case DxbcOpcode::IMad:
case DxbcOpcode::IMax:
case DxbcOpcode::IMin:
case DxbcOpcode::INeg:
return this->emitVectorAlu(ins);
case DxbcOpcode::Movc:
return this->emitVectorCmov(ins);
case DxbcOpcode::Eq:
case DxbcOpcode::Ge:
case DxbcOpcode::Lt:
case DxbcOpcode::Ne:
case DxbcOpcode::IEq:
case DxbcOpcode::IGe:
case DxbcOpcode::ILt:
case DxbcOpcode::INe:
return this->emitVectorCmp(ins);
case DxbcOpcode::Dp2:
case DxbcOpcode::Dp3:
case DxbcOpcode::Dp4:
return this->emitVectorDot(ins);
case DxbcOpcode::IMul:
return this->emitVectorImul(ins);
case DxbcOpcode::SinCos:
return this->emitVectorSinCos(ins);
case DxbcOpcode::Sample:
case DxbcInstClass::TextureSample:
return this->emitSample(ins);
case DxbcOpcode::Ret:
return this->emitRet(ins);
case DxbcInstClass::VectorAlu:
return this->emitVectorAlu(ins);
case DxbcInstClass::VectorCmov:
return this->emitVectorCmov(ins);
case DxbcInstClass::VectorCmp:
return this->emitVectorCmp(ins);
case DxbcInstClass::VectorDot:
return this->emitVectorDot(ins);
case DxbcInstClass::VectorImul:
return this->emitVectorImul(ins);
case DxbcInstClass::VectorSinCos:
return this->emitVectorSinCos(ins);
default:
Logger::warn(
str::format("DxbcCompiler: Unhandled opcode: ",
str::format("DxbcCompiler: Unhandled opcode class: ",
ins.op));
}
}
@ -171,6 +124,42 @@ namespace dxvk {
}
void DxbcCompiler::emitDcl(const DxbcShaderInstruction& ins) {
switch (ins.op) {
case DxbcOpcode::DclGlobalFlags:
return this->emitDclGlobalFlags(ins);
case DxbcOpcode::DclTemps:
return this->emitDclTemps(ins);
case DxbcOpcode::DclInput:
case DxbcOpcode::DclInputSgv:
case DxbcOpcode::DclInputSiv:
case DxbcOpcode::DclInputPs:
case DxbcOpcode::DclInputPsSgv:
case DxbcOpcode::DclInputPsSiv:
case DxbcOpcode::DclOutput:
case DxbcOpcode::DclOutputSgv:
case DxbcOpcode::DclOutputSiv:
return this->emitDclInterfaceReg(ins);
case DxbcOpcode::DclConstantBuffer:
return this->emitDclConstantBuffer(ins);
case DxbcOpcode::DclSampler:
return this->emitDclSampler(ins);
case DxbcOpcode::DclResource:
return this->emitDclResource(ins);
default:
Logger::warn(
str::format("DxbcCompiler: Unhandled opcode: ",
ins.op));
}
}
void DxbcCompiler::emitDclGlobalFlags(const DxbcShaderInstruction& ins) {
// TODO implement properly
}
@ -962,13 +951,191 @@ namespace dxvk {
emitRegisterStore(ins.dst[0], result);
}
void DxbcCompiler::emitControlFlowIf(const DxbcShaderInstruction& ins) {
// Load the first component of the condition
// operand and perform a zero test on it.
const DxbcRegisterValue condition = emitRegisterLoad(
ins.src[0], DxbcRegMask(true, false, false, false));
void DxbcCompiler::emitRet(const DxbcShaderInstruction& ins) {
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
condition, ins.controls.zeroTest);
// Declare the 'if' block. We do not know if there
// will be an 'else' block or not, so we'll assume
// that there is one and leave it empty otherwise.
DxbcCfgBlock block;
block.type = DxbcCfgBlockType::If;
block.b_if.labelIf = m_module.allocateId();
block.b_if.labelElse = m_module.allocateId();
block.b_if.labelEnd = m_module.allocateId();
block.b_if.hadElse = false;
m_controlFlowBlocks.push_back(block);
m_module.opSelectionMerge(
block.b_if.labelEnd,
spv::SelectionControlMaskNone);
m_module.opBranchConditional(
zeroTest.id,
block.b_if.labelIf,
block.b_if.labelElse);
m_module.opLabel(block.b_if.labelIf);
}
void DxbcCompiler::emitControlFlowElse(const DxbcShaderInstruction& ins) {
if (m_controlFlowBlocks.size() == 0
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If
|| m_controlFlowBlocks.back().b_if.hadElse)
throw DxvkError("DxbcCompiler: 'Else' without 'If' found");
// Set the 'Else' flag so that we do
// not insert a dummy block on 'EndIf'
DxbcCfgBlock& block = m_controlFlowBlocks.back();
block.b_if.hadElse = true;
// Close the 'If' block by branching to
// the merge block we declared earlier
m_module.opBranch(block.b_if.labelEnd);
m_module.opLabel (block.b_if.labelElse);
}
void DxbcCompiler::emitControlFlowEndIf(const DxbcShaderInstruction& ins) {
if (m_controlFlowBlocks.size() == 0
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If)
throw DxvkError("DxbcCompiler: 'EndIf' without 'If' found");
// Remove the block from the stack, it's closed
const DxbcCfgBlock block = m_controlFlowBlocks.back();
m_controlFlowBlocks.pop_back();
// End the active 'if' or 'else' block
m_module.opBranch(block.b_if.labelEnd);
// If there was no 'else' block in this construct, we still
// have to declare it because we used it as a branch target.
if (!block.b_if.hadElse) {
m_module.opLabel (block.b_if.labelElse);
m_module.opBranch(block.b_if.labelEnd);
}
// Declare the merge block
m_module.opLabel(block.b_if.labelEnd);
}
void DxbcCompiler::emitControlFlowLoop(const DxbcShaderInstruction& ins) {
// Declare the 'loop' block
DxbcCfgBlock block;
block.type = DxbcCfgBlockType::Loop;
block.b_loop.labelHeader = m_module.allocateId();
block.b_loop.labelBegin = m_module.allocateId();
block.b_loop.labelContinue = m_module.allocateId();
block.b_loop.labelBreak = m_module.allocateId();
m_controlFlowBlocks.push_back(block);
m_module.opBranch(block.b_loop.labelHeader);
m_module.opLabel (block.b_loop.labelHeader);
m_module.opLoopMerge(
block.b_loop.labelBreak,
block.b_loop.labelContinue,
spv::LoopControlMaskNone);
m_module.opBranch(block.b_loop.labelBegin);
m_module.opLabel (block.b_loop.labelBegin);
}
void DxbcCompiler::emitControlFlowEndLoop(const DxbcShaderInstruction& ins) {
if (m_controlFlowBlocks.size() == 0
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::Loop)
throw DxvkError("DxbcCompiler: 'EndLoop' without 'Loop' found");
// Remove the block from the stack, it's closed
const DxbcCfgBlock block = m_controlFlowBlocks.back();
m_controlFlowBlocks.pop_back();
// Declare the continue block
m_module.opBranch(block.b_loop.labelContinue);
m_module.opLabel (block.b_loop.labelContinue);
// Declare the merge block
m_module.opBranch(block.b_loop.labelHeader);
m_module.opLabel (block.b_loop.labelBreak);
}
void DxbcCompiler::emitControlFlowBreakc(const DxbcShaderInstruction& ins) {
DxbcCfgBlock* loopBlock = cfgFindLoopBlock();
if (loopBlock == nullptr)
throw DxvkError("DxbcCompiler: 'Breakc' outside 'Loop' found");
// Perform zero test on the first component of the condition
const DxbcRegisterValue condition = emitRegisterLoad(
ins.src[0], DxbcRegMask(true, false, false, false));
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
condition, ins.controls.zeroTest);
// We basically have to wrap this into an 'if' block
const uint32_t breakBlock = m_module.allocateId();
const uint32_t mergeBlock = m_module.allocateId();
m_module.opSelectionMerge(mergeBlock,
spv::SelectionControlMaskNone);
m_module.opBranchConditional(
zeroTest.id, breakBlock, mergeBlock);
m_module.opLabel(breakBlock);
m_module.opBranch(loopBlock->b_loop.labelBreak);
m_module.opLabel(mergeBlock);
}
void DxbcCompiler::emitControlFlowRet(const DxbcShaderInstruction& ins) {
// TODO implement properly
m_module.opReturn();
m_module.functionEnd();
}
void DxbcCompiler::emitControlFlow(const DxbcShaderInstruction& ins) {
switch (ins.op) {
case DxbcOpcode::If:
return this->emitControlFlowIf(ins);
case DxbcOpcode::Else:
return this->emitControlFlowElse(ins);
case DxbcOpcode::EndIf:
return this->emitControlFlowEndIf(ins);
case DxbcOpcode::Loop:
return this->emitControlFlowLoop(ins);
case DxbcOpcode::EndLoop:
return this->emitControlFlowEndLoop(ins);
case DxbcOpcode::Breakc:
return this->emitControlFlowBreakc(ins);
case DxbcOpcode::Ret:
return this->emitControlFlowRet(ins);
default:
Logger::warn(str::format(
"DxbcCompiler: Unhandled instruction: ",
ins.op));
}
}
DxbcRegisterValue DxbcCompiler::emitRegisterBitcast(
DxbcRegisterValue srcValue,
@ -1131,6 +1298,23 @@ namespace dxvk {
}
DxbcRegisterValue DxbcCompiler::emitRegisterZeroTest(
DxbcRegisterValue value,
DxbcZeroTest test) {
DxbcRegisterValue result;
result.type.ctype = DxbcScalarType::Bool;
result.type.ccount = 1;
const uint32_t zeroId = m_module.constu32(0u);
const uint32_t typeId = getVectorTypeId(result.type);
result.id = test == DxbcZeroTest::TestZ
? m_module.opIEqual (typeId, value.id, zeroId)
: m_module.opINotEqual(typeId, value.id, zeroId);
return result;
}
DxbcRegisterValue DxbcCompiler::emitSrcOperandModifiers(
DxbcRegisterValue value,
DxbcRegModifiers modifiers) {
@ -1433,7 +1617,7 @@ namespace dxvk {
default:
Logger::warn(str::format(
"dxbc: Unhandled vertex sv output: ",
"DxbcCompiler: Unhandled vertex sv output: ",
svMapping.sv));
}
}
@ -1538,14 +1722,26 @@ namespace dxvk {
}
DxbcCfgBlock* DxbcCompiler::cfgFindLoopBlock() {
for (auto cur = m_controlFlowBlocks.rbegin();
cur != m_controlFlowBlocks.rend(); cur++) {
if (cur->type == DxbcCfgBlockType::Loop)
return &(*cur);
}
return nullptr;
}
uint32_t DxbcCompiler::getScalarTypeId(DxbcScalarType type) {
switch (type) {
case DxbcScalarType::Uint32: return m_module.defIntType(32, 0);
case DxbcScalarType::Uint64: return m_module.defIntType(64, 0);
case DxbcScalarType::Sint32: return m_module.defIntType(32, 1);
case DxbcScalarType::Sint64: return m_module.defIntType(64, 1);
case DxbcScalarType::Uint32: return m_module.defIntType(32, 0);
case DxbcScalarType::Uint64: return m_module.defIntType(64, 0);
case DxbcScalarType::Sint32: return m_module.defIntType(32, 1);
case DxbcScalarType::Sint64: return m_module.defIntType(64, 1);
case DxbcScalarType::Float32: return m_module.defFloatType(32);
case DxbcScalarType::Float64: return m_module.defFloatType(64);
case DxbcScalarType::Bool: return m_module.defBoolType();
}
throw DxvkError("DxbcCompiler: Invalid scalar type");

View File

@ -82,6 +82,37 @@ namespace dxvk {
};
enum class DxbcCfgBlockType : uint32_t {
If, Loop,
};
struct DxbcCfgBlockIf {
uint32_t labelIf;
uint32_t labelElse;
uint32_t labelEnd;
bool hadElse;
};
struct DxbcCfgBlockLoop {
uint32_t labelHeader;
uint32_t labelBegin;
uint32_t labelContinue;
uint32_t labelBreak;
};
struct DxbcCfgBlock {
DxbcCfgBlockType type;
union {
DxbcCfgBlockIf b_if;
DxbcCfgBlockLoop b_loop;
};
};
/**
* \brief DXBC to SPIR-V shader compiler
*
@ -149,6 +180,11 @@ namespace dxvk {
std::array<DxbcSampler, 16> m_samplers;
std::array<DxbcShaderResource, 128> m_textures;
///////////////////////////////////////////////
// Control flow information. Stores labels for
// currently active if-else blocks and loops.
std::vector<DxbcCfgBlock> m_controlFlowBlocks;
///////////////////////////////////////////////////////////
// Array of input values. Since v# registers are indexable
// in DXBC, we need to copy them into an array first.
@ -173,14 +209,17 @@ namespace dxvk {
/////////////////////////////////////////////////////
// Shader interface and metadata declaration methods
void emitDcl(
const DxbcShaderInstruction& ins);
void emitDclGlobalFlags(
const DxbcShaderInstruction& ins);
const DxbcShaderInstruction& ins);
void emitDclTemps(
const DxbcShaderInstruction& ins);
const DxbcShaderInstruction& ins);
void emitDclInterfaceReg(
const DxbcShaderInstruction& ins);
const DxbcShaderInstruction& ins);
void emitDclInput(
uint32_t regIdx,
@ -228,9 +267,31 @@ namespace dxvk {
void emitSample(
const DxbcShaderInstruction& ins);
void emitRet(
/////////////////////////////////////
// Control flow instruction handlers
void emitControlFlowIf(
const DxbcShaderInstruction& ins);
void emitControlFlowElse(
const DxbcShaderInstruction& ins);
void emitControlFlowEndIf(
const DxbcShaderInstruction& ins);
void emitControlFlowLoop(
const DxbcShaderInstruction& ins);
void emitControlFlowEndLoop(
const DxbcShaderInstruction& ins);
void emitControlFlowBreakc(
const DxbcShaderInstruction& ins);
void emitControlFlowRet(
const DxbcShaderInstruction& ins);
void emitControlFlow(
const DxbcShaderInstruction& ins);
/////////////////////////////////////////
// Generic register manipulation methods
@ -262,6 +323,10 @@ namespace dxvk {
DxbcRegisterValue emitRegisterNegate(
DxbcRegisterValue value);
DxbcRegisterValue emitRegisterZeroTest(
DxbcRegisterValue value,
DxbcZeroTest test);
DxbcRegisterValue emitSrcOperandModifiers(
DxbcRegisterValue value,
DxbcRegModifiers modifiers);
@ -333,6 +398,10 @@ namespace dxvk {
uint32_t emitNewVariable(
const DxbcRegisterInfo& info);
/////////////////////////////////////
// Control flow block search methods
DxbcCfgBlock* cfgFindLoopBlock();
///////////////////////////
// Type definition methods
uint32_t getScalarTypeId(

View File

@ -122,6 +122,7 @@ namespace dxvk {
// operands. Doing this mostly automatically means that
// the compiler can rely on the operands being valid.
const DxbcInstFormat format = dxbcInstructionFormat(m_instruction.op);
m_instruction.opClass = format.instructionClass;
for (uint32_t i = 0; i < format.operandCount; i++)
this->decodeOperand(code, format.operands[i]);

View File

@ -154,7 +154,7 @@ namespace dxvk {
struct DxbcRegister {
DxbcOperandType type;
DxbcScalarType dataType;
DxbcComponentCount componentCount;
DxbcComponentCount componentCount;
uint32_t idxDim;
DxbcRegIndex idx[DxbcMaxRegIndexDim];
@ -225,6 +225,7 @@ namespace dxvk {
*/
struct DxbcShaderInstruction {
DxbcOpcode op;
DxbcInstClass opClass;
DxbcOpModifiers modifiers;
DxbcShaderOpcodeControls controls;
DxbcShaderSampleControls sampleControls;

View File

@ -12,19 +12,27 @@ namespace dxvk {
/* And */
{ },
/* Break */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* Breakc */
{ },
{ 1, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* Call */
{ },
{ 1, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* Callc */
{ },
{ 2, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* Case */
{ },
/* Continue */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* Continuec */
{ },
{ 1, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* Cut */
{ },
/* Default */
@ -60,15 +68,15 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Else */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* Emit */
{ },
/* EmitThenCut */
{ },
/* EndIf */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* EndLoop */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* EndSwitch */
{ },
/* Eq */
@ -101,7 +109,9 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* If */
{ },
{ 1, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* IEq */
{ 3, DxbcInstClass::VectorCmp, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
@ -175,7 +185,7 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Loop */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* Lt */
{ 3, DxbcInstClass::VectorCmp, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Uint32 },
@ -238,7 +248,9 @@ namespace dxvk {
/* Ret */
{ 0, DxbcInstClass::ControlFlow },
/* Retc */
{ },
{ 1, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* RoundNe */
{ },
/* RoundNi */

View File

@ -29,6 +29,7 @@ namespace dxvk {
*/
enum class DxbcInstClass {
Declaration, ///< Interface or resource declaration
ControlFlow, ///< Control flow instructions
TextureSample, ///< Texture sampling instruction
VectorAlu, ///< Component-wise vector instructions
VectorCmov, ///< Component-wise conditional move
@ -36,7 +37,6 @@ namespace dxvk {
VectorDot, ///< Dot product instruction
VectorImul, ///< Component-wise integer multiplication
VectorSinCos, ///< Sine and Cosine instruction
ControlFlow, ///< Control flow instructions
Undefined, ///< Instruction code not defined
};

View File

@ -470,6 +470,7 @@ namespace dxvk {
Sint64 = 3,
Float32 = 4,
Float64 = 5,
Bool = 6,
};
}

View File

@ -1267,6 +1267,44 @@ namespace dxvk {
}
void SpirvModule::opLoopMerge(
uint32_t mergeBlock,
uint32_t continueTarget,
uint32_t loopControl) {
m_code.putIns (spv::OpLoopMerge, 4);
m_code.putWord(mergeBlock);
m_code.putWord(continueTarget);
m_code.putWord(loopControl);
}
void SpirvModule::opSelectionMerge(
uint32_t mergeBlock,
uint32_t selectionControl) {
m_code.putIns (spv::OpSelectionMerge, 3);
m_code.putWord(mergeBlock);
m_code.putWord(selectionControl);
}
void SpirvModule::opBranch(
uint32_t label) {
m_code.putIns (spv::OpBranch, 2);
m_code.putWord(label);
}
void SpirvModule::opBranchConditional(
uint32_t condition,
uint32_t trueLabel,
uint32_t falseLabel) {
m_code.putIns (spv::OpBranchConditional, 4);
m_code.putWord(condition);
m_code.putWord(trueLabel);
m_code.putWord(falseLabel);
}
void SpirvModule::opReturn() {
m_code.putIns (spv::OpReturn, 1);
}

View File

@ -434,6 +434,23 @@ namespace dxvk {
uint32_t sampledImage,
uint32_t coordinates);
void opLoopMerge(
uint32_t mergeBlock,
uint32_t continueTarget,
uint32_t loopControl);
void opSelectionMerge(
uint32_t mergeBlock,
uint32_t selectionControl);
void opBranch(
uint32_t label);
void opBranchConditional(
uint32_t condition,
uint32_t trueLabel,
uint32_t falseLabel);
void opReturn();
private: