diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index eef4e533..42a3b820 100644 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -99,6 +99,7 @@ protected: spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat); spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destTypeId, spv::Id operand); spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); + spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands); spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands); spv::Id createNoArgOperation(glslang::TOperator op); spv::Id getSymbolId(const glslang::TIntermSymbol* node); @@ -718,6 +719,16 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI builder.createNoResultOp(spv::OpEndStreamPrimitive, operand); return false; + case glslang::EOpAtomicCounterIncrement: + case glslang::EOpAtomicCounterDecrement: + case glslang::EOpAtomicCounter: + { + // Handle all of the atomics in one place, in createAtomicOperation() + std::vector operands; + operands.push_back(operand); + result = createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands); + return false; + } default: spv::MissingFunctionality("glslang unary"); break; @@ -733,6 +744,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt bool reduceComparison = true; bool isMatrix = false; bool noReturnValue = false; + bool atomic = false; assert(node->getOp()); @@ -952,6 +964,17 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt // These all have 0 operands and will naturally finish up in the code below for 0 operands break; + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + atomic = true; + break; + default: break; } @@ -959,7 +982,6 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt // // See if it maps to a regular operation. // - if (binOp != glslang::EOpNull) { glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped(); glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped(); @@ -987,6 +1009,9 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt return false; } + // + // Create the list of operands. + // glslang::TIntermSequence& glslangOperands = node->getSequence(); std::vector operands; for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { @@ -1012,16 +1037,23 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt else operands.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangOperands[arg]->getAsTyped()->getType()))); } - switch (glslangOperands.size()) { - case 0: - result = createNoArgOperation(node->getOp()); - break; - case 1: - result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble); - break; - default: - result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands); - break; + + if (atomic) { + // Handle all atomics + result = createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands); + } else { + // Pass through to generic operations. + switch (glslangOperands.size()) { + case 0: + result = createNoArgOperation(node->getOp()); + break; + case 1: + result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble); + break; + default: + result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands); + break; + } } if (noReturnValue) @@ -1272,6 +1304,10 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty case glslang::EbtUint: spvType = builder.makeUintType(32); break; + case glslang::EbtAtomicUint: + spv::TbdFunctionality("Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?"); + spvType = builder.makeUintType(32); + break; case glslang::EbtSampler: { const glslang::TSampler& sampler = type.getSampler(); @@ -2245,6 +2281,67 @@ spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vector return builder.makeCompositeConstant(vectorTypeId, components); } +// For glslang ops that map to SPV atomic opCodes +spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands) +{ + spv::Op opCode = spv::OpNop; + + switch (op) { + case glslang::EOpAtomicAdd: + opCode = spv::OpAtomicIAdd; + break; + case glslang::EOpAtomicMin: + opCode = spv::OpAtomicIMin; + break; + case glslang::EOpAtomicMax: + opCode = spv::OpAtomicIMax; + break; + case glslang::EOpAtomicAnd: + opCode = spv::OpAtomicAnd; + break; + case glslang::EOpAtomicOr: + opCode = spv::OpAtomicOr; + break; + case glslang::EOpAtomicXor: + opCode = spv::OpAtomicXor; + break; + case glslang::EOpAtomicExchange: + opCode = spv::OpAtomicExchange; + break; + case glslang::EOpAtomicCompSwap: + opCode = spv::OpAtomicCompareExchange; + break; + case glslang::EOpAtomicCounterIncrement: + opCode = spv::OpAtomicIIncrement; + break; + case glslang::EOpAtomicCounterDecrement: + opCode = spv::OpAtomicIDecrement; + break; + case glslang::EOpAtomicCounter: + opCode = spv::OpAtomicLoad; + break; + default: + spv::MissingFunctionality("missing nested atomic"); + break; + } + + // Sort out the operands + // - mapping from glslang -> SPV + // - there are extra SPV operands with no glslang source + std::vector spvAtomicOperands; // hold the spv operands + auto opIt = operands.begin(); // walk the glslang operands + spvAtomicOperands.push_back(*(opIt++)); + spvAtomicOperands.push_back(spv::ExecutionScopeDevice); // TBD: what is the correct scope? + spvAtomicOperands.push_back( spv::MemorySemanticsMaskNone); // TBD: what are the correct memory semantics? + + // Add the rest of the operands, skipping the first one, which was dealt with above. + // For some ops, there are none, for some 1, for compare-exchange, 2. + for (; opIt != operands.end(); ++opIt) + spvAtomicOperands.push_back(*opIt); + + return builder.createOp(opCode, typeId, spvAtomicOperands); +} + spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands) { spv::Op opCode = spv::OpNop; @@ -2298,6 +2395,7 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: case glslang::EOpRefract: libCall = GLSL_STD_450::Refract; break; + default: return 0; } @@ -2319,7 +2417,7 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: id = builder.createBinOp(opCode, typeId, operands[0], operands[1]); break; case 3: - id = builder.createTernaryOp(opCode, typeId, operands[0], operands[1], operands[2]); + id = builder.createTriOp(opCode, typeId, operands[0], operands[1], operands[2]); break; default: // These do not exist yet diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index 91cb2d07..d62d2bcf 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -45,6 +45,8 @@ #include #include +#include + #include "SpvBuilder.h" #ifndef _WIN32 @@ -989,12 +991,11 @@ Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) return op->getResultId(); } -Id Builder::createTernaryOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) +Id Builder::createOp(Op opCode, Id typeId, std::vector& operands) { Instruction* op = new Instruction(getUniqueId(), typeId, opCode); - op->addIdOperand(op1); - op->addIdOperand(op2); - op->addIdOperand(op3); + for (auto operand : operands) + op->addIdOperand(operand); buildPoint->addInstruction(op); return op->getResultId(); @@ -2172,17 +2173,22 @@ void Builder::dumpInstructions(std::vector& out, const std::vector } } +void TbdFunctionality(const char* tbd) +{ + static std::unordered_set issued; + + if (issued.find(tbd) == issued.end()) { + printf("TBD functionality: %s\n", tbd); + issued.insert(tbd); + } +} + void MissingFunctionality(const char* fun) { printf("Missing functionality: %s\n", fun); exit(1); } -void ValidationError(const char* error) -{ - printf("Validation Error: %s\n", error); -} - Builder::Loop::Loop(Builder& builder, bool testFirstArg) : function(&builder.getBuildPoint()->getParent()), header(new Block(builder.getUniqueId(), *function)), diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index c2209602..e8711e34 100644 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -238,7 +238,7 @@ public: Id createUnaryOp(Op, Id typeId, Id operand); Id createBinOp(Op, Id typeId, Id operand1, Id operand2); Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); - Id createTernaryOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); + Id createOp(Op, Id typeId, std::vector& operands); Id createFunctionCall(spv::Function*, std::vector&); // Take an rvalue (source) and a set of channels to extract from it to @@ -564,8 +564,11 @@ protected: std::stack loops; }; // end Builder class +// Use for non-fatal notes about what's not complete +void TbdFunctionality(const char*); + +// Use for fatal missing functionality void MissingFunctionality(const char*); -void ValidationError(const char* error); }; // end spv namespace diff --git a/Test/baseResults/spv.atomic.comp.out b/Test/baseResults/spv.atomic.comp.out new file mode 100644 index 00000000..12da7dba --- /dev/null +++ b/Test/baseResults/spv.atomic.comp.out @@ -0,0 +1,142 @@ +spv.atomic.comp +Warning, version 310 is not yet complete; most version-specific features are present, but some are missing. + + +Linked compute stage: + + +TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class? +TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class? +TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class? +TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class? +TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class? +// Module Version 99 +// Generated by (magic number): 51a00bb +// Id's are bound by 75 + + Source ESSL 310 + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint GLCompute 4 + Name 4 "main" + Name 11 "func(au1;" + Name 10 "c" + Name 13 "atoms(" + Name 20 "counter" + Name 21 "param" + Name 24 "val" + Name 28 "countArr" + Name 38 "origi" + Name 40 "atomi" + Name 44 "origu" + Name 46 "atomu" + Name 48 "value" + Name 72 "arrX" + Name 73 "arrY" + Name 74 "arrZ" + Decorate 20(counter) PrecisionHigh + Decorate 20(counter) Binding 0 + Decorate 24(val) PrecisionHigh + Decorate 28(countArr) PrecisionHigh + Decorate 28(countArr) Binding 0 + Decorate 38(origi) PrecisionHigh + Decorate 40(atomi) PrecisionHigh + Decorate 44(origu) PrecisionHigh + Decorate 46(atomu) PrecisionHigh + Decorate 48(value) PrecisionHigh + Decorate 72(arrX) PrecisionHigh + Decorate 72(arrX) NoStaticUse + Decorate 73(arrY) PrecisionHigh + Decorate 73(arrY) NoStaticUse + Decorate 74(arrZ) PrecisionHigh + Decorate 74(arrZ) NoStaticUse + 2: TypeVoid + 3: TypeFunction 2 + 7: TypeInt 32 0 + 8: TypePointer Function 7(int) + 9: TypeFunction 7(int) 8(ptr) + 19: TypePointer UniformConstant 7(int) + 20(counter): 19(ptr) Variable UniformConstant + 25: 7(int) Constant 4 + 26: TypeArray 7(int) 25 + 27: TypePointer UniformConstant 26 + 28(countArr): 27(ptr) Variable UniformConstant + 29: TypeInt 32 1 + 30: 29(int) Constant 2 + 37: TypePointer Function 29(int) + 39: TypePointer WorkgroupLocal 29(int) + 40(atomi): 39(ptr) Variable WorkgroupLocal + 42: 29(int) Constant 3 + 45: TypePointer WorkgroupLocal 7(int) + 46(atomu): 45(ptr) Variable WorkgroupLocal + 48(value): 19(ptr) Variable UniformConstant + 52: 7(int) Constant 7 + 60: 29(int) Constant 7 + 66: 7(int) Constant 10 + 69: 7(int) Constant 1 + 70: TypeArray 29(int) 69 + 71: TypePointer PrivateGlobal 70 + 72(arrX): 71(ptr) Variable PrivateGlobal + 73(arrY): 71(ptr) Variable PrivateGlobal + 74(arrZ): 71(ptr) Variable PrivateGlobal + 4(main): 2 Function None 3 + 5: Label + 21(param): 8(ptr) Variable Function + 24(val): 8(ptr) Variable Function + MemoryBarrier Device AtomicCounterMemory + 22: 7(int) Load 20(counter) + Store 21(param) 22 + 23: 7(int) FunctionCall 11(func(au1;) 21(param) + 31: 19(ptr) AccessChain 28(countArr) 30 + 32: 7(int) Load 31 + 33: 7(int) AtomicLoad 32 Device None + 34: 7(int) Load 31 + Store 24(val) 34 + 35: 7(int) Load 20(counter) + 36: 7(int) AtomicIDecrement 35 Device None + Branch 6 + 6: Label + Return + FunctionEnd + 11(func(au1;): 7(int) Function None 9 + 10(c): 8(ptr) FunctionParameter + 12: Label + 15: 7(int) Load 10(c) + 16: 7(int) AtomicIIncrement 15 Device None + 17: 7(int) Load 10(c) + ReturnValue 17 + FunctionEnd + 13(atoms(): 2 Function None 3 + 14: Label + 38(origi): 37(ptr) Variable Function + 44(origu): 8(ptr) Variable Function + 41: 29(int) Load 40(atomi) + 43: 29(int) AtomicIAdd 41 Device None 42 + Store 38(origi) 43 + 47: 7(int) Load 46(atomu) + 49: 7(int) Load 48(value) + 50: 7(int) AtomicAnd 47 Device None 49 + Store 44(origu) 50 + 51: 7(int) Load 46(atomu) + 53: 7(int) AtomicOr 51 Device None 52 + Store 44(origu) 53 + 54: 7(int) Load 46(atomu) + 55: 7(int) AtomicXor 54 Device None 52 + Store 44(origu) 55 + 56: 7(int) Load 46(atomu) + 57: 7(int) Load 48(value) + 58: 7(int) AtomicIMin 56 Device None 57 + Store 44(origu) 58 + 59: 29(int) Load 40(atomi) + 61: 29(int) AtomicIMax 59 Device None 60 + Store 38(origi) 61 + 62: 29(int) Load 40(atomi) + 63: 29(int) Load 38(origi) + 64: 29(int) AtomicExchange 62 Device None 63 + Store 38(origi) 64 + 65: 7(int) Load 46(atomu) + 67: 7(int) Load 48(value) + 68: 7(int) AtomicCompareExchange 65 Device None 66 67 + Store 44(origu) 68 + Return + FunctionEnd diff --git a/Test/spv.atomic.comp b/Test/spv.atomic.comp new file mode 100644 index 00000000..1a91b3e7 --- /dev/null +++ b/Test/spv.atomic.comp @@ -0,0 +1,38 @@ +#version 310 es + +layout(binding = 0) uniform atomic_uint counter; + +layout(binding = 0, offset = 4) uniform atomic_uint countArr[4]; +uniform uint value; + +int arrX[gl_WorkGroupSize.x]; +int arrY[gl_WorkGroupSize.y]; +int arrZ[gl_WorkGroupSize.z]; + +uint func(atomic_uint c) +{ + return atomicCounterIncrement(c); +} + +void main() +{ + memoryBarrierAtomicCounter(); + func(counter); + uint val = atomicCounter(countArr[2]); + atomicCounterDecrement(counter); +} + +shared int atomi; +shared uint atomu; + +void atoms() +{ + int origi = atomicAdd(atomi, 3); + uint origu = atomicAnd(atomu, value); + origu = atomicOr(atomu, 7u); + origu = atomicXor(atomu, 7u); + origu = atomicMin(atomu, value); + origi = atomicMax(atomi, 7); + origi = atomicExchange(atomi, origi); + origu = atomicCompSwap(atomu, 10u, value); +} diff --git a/Test/test-spirv-list b/Test/test-spirv-list index fed56883..ccfa0686 100644 --- a/Test/test-spirv-list +++ b/Test/test-spirv-list @@ -78,3 +78,4 @@ spv.varyingArray.frag spv.varyingArrayIndirect.frag spv.voidFunction.frag spv.whileLoop.frag +spv.atomic.comp