AST -> SPV: Add basic atomic_uint and atomic*() built-in function functionality.

This commit is contained in:
John Kessenich 2015-07-23 10:22:48 -06:00
parent 917ec4ac8c
commit 426394d0c8
6 changed files with 311 additions and 23 deletions

View File

@ -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<spv::Id>& operands);
spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& 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<spv::Id> 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<spv::Id> 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<spv::Id>& 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<spv::Id> 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<spv::Id>& 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

View File

@ -45,6 +45,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <unordered_set>
#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<Id>& 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<unsigned int>& out, const std::vector
}
}
void TbdFunctionality(const char* tbd)
{
static std::unordered_set<const char*> 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)),

View File

@ -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<Id>& operands);
Id createFunctionCall(spv::Function*, std::vector<spv::Id>&);
// Take an rvalue (source) and a set of channels to extract from it to
@ -564,8 +564,11 @@ protected:
std::stack<Loop> 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

View File

@ -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

38
Test/spv.atomic.comp Normal file
View File

@ -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);
}

View File

@ -78,3 +78,4 @@ spv.varyingArray.frag
spv.varyingArrayIndirect.frag
spv.voidFunction.frag
spv.whileLoop.frag
spv.atomic.comp