Commit 9b156615 by Ben Clayton

SpirvShader: Split instructions into blocks.

First step towards flow control. Bug: b/128527271 Change-Id: I7e031ccc22148e37dc058150edc93d28de54f4c4 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27096Reviewed-by: 's avatarChris Forbes <chrisforbes@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
parent becb44f1
...@@ -36,12 +36,8 @@ namespace sw ...@@ -36,12 +36,8 @@ namespace sw
// - There is exactly one entrypoint in the module, and it's the one we want // - There is exactly one entrypoint in the module, and it's the one we want
// - The only input/output OpVariables present are those used by the entrypoint // - The only input/output OpVariables present are those used by the entrypoint
// TODO: Add real support for control flow. For now, track whether we've seen Block::ID currentBlock;
// a label or a return already (if so, the shader does things we will mishandle). InsnIterator blockStart;
// We expect there to be one of each in a simple shader -- the first and last instruction
// of the entrypoint function.
bool seenLabel = false;
bool seenReturn = false;
for (auto insn : *this) for (auto insn : *this)
{ {
...@@ -114,16 +110,35 @@ namespace sw ...@@ -114,16 +110,35 @@ namespace sw
} }
case spv::OpLabel: case spv::OpLabel:
if (seenLabel) {
UNIMPLEMENTED("Shader contains multiple labels, has control flow"); ASSERT(currentBlock.value() == 0);
seenLabel = true; currentBlock = Block::ID(insn.word(1));
blockStart = insn;
break; break;
}
// Branch Instructions (subset of Termination Instructions):
case spv::OpBranch:
case spv::OpBranchConditional:
case spv::OpSwitch:
case spv::OpReturn: case spv::OpReturn:
if (seenReturn) // fallthrough
UNIMPLEMENTED("Shader contains multiple returns, has control flow");
seenReturn = true; // Termination instruction:
case spv::OpKill:
case spv::OpUnreachable:
{
ASSERT(currentBlock.value() != 0);
auto blockEnd = insn; blockEnd++;
blocks[currentBlock] = Block(blockStart, blockEnd);
currentBlock = Block::ID(0);
if (insn.opcode() == spv::OpKill)
{
modes.ContainsKill = true;
}
break; break;
}
case spv::OpTypeVoid: case spv::OpTypeVoid:
case spv::OpTypeBool: case spv::OpTypeBool:
...@@ -225,11 +240,31 @@ namespace sw ...@@ -225,11 +240,31 @@ namespace sw
} }
case spv::OpCapability: case spv::OpCapability:
// Various capabilities will be declared, but none affect our code generation at this point. break; // Various capabilities will be declared, but none affect our code generation at this point.
case spv::OpMemoryModel: case spv::OpMemoryModel:
// Memory model does not affect our code generation until we decide to do Vulkan Memory Model support. break; // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
case spv::OpEntryPoint: case spv::OpEntryPoint:
break;
case spv::OpFunction: case spv::OpFunction:
ASSERT(mainBlockId.value() == 0); // Multiple functions found
// Scan forward to find the function's label.
for (auto it = insn; it != end() && mainBlockId.value() == 0; it++)
{
switch (it.opcode())
{
case spv::OpFunction:
case spv::OpFunctionParameter:
break;
case spv::OpLabel:
mainBlockId = Block::ID(it.word(1));
break;
default:
ERR("Unexpected opcode '%s' following OpFunction", OpcodeName(it.opcode()).c_str());
}
}
ASSERT(mainBlockId.value() != 0); // Function's OpLabel not found
break;
case spv::OpFunctionEnd: case spv::OpFunctionEnd:
// Due to preprocessing, the entrypoint and its function provide no value. // Due to preprocessing, the entrypoint and its function provide no value.
break; break;
...@@ -363,10 +398,6 @@ namespace sw ...@@ -363,10 +398,6 @@ namespace sw
// Don't need to do anything during analysis pass // Don't need to do anything during analysis pass
break; break;
case spv::OpKill:
modes.ContainsKill = true;
break;
default: default:
UNIMPLEMENTED(OpcodeName(insn.opcode()).c_str()); UNIMPLEMENTED(OpcodeName(insn.opcode()).c_str());
} }
...@@ -530,7 +561,7 @@ namespace sw ...@@ -530,7 +561,7 @@ namespace sw
} }
} }
uint32_t SpirvShader::ComputeTypeSize(sw::SpirvShader::InsnIterator insn) uint32_t SpirvShader::ComputeTypeSize(InsnIterator insn)
{ {
// Types are always built from the bottom up (with the exception of forward ptrs, which // Types are always built from the bottom up (with the exception of forward ptrs, which
// don't appear in Vulkan shaders. Therefore, we can always assume our component parts have // don't appear in Vulkan shaders. Therefore, we can always assume our component parts have
...@@ -959,182 +990,204 @@ namespace sw ...@@ -959,182 +990,204 @@ namespace sw
void SpirvShader::emit(SpirvRoutine *routine) const void SpirvShader::emit(SpirvRoutine *routine) const
{ {
// Emit everything up to the first label
// TODO: Separate out dispatch of block from non-block instructions?
for (auto insn : *this) for (auto insn : *this)
{ {
switch (insn.opcode()) if (insn.opcode() == spv::OpLabel)
{ {
case spv::OpTypeVoid:
case spv::OpTypeInt:
case spv::OpTypeFloat:
case spv::OpTypeBool:
case spv::OpTypeVector:
case spv::OpTypeArray:
case spv::OpTypeRuntimeArray:
case spv::OpTypeMatrix:
case spv::OpTypeStruct:
case spv::OpTypePointer:
case spv::OpTypeFunction:
case spv::OpExecutionMode:
case spv::OpMemoryModel:
case spv::OpFunction:
case spv::OpFunctionEnd:
case spv::OpConstant:
case spv::OpConstantNull:
case spv::OpConstantTrue:
case spv::OpConstantFalse:
case spv::OpConstantComposite:
case spv::OpExtension:
case spv::OpCapability:
case spv::OpEntryPoint:
case spv::OpExtInstImport:
case spv::OpDecorate:
case spv::OpMemberDecorate:
case spv::OpGroupDecorate:
case spv::OpGroupMemberDecorate:
case spv::OpDecorationGroup:
case spv::OpName:
case spv::OpMemberName:
case spv::OpSource:
case spv::OpSourceContinued:
case spv::OpSourceExtension:
case spv::OpLine:
case spv::OpNoLine:
case spv::OpModuleProcessed:
case spv::OpString:
// Nothing to do at emit time. These are either fully handled at analysis time,
// or don't require any work at all.
break; break;
}
EmitInstruction(routine, insn);
}
case spv::OpLabel: // Emit the main function block
case spv::OpReturn: EmitBlock(routine, getBlock(mainBlockId));
// TODO: when we do control flow, will need to do some work here. }
// Until then, there is nothing to do -- we expect there to be an initial OpLabel
// in the entrypoint function, for which we do nothing; and a final OpReturn at the
// end of the entrypoint function, for which we do nothing.
break;
case spv::OpVariable: void SpirvShader::EmitBlock(SpirvRoutine *routine, Block const &block) const
EmitVariable(insn, routine); {
break; for (auto insn : block)
{
EmitInstruction(routine, insn);
}
}
case spv::OpLoad: void SpirvShader::EmitInstruction(SpirvRoutine *routine, InsnIterator insn) const
EmitLoad(insn, routine); {
break; switch (insn.opcode())
{
case spv::OpTypeVoid:
case spv::OpTypeInt:
case spv::OpTypeFloat:
case spv::OpTypeBool:
case spv::OpTypeVector:
case spv::OpTypeArray:
case spv::OpTypeRuntimeArray:
case spv::OpTypeMatrix:
case spv::OpTypeStruct:
case spv::OpTypePointer:
case spv::OpTypeFunction:
case spv::OpExecutionMode:
case spv::OpMemoryModel:
case spv::OpFunction:
case spv::OpFunctionEnd:
case spv::OpConstant:
case spv::OpConstantNull:
case spv::OpConstantTrue:
case spv::OpConstantFalse:
case spv::OpConstantComposite:
case spv::OpExtension:
case spv::OpCapability:
case spv::OpEntryPoint:
case spv::OpExtInstImport:
case spv::OpDecorate:
case spv::OpMemberDecorate:
case spv::OpGroupDecorate:
case spv::OpGroupMemberDecorate:
case spv::OpDecorationGroup:
case spv::OpName:
case spv::OpMemberName:
case spv::OpSource:
case spv::OpSourceContinued:
case spv::OpSourceExtension:
case spv::OpLine:
case spv::OpNoLine:
case spv::OpModuleProcessed:
case spv::OpString:
// Nothing to do at emit time. These are either fully handled at analysis time,
// or don't require any work at all.
break;
case spv::OpStore: case spv::OpLabel:
EmitStore(insn, routine); case spv::OpReturn:
break; // TODO: when we do control flow, will need to do some work here.
// Until then, there is nothing to do -- we expect there to be an initial OpLabel
// in the entrypoint function, for which we do nothing; and a final OpReturn at the
// end of the entrypoint function, for which we do nothing.
break;
case spv::OpAccessChain: case spv::OpVariable:
EmitAccessChain(insn, routine); EmitVariable(insn, routine);
break; break;
case spv::OpCompositeConstruct: case spv::OpLoad:
EmitCompositeConstruct(insn, routine); EmitLoad(insn, routine);
break; break;
case spv::OpCompositeInsert: case spv::OpStore:
EmitCompositeInsert(insn, routine); EmitStore(insn, routine);
break; break;
case spv::OpCompositeExtract: case spv::OpAccessChain:
EmitCompositeExtract(insn, routine); EmitAccessChain(insn, routine);
break; break;
case spv::OpVectorShuffle: case spv::OpCompositeConstruct:
EmitVectorShuffle(insn, routine); EmitCompositeConstruct(insn, routine);
break; break;
case spv::OpVectorTimesScalar: case spv::OpCompositeInsert:
EmitVectorTimesScalar(insn, routine); EmitCompositeInsert(insn, routine);
break; break;
case spv::OpNot: case spv::OpCompositeExtract:
case spv::OpSNegate: EmitCompositeExtract(insn, routine);
case spv::OpFNegate: break;
case spv::OpLogicalNot:
case spv::OpConvertFToU:
case spv::OpConvertFToS:
case spv::OpConvertSToF:
case spv::OpConvertUToF:
case spv::OpBitcast:
case spv::OpIsInf:
case spv::OpIsNan:
EmitUnaryOp(insn, routine);
break;
case spv::OpIAdd: case spv::OpVectorShuffle:
case spv::OpISub: EmitVectorShuffle(insn, routine);
case spv::OpIMul: break;
case spv::OpSDiv:
case spv::OpUDiv:
case spv::OpFAdd:
case spv::OpFSub:
case spv::OpFMul:
case spv::OpFDiv:
case spv::OpFOrdEqual:
case spv::OpFUnordEqual:
case spv::OpFOrdNotEqual:
case spv::OpFUnordNotEqual:
case spv::OpFOrdLessThan:
case spv::OpFUnordLessThan:
case spv::OpFOrdGreaterThan:
case spv::OpFUnordGreaterThan:
case spv::OpFOrdLessThanEqual:
case spv::OpFUnordLessThanEqual:
case spv::OpFOrdGreaterThanEqual:
case spv::OpFUnordGreaterThanEqual:
case spv::OpSMod:
case spv::OpUMod:
case spv::OpIEqual:
case spv::OpINotEqual:
case spv::OpUGreaterThan:
case spv::OpSGreaterThan:
case spv::OpUGreaterThanEqual:
case spv::OpSGreaterThanEqual:
case spv::OpULessThan:
case spv::OpSLessThan:
case spv::OpULessThanEqual:
case spv::OpSLessThanEqual:
case spv::OpShiftRightLogical:
case spv::OpShiftRightArithmetic:
case spv::OpShiftLeftLogical:
case spv::OpBitwiseOr:
case spv::OpBitwiseXor:
case spv::OpBitwiseAnd:
case spv::OpLogicalOr:
case spv::OpLogicalAnd:
case spv::OpLogicalEqual:
case spv::OpLogicalNotEqual:
case spv::OpUMulExtended:
case spv::OpSMulExtended:
EmitBinaryOp(insn, routine);
break;
case spv::OpDot: case spv::OpVectorTimesScalar:
EmitDot(insn, routine); EmitVectorTimesScalar(insn, routine);
break; break;
case spv::OpSelect: case spv::OpNot:
EmitSelect(insn, routine); case spv::OpSNegate:
break; case spv::OpFNegate:
case spv::OpLogicalNot:
case spv::OpConvertFToU:
case spv::OpConvertFToS:
case spv::OpConvertSToF:
case spv::OpConvertUToF:
case spv::OpBitcast:
case spv::OpIsInf:
case spv::OpIsNan:
EmitUnaryOp(insn, routine);
break;
case spv::OpExtInst: case spv::OpIAdd:
EmitExtendedInstruction(insn, routine); case spv::OpISub:
break; case spv::OpIMul:
case spv::OpSDiv:
case spv::OpUDiv:
case spv::OpFAdd:
case spv::OpFSub:
case spv::OpFMul:
case spv::OpFDiv:
case spv::OpFOrdEqual:
case spv::OpFUnordEqual:
case spv::OpFOrdNotEqual:
case spv::OpFUnordNotEqual:
case spv::OpFOrdLessThan:
case spv::OpFUnordLessThan:
case spv::OpFOrdGreaterThan:
case spv::OpFUnordGreaterThan:
case spv::OpFOrdLessThanEqual:
case spv::OpFUnordLessThanEqual:
case spv::OpFOrdGreaterThanEqual:
case spv::OpFUnordGreaterThanEqual:
case spv::OpSMod:
case spv::OpUMod:
case spv::OpIEqual:
case spv::OpINotEqual:
case spv::OpUGreaterThan:
case spv::OpSGreaterThan:
case spv::OpUGreaterThanEqual:
case spv::OpSGreaterThanEqual:
case spv::OpULessThan:
case spv::OpSLessThan:
case spv::OpULessThanEqual:
case spv::OpSLessThanEqual:
case spv::OpShiftRightLogical:
case spv::OpShiftRightArithmetic:
case spv::OpShiftLeftLogical:
case spv::OpBitwiseOr:
case spv::OpBitwiseXor:
case spv::OpBitwiseAnd:
case spv::OpLogicalOr:
case spv::OpLogicalAnd:
case spv::OpLogicalEqual:
case spv::OpLogicalNotEqual:
case spv::OpUMulExtended:
case spv::OpSMulExtended:
EmitBinaryOp(insn, routine);
break;
case spv::OpAny: case spv::OpDot:
EmitAny(insn, routine); EmitDot(insn, routine);
break; break;
case spv::OpAll: case spv::OpSelect:
EmitAll(insn, routine); EmitSelect(insn, routine);
break; break;
default: case spv::OpExtInst:
UNIMPLEMENTED(OpcodeName(insn.opcode()).c_str()); EmitExtendedInstruction(insn, routine);
break; break;
}
case spv::OpAny:
EmitAny(insn, routine);
break;
case spv::OpAll:
EmitAll(insn, routine);
break;
default:
UNIMPLEMENTED(OpcodeName(insn.opcode()).c_str());
break;
} }
} }
......
...@@ -234,6 +234,26 @@ namespace sw ...@@ -234,6 +234,26 @@ namespace sw
} kind = Kind::Unknown; } kind = Kind::Unknown;
}; };
// Block is an interval of SPIR-V instructions, starting with the
// opening OpLabel, and ending with a termination instruction.
class Block
{
public:
using ID = SpirvID<Block>;
Block() = default;
Block(const Block& other) = default;
explicit Block(InsnIterator begin, InsnIterator end) : begin_(begin), end_(end) {}
/* range-based-for interface */
inline InsnIterator begin() const { return begin_; }
inline InsnIterator end() const { return end_; }
private:
InsnIterator begin_;
InsnIterator end_;
};
struct TypeOrObject {}; // Dummy struct to represent a Type or Object. struct TypeOrObject {}; // Dummy struct to represent a Type or Object.
// TypeOrObjectID is an identifier that represents a Type or an Object, // TypeOrObjectID is an identifier that represents a Type or an Object,
...@@ -382,12 +402,24 @@ namespace sw ...@@ -382,12 +402,24 @@ namespace sw
return it->second; return it->second;
} }
Block const &getBlock(Block::ID id) const
{
auto it = blocks.find(id);
ASSERT(it != blocks.end());
return it->second;
}
private: private:
const int serialID; const int serialID;
static volatile int serialCounter; static volatile int serialCounter;
Modes modes; Modes modes;
HandleMap<Type> types; HandleMap<Type> types;
HandleMap<Object> defs; HandleMap<Object> defs;
HandleMap<Block> blocks;
Block::ID mainBlockId; // Block of the entry point function.
void EmitBlock(SpirvRoutine *routine, Block const &block) const;
void EmitInstruction(SpirvRoutine *routine, InsnIterator insn) const;
// DeclareType creates a Type for the given OpTypeX instruction, storing // DeclareType creates a Type for the given OpTypeX instruction, storing
// it into the types map. It is called from the analysis pass (constructor). // it into the types map. It is called from the analysis pass (constructor).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment