Commit 8e26feb8 by steve-lunarg

WIP: HLSL: structuredbuffer counter functionality

This is WIP, heavy on the IP part. There's not yet enough to use in real workloads. Currently present: * Creation of separate counter buffers for structured buffer types needing them. * IncrementCounter / DecrementCounter methods * Postprocess to remove unused counter buffers from linkage * Associated counter buffers are given @count suffix (invalid as a user identifier) Not yet present: * reflection queries to obtain bindings for counter buffers * Append/Consume buffers * Ability to use SB references passed as fn parameters
parent d1141843
RWStructuredBuffer<uint4> sbuf_rw_i;
RWStructuredBuffer<uint4> sbuf_rw_d;
RWStructuredBuffer<uint4> sbuf_rw_nocounter; // doesn't use inc or dec
float4 main(uint pos : FOO) : SV_Target0
{
uint4 result = 0;
sbuf_rw_i[7];
sbuf_rw_d[7];
sbuf_rw_nocounter[5] = 2;
uint c1 = sbuf_rw_i.IncrementCounter();
uint c2 = sbuf_rw_d.DecrementCounter();
return float4(result.x, result.y, c1, c2);
}
...@@ -223,6 +223,13 @@ enum TBuiltInVariable { ...@@ -223,6 +223,13 @@ enum TBuiltInVariable {
EbvOutputPatch, EbvOutputPatch,
EbvInputPatch, EbvInputPatch,
// structbuffer types
EbvAppendConsume, // no need to differentiate append and consume
EbvRWStructuredBuffer,
EbvStructuredBuffer,
EbvByteAddressBuffer,
EbvRWByteAddressBuffer,
EbvLast EbvLast
}; };
......
...@@ -633,6 +633,10 @@ enum TOperator { ...@@ -633,6 +633,10 @@ enum TOperator {
EOpMethodStore2, // ... EOpMethodStore2, // ...
EOpMethodStore3, // ... EOpMethodStore3, // ...
EOpMethodStore4, // ... EOpMethodStore4, // ...
EOpMethodIncrementCounter, // ...
EOpMethodDecrementCounter, // ...
// EOpMethodAppend is defined for geo shaders below
EOpMethodConsume,
// SM5 texture methods // SM5 texture methods
EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about
......
...@@ -248,6 +248,7 @@ INSTANTIATE_TEST_CASE_P( ...@@ -248,6 +248,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.structbuffer.atomics.frag", "main"}, {"hlsl.structbuffer.atomics.frag", "main"},
{"hlsl.structbuffer.byte.frag", "main"}, {"hlsl.structbuffer.byte.frag", "main"},
{"hlsl.structbuffer.coherent.frag", "main"}, {"hlsl.structbuffer.coherent.frag", "main"},
{"hlsl.structbuffer.incdec.frag", "main"},
{"hlsl.structbuffer.fn.frag", "main"}, {"hlsl.structbuffer.fn.frag", "main"},
{"hlsl.structbuffer.rw.frag", "main"}, {"hlsl.structbuffer.rw.frag", "main"},
{"hlsl.structbuffer.rwbyte.frag", "main"}, {"hlsl.structbuffer.rwbyte.frag", "main"},
......
...@@ -475,9 +475,10 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) ...@@ -475,9 +475,10 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) { if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) {
if (typedefDecl) if (typedefDecl)
parseContext.declareTypedef(idToken.loc, *fullName, variableType); parseContext.declareTypedef(idToken.loc, *fullName, variableType);
else if (variableType.getBasicType() == EbtBlock) else if (variableType.getBasicType() == EbtBlock) {
parseContext.declareBlock(idToken.loc, variableType, fullName); parseContext.declareBlock(idToken.loc, variableType, fullName);
else { parseContext.declareStructBufferCounter(idToken.loc, variableType, *fullName);
} else {
if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) { if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) {
// this isn't really an individual variable, but a member of the $Global buffer // this isn't really an individual variable, but a member of the $Global buffer
parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName); parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName);
...@@ -1955,24 +1956,29 @@ bool HlslGrammar::acceptStructBufferType(TType& type) ...@@ -1955,24 +1956,29 @@ bool HlslGrammar::acceptStructBufferType(TType& type)
bool readonly = false; bool readonly = false;
TStorageQualifier storage = EvqBuffer; TStorageQualifier storage = EvqBuffer;
TBuiltInVariable builtinType = EbvNone;
switch (structBuffType) { switch (structBuffType) {
case EHTokAppendStructuredBuffer: case EHTokAppendStructuredBuffer:
unimplemented("AppendStructuredBuffer"); builtinType = EbvAppendConsume;
return false; break;
case EHTokByteAddressBuffer: case EHTokByteAddressBuffer:
hasTemplateType = false; hasTemplateType = false;
readonly = true; readonly = true;
builtinType = EbvByteAddressBuffer;
break; break;
case EHTokConsumeStructuredBuffer: case EHTokConsumeStructuredBuffer:
unimplemented("ConsumeStructuredBuffer"); builtinType = EbvAppendConsume;
return false; break;
case EHTokRWByteAddressBuffer: case EHTokRWByteAddressBuffer:
hasTemplateType = false; hasTemplateType = false;
builtinType = EbvRWByteAddressBuffer;
break; break;
case EHTokRWStructuredBuffer: case EHTokRWStructuredBuffer:
builtinType = EbvRWStructuredBuffer;
break; break;
case EHTokStructuredBuffer: case EHTokStructuredBuffer:
builtinType = EbvStructuredBuffer;
readonly = true; readonly = true;
break; break;
default: default:
...@@ -2014,8 +2020,6 @@ bool HlslGrammar::acceptStructBufferType(TType& type) ...@@ -2014,8 +2020,6 @@ bool HlslGrammar::acceptStructBufferType(TType& type)
// field name is canonical for all structbuffers // field name is canonical for all structbuffers
templateType->setFieldName("@data"); templateType->setFieldName("@data");
// Create block type. TODO: hidden internal uint member when needed
TTypeList* blockStruct = new TTypeList; TTypeList* blockStruct = new TTypeList;
TTypeLoc member = { templateType, token.loc }; TTypeLoc member = { templateType, token.loc };
blockStruct->push_back(member); blockStruct->push_back(member);
...@@ -2025,6 +2029,7 @@ bool HlslGrammar::acceptStructBufferType(TType& type) ...@@ -2025,6 +2029,7 @@ bool HlslGrammar::acceptStructBufferType(TType& type)
blockType.getQualifier().storage = storage; blockType.getQualifier().storage = storage;
blockType.getQualifier().readonly = readonly; blockType.getQualifier().readonly = readonly;
blockType.getQualifier().builtIn = builtinType;
// We may have created an equivalent type before, in which case we should use its // We may have created an equivalent type before, in which case we should use its
// deep structure. // deep structure.
......
...@@ -842,7 +842,11 @@ bool HlslParseContext::isStructBufferMethod(const TString& name) const ...@@ -842,7 +842,11 @@ bool HlslParseContext::isStructBufferMethod(const TString& name) const
name == "InterlockedMax" || name == "InterlockedMax" ||
name == "InterlockedMin" || name == "InterlockedMin" ||
name == "InterlockedOr" || name == "InterlockedOr" ||
name == "InterlockedXor"; name == "InterlockedXor" ||
name == "IncrementCounter" ||
name == "DecrementCounter" ||
name == "Append" ||
name == "Consume";
} }
// //
...@@ -1514,7 +1518,7 @@ void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction ...@@ -1514,7 +1518,7 @@ void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction
error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
} }
// Add interstage IO variables to the linkage in canonical order. // Finalization step: Add interstage IO variables to the linkage in canonical order.
void HlslParseContext::addInterstageIoToLinkage() void HlslParseContext::addInterstageIoToLinkage()
{ {
TSourceLoc loc; TSourceLoc loc;
...@@ -2438,24 +2442,96 @@ TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc ...@@ -2438,24 +2442,96 @@ TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc
return txcombine; return txcombine;
} }
// Return true if this a buffer type that has an associated counter buffer.
bool HlslParseContext::hasStructBuffCounter(const TString& name) const
{
const auto bivIt = structBufferBuiltIn.find(name);
if (bivIt == structBufferBuiltIn.end())
return false;
switch (bivIt->second) {
case EbvAppendConsume: // fall through...
case EbvRWStructuredBuffer: // ...
return true;
default:
return false; // other builtin types do not have.
}
}
// declare counter for a structured buffer type
void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name)
{
// Bail out if not a struct buffer
if (! isStructBufferType(bufferType))
return;
if (! hasStructBuffCounter(name))
return;
// Counter type
TType* counterType = new TType(EbtInt, EvqBuffer);
counterType->setFieldName("@count");
TTypeList* blockStruct = new TTypeList;
TTypeLoc member = { counterType, loc };
blockStruct->push_back(member);
TString* blockName = new TString(name);
*blockName += "@count";
structBufferCounter[*blockName] = false;
TType blockType(blockStruct, "", counterType->getQualifier());
blockType.getQualifier().storage = EvqBuffer;
shareStructBufferType(blockType);
declareBlock(loc, blockType, blockName);
}
// return the counter that goes with a given structuredbuffer
TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer)
{
// Bail out if not a struct buffer
if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
return nullptr;
TString blockName(buffer->getAsSymbolNode()->getName());
blockName += "@count";
// Mark the counter as being used
structBufferCounter[blockName] = true;
TIntermTyped* counterVar = handleVariable(loc, &blockName); // find the block structure
TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct
TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc);
counterMember->setType(TType(EbtInt));
return counterMember;
}
// //
// Decompose structure buffer methods into AST // Decompose structure buffer methods into AST
// //
void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
{ {
if (!node || !node->getAsOperator()) if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr)
return; return;
const TOperator op = node->getAsOperator()->getOp(); const TOperator op = node->getAsOperator()->getOp();
TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; TIntermAggregate* argAggregate = arguments->getAsAggregate();
if (argAggregate == nullptr)
return;
if (argAggregate->getSequence().empty())
return;
// Buffer is the object upon which method is called, so always arg 0 // Buffer is the object upon which method is called, so always arg 0
TIntermTyped* bufferObj = argAggregate->getSequence()[0]->getAsTyped(); TIntermTyped* bufferObj = nullptr;
// The parameters can be an aggregate, or just a the object as a symbol if there are no fn params.
if (argAggregate) {
if (argAggregate->getSequence().empty())
return;
bufferObj = argAggregate->getSequence()[0]->getAsTyped();
} else {
bufferObj = arguments->getAsSymbolNode();
}
// Index to obtain the runtime sized array out of the buffer. // Index to obtain the runtime sized array out of the buffer.
TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj); TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
...@@ -2670,6 +2746,29 @@ void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TInte ...@@ -2670,6 +2746,29 @@ void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TInte
} }
break; break;
case EOpMethodIncrementCounter:
case EOpMethodDecrementCounter:
{
// These methods require a hidden internal counter, obtained via getStructBufferCounter()
TIntermTyped* incrementValue = intermediate.addConstantUnion(op == EOpMethodIncrementCounter ? 1 : -1, loc, true);
TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member
node = incrementValue;
if (counter == nullptr)
break;
TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd);
counterIncrement->setType(TType(EbtUint, EvqTemporary));
counterIncrement->setLoc(loc);
counterIncrement->getSequence().push_back(counter);
counterIncrement->getSequence().push_back(incrementValue);
node = counterIncrement;
}
break;
default: default:
break; // most pass through unchanged break; // most pass through unchanged
} }
...@@ -3978,10 +4077,18 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct ...@@ -3978,10 +4077,18 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct
// TODO: this needs improvement: there's no way at present to look up a signature in // TODO: this needs improvement: there's no way at present to look up a signature in
// the symbol table for an arbitrary type. This is a temporary hack until that ability exists. // the symbol table for an arbitrary type. This is a temporary hack until that ability exists.
// It will have false positives, since it doesn't check arg counts or types. // It will have false positives, since it doesn't check arg counts or types.
if (arguments && arguments->getAsAggregate()) { if (arguments) {
const TIntermSequence& sequence = arguments->getAsAggregate()->getSequence(); // Check if first argument is struct buffer type. It may be an aggregate or a symbol, so we
// look for either case.
TIntermTyped* arg0 = nullptr;
if (!sequence.empty() && isStructBufferType(sequence[0]->getAsTyped()->getType())) { if (arguments->getAsAggregate() && arguments->getAsAggregate()->getSequence().size() > 0)
arg0 = arguments->getAsAggregate()->getSequence()[0]->getAsTyped();
else if (arguments->getAsSymbolNode())
arg0 = arguments->getAsSymbolNode();
if (arg0 != nullptr && isStructBufferType(arg0->getType())) {
static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1; static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
if (function->getName().length() > methodPrefixSize && if (function->getName().length() > methodPrefixSize &&
...@@ -5845,8 +5952,9 @@ const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction ...@@ -5845,8 +5952,9 @@ const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction
// These builtin ops can accept any type, so we bypass the argument selection // These builtin ops can accept any type, so we bypass the argument selection
if (candidateList.size() == 1 && builtIn && if (candidateList.size() == 1 && builtIn &&
(candidateList[0]->getBuiltInOp() == EOpMethodAppend || (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip)) { candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip ||
candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter ||
candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter)) {
return candidateList[0]; return candidateList[0];
} }
...@@ -6856,6 +6964,10 @@ void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TS ...@@ -6856,6 +6964,10 @@ void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TS
switch (type.getQualifier().storage) { switch (type.getQualifier().storage) {
case EvqUniform: case EvqUniform:
case EvqBuffer: case EvqBuffer:
// remember pre-sanitized builtin type
if (type.getQualifier().storage == EvqBuffer && instanceName != nullptr)
structBufferBuiltIn[*instanceName] = type.getQualifier().builtIn;
correctUniform(type.getQualifier()); correctUniform(type.getQualifier());
break; break;
case EvqVaryingIn: case EvqVaryingIn:
...@@ -7670,7 +7782,7 @@ TIntermSymbol* HlslParseContext::findLinkageSymbol(TBuiltInVariable biType) cons ...@@ -7670,7 +7782,7 @@ TIntermSymbol* HlslParseContext::findLinkageSymbol(TBuiltInVariable biType) cons
return intermediate.addSymbol(*it->second->getAsVariable()); return intermediate.addSymbol(*it->second->getAsVariable());
} }
// Add patch constant function invocation // Finalization step: Add patch constant function invocation
void HlslParseContext::addPatchConstantInvocation() void HlslParseContext::addPatchConstantInvocation()
{ {
TSourceLoc loc; TSourceLoc loc;
...@@ -8039,9 +8151,23 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -8039,9 +8151,23 @@ void HlslParseContext::addPatchConstantInvocation()
epBodySeq.insert(epBodySeq.end(), invocationIdTest); epBodySeq.insert(epBodySeq.end(), invocationIdTest);
} }
// Finalization step: remove unused buffer blocks from linkage (we don't know until the
// shader is entirely compiled)
void HlslParseContext::removeUnusedStructBufferCounters()
{
const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(),
[this](const TSymbol* sym) {
const auto sbcIt = structBufferCounter.find(sym->getName());
return sbcIt != structBufferCounter.end() && !sbcIt->second;
});
linkageSymbols.erase(endIt, linkageSymbols.end());
}
// post-processing // post-processing
void HlslParseContext::finish() void HlslParseContext::finish()
{ {
removeUnusedStructBufferCounters();
addPatchConstantInvocation(); addPatchConstantInvocation();
addInterstageIoToLinkage(); addInterstageIoToLinkage();
......
...@@ -146,6 +146,7 @@ public: ...@@ -146,6 +146,7 @@ public:
TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0); void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
void declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name);
void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
void fixBlockXfbOffsets(TQualifier&, TTypeList&); void fixBlockXfbOffsets(TQualifier&, TTypeList&);
void fixBlockUniformOffsets(const TQualifier&, TTypeList&); void fixBlockUniformOffsets(const TQualifier&, TTypeList&);
...@@ -274,11 +275,19 @@ protected: ...@@ -274,11 +275,19 @@ protected:
TType* getStructBufferContentType(const TType& type) const; TType* getStructBufferContentType(const TType& type) const;
bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; } bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; }
TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const; TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const;
TIntermTyped* getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer);
// Return true if this type is a reference. This is not currently a type method in case that's // Return true if this type is a reference. This is not currently a type method in case that's
// a language specific answer. // a language specific answer.
bool isReference(const TType& type) const { return isStructBufferType(type); } bool isReference(const TType& type) const { return isStructBufferType(type); }
// Return true if this a buffer type that has an associated counter buffer.
bool hasStructBuffCounter(const TString& name) const;
// Finalization step: remove unused buffer blocks from linkage (we don't know until the
// shader is entirely compiled)
void removeUnusedStructBufferCounters();
// Pass through to base class after remembering builtin mappings. // Pass through to base class after remembering builtin mappings.
using TParseContextBase::trackLinkage; using TParseContextBase::trackLinkage;
void trackLinkage(TSymbol& variable) override; void trackLinkage(TSymbol& variable) override;
...@@ -366,6 +375,9 @@ protected: ...@@ -366,6 +375,9 @@ protected:
// Structuredbuffer shared types. Typically there are only a few. // Structuredbuffer shared types. Typically there are only a few.
TVector<TType*> structBufferTypes; TVector<TType*> structBufferTypes;
TMap<TString, TBuiltInVariable> structBufferBuiltIn;
TMap<TString, bool> structBufferCounter;
// The builtin interstage IO map considers e.g, EvqPosition on input and output separately, so that we // The builtin interstage IO map considers e.g, EvqPosition on input and output separately, so that we
// can build the linkage correctly if position appears on both sides. Otherwise, multiple positions // can build the linkage correctly if position appears on both sides. Otherwise, multiple positions
......
...@@ -871,6 +871,8 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c ...@@ -871,6 +871,8 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
{ "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true }, { "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true }, { "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true }, { "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "IncrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "DecrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
// Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet. // Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet.
{ nullptr, nullptr, nullptr, nullptr, nullptr, 0, false }, { nullptr, nullptr, nullptr, nullptr, nullptr, 0, false },
...@@ -1180,6 +1182,10 @@ void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profil ...@@ -1180,6 +1182,10 @@ void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profil
symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2); symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2);
symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3); symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3);
symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4); symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4);
symbolTable.relateToOperator(BUILTIN_PREFIX "IncrementCounter", EOpMethodIncrementCounter);
symbolTable.relateToOperator(BUILTIN_PREFIX "DecrementCounter", EOpMethodDecrementCounter);
symbolTable.relateToOperator(BUILTIN_PREFIX "Append", EOpMethodAppend);
symbolTable.relateToOperator(BUILTIN_PREFIX "Consume", EOpMethodConsume);
symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd); symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd);
symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd); symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd);
......
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