ES31: Add atomic memory functions
BUG=angleproject:1442
TEST=angle_unittests, angle_end2end_tests
dEQP-GLES31.functional.compute.shared_var.atomic*
dEQP-GLES31.functional.compute.basic.shared_atomic_op*
dEQP-GLES31.functional.ssbo.atomic.*
Change-Id: I82b54fde3a852d3bd917b1e19680baa1c28fce4d
Reviewed-on: https://chromium-review.googlesource.com/765061
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index aeb8d58..7283bdb 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -635,6 +635,26 @@
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterIncrement", atomicCounter);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterDecrement", atomicCounter);
+ // Insert all atomic memory functions
+ const TType *int1InOut = TCache::getType(EbtInt, EvqInOut);
+ const TType *uint1InOut = TCache::getType(EbtUInt, EvqInOut);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicAdd", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicAdd", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicMin", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicMin", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicMax", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicMax", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicAnd", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicAnd", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicOr", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicOr", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicXor", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicXor", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicExchange", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicExchange", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCompSwap", uint1InOut, uint1, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicCompSwap", int1InOut, int1, int1);
+
const TType *gimage2D = TCache::getType(EbtGImage2D);
const TType *gimage3D = TCache::getType(EbtGImage3D);
const TType *gimage2DArray = TCache::getType(EbtGImage2DArray);
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index ab0ece3..523228a 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -32,6 +32,22 @@
const int kWebGLMaxStructNesting = 4;
+const std::array<const char *, 8> kAtomicBuiltin = {{"atomicAdd", "atomicMin", "atomicMax",
+ "atomicAnd", "atomicOr", "atomicXor",
+ "atomicExchange", "atomicCompSwap"}};
+
+bool IsAtomicBuiltin(const TString &name)
+{
+ for (size_t i = 0; i < kAtomicBuiltin.size(); ++i)
+ {
+ if (name.compare(kAtomicBuiltin[i]) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
bool ContainsSampler(const TStructure *structType);
bool ContainsSampler(const TType &type)
@@ -116,6 +132,16 @@
}
}
+bool IsBufferOrSharedVariable(TIntermTyped *var)
+{
+ if (var->isInterfaceBlock() || var->getQualifier() == EvqBuffer ||
+ var->getQualifier() == EvqShared)
+ {
+ return true;
+ }
+ return false;
+}
+
} // namespace
// This tracks each binding point's current default offset for inheritance of subsequent
@@ -5592,6 +5618,35 @@
}
}
+void TParseContext::checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall)
+{
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
+ if (IsAtomicBuiltin(name))
+ {
+ TIntermSequence *arguments = functionCall->getSequence();
+ TIntermTyped *memNode = (*arguments)[0]->getAsTyped();
+
+ if (IsBufferOrSharedVariable(memNode))
+ {
+ return;
+ }
+
+ while (memNode->getAsBinaryNode())
+ {
+ memNode = memNode->getAsBinaryNode()->getLeft();
+ if (IsBufferOrSharedVariable(memNode))
+ {
+ return;
+ }
+ }
+
+ error(memNode->getLine(),
+ "The value passed to the mem argument of an atomic memory function does not "
+ "correspond to a buffer or shared variable.",
+ functionCall->getFunctionSymbolInfo()->getName().c_str());
+ }
+}
+
// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall)
{
@@ -5828,6 +5883,7 @@
checkTextureOffsetConst(callNode);
checkTextureGather(callNode);
checkImageMemoryAccessForBuiltinFunctions(callNode);
+ checkAtomicMemoryBuiltinFunctions(callNode);
}
else
{
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index c611b31..e917db7 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -417,6 +417,7 @@
void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
const TIntermAggregate *functionCall);
+ void checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall);
TIntermSequence *createEmptyArgumentsList();
// fnCall is only storing the built-in op, and function name or constructor type. arguments