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/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
                 {