Store symbol names as a ImmutableString

This will enable compile-time initialization of built-in symbols as
well as reducing copying strings.

Most of the code that deals with names is changed to use
ImmutableString where it makes sense to avoid conversions.

The lexer/parser now allocate const char pointers into pool memory
instead of allocating TStrings. These are then converted to
ImmutableString upon entering TParseContext.

BUG=angleproject:2267
TEST=angle_unittests, angle_end2end_tests

Change-Id: I244d6271ea1ecf7150d4f89dfa388a7745a1150c
Reviewed-on: https://chromium-review.googlesource.com/881561
Commit-Queue: 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 dc7ba1c..d912576 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -33,15 +33,26 @@
 
 const int kWebGLMaxStructNesting = 4;
 
-const std::array<const char *, 8> kAtomicBuiltin = {{"atomicAdd", "atomicMin", "atomicMax",
-                                                     "atomicAnd", "atomicOr", "atomicXor",
-                                                     "atomicExchange", "atomicCompSwap"}};
+constexpr const ImmutableString kTexelFetchOffsetName("texelFetchOffsetName");
+constexpr const ImmutableString kTextureLodOffsetName("textureLodOffset");
+constexpr const ImmutableString kTextureProjLodOffsetName("textureProjLodOffset");
+constexpr const ImmutableString kTextureGradOffsetName("textureGradOffset");
+constexpr const ImmutableString kTextureProjGradOffsetName("textureProjGradOffset");
+constexpr const ImmutableString kTextureOffsetName("textureOffset");
+constexpr const ImmutableString kTextureProjOffsetName("textureProjOffset");
+constexpr const ImmutableString kTextureGatherName("textureGather");
+constexpr const ImmutableString kTextureGatherOffsetName("textureGatherOffset");
 
-bool IsAtomicBuiltin(const TString &name)
+constexpr const std::array<ImmutableString, 8> kAtomicBuiltin = {
+    {ImmutableString("atomicAdd"), ImmutableString("atomicMin"), ImmutableString("atomicMax"),
+     ImmutableString("atomicAnd"), ImmutableString("atomicOr"), ImmutableString("atomicXor"),
+     ImmutableString("atomicExchange"), ImmutableString("atomicCompSwap")}};
+
+bool IsAtomicBuiltin(const ImmutableString &name)
 {
     for (size_t i = 0; i < kAtomicBuiltin.size(); ++i)
     {
-        if (name.compare(kAtomicBuiltin[i]) == 0)
+        if (name == kAtomicBuiltin[i])
         {
             return true;
         }
@@ -88,7 +99,7 @@
     TIntermSymbol *imageSymbol = imageNode->getAsSymbolNode();
     if (imageSymbol)
     {
-        return imageSymbol->getName().c_str();
+        return imageSymbol->getName().data();
     }
     return "image";
 }
@@ -244,15 +255,15 @@
 }
 
 bool TParseContext::parseVectorFields(const TSourceLoc &line,
-                                      const TString &compString,
+                                      const ImmutableString &compString,
                                       int vecSize,
                                       TVector<int> *fieldOffsets)
 {
     ASSERT(fieldOffsets);
-    size_t fieldCount = compString.size();
+    size_t fieldCount = compString.length();
     if (fieldCount > 4u)
     {
-        error(line, "illegal vector field selection", compString.c_str());
+        error(line, "illegal vector field selection", compString);
         return false;
     }
     fieldOffsets->resize(fieldCount);
@@ -318,7 +329,7 @@
                 fieldSet[i]       = estpq;
                 break;
             default:
-                error(line, "illegal vector field selection", compString.c_str());
+                error(line, "illegal vector field selection", compString);
                 return false;
         }
     }
@@ -327,7 +338,7 @@
     {
         if ((*fieldOffsets)[i] >= vecSize)
         {
-            error(line, "vector field selection out of range", compString.c_str());
+            error(line, "vector field selection out of range", compString);
             return false;
         }
 
@@ -335,8 +346,7 @@
         {
             if (fieldSet[i] != fieldSet[i - 1])
             {
-                error(line, "illegal - vector component fields not from the same set",
-                      compString.c_str());
+                error(line, "illegal - vector component fields not from the same set", compString);
                 return false;
             }
         }
@@ -359,6 +369,11 @@
     mDiagnostics->error(loc, reason, token);
 }
 
+void TParseContext::error(const TSourceLoc &loc, const char *reason, const ImmutableString &token)
+{
+    mDiagnostics->error(loc, reason, token.data());
+}
+
 void TParseContext::warning(const TSourceLoc &loc, const char *reason, const char *token)
 {
     mDiagnostics->warning(loc, reason, token);
@@ -609,7 +624,7 @@
         // Symbol inside an expression can't be nameless.
         ASSERT(symNode->variable().symbolType() != SymbolType::Empty);
 
-        const char *symbol = symNode->getName().c_str();
+        const ImmutableString &symbol = symNode->getName();
         std::stringstream reasonStream;
         reasonStream << "l-value required (" << message << " \"" << symbol << "\")";
         std::string reason = reasonStream.str();
@@ -661,33 +676,33 @@
 // ESSL 3.00.5 sections 3.8 and 3.9.
 // If it starts "gl_" or contains two consecutive underscores, it's reserved.
 // Also checks for "webgl_" and "_webgl_" reserved identifiers if parsing a webgl shader.
-bool TParseContext::checkIsNotReserved(const TSourceLoc &line, const TString &identifier)
+bool TParseContext::checkIsNotReserved(const TSourceLoc &line, const ImmutableString &identifier)
 {
     static const char *reservedErrMsg = "reserved built-in name";
-    if (identifier.compare(0, 3, "gl_") == 0)
+    if (identifier.beginsWith("gl_"))
     {
         error(line, reservedErrMsg, "gl_");
         return false;
     }
     if (sh::IsWebGLBasedSpec(mShaderSpec))
     {
-        if (identifier.compare(0, 6, "webgl_") == 0)
+        if (identifier.beginsWith("webgl_"))
         {
             error(line, reservedErrMsg, "webgl_");
             return false;
         }
-        if (identifier.compare(0, 7, "_webgl_") == 0)
+        if (identifier.beginsWith("_webgl_"))
         {
             error(line, reservedErrMsg, "_webgl_");
             return false;
         }
     }
-    if (identifier.find("__") != TString::npos)
+    if (identifier.contains("__"))
     {
         error(line,
               "identifiers containing two consecutive underscores (__) are reserved as "
               "possible future keywords",
-              identifier.c_str());
+              identifier);
         return false;
     }
     return true;
@@ -852,12 +867,12 @@
 // returns true in case of an error
 //
 bool TParseContext::checkIsNonVoid(const TSourceLoc &line,
-                                   const TString &identifier,
+                                   const ImmutableString &identifier,
                                    const TBasicType &type)
 {
     if (type == EbtVoid)
     {
-        error(line, "illegal use of type 'void'", identifier.c_str());
+        error(line, "illegal use of type 'void'", identifier);
         return false;
     }
 
@@ -1074,7 +1089,7 @@
 
 // Enforce non-initializer type/qualifier rules.
 void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
-                                                         const TString &identifier,
+                                                         const ImmutableString &identifier,
                                                          TType *type)
 {
     ASSERT(type != nullptr);
@@ -1090,16 +1105,16 @@
             error(line,
                   "structures containing arrays may not be declared constant since they cannot be "
                   "initialized",
-                  identifier.c_str());
+                  identifier);
         }
         else
         {
-            error(line, "variables with qualifier 'const' must be initialized", identifier.c_str());
+            error(line, "variables with qualifier 'const' must be initialized", identifier);
         }
     }
     // This will make the type sized if it isn't sized yet.
-    checkIsNotUnsizedArray(line, "implicitly sized arrays need to be initialized",
-                           identifier.c_str(), type);
+    checkIsNotUnsizedArray(line, "implicitly sized arrays need to be initialized", identifier,
+                           type);
 }
 
 // Do some simple checks that are shared between all variable declarations,
@@ -1108,27 +1123,26 @@
 // Returns true if declaring the variable succeeded.
 //
 bool TParseContext::declareVariable(const TSourceLoc &line,
-                                    const TString &identifier,
+                                    const ImmutableString &identifier,
                                     const TType *type,
                                     TVariable **variable)
 {
     ASSERT((*variable) == nullptr);
 
-    (*variable) = new TVariable(&symbolTable, &identifier, type, SymbolType::UserDefined);
+    (*variable) = new TVariable(&symbolTable, identifier, type, SymbolType::UserDefined);
 
     checkBindingIsValid(line, *type);
 
     bool needsReservedCheck = true;
 
     // gl_LastFragData may be redeclared with a new precision qualifier
-    if (type->isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0)
+    if (type->isArray() && identifier.beginsWith("gl_LastFragData"))
     {
         const TVariable *maxDrawBuffers = static_cast<const TVariable *>(
-            symbolTable.findBuiltIn("gl_MaxDrawBuffers", mShaderVersion));
+            symbolTable.findBuiltIn(ImmutableString("gl_MaxDrawBuffers"), mShaderVersion));
         if (type->isArrayOfArrays())
         {
-            error(line, "redeclaration of gl_LastFragData as an array of arrays",
-                  identifier.c_str());
+            error(line, "redeclaration of gl_LastFragData as an array of arrays", identifier);
             return false;
         }
         else if (static_cast<int>(type->getOutermostArraySize()) ==
@@ -1142,7 +1156,7 @@
         else
         {
             error(line, "redeclaration of gl_LastFragData with size != gl_MaxDrawBuffers",
-                  identifier.c_str());
+                  identifier);
             return false;
         }
     }
@@ -1152,7 +1166,7 @@
 
     if (!symbolTable.declareVariable(*variable))
     {
-        error(line, "redefinition", identifier.c_str());
+        error(line, "redefinition", identifier);
         return false;
     }
 
@@ -1544,13 +1558,13 @@
 }
 
 void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
-                                                  const TString &layoutQualifierName,
+                                                  const ImmutableString &layoutQualifierName,
                                                   int versionRequired)
 {
 
     if (mShaderVersion < versionRequired)
     {
-        error(location, "invalid layout qualifier: not supported", layoutQualifierName.c_str());
+        error(location, "invalid layout qualifier: not supported", layoutQualifierName);
     }
 }
 
@@ -1790,18 +1804,18 @@
 /////////////////////////////////////////////////////////////////////////////////
 
 const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location,
-                                                 const TString *name,
+                                                 const ImmutableString &name,
                                                  const TSymbol *symbol)
 {
     if (!symbol)
     {
-        error(location, "undeclared identifier", name->c_str());
+        error(location, "undeclared identifier", name);
         return nullptr;
     }
 
     if (!symbol->isVariable())
     {
-        error(location, "variable expected", name->c_str());
+        error(location, "variable expected", name);
         return nullptr;
     }
 
@@ -1840,7 +1854,7 @@
                 "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)"
                 " and (gl_FragColor, gl_SecondaryFragColorEXT)";
         }
-        error(location, errorMessage, name->c_str());
+        error(location, errorMessage, name);
     }
 
     // GLSL ES 3.1 Revision 4, 7.1.3 Compute Shader Special Variables
@@ -1855,7 +1869,7 @@
 }
 
 TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
-                                                     const TString *name,
+                                                     const ImmutableString &name,
                                                      const TSymbol *symbol)
 {
     const TVariable *variable = getNamedVariable(location, name, symbol);
@@ -1913,7 +1927,7 @@
 //
 // Returns true on success.
 bool TParseContext::executeInitializer(const TSourceLoc &line,
-                                       const TString &identifier,
+                                       const ImmutableString &identifier,
                                        TType *type,
                                        TIntermTyped *initializer,
                                        TIntermBinary **initNode)
@@ -2017,7 +2031,7 @@
 }
 
 TIntermNode *TParseContext::addConditionInitializer(const TPublicType &pType,
-                                                    const TString &identifier,
+                                                    const ImmutableString &identifier,
                                                     TIntermTyped *initializer,
                                                     const TSourceLoc &loc)
 {
@@ -2353,7 +2367,7 @@
 }
 
 void TParseContext::checkGeometryShaderInputAndSetArraySize(const TSourceLoc &location,
-                                                            const char *token,
+                                                            const ImmutableString &token,
                                                             TType *type)
 {
     if (IsGeometryShaderInput(mShaderType, type->getQualifier()))
@@ -2393,7 +2407,7 @@
 TIntermDeclaration *TParseContext::parseSingleDeclaration(
     TPublicType &publicType,
     const TSourceLoc &identifierOrTypeLocation,
-    const TString &identifier)
+    const ImmutableString &identifier)
 {
     TType *type = new TType(publicType);
     if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) &&
@@ -2423,7 +2437,7 @@
         }
     }
 
-    checkGeometryShaderInputAndSetArraySize(identifierOrTypeLocation, identifier.c_str(), type);
+    checkGeometryShaderInputAndSetArraySize(identifierOrTypeLocation, identifier, type);
 
     declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier,
                                    identifierOrTypeLocation);
@@ -2440,7 +2454,7 @@
         if (type->getBasicType() == EbtStruct)
         {
             TVariable *emptyVariable =
-                new TVariable(&symbolTable, nullptr, type, SymbolType::Empty);
+                new TVariable(&symbolTable, ImmutableString(""), type, SymbolType::Empty);
             symbol = new TIntermSymbol(emptyVariable);
         }
         else if (IsAtomicCounter(publicType.getBasicType()))
@@ -2476,7 +2490,7 @@
 TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(
     TPublicType &elementType,
     const TSourceLoc &identifierLocation,
-    const TString &identifier,
+    const ImmutableString &identifier,
     const TSourceLoc &indexLocation,
     const TVector<unsigned int> &arraySizes)
 {
@@ -2492,7 +2506,7 @@
     TType *arrayType = new TType(elementType);
     arrayType->makeArrays(arraySizes);
 
-    checkGeometryShaderInputAndSetArraySize(indexLocation, identifier.c_str(), arrayType);
+    checkGeometryShaderInputAndSetArraySize(indexLocation, identifier, arrayType);
 
     checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, arrayType);
 
@@ -2514,7 +2528,7 @@
 
 TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
                                                               const TSourceLoc &identifierLocation,
-                                                              const TString &identifier,
+                                                              const ImmutableString &identifier,
                                                               const TSourceLoc &initLocation,
                                                               TIntermTyped *initializer)
 {
@@ -2543,7 +2557,7 @@
 TIntermDeclaration *TParseContext::parseSingleArrayInitDeclaration(
     TPublicType &elementType,
     const TSourceLoc &identifierLocation,
-    const TString &identifier,
+    const ImmutableString &identifier,
     const TSourceLoc &indexLocation,
     const TVector<unsigned int> &arraySizes,
     const TSourceLoc &initLocation,
@@ -2580,14 +2594,14 @@
 TIntermInvariantDeclaration *TParseContext::parseInvariantDeclaration(
     const TTypeQualifierBuilder &typeQualifierBuilder,
     const TSourceLoc &identifierLoc,
-    const TString *identifier,
+    const ImmutableString &identifier,
     const TSymbol *symbol)
 {
     TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
 
     if (!typeQualifier.invariant)
     {
-        error(identifierLoc, "Expected invariant", identifier->c_str());
+        error(identifierLoc, "Expected invariant", identifier);
         return nullptr;
     }
     if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying"))
@@ -2596,7 +2610,7 @@
     }
     if (!symbol)
     {
-        error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
+        error(identifierLoc, "undeclared identifier declared as invariant", identifier);
         return nullptr;
     }
     if (!IsQualifierUnspecified(typeQualifier.qualifier))
@@ -2625,7 +2639,7 @@
                                     typeQualifier.line);
     checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
 
-    symbolTable.addInvariantVarying(std::string(identifier->c_str()));
+    symbolTable.addInvariantVarying(std::string(identifier.data()));
 
     TIntermSymbol *intermSymbol = new TIntermSymbol(variable);
     intermSymbol->setLine(identifierLoc);
@@ -2635,7 +2649,7 @@
 
 void TParseContext::parseDeclarator(TPublicType &publicType,
                                     const TSourceLoc &identifierLocation,
-                                    const TString &identifier,
+                                    const ImmutableString &identifier,
                                     TIntermDeclaration *declarationOut)
 {
     // If the declaration starting this declarator list was empty (example: int,), some checks were
@@ -2650,7 +2664,7 @@
 
     TType *type = new TType(publicType);
 
-    checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), type);
+    checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier, type);
 
     checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, type);
 
@@ -2667,7 +2681,7 @@
 
 void TParseContext::parseArrayDeclarator(TPublicType &elementType,
                                          const TSourceLoc &identifierLocation,
-                                         const TString &identifier,
+                                         const ImmutableString &identifier,
                                          const TSourceLoc &arrayLocation,
                                          const TVector<unsigned int> &arraySizes,
                                          TIntermDeclaration *declarationOut)
@@ -2687,7 +2701,7 @@
         TType *arrayType = new TType(elementType);
         arrayType->makeArrays(arraySizes);
 
-        checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), arrayType);
+        checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier, arrayType);
 
         checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, arrayType);
 
@@ -2705,7 +2719,7 @@
 
 void TParseContext::parseInitDeclarator(const TPublicType &publicType,
                                         const TSourceLoc &identifierLocation,
-                                        const TString &identifier,
+                                        const ImmutableString &identifier,
                                         const TSourceLoc &initLocation,
                                         TIntermTyped *initializer,
                                         TIntermDeclaration *declarationOut)
@@ -2736,7 +2750,7 @@
 
 void TParseContext::parseArrayInitDeclarator(const TPublicType &elementType,
                                              const TSourceLoc &identifierLocation,
-                                             const TString &identifier,
+                                             const ImmutableString &identifier,
                                              const TSourceLoc &indexLocation,
                                              const TVector<unsigned int> &arraySizes,
                                              const TSourceLoc &initLocation,
@@ -2838,12 +2852,12 @@
 {
     if (mGlInVariableWithArraySize == nullptr)
     {
-        const TSymbol *glPerVertex              = symbolTable.findBuiltIn("gl_PerVertex", 310);
+        const TSymbol *glPerVertex = symbolTable.findBuiltIn(ImmutableString("gl_PerVertex"), 310);
         const TInterfaceBlock *glPerVertexBlock = static_cast<const TInterfaceBlock *>(glPerVertex);
         TType *glInType = new TType(glPerVertexBlock, EvqPerVertexIn, TLayoutQualifier::Create());
         glInType->makeArray(inputArraySize);
         mGlInVariableWithArraySize =
-            new TVariable(&symbolTable, NewPoolTString("gl_in"), glInType, SymbolType::BuiltIn,
+            new TVariable(&symbolTable, ImmutableString("gl_in"), glInType, SymbolType::BuiltIn,
                           TExtension::EXT_geometry_shader);
     }
     else if (mGlInVariableWithArraySize->getType().getOutermostArraySize() != inputArraySize)
@@ -3019,7 +3033,7 @@
         }
 
         const TVariable *maxComputeWorkGroupSize = static_cast<const TVariable *>(
-            symbolTable.findBuiltIn("gl_MaxComputeWorkGroupSize", mShaderVersion));
+            symbolTable.findBuiltIn(ImmutableString("gl_MaxComputeWorkGroupSize"), mShaderVersion));
 
         const TConstantUnion *maxComputeWorkGroupSizeData =
             maxComputeWorkGroupSize->getConstPointer();
@@ -3177,7 +3191,7 @@
             {
                 if (!symbolTable.declareVariable(variable))
                 {
-                    error(location, "redefinition", param.name->c_str());
+                    error(location, "redefinition", param.name);
                 }
             }
             // Unsized type of a named parameter should have already been checked and sanitized.
@@ -3197,7 +3211,7 @@
             // The parameter had no name or declaring the symbol failed - either way, add a nameless
             // symbol.
             TVariable *emptyVariable =
-                new TVariable(&symbolTable, nullptr, param.type, SymbolType::Empty);
+                new TVariable(&symbolTable, ImmutableString(""), param.type, SymbolType::Empty);
             symbol = new TIntermSymbol(emptyVariable);
         }
         symbol->setLine(location);
@@ -3246,8 +3260,8 @@
     // Check that non-void functions have at least one return statement.
     if (mCurrentFunctionType->getBasicType() != EbtVoid && !mFunctionReturnsValue)
     {
-        error(location, "function does not return a value:",
-              functionPrototype->getFunction()->name().c_str());
+        error(location,
+              "function does not return a value:", functionPrototype->getFunction()->name());
     }
 
     if (functionBody == nullptr)
@@ -3269,11 +3283,11 @@
 {
     ASSERT(function);
     const TSymbol *builtIn =
-        symbolTable.findBuiltIn(function->getMangledName(), getShaderVersion());
+        symbolTable.findBuiltIn(ImmutableString(function->getMangledName()), getShaderVersion());
 
     if (builtIn)
     {
-        error(location, "built-in functions cannot be redefined", function->name().c_str());
+        error(location, "built-in functions cannot be redefined", function->name());
     }
     else
     {
@@ -3282,7 +3296,7 @@
             symbolTable.setUserDefinedFunctionParameterNamesFromDefinition(function, &wasDefined);
         if (wasDefined)
         {
-            error(location, "function already has a body", function->name().c_str());
+            error(location, "function already has a body", function->name());
         }
     }
 
@@ -3305,7 +3319,7 @@
     // here.
     //
     const TFunction *prevDec = static_cast<const TFunction *>(
-        symbolTable.find(function->getMangledName(), getShaderVersion()));
+        symbolTable.find(ImmutableString(function->getMangledName()), getShaderVersion()));
 
     for (size_t i = 0u; i < function->getParamCount(); ++i)
     {
@@ -3314,17 +3328,17 @@
         {
             // ESSL 3.00.6 section 12.10.
             error(location, "Function parameter type cannot be a structure definition",
-                  function->name().c_str());
+                  function->name());
         }
     }
 
     if (getShaderVersion() >= 300 && symbolTable.hasUnmangledBuiltInForShaderVersion(
-                                         function->name().c_str(), getShaderVersion()))
+                                         function->name().data(), getShaderVersion()))
     {
         // With ESSL 3.00 and above, names of built-in functions cannot be redeclared as functions.
         // Therefore overloading or redefining builtin functions is an error.
         error(location, "Name of a built-in function cannot be redeclared as function",
-              function->name().c_str());
+              function->name());
     }
     else if (prevDec)
     {
@@ -3352,7 +3366,7 @@
     {
         if (!prevSym->isFunction())
         {
-            error(location, "redefinition of a function", function->name().c_str());
+            error(location, "redefinition of a function", function->name());
         }
         insertUnmangledName = false;
     }
@@ -3362,7 +3376,7 @@
     symbolTable.declareUserDefinedFunction(function, insertUnmangledName);
 
     // Raise error message if main function takes any parameters or return anything other than void
-    if (function->name() == "main")
+    if (function->isMain())
     {
         if (function->getParamCount() > 0)
         {
@@ -3384,7 +3398,7 @@
 }
 
 TFunction *TParseContext::parseFunctionHeader(const TPublicType &type,
-                                              const TString *name,
+                                              const ImmutableString &name,
                                               const TSourceLoc &location)
 {
     if (type.qualifier != EvqGlobal && type.qualifier != EvqTemporary)
@@ -3418,7 +3432,7 @@
     return new TFunction(&symbolTable, name, new TType(type), SymbolType::UserDefined, false);
 }
 
-TFunctionLookup *TParseContext::addNonConstructorFunc(const TString *name)
+TFunctionLookup *TParseContext::addNonConstructorFunc(const ImmutableString &name)
 {
     return TFunctionLookup::CreateFunctionCall(name);
 }
@@ -3448,7 +3462,7 @@
 
 void TParseContext::checkIsNotUnsizedArray(const TSourceLoc &line,
                                            const char *errorMessage,
-                                           const char *token,
+                                           const ImmutableString &token,
                                            TType *arrayType)
 {
     if (arrayType->isUnsizedArray())
@@ -3459,30 +3473,29 @@
 }
 
 TParameter TParseContext::parseParameterDeclarator(TType *type,
-                                                   const TString *name,
+                                                   const ImmutableString &name,
                                                    const TSourceLoc &nameLoc)
 {
     ASSERT(type);
-    checkIsNotUnsizedArray(nameLoc, "function parameter array must specify a size", name->c_str(),
-                           type);
+    checkIsNotUnsizedArray(nameLoc, "function parameter array must specify a size", name, type);
     if (type->getBasicType() == EbtVoid)
     {
-        error(nameLoc, "illegal use of type 'void'", name->c_str());
+        error(nameLoc, "illegal use of type 'void'", name);
     }
-    checkIsNotReserved(nameLoc, *name);
-    TParameter param = {name, type};
+    checkIsNotReserved(nameLoc, name);
+    TParameter param = {name.data(), type};
     return param;
 }
 
 TParameter TParseContext::parseParameterDeclarator(const TPublicType &publicType,
-                                                   const TString *name,
+                                                   const ImmutableString &name,
                                                    const TSourceLoc &nameLoc)
 {
     TType *type = new TType(publicType);
     return parseParameterDeclarator(type, name, nameLoc);
 }
 
-TParameter TParseContext::parseParameterArrayDeclarator(const TString *name,
+TParameter TParseContext::parseParameterArrayDeclarator(const ImmutableString &name,
                                                         const TSourceLoc &nameLoc,
                                                         const TVector<unsigned int> &arraySizes,
                                                         const TSourceLoc &arrayLoc,
@@ -3584,9 +3597,9 @@
 TIntermDeclaration *TParseContext::addInterfaceBlock(
     const TTypeQualifierBuilder &typeQualifierBuilder,
     const TSourceLoc &nameLine,
-    const TString &blockName,
+    const ImmutableString &blockName,
     TFieldList *fieldList,
-    const TString *instanceName,
+    const ImmutableString &instanceName,
     const TSourceLoc &instanceLine,
     TIntermTyped *arrayIndex,
     const TSourceLoc &arrayIndexLine)
@@ -3743,7 +3756,7 @@
             // ESSL 3.10 spec section 4.1.9 allows for runtime-sized arrays.
             checkIsNotUnsizedArray(field->line(),
                                    "array members of interface blocks must specify a size",
-                                   field->name().c_str(), field->type());
+                                   field->name(), field->type());
         }
 
         if (typeQualifier.qualifier == EvqBuffer)
@@ -3766,10 +3779,10 @@
     }
 
     TInterfaceBlock *interfaceBlock = new TInterfaceBlock(
-        &symbolTable, &blockName, fieldList, blockLayoutQualifier, SymbolType::UserDefined);
+        &symbolTable, blockName, fieldList, blockLayoutQualifier, SymbolType::UserDefined);
     if (!symbolTable.declareInterfaceBlock(interfaceBlock))
     {
-        error(nameLine, "redefinition of an interface block name", blockName.c_str());
+        error(nameLine, "redefinition of an interface block name", blockName);
     }
 
     TType *interfaceBlockType =
@@ -3784,7 +3797,7 @@
     // instance name.
     TVariable *instanceVariable =
         new TVariable(&symbolTable, instanceName, interfaceBlockType,
-                      instanceName ? SymbolType::UserDefined : SymbolType::Empty);
+                      instanceName.empty() ? SymbolType::Empty : SymbolType::UserDefined);
 
     if (instanceVariable->symbolType() == SymbolType::Empty)
     {
@@ -3800,23 +3813,22 @@
             fieldType->setQualifier(typeQualifier.qualifier);
 
             TVariable *fieldVariable =
-                new TVariable(&symbolTable, &field->name(), fieldType, SymbolType::UserDefined);
+                new TVariable(&symbolTable, field->name(), fieldType, SymbolType::UserDefined);
             if (!symbolTable.declareVariable(fieldVariable))
             {
                 error(field->line(), "redefinition of an interface block member name",
-                      field->name().c_str());
+                      field->name());
             }
         }
     }
     else
     {
-        checkIsNotReserved(instanceLine, *instanceName);
+        checkIsNotReserved(instanceLine, instanceName);
 
         // add a symbol for this interface block
         if (!symbolTable.declareVariable(instanceVariable))
         {
-            error(instanceLine, "redefinition of an interface block instance name",
-                  instanceName->c_str());
+            error(instanceLine, "redefinition of an interface block instance name", instanceName);
         }
     }
 
@@ -3830,7 +3842,8 @@
     return declaration;
 }
 
-void TParseContext::enterStructDeclaration(const TSourceLoc &line, const TString &identifier)
+void TParseContext::enterStructDeclaration(const TSourceLoc &line,
+                                           const ImmutableString &identifier)
 {
     ++mStructNestingLevel;
 
@@ -3872,12 +3885,11 @@
         }
         else
         {
-            reasonStream << "Reference of struct type "
-                         << field.type()->getStruct()->name().c_str();
+            reasonStream << "Reference of struct type " << field.type()->getStruct()->name();
         }
         reasonStream << " exceeds maximum allowed nesting level of " << kWebGLMaxStructNesting;
         std::string reason = reasonStream.str();
-        error(line, reason.c_str(), field.name().c_str());
+        error(line, reason.c_str(), field.name());
         return;
     }
 }
@@ -3894,7 +3906,7 @@
         if (baseExpression->getAsSymbolNode())
         {
             error(location, " left of '[' is not of type array, matrix, or vector ",
-                  baseExpression->getAsSymbolNode()->getName().c_str());
+                  baseExpression->getAsSymbolNode()->getName());
         }
         else
         {
@@ -4068,7 +4080,7 @@
 
 TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression,
                                                          const TSourceLoc &dotLocation,
-                                                         const TString &fieldString,
+                                                         const ImmutableString &fieldString,
                                                          const TSourceLoc &fieldLocation)
 {
     if (baseExpression->isArray())
@@ -4122,7 +4134,7 @@
             }
             else
             {
-                error(dotLocation, " no such field in structure", fieldString.c_str());
+                error(dotLocation, " no such field in structure", fieldString);
                 return baseExpression;
             }
         }
@@ -4159,7 +4171,7 @@
             }
             else
             {
-                error(dotLocation, " no such field in interface block", fieldString.c_str());
+                error(dotLocation, " no such field in interface block", fieldString);
                 return baseExpression;
             }
         }
@@ -4169,20 +4181,20 @@
         if (mShaderVersion < 300)
         {
             error(dotLocation, " field selection requires structure or vector on left hand side",
-                  fieldString.c_str());
+                  fieldString);
         }
         else
         {
             error(dotLocation,
                   " field selection requires structure, vector, or interface block on left hand "
                   "side",
-                  fieldString.c_str());
+                  fieldString);
         }
         return baseExpression;
     }
 }
 
-TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
+TLayoutQualifier TParseContext::parseLayoutQualifier(const ImmutableString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine)
 {
     TLayoutQualifier qualifier = TLayoutQualifier::Create();
@@ -4223,7 +4235,7 @@
     else if (qualifierType == "location")
     {
         error(qualifierTypeLine, "invalid layout qualifier: location requires an argument",
-              qualifierType.c_str());
+              qualifierType);
     }
     else if (qualifierType == "yuv" && mShaderType == GL_FRAGMENT_SHADER)
     {
@@ -4342,13 +4354,13 @@
 
     else
     {
-        error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+        error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
     }
 
     return qualifier;
 }
 
-void TParseContext::parseLocalSize(const TString &qualifierType,
+void TParseContext::parseLocalSize(const ImmutableString &qualifierType,
                                    const TSourceLoc &qualifierTypeLine,
                                    int intValue,
                                    const TSourceLoc &intValueLine,
@@ -4421,7 +4433,7 @@
     }
 }
 
-TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
+TLayoutQualifier TParseContext::parseLayoutQualifier(const ImmutableString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine,
                                                      int intValue,
                                                      const TSourceLoc &intValueLine)
@@ -4505,7 +4517,7 @@
 
     else
     {
-        error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+        error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
     }
 
     return qualifier;
@@ -4633,30 +4645,31 @@
                                     mDiagnostics);
 }
 
-TDeclarator *TParseContext::parseStructDeclarator(const TString *identifier, const TSourceLoc &loc)
+TDeclarator *TParseContext::parseStructDeclarator(const ImmutableString &identifier,
+                                                  const TSourceLoc &loc)
 {
-    checkIsNotReserved(loc, *identifier);
+    checkIsNotReserved(loc, identifier);
     return new TDeclarator(identifier, loc);
 }
 
-TDeclarator *TParseContext::parseStructArrayDeclarator(const TString *identifier,
+TDeclarator *TParseContext::parseStructArrayDeclarator(const ImmutableString &identifier,
                                                        const TSourceLoc &loc,
                                                        const TVector<unsigned int> *arraySizes)
 {
-    checkIsNotReserved(loc, *identifier);
+    checkIsNotReserved(loc, identifier);
     return new TDeclarator(identifier, arraySizes, loc);
 }
 
 void TParseContext::checkDoesNotHaveDuplicateFieldName(const TFieldList::const_iterator begin,
                                                        const TFieldList::const_iterator end,
-                                                       const TString &name,
+                                                       const ImmutableString &name,
                                                        const TSourceLoc &location)
 {
     for (auto fieldIter = begin; fieldIter != end; ++fieldIter)
     {
         if ((*fieldIter)->name() == name)
         {
-            error(location, "duplicate field name in structure", name.c_str());
+            error(location, "duplicate field name in structure", name);
         }
     }
 }
@@ -4709,7 +4722,7 @@
     checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision,
                             typeSpecifier.getBasicType());
 
-    checkIsNonVoid(typeSpecifier.getLine(), *(*declaratorList)[0]->name(),
+    checkIsNonVoid(typeSpecifier.getLine(), (*declaratorList)[0]->name(),
                    typeSpecifier.getBasicType());
 
     checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
@@ -4736,11 +4749,11 @@
 
 TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine,
                                                    const TSourceLoc &nameLine,
-                                                   const TString *structName,
+                                                   const ImmutableString &structName,
                                                    TFieldList *fieldList)
 {
     SymbolType structSymbolType = SymbolType::UserDefined;
-    if (structName == nullptr)
+    if (structName.empty())
     {
         structSymbolType = SymbolType::Empty;
     }
@@ -4752,10 +4765,10 @@
 
     if (structSymbolType != SymbolType::Empty)
     {
-        checkIsNotReserved(nameLine, *structName);
+        checkIsNotReserved(nameLine, structName);
         if (!symbolTable.declareStructType(structure))
         {
-            error(nameLine, "redefinition of a struct", structName->c_str());
+            error(nameLine, "redefinition of a struct", structName);
         }
     }
 
@@ -4785,7 +4798,7 @@
         }
 
         checkIsNotUnsizedArray(field.line(), "array members of structs must specify a size",
-                               field.name().c_str(), field.type());
+                               field.name(), field.type());
 
         checkMemoryQualifierIsNotSpecified(field.type()->getMemoryQualifier(), field.line());
 
@@ -5474,9 +5487,9 @@
 void TParseContext::checkTextureGather(TIntermAggregate *functionCall)
 {
     ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
-    const TString &name        = functionCall->getFunction()->name();
-    bool isTextureGather       = (name == "textureGather");
-    bool isTextureGatherOffset = (name == "textureGatherOffset");
+    const ImmutableString &name = functionCall->getFunction()->name();
+    bool isTextureGather        = name == kTextureGatherName;
+    bool isTextureGatherOffset  = name == kTextureGatherOffsetName;
     if (isTextureGather || isTextureGatherOffset)
     {
         TIntermNode *componentNode = nullptr;
@@ -5522,15 +5535,14 @@
             if (componentNode->getAsTyped()->getQualifier() != EvqConst || !componentConstantUnion)
             {
                 error(functionCall->getLine(), "Texture component must be a constant expression",
-                      name.c_str());
+                      name);
             }
             else
             {
                 int component = componentConstantUnion->getIConst(0);
                 if (component < 0 || component > 3)
                 {
-                    error(functionCall->getLine(), "Component must be in the range [0;3]",
-                          name.c_str());
+                    error(functionCall->getLine(), "Component must be in the range [0;3]", name);
                 }
             }
         }
@@ -5540,23 +5552,23 @@
 void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
 {
     ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
-    const TString &name                    = functionCall->getFunction()->name();
+    const ImmutableString &name            = functionCall->getFunction()->name();
     TIntermNode *offset        = nullptr;
     TIntermSequence *arguments = functionCall->getSequence();
     bool useTextureGatherOffsetConstraints = false;
-    if (name == "texelFetchOffset" || name == "textureLodOffset" ||
-        name == "textureProjLodOffset" || name == "textureGradOffset" ||
-        name == "textureProjGradOffset")
+    if (name == kTexelFetchOffsetName || name == kTextureLodOffsetName ||
+        name == kTextureProjLodOffsetName || name == kTextureGradOffsetName ||
+        name == kTextureProjGradOffsetName)
     {
         offset = arguments->back();
     }
-    else if (name == "textureOffset" || name == "textureProjOffset")
+    else if (name == kTextureOffsetName || name == kTextureProjOffsetName)
     {
         // A bias parameter might follow the offset parameter.
         ASSERT(arguments->size() >= 3);
         offset = (*arguments)[2];
     }
-    else if (name == "textureGatherOffset")
+    else if (name == kTextureGatherOffsetName)
     {
         ASSERT(arguments->size() >= 3u);
         const TIntermTyped *sampler = arguments->front()->getAsTyped();
@@ -5586,8 +5598,7 @@
         TIntermConstantUnion *offsetConstantUnion = offset->getAsConstantUnion();
         if (offset->getAsTyped()->getQualifier() != EvqConst || !offsetConstantUnion)
         {
-            error(functionCall->getLine(), "Texture offset must be a constant expression",
-                  name.c_str());
+            error(functionCall->getLine(), "Texture offset must be a constant expression", name);
         }
         else
         {
@@ -5617,7 +5628,7 @@
 void TParseContext::checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall)
 {
     ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
-    const TString &functionName = functionCall->getFunction()->name();
+    const ImmutableString &functionName = functionCall->getFunction()->name();
     if (IsAtomicBuiltin(functionName))
     {
         TIntermSequence *arguments = functionCall->getSequence();
@@ -5640,7 +5651,7 @@
         error(memNode->getLine(),
               "The value passed to the mem argument of an atomic memory function does not "
               "correspond to a buffer or shared variable.",
-              functionName.c_str());
+              functionName);
     }
 }
 
@@ -5648,16 +5659,16 @@
 void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall)
 {
     ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
-    const TString &name = functionCall->getFunction()->name();
 
-    if (name.compare(0, 5, "image") == 0)
+    if (functionCall->getFunction()->isImageFunction())
     {
+        const ImmutableString &name = functionCall->getFunction()->name();
         TIntermSequence *arguments = functionCall->getSequence();
         TIntermTyped *imageNode    = (*arguments)[0]->getAsTyped();
 
         const TMemoryQualifier &memoryQualifier = imageNode->getMemoryQualifier();
 
-        if (name.compare(5, 5, "Store") == 0)
+        if (strcmp(name.data() + 5u, "Store") == 0)
         {
             if (memoryQualifier.readonly)
             {
@@ -5666,7 +5677,7 @@
                       GetImageArgumentToken(imageNode));
             }
         }
-        else if (name.compare(5, 4, "Load") == 0)
+        else if (strcmp(name.data() + 5u, "Load") == 0)
         {
             if (memoryQualifier.writeonly)
             {
@@ -5759,7 +5770,7 @@
     // So accessing fnCall->name() below is safe.
     if (fnCall->name() != "length")
     {
-        error(loc, "invalid method", fnCall->name().c_str());
+        error(loc, "invalid method", fnCall->name());
     }
     else if (!fnCall->arguments().empty())
     {
@@ -5793,14 +5804,14 @@
     const TSymbol *symbol = symbolTable.find(fnCall->name(), mShaderVersion);
     if (symbol != nullptr && !symbol->isFunction())
     {
-        error(loc, "function name expected", fnCall->name().c_str());
+        error(loc, "function name expected", fnCall->name());
     }
     else
     {
         symbol = symbolTable.find(fnCall->getMangledName(), mShaderVersion);
         if (symbol == nullptr)
         {
-            error(loc, "no matching overloaded function found", fnCall->name().c_str());
+            error(loc, "no matching overloaded function found", fnCall->name());
         }
         else
         {