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/ASTMetadataHLSL.cpp b/src/compiler/translator/ASTMetadataHLSL.cpp
index a15fc36..055408d 100644
--- a/src/compiler/translator/ASTMetadataHLSL.cpp
+++ b/src/compiler/translator/ASTMetadataHLSL.cpp
@@ -35,15 +35,15 @@
         ASSERT(index < metadataList->size());
 
         // ESSL 100 builtin gradient functions
-        mGradientBuiltinFunctions.insert("texture2D");
-        mGradientBuiltinFunctions.insert("texture2DProj");
-        mGradientBuiltinFunctions.insert("textureCube");
+        mGradientBuiltinFunctions.insert(ImmutableString("texture2D"));
+        mGradientBuiltinFunctions.insert(ImmutableString("texture2DProj"));
+        mGradientBuiltinFunctions.insert(ImmutableString("textureCube"));
 
         // ESSL 300 builtin gradient functions
-        mGradientBuiltinFunctions.insert("texture");
-        mGradientBuiltinFunctions.insert("textureProj");
-        mGradientBuiltinFunctions.insert("textureOffset");
-        mGradientBuiltinFunctions.insert("textureProjOffset");
+        mGradientBuiltinFunctions.insert(ImmutableString("texture"));
+        mGradientBuiltinFunctions.insert(ImmutableString("textureProj"));
+        mGradientBuiltinFunctions.insert(ImmutableString("textureOffset"));
+        mGradientBuiltinFunctions.insert(ImmutableString("textureProjOffset"));
 
         // ESSL 310 doesn't add builtin gradient functions
     }
@@ -151,7 +151,7 @@
     std::vector<TIntermNode *> mParents;
 
     // A list of builtin functions that use gradients
-    std::set<TString> mGradientBuiltinFunctions;
+    std::set<ImmutableString> mGradientBuiltinFunctions;
 };
 
 // Traverses the AST of a function definition to compute the the discontinuous loops
diff --git a/src/compiler/translator/ArrayReturnValueToOutParameter.cpp b/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
index 2907f1d..5111aaa 100644
--- a/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
+++ b/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
@@ -21,6 +21,8 @@
 namespace
 {
 
+constexpr const ImmutableString kReturnValueVariableName("angle_return");
+
 void CopyAggregateChildren(TIntermAggregateBase *from, TIntermAggregateBase *to)
 {
     const TIntermSequence *fromSequence = from->getSequence();
@@ -58,8 +60,6 @@
 
     // Map from function symbol ids to the changed function.
     std::map<int, ChangedFunction> mChangedFunctions;
-
-    const TString *const mReturnValueVariableName;
 };
 
 TIntermAggregate *ArrayReturnValueToOutParameterTraverser::createReplacementCall(
@@ -90,9 +90,7 @@
 
 ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser(
     TSymbolTable *symbolTable)
-    : TIntermTraverser(true, false, true, symbolTable),
-      mFunctionWithArrayReturnValue(nullptr),
-      mReturnValueVariableName(NewPoolTString("angle_return"))
+    : TIntermTraverser(true, false, true, symbolTable), mFunctionWithArrayReturnValue(nullptr)
 {
 }
 
@@ -126,9 +124,9 @@
             returnValueVariableType->setQualifier(EvqOut);
             ChangedFunction changedFunction;
             changedFunction.returnValueVariable =
-                new TVariable(mSymbolTable, mReturnValueVariableName, returnValueVariableType,
+                new TVariable(mSymbolTable, kReturnValueVariableName, returnValueVariableType,
                               SymbolType::AngleInternal);
-            TFunction *func = new TFunction(mSymbolTable, &node->getFunction()->name(),
+            TFunction *func = new TFunction(mSymbolTable, node->getFunction()->name(),
                                             StaticType::GetBasic<EbtVoid>(),
                                             node->getFunction()->symbolType(), false);
             for (size_t i = 0; i < node->getFunction()->getParamCount(); ++i)
@@ -136,7 +134,7 @@
                 func->addParameter(node->getFunction()->getParam(i));
             }
             func->addParameter(TConstParameter(
-                mReturnValueVariableName, static_cast<const TType *>(returnValueVariableType)));
+                kReturnValueVariableName, static_cast<const TType *>(returnValueVariableType)));
             changedFunction.func                = func;
             mChangedFunctions[functionId.get()] = changedFunction;
         }
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index 3dc8846..e8a6fd8 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -10,8 +10,9 @@
 #include <algorithm>
 #include <array>
 
-#include "common/debug.h"
 #include "GLSLANG/ShaderLang.h"
+#include "common/debug.h"
+#include "compiler/translator/ImmutableString.h"
 
 namespace sh
 {
@@ -1054,7 +1055,7 @@
     }
 }
 
-inline TYuvCscStandardEXT getYuvCscStandardEXT(const std::string &str)
+inline TYuvCscStandardEXT getYuvCscStandardEXT(const ImmutableString &str)
 {
     if (str == "itu_601")
         return EycsItu601;
diff --git a/src/compiler/translator/CallDAG.cpp b/src/compiler/translator/CallDAG.cpp
index fc61d96..a931ed1 100644
--- a/src/compiler/translator/CallDAG.cpp
+++ b/src/compiler/translator/CallDAG.cpp
@@ -88,17 +88,13 @@
     struct CreatorFunctionData
     {
         CreatorFunctionData()
-            : definitionNode(nullptr),
-              name(nullptr),
-              index(0),
-              indexAssigned(false),
-              visiting(false)
+            : definitionNode(nullptr), name(""), index(0), indexAssigned(false), visiting(false)
         {
         }
 
         std::set<CreatorFunctionData *> callees;
         TIntermFunctionDefinition *definitionNode;
-        const TString *name;
+        ImmutableString name;
         size_t index;
         bool indexAssigned;
         bool visiting;
@@ -110,9 +106,9 @@
         mCurrentFunction = &mFunctions[node->getFunction()->uniqueId().get()];
         // Name will be overwritten here. If we've already traversed the prototype of this function,
         // it should have had the same name.
-        ASSERT(mCurrentFunction->name == nullptr ||
-               *mCurrentFunction->name == node->getFunction()->name());
-        mCurrentFunction->name           = &node->getFunction()->name();
+        ASSERT(mCurrentFunction->name == "" ||
+               mCurrentFunction->name == node->getFunction()->name());
+        mCurrentFunction->name           = node->getFunction()->name();
         mCurrentFunction->definitionNode = node;
 
         node->getBody()->traverse(this);
@@ -126,7 +122,7 @@
 
         // Function declaration, create an empty record.
         auto &record = mFunctions[node->getFunction()->uniqueId().get()];
-        record.name  = &node->getFunction()->name();
+        record.name  = node->getFunction()->name();
 
         // No need to traverse the parameters.
         return false;
@@ -198,7 +194,7 @@
 
             if (!function->definitionNode)
             {
-                errorStream << "Undefined function '" << *function->name
+                errorStream << "Undefined function '" << function->name
                             << ")' used in the following call chain:";
                 result = INITDAG_UNDEFINED;
                 break;
@@ -244,7 +240,7 @@
                     {
                         errorStream << " -> ";
                     }
-                    errorStream << *function->name << ")";
+                    errorStream << function->name << ")";
                     first = false;
                 }
             }
diff --git a/src/compiler/translator/ClampFragDepth.cpp b/src/compiler/translator/ClampFragDepth.cpp
index 70852aa..448c9c0 100644
--- a/src/compiler/translator/ClampFragDepth.cpp
+++ b/src/compiler/translator/ClampFragDepth.cpp
@@ -11,6 +11,7 @@
 #include "compiler/translator/ClampFragDepth.h"
 
 #include "compiler/translator/FindSymbolNode.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/IntermNode_util.h"
 #include "compiler/translator/RunAtTheEndOfShader.h"
 #include "compiler/translator/SymbolTable.h"
@@ -21,12 +22,13 @@
 void ClampFragDepth(TIntermBlock *root, TSymbolTable *symbolTable)
 {
     // Only clamp gl_FragDepth if it's used in the shader.
-    if (!FindSymbolNode(root, TString("gl_FragDepth")))
+    if (!FindSymbolNode(root, ImmutableString("gl_FragDepth")))
     {
         return;
     }
 
-    TIntermSymbol *fragDepthNode = ReferenceBuiltInVariable("gl_FragDepth", *symbolTable, 300);
+    TIntermSymbol *fragDepthNode =
+        ReferenceBuiltInVariable(ImmutableString("gl_FragDepth"), *symbolTable, 300);
 
     TIntermTyped *minFragDepthNode = CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
 
diff --git a/src/compiler/translator/ClampPointSize.cpp b/src/compiler/translator/ClampPointSize.cpp
index b16a2f3..e6e9a0e 100644
--- a/src/compiler/translator/ClampPointSize.cpp
+++ b/src/compiler/translator/ClampPointSize.cpp
@@ -19,12 +19,13 @@
 void ClampPointSize(TIntermBlock *root, float maxPointSize, TSymbolTable *symbolTable)
 {
     // Only clamp gl_PointSize if it's used in the shader.
-    if (!FindSymbolNode(root, TString("gl_PointSize")))
+    if (!FindSymbolNode(root, ImmutableString("gl_PointSize")))
     {
         return;
     }
 
-    TIntermSymbol *pointSizeNode = ReferenceBuiltInVariable("gl_PointSize", *symbolTable, 100);
+    TIntermSymbol *pointSizeNode =
+        ReferenceBuiltInVariable(ImmutableString("gl_PointSize"), *symbolTable, 100);
 
     TConstantUnion *maxPointSizeConstant = new TConstantUnion();
     maxPointSizeConstant->setFConst(maxPointSize);
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index 0b7cd70..e0f73ca 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -56,12 +56,12 @@
 }
 
 template <class VarT>
-VarT *FindVariable(const TString &name, std::vector<VarT> *infoList)
+VarT *FindVariable(const ImmutableString &name, std::vector<VarT> *infoList)
 {
     // TODO(zmo): optimize this function.
     for (size_t ii = 0; ii < infoList->size(); ++ii)
     {
-        if ((*infoList)[ii].name.c_str() == name)
+        if (name == (*infoList)[ii].name)
             return &((*infoList)[ii]);
     }
 
@@ -86,7 +86,7 @@
     }
 }
 
-ShaderVariable *FindVariableInInterfaceBlock(const TString &name,
+ShaderVariable *FindVariableInInterfaceBlock(const ImmutableString &name,
                                              const TInterfaceBlock *interfaceBlock,
                                              std::vector<InterfaceBlock> *infoList)
 {
@@ -127,7 +127,7 @@
 
     void setFieldOrVariableProperties(const TType &type, ShaderVariable *variableOut) const;
     void setFieldProperties(const TType &type,
-                            const TString &name,
+                            const ImmutableString &name,
                             ShaderVariable *variableOut) const;
     void setCommonVariableProperties(const TType &type,
                                      const TVariable &variable,
@@ -141,15 +141,15 @@
                               InterfaceBlock *interfaceBlock) const;
     Uniform recordUniform(const TIntermSymbol &variable) const;
 
-    void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
+    void setBuiltInInfoFromSymbolTable(const ImmutableString &name, ShaderVariable *info);
 
-    void recordBuiltInVaryingUsed(const char *name,
+    void recordBuiltInVaryingUsed(const ImmutableString &name,
                                   bool *addedFlag,
                                   std::vector<Varying> *varyings);
-    void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
-    void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
+    void recordBuiltInFragmentOutputUsed(const ImmutableString &name, bool *addedFlag);
+    void recordBuiltInAttributeUsed(const ImmutableString &name, bool *addedFlag);
     InterfaceBlock *recordGLInUsed(const TType &glInType);
-    InterfaceBlock *findNamedInterfaceBlock(const TString &name) const;
+    InterfaceBlock *findNamedInterfaceBlock(const ImmutableString &name) const;
 
     std::vector<Attribute> *mAttribs;
     std::vector<OutputVariable> *mOutputVariables;
@@ -253,10 +253,10 @@
 
 std::string CollectVariablesTraverser::getMappedName(const TSymbol *symbol) const
 {
-    return HashName(symbol, mHashFunction, nullptr).c_str();
+    return HashName(symbol, mHashFunction, nullptr).data();
 }
 
-void CollectVariablesTraverser::setBuiltInInfoFromSymbolTable(const char *name,
+void CollectVariablesTraverser::setBuiltInInfoFromSymbolTable(const ImmutableString &name,
                                                               ShaderVariable *info)
 {
     const TVariable *symbolTableVar =
@@ -264,8 +264,8 @@
     ASSERT(symbolTableVar);
     const TType &type = symbolTableVar->getType();
 
-    info->name       = name;
-    info->mappedName = name;
+    info->name       = name.data();
+    info->mappedName = name.data();
     info->type       = GLVariableType(type);
     info->precision = GLVariablePrecision(type);
     if (auto *arraySizes = type.getArraySizes())
@@ -274,7 +274,7 @@
     }
 }
 
-void CollectVariablesTraverser::recordBuiltInVaryingUsed(const char *name,
+void CollectVariablesTraverser::recordBuiltInVaryingUsed(const ImmutableString &name,
                                                          bool *addedFlag,
                                                          std::vector<Varying> *varyings)
 {
@@ -284,13 +284,14 @@
         Varying info;
         setBuiltInInfoFromSymbolTable(name, &info);
         info.staticUse   = true;
-        info.isInvariant = mSymbolTable->isVaryingInvariant(name);
+        info.isInvariant = mSymbolTable->isVaryingInvariant(name.data());
         varyings->push_back(info);
         (*addedFlag) = true;
     }
 }
 
-void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag)
+void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const ImmutableString &name,
+                                                                bool *addedFlag)
 {
     if (!(*addedFlag))
     {
@@ -302,7 +303,8 @@
     }
 }
 
-void CollectVariablesTraverser::recordBuiltInAttributeUsed(const char *name, bool *addedFlag)
+void CollectVariablesTraverser::recordBuiltInAttributeUsed(const ImmutableString &name,
+                                                           bool *addedFlag)
 {
     if (!(*addedFlag))
     {
@@ -330,7 +332,7 @@
     }
     else
     {
-        return FindVariable("gl_PerVertex", mInBlocks);
+        return FindVariable(ImmutableString("gl_PerVertex"), mInBlocks);
     }
 }
 
@@ -352,7 +354,7 @@
 
     ShaderVariable *var       = nullptr;
 
-    const TString &symbolName = symbol->getName();
+    const ImmutableString &symbolName = symbol->getName();
 
     // Check the qualifier from the variable, not from the symbol node. The node may have a
     // different qualifier if it's the result of a folded ternary node.
@@ -437,7 +439,7 @@
                 }
 
                 // It's an internal error to reference an undefined user uniform
-                ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
+                ASSERT(!symbolName.beginsWith("gl_") || var);
             }
             break;
             case EvqBuffer:
@@ -448,13 +450,16 @@
             }
             break;
             case EvqFragCoord:
-                recordBuiltInVaryingUsed("gl_FragCoord", &mFragCoordAdded, mInputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_FragCoord"), &mFragCoordAdded,
+                                         mInputVaryings);
                 return;
             case EvqFrontFacing:
-                recordBuiltInVaryingUsed("gl_FrontFacing", &mFrontFacingAdded, mInputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_FrontFacing"), &mFrontFacingAdded,
+                                         mInputVaryings);
                 return;
             case EvqPointCoord:
-                recordBuiltInVaryingUsed("gl_PointCoord", &mPointCoordAdded, mInputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_PointCoord"), &mPointCoordAdded,
+                                         mInputVaryings);
                 return;
             case EvqInstanceID:
                 // Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
@@ -477,25 +482,28 @@
                 }
                 return;
             case EvqVertexID:
-                recordBuiltInAttributeUsed("gl_VertexID", &mVertexIDAdded);
+                recordBuiltInAttributeUsed(ImmutableString("gl_VertexID"), &mVertexIDAdded);
                 return;
             case EvqPosition:
-                recordBuiltInVaryingUsed("gl_Position", &mPositionAdded, mOutputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_Position"), &mPositionAdded,
+                                         mOutputVaryings);
                 return;
             case EvqPointSize:
-                recordBuiltInVaryingUsed("gl_PointSize", &mPointSizeAdded, mOutputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_PointSize"), &mPointSizeAdded,
+                                         mOutputVaryings);
                 return;
             case EvqLastFragData:
-                recordBuiltInVaryingUsed("gl_LastFragData", &mLastFragDataAdded, mInputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_LastFragData"), &mLastFragDataAdded,
+                                         mInputVaryings);
                 return;
             case EvqFragColor:
-                recordBuiltInFragmentOutputUsed("gl_FragColor", &mFragColorAdded);
+                recordBuiltInFragmentOutputUsed(ImmutableString("gl_FragColor"), &mFragColorAdded);
                 return;
             case EvqFragData:
                 if (!mFragDataAdded)
                 {
                     OutputVariable info;
-                    setBuiltInInfoFromSymbolTable("gl_FragData", &info);
+                    setBuiltInInfoFromSymbolTable(ImmutableString("gl_FragData"), &info);
                     if (!IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers))
                     {
                         ASSERT(info.arraySizes.size() == 1u);
@@ -507,44 +515,51 @@
                 }
                 return;
             case EvqFragDepthEXT:
-                recordBuiltInFragmentOutputUsed("gl_FragDepthEXT", &mFragDepthEXTAdded);
+                recordBuiltInFragmentOutputUsed(ImmutableString("gl_FragDepthEXT"),
+                                                &mFragDepthEXTAdded);
                 return;
             case EvqFragDepth:
-                recordBuiltInFragmentOutputUsed("gl_FragDepth", &mFragDepthAdded);
+                recordBuiltInFragmentOutputUsed(ImmutableString("gl_FragDepth"), &mFragDepthAdded);
                 return;
             case EvqSecondaryFragColorEXT:
-                recordBuiltInFragmentOutputUsed("gl_SecondaryFragColorEXT",
+                recordBuiltInFragmentOutputUsed(ImmutableString("gl_SecondaryFragColorEXT"),
                                                 &mSecondaryFragColorEXTAdded);
                 return;
             case EvqSecondaryFragDataEXT:
-                recordBuiltInFragmentOutputUsed("gl_SecondaryFragDataEXT",
+                recordBuiltInFragmentOutputUsed(ImmutableString("gl_SecondaryFragDataEXT"),
                                                 &mSecondaryFragDataEXTAdded);
                 return;
             case EvqInvocationID:
-                recordBuiltInVaryingUsed("gl_InvocationID", &mInvocationIDAdded, mInputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_InvocationID"), &mInvocationIDAdded,
+                                         mInputVaryings);
                 break;
             case EvqPrimitiveIDIn:
-                recordBuiltInVaryingUsed("gl_PrimitiveIDIn", &mPrimitiveIDInAdded, mInputVaryings);
+                recordBuiltInVaryingUsed(ImmutableString("gl_PrimitiveIDIn"), &mPrimitiveIDInAdded,
+                                         mInputVaryings);
                 break;
             case EvqPrimitiveID:
                 if (mShaderType == GL_GEOMETRY_SHADER_EXT)
                 {
-                    recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mOutputVaryings);
+                    recordBuiltInVaryingUsed(ImmutableString("gl_PrimitiveID"), &mPrimitiveIDAdded,
+                                             mOutputVaryings);
                 }
                 else
                 {
                     ASSERT(mShaderType == GL_FRAGMENT_SHADER);
-                    recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mInputVaryings);
+                    recordBuiltInVaryingUsed(ImmutableString("gl_PrimitiveID"), &mPrimitiveIDAdded,
+                                             mInputVaryings);
                 }
                 break;
             case EvqLayer:
                 if (mShaderType == GL_GEOMETRY_SHADER_EXT)
                 {
-                    recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mOutputVaryings);
+                    recordBuiltInVaryingUsed(ImmutableString("gl_Layer"), &mLayerAdded,
+                                             mOutputVaryings);
                 }
                 else if (mShaderType == GL_FRAGMENT_SHADER)
                 {
-                    recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mInputVaryings);
+                    recordBuiltInVaryingUsed(ImmutableString("gl_Layer"), &mLayerAdded,
+                                             mInputVaryings);
                 }
                 else
                 {
@@ -579,7 +594,7 @@
         variableOut->type       = GL_NONE;
         if (structure->symbolType() != SymbolType::Empty)
         {
-            variableOut->structName = structure->name().c_str();
+            variableOut->structName = structure->name().data();
         }
 
         const TFieldList &fields = structure->fields();
@@ -600,13 +615,13 @@
 }
 
 void CollectVariablesTraverser::setFieldProperties(const TType &type,
-                                                   const TString &name,
+                                                   const ImmutableString &name,
                                                    ShaderVariable *variableOut) const
 {
     ASSERT(variableOut);
     setFieldOrVariableProperties(type, variableOut);
-    variableOut->name       = name.c_str();
-    variableOut->mappedName = HashName(name, mHashFunction, nullptr).c_str();
+    variableOut->name.assign(name.data(), name.length());
+    variableOut->mappedName = HashName(name, mHashFunction, nullptr).data();
 }
 
 void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
@@ -617,7 +632,7 @@
 
     setFieldOrVariableProperties(type, variableOut);
     ASSERT(variable.symbolType() != SymbolType::Empty);
-    variableOut->name       = variable.name().c_str();
+    variableOut->name.assign(variable.name().data(), variable.name().length());
     variableOut->mappedName = getMappedName(&variable);
 }
 
@@ -686,7 +701,7 @@
     const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
     ASSERT(blockType);
 
-    interfaceBlock->name       = blockType->name().c_str();
+    interfaceBlock->name       = blockType->name().data();
     interfaceBlock->mappedName = getMappedName(blockType);
     if (instanceName != nullptr)
     {
@@ -765,7 +780,7 @@
         {
             InterfaceBlock interfaceBlock;
             recordInterfaceBlock(variable.variable().symbolType() != SymbolType::Empty
-                                     ? variable.getName().c_str()
+                                     ? variable.getName().data()
                                      : nullptr,
                                  variable.getType(), &interfaceBlock);
 
@@ -818,7 +833,8 @@
 
 // TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing
 // GL_EXT_shader_io_blocks.
-InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(const TString &blockName) const
+InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(
+    const ImmutableString &blockName) const
 {
     InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
     if (!namedBlock)
diff --git a/src/compiler/translator/Common.h b/src/compiler/translator/Common.h
index e022194..b606e85 100644
--- a/src/compiler/translator/Common.h
+++ b/src/compiler/translator/Common.h
@@ -50,11 +50,6 @@
 typedef pool_allocator<char> TStringAllocator;
 typedef std::basic_string<char, std::char_traits<char>, TStringAllocator> TString;
 typedef std::basic_ostringstream<char, std::char_traits<char>, TStringAllocator> TStringStream;
-inline TString *NewPoolTString(const char *s)
-{
-    void *memory = GetGlobalPoolAllocator()->allocate(sizeof(TString));
-    return new (memory) TString(s);
-}
 
 //
 // Persistent string memory.  Should only be used for strings that survive
@@ -128,7 +123,7 @@
     size_t requiredSize = strLength + 1;
     char *buffer = reinterpret_cast<char *>(GetGlobalPoolAllocator()->allocate(requiredSize));
     memcpy(buffer, str, requiredSize);
-    ASSERT(buffer[strLength] == 0);
+    ASSERT(buffer[strLength] == '\0');
     return buffer;
 }
 
diff --git a/src/compiler/translator/Declarator.cpp b/src/compiler/translator/Declarator.cpp
index 7ca0011..7a17a82 100644
--- a/src/compiler/translator/Declarator.cpp
+++ b/src/compiler/translator/Declarator.cpp
@@ -11,18 +11,17 @@
 namespace sh
 {
 
-TDeclarator::TDeclarator(const TString *name, const TSourceLoc &line)
+TDeclarator::TDeclarator(const ImmutableString &name, const TSourceLoc &line)
     : mName(name), mArraySizes(nullptr), mLine(line)
 {
-    ASSERT(mName);
+    ASSERT(mName != "");
 }
 
-TDeclarator::TDeclarator(const TString *name,
+TDeclarator::TDeclarator(const ImmutableString &name,
                          const TVector<unsigned int> *arraySizes,
                          const TSourceLoc &line)
     : mName(name), mArraySizes(arraySizes), mLine(line)
 {
-    ASSERT(mName);
     ASSERT(mArraySizes);
 }
 
diff --git a/src/compiler/translator/Declarator.h b/src/compiler/translator/Declarator.h
index f013036..7801b65 100644
--- a/src/compiler/translator/Declarator.h
+++ b/src/compiler/translator/Declarator.h
@@ -10,6 +10,7 @@
 #define COMPILER_TRANSLATOR_DECLARATOR_H_
 
 #include "compiler/translator/Common.h"
+#include "compiler/translator/ImmutableString.h"
 
 namespace sh
 {
@@ -19,13 +20,13 @@
 {
   public:
     POOL_ALLOCATOR_NEW_DELETE();
-    TDeclarator(const TString *name, const TSourceLoc &line);
+    TDeclarator(const ImmutableString &name, const TSourceLoc &line);
 
-    TDeclarator(const TString *name,
+    TDeclarator(const ImmutableString &name,
                 const TVector<unsigned int> *arraySizes,
                 const TSourceLoc &line);
 
-    const TString *name() const { return mName; }
+    const ImmutableString &name() const { return mName; }
 
     bool isArray() const;
     const TVector<unsigned int> *arraySizes() const { return mArraySizes; }
@@ -33,7 +34,7 @@
     const TSourceLoc &line() const { return mLine; }
 
   private:
-    const TString *const mName;
+    const ImmutableString mName;
 
     // Outermost array size is stored at the end of the vector.
     const TVector<unsigned int> *const mArraySizes;
diff --git a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
index 837e4bc..5929577 100644
--- a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
+++ b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
@@ -24,9 +24,18 @@
 namespace
 {
 
+constexpr const ImmutableString kGlLayerString("gl_Layer");
+constexpr const ImmutableString kGlViewportIndexString("gl_ViewportIndex");
+constexpr const ImmutableString kGlViewIdOVRString("gl_ViewID_OVR");
+constexpr const ImmutableString kGlInstanceIdString("gl_InstanceID");
+constexpr const ImmutableString kViewIDVariableName("ViewID_OVR");
+constexpr const ImmutableString kInstanceIDVariableName("InstanceID");
+constexpr const ImmutableString kMultiviewBaseViewLayerIndexVariableName(
+    "multiviewBaseViewLayerIndex");
+
 TIntermSymbol *CreateGLInstanceIDSymbol(const TSymbolTable &symbolTable)
 {
-    return ReferenceBuiltInVariable("gl_InstanceID", symbolTable, 300);
+    return ReferenceBuiltInVariable(kGlInstanceIdString, symbolTable, 300);
 }
 
 // Adds the InstanceID and ViewID_OVR initializers to the end of the initializers' sequence.
@@ -97,7 +106,7 @@
 
     // Create a gl_ViewportIndex node.
     TIntermSymbol *viewportIndexSymbol =
-        ReferenceBuiltInVariable("gl_ViewportIndex", symbolTable, 0);
+        ReferenceBuiltInVariable(kGlViewportIndexString, symbolTable, 0);
 
     // Create a { gl_ViewportIndex = int(ViewID_OVR) } node.
     TIntermBlock *viewportIndexInitializerInBlock = new TIntermBlock();
@@ -105,7 +114,7 @@
         new TIntermBinary(EOpAssign, viewportIndexSymbol, viewIDAsInt));
 
     // Create a gl_Layer node.
-    TIntermSymbol *layerSymbol = ReferenceBuiltInVariable("gl_Layer", symbolTable, 0);
+    TIntermSymbol *layerSymbol = ReferenceBuiltInVariable(kGlLayerString, symbolTable, 0);
 
     // Create an int(ViewID_OVR) + multiviewBaseViewLayerIndex node
     TIntermBinary *sumOfViewIDAndBaseViewIndex = new TIntermBinary(
@@ -141,28 +150,28 @@
     ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
 
     TQualifier viewIDQualifier  = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
-    const TString *viewIDVariableName = NewPoolTString("ViewID_OVR");
     const TVariable *viewID =
-        new TVariable(symbolTable, viewIDVariableName, new TType(EbtUInt, EbpHigh, viewIDQualifier),
-                      SymbolType::AngleInternal);
+        new TVariable(symbolTable, kViewIDVariableName,
+                      new TType(EbtUInt, EbpHigh, viewIDQualifier), SymbolType::AngleInternal);
 
     DeclareGlobalVariable(root, viewID);
     ReplaceVariable(
-        root, static_cast<const TVariable *>(symbolTable->findBuiltIn("gl_ViewID_OVR", 300, true)),
+        root,
+        static_cast<const TVariable *>(symbolTable->findBuiltIn(kGlViewIdOVRString, 300, true)),
         viewID);
     if (shaderType == GL_VERTEX_SHADER)
     {
         // Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
         // InstanceID and ViewID.
-        const TString *instanceIDVariableName = NewPoolTString("InstanceID");
         const TType *instanceIDVariableType   = StaticType::Get<EbtInt, EbpHigh, EvqGlobal, 1, 1>();
-        const TVariable *instanceID           = new TVariable(
-            symbolTable, instanceIDVariableName, instanceIDVariableType, SymbolType::AngleInternal);
+        const TVariable *instanceID =
+            new TVariable(symbolTable, kInstanceIDVariableName, instanceIDVariableType,
+                          SymbolType::AngleInternal);
         DeclareGlobalVariable(root, instanceID);
-        ReplaceVariable(
-            root,
-            static_cast<const TVariable *>(symbolTable->findBuiltIn("gl_InstanceID", 300, true)),
-            instanceID);
+        ReplaceVariable(root,
+                        static_cast<const TVariable *>(
+                            symbolTable->findBuiltIn(kGlInstanceIdString, 300, true)),
+                        instanceID);
 
         TIntermSequence *initializers = new TIntermSequence();
         InitializeViewIDAndInstanceID(viewID, instanceID, numberOfViews, *symbolTable,
@@ -177,12 +186,10 @@
         if (selectView)
         {
             // Add a uniform to switch between side-by-side and layered rendering.
-            const TString *multiviewBaseViewLayerIndexVariableName =
-                NewPoolTString("multiviewBaseViewLayerIndex");
             const TType *baseLayerIndexVariableType =
                 StaticType::Get<EbtInt, EbpHigh, EvqUniform, 1, 1>();
             const TVariable *multiviewBaseViewLayerIndex =
-                new TVariable(symbolTable, multiviewBaseViewLayerIndexVariableName,
+                new TVariable(symbolTable, kMultiviewBaseViewLayerIndexVariableName,
                               baseLayerIndexVariableType, SymbolType::AngleInternal);
             DeclareGlobalVariable(root, multiviewBaseViewLayerIndex);
 
diff --git a/src/compiler/translator/DeferGlobalInitializers.cpp b/src/compiler/translator/DeferGlobalInitializers.cpp
index b4c742b..1704d41 100644
--- a/src/compiler/translator/DeferGlobalInitializers.cpp
+++ b/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -30,6 +30,8 @@
 namespace
 {
 
+constexpr const ImmutableString kInitGlobalsString("initGlobals");
+
 void GetDeferredInitializers(TIntermDeclaration *declaration,
                              bool initializeUninitializedGlobals,
                              bool canUseLoopsToInitialize,
@@ -102,9 +104,8 @@
     TIntermBlock *initGlobalsBlock = new TIntermBlock();
     initGlobalsBlock->getSequence()->swap(*deferredInitializers);
 
-    TFunction *initGlobalsFunction =
-        new TFunction(symbolTable, NewPoolTString("initGlobals"), new TType(EbtVoid),
-                      SymbolType::AngleInternal, false);
+    TFunction *initGlobalsFunction = new TFunction(
+        symbolTable, kInitGlobalsString, new TType(EbtVoid), SymbolType::AngleInternal, false);
 
     TIntermFunctionPrototype *initGlobalsFunctionPrototype =
         CreateInternalFunctionPrototypeNode(*initGlobalsFunction);
@@ -156,7 +157,7 @@
         TType *replacementType = new TType(var->getType());
         replacementType->setQualifier(EvqGlobal);
         TVariable *replacement =
-            new TVariable(symbolTable, &var->name(), replacementType, var->symbolType());
+            new TVariable(symbolTable, var->name(), replacementType, var->symbolType());
         ReplaceVariable(root, var, replacement);
     }
 }
diff --git a/src/compiler/translator/EmulateGLFragColorBroadcast.cpp b/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
index f1a8d8d..7689f04 100644
--- a/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
+++ b/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
@@ -24,6 +24,8 @@
 namespace
 {
 
+constexpr const ImmutableString kGlFragDataString("gl_FragData");
+
 class GLFragColorBroadcastTraverser : public TIntermTraverser
 {
   public:
@@ -54,7 +56,7 @@
 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
 {
     TIntermSymbol *symbol =
-        ReferenceBuiltInVariable(TString("gl_FragData"), *mSymbolTable, mShaderVersion);
+        ReferenceBuiltInVariable(kGlFragDataString, *mSymbolTable, mShaderVersion);
     TIntermTyped *indexNode = CreateIndexNode(index);
 
     TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
diff --git a/src/compiler/translator/EmulatePrecision.cpp b/src/compiler/translator/EmulatePrecision.cpp
index da3ff5a..d3acfbd 100644
--- a/src/compiler/translator/EmulatePrecision.cpp
+++ b/src/compiler/translator/EmulatePrecision.cpp
@@ -16,6 +16,11 @@
 namespace
 {
 
+constexpr const ImmutableString kParamXName("x");
+constexpr const ImmutableString kParamYName("y");
+constexpr const ImmutableString kAngleFrmString("angle_frm");
+constexpr const ImmutableString kAngleFrlString("angle_frl");
+
 class RoundingHelperWriter : angle::NonCopyable
 {
   public:
@@ -473,10 +478,7 @@
 }  // namespace anonymous
 
 EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable)
-    : TLValueTrackingTraverser(true, true, true, symbolTable),
-      mDeclaringVariables(false),
-      mParamXName(NewPoolTString("x")),
-      mParamYName(NewPoolTString("y"))
+    : TLValueTrackingTraverser(true, true, true, symbolTable), mDeclaringVariables(false)
 {
 }
 
@@ -709,13 +711,13 @@
     }
 }
 
-const TFunction *EmulatePrecision::getInternalFunction(TString *functionName,
+const TFunction *EmulatePrecision::getInternalFunction(const ImmutableString &functionName,
                                                        const TType &returnType,
                                                        TIntermSequence *arguments,
                                                        const TVector<TConstParameter> &parameters,
                                                        bool knownToNotHaveSideEffects)
 {
-    TString mangledName = TFunctionLookup::GetMangledName(*functionName, *arguments);
+    ImmutableString mangledName = TFunctionLookup::GetMangledName(functionName.data(), *arguments);
     if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
     {
         TFunction *func = new TFunction(mSymbolTable, functionName, new TType(returnType),
@@ -732,12 +734,9 @@
 
 TIntermAggregate *EmulatePrecision::createRoundingFunctionCallNode(TIntermTyped *roundedChild)
 {
-    const char *roundFunctionName;
-    if (roundedChild->getPrecision() == EbpMedium)
-        roundFunctionName = "angle_frm";
-    else
-        roundFunctionName = "angle_frl";
-    TString *functionName      = NewPoolTString(roundFunctionName);
+    const ImmutableString *roundFunctionName = &kAngleFrmString;
+    if (roundedChild->getPrecision() == EbpLow)
+        roundFunctionName = &kAngleFrlString;
     TIntermSequence *arguments = new TIntermSequence();
     arguments->push_back(roundedChild);
 
@@ -745,10 +744,11 @@
     TType *paramType = new TType(roundedChild->getType());
     paramType->setPrecision(EbpHigh);
     paramType->setQualifier(EvqIn);
-    parameters.push_back(TConstParameter(mParamXName, static_cast<const TType *>(paramType)));
+    parameters.push_back(TConstParameter(kParamXName, static_cast<const TType *>(paramType)));
 
     return TIntermAggregate::CreateRawFunctionCall(
-        *getInternalFunction(functionName, roundedChild->getType(), arguments, parameters, true),
+        *getInternalFunction(*roundFunctionName, roundedChild->getType(), arguments, parameters,
+                             true),
         arguments);
 }
 
@@ -761,7 +761,7 @@
         strstr << "angle_compound_" << opNameStr << "_frm";
     else
         strstr << "angle_compound_" << opNameStr << "_frl";
-    TString *functionName      = NewPoolTString(strstr.str().c_str());
+    ImmutableString functionName = ImmutableString(strstr.str());
     TIntermSequence *arguments = new TIntermSequence();
     arguments->push_back(left);
     arguments->push_back(right);
@@ -770,11 +770,11 @@
     TType *leftParamType = new TType(left->getType());
     leftParamType->setPrecision(EbpHigh);
     leftParamType->setQualifier(EvqOut);
-    parameters.push_back(TConstParameter(mParamXName, static_cast<const TType *>(leftParamType)));
+    parameters.push_back(TConstParameter(kParamXName, static_cast<const TType *>(leftParamType)));
     TType *rightParamType = new TType(right->getType());
     rightParamType->setPrecision(EbpHigh);
     rightParamType->setQualifier(EvqIn);
-    parameters.push_back(TConstParameter(mParamYName, static_cast<const TType *>(rightParamType)));
+    parameters.push_back(TConstParameter(kParamYName, static_cast<const TType *>(rightParamType)));
 
     return TIntermAggregate::CreateRawFunctionCall(
         *getInternalFunction(functionName, left->getType(), arguments, parameters, false),
diff --git a/src/compiler/translator/EmulatePrecision.h b/src/compiler/translator/EmulatePrecision.h
index c8eadc1..5010882 100644
--- a/src/compiler/translator/EmulatePrecision.h
+++ b/src/compiler/translator/EmulatePrecision.h
@@ -59,7 +59,7 @@
         }
     };
 
-    const TFunction *getInternalFunction(TString *functionName,
+    const TFunction *getInternalFunction(const ImmutableString &functionName,
                                          const TType &returnType,
                                          TIntermSequence *arguments,
                                          const TVector<TConstParameter> &parameters,
@@ -76,12 +76,9 @@
     EmulationSet mEmulateCompoundDiv;
 
     // Map from mangled name to function.
-    TMap<TString, const TFunction *> mInternalFunctions;
+    TMap<ImmutableString, const TFunction *> mInternalFunctions;
 
     bool mDeclaringVariables;
-
-    const TString *mParamXName;
-    const TString *mParamYName;
 };
 
 }  // namespace sh
diff --git a/src/compiler/translator/FindSymbolNode.cpp b/src/compiler/translator/FindSymbolNode.cpp
index a1e5634..f63acf5 100644
--- a/src/compiler/translator/FindSymbolNode.cpp
+++ b/src/compiler/translator/FindSymbolNode.cpp
@@ -8,6 +8,7 @@
 
 #include "compiler/translator/FindSymbolNode.h"
 
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/IntermTraverse.h"
 #include "compiler/translator/Symbol.h"
 
@@ -20,7 +21,7 @@
 class SymbolFinder : public TIntermTraverser
 {
   public:
-    SymbolFinder(const TString &symbolName)
+    SymbolFinder(const ImmutableString &symbolName)
         : TIntermTraverser(true, false, false), mSymbolName(symbolName), mNodeFound(nullptr)
     {
     }
@@ -37,13 +38,13 @@
     const TIntermSymbol *getNode() const { return mNodeFound; }
 
   private:
-    TString mSymbolName;
+    ImmutableString mSymbolName;
     TIntermSymbol *mNodeFound;
 };
 
 }  // anonymous namespace
 
-const TIntermSymbol *FindSymbolNode(TIntermNode *root, const TString &symbolName)
+const TIntermSymbol *FindSymbolNode(TIntermNode *root, const ImmutableString &symbolName)
 {
     SymbolFinder finder(symbolName);
     root->traverse(&finder);
diff --git a/src/compiler/translator/FindSymbolNode.h b/src/compiler/translator/FindSymbolNode.h
index 7f65449..0e6f8cb 100644
--- a/src/compiler/translator/FindSymbolNode.h
+++ b/src/compiler/translator/FindSymbolNode.h
@@ -9,16 +9,14 @@
 #ifndef COMPILER_TRANSLATOR_FIND_SYMBOL_H_
 #define COMPILER_TRANSLATOR_FIND_SYMBOL_H_
 
-#include "compiler/translator/BaseTypes.h"
-#include "compiler/translator/Common.h"
-
 namespace sh
 {
 
+class ImmutableString;
 class TIntermNode;
 class TIntermSymbol;
 
-const TIntermSymbol *FindSymbolNode(TIntermNode *root, const TString &symbolName);
+const TIntermSymbol *FindSymbolNode(TIntermNode *root, const ImmutableString &symbolName);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/FunctionLookup.cpp b/src/compiler/translator/FunctionLookup.cpp
index ee575a8..1a81f9a 100644
--- a/src/compiler/translator/FunctionLookup.cpp
+++ b/src/compiler/translator/FunctionLookup.cpp
@@ -17,9 +17,11 @@
 
 const char kFunctionMangledNameSeparator = '(';
 
+constexpr const ImmutableString kEmptyName("");
+
 }  // anonymous namespace
 
-TFunctionLookup::TFunctionLookup(const TString *name, const TType *constructorType)
+TFunctionLookup::TFunctionLookup(const ImmutableString &name, const TType *constructorType)
     : mName(name), mConstructorType(constructorType), mThisNode(nullptr)
 {
 }
@@ -28,37 +30,37 @@
 TFunctionLookup *TFunctionLookup::CreateConstructor(const TType *type)
 {
     ASSERT(type != nullptr);
-    return new TFunctionLookup(nullptr, type);
+    return new TFunctionLookup(kEmptyName, type);
 }
 
 // static
-TFunctionLookup *TFunctionLookup::CreateFunctionCall(const TString *name)
+TFunctionLookup *TFunctionLookup::CreateFunctionCall(const ImmutableString &name)
 {
-    ASSERT(name != nullptr);
+    ASSERT(name != "");
     return new TFunctionLookup(name, nullptr);
 }
 
-const TString &TFunctionLookup::name() const
+const ImmutableString &TFunctionLookup::name() const
 {
-    return *mName;
+    return mName;
 }
 
-const TString &TFunctionLookup::getMangledName() const
+ImmutableString TFunctionLookup::getMangledName() const
 {
-    return GetMangledName(*mName, mArguments);
+    return GetMangledName(mName.data(), mArguments);
 }
 
-const TString &TFunctionLookup::GetMangledName(const TString &functionName,
-                                               const TIntermSequence &arguments)
+ImmutableString TFunctionLookup::GetMangledName(const char *functionName,
+                                                const TIntermSequence &arguments)
 {
-    std::string newName = functionName.c_str();
+    std::string newName(functionName);
     newName += kFunctionMangledNameSeparator;
 
     for (TIntermNode *argument : arguments)
     {
         newName += argument->getAsTyped()->getType().getMangledName();
     }
-    return *NewPoolTString(newName.c_str());
+    return ImmutableString(newName);
 }
 
 bool TFunctionLookup::isConstructor() const
diff --git a/src/compiler/translator/FunctionLookup.h b/src/compiler/translator/FunctionLookup.h
index 1a62357..ed14822 100644
--- a/src/compiler/translator/FunctionLookup.h
+++ b/src/compiler/translator/FunctionLookup.h
@@ -9,6 +9,7 @@
 #ifndef COMPILER_TRANSLATOR_FUNCTIONLOOKUP_H_
 #define COMPILER_TRANSLATOR_FUNCTIONLOOKUP_H_
 
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/IntermNode.h"
 
 namespace sh
@@ -21,12 +22,12 @@
     POOL_ALLOCATOR_NEW_DELETE();
 
     static TFunctionLookup *CreateConstructor(const TType *type);
-    static TFunctionLookup *CreateFunctionCall(const TString *name);
+    static TFunctionLookup *CreateFunctionCall(const ImmutableString &name);
 
-    const TString &name() const;
-    const TString &getMangledName() const;
-    static const TString &GetMangledName(const TString &functionName,
-                                         const TIntermSequence &arguments);
+    const ImmutableString &name() const;
+    ImmutableString getMangledName() const;
+    static ImmutableString GetMangledName(const char *functionName,
+                                          const TIntermSequence &arguments);
 
     bool isConstructor() const;
     const TType &constructorType() const;
@@ -38,9 +39,9 @@
     TIntermSequence &arguments();
 
   private:
-    TFunctionLookup(const TString *name, const TType *constructorType);
+    TFunctionLookup(const ImmutableString &name, const TType *constructorType);
 
-    const TString *mName;
+    const ImmutableString mName;
     const TType *const mConstructorType;
     TIntermTyped *mThisNode;
     TIntermSequence mArguments;
diff --git a/src/compiler/translator/HashNames.cpp b/src/compiler/translator/HashNames.cpp
index df448d8..6f7a419 100644
--- a/src/compiler/translator/HashNames.cpp
+++ b/src/compiler/translator/HashNames.cpp
@@ -6,6 +6,8 @@
 
 #include "compiler/translator/HashNames.h"
 
+#include "compiler/translator/ImmutableString.h"
+#include "compiler/translator/ImmutableStringBuilder.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/Symbol.h"
 
@@ -18,59 +20,72 @@
 // GLSL ES 3.00.6 section 3.9: the maximum length of an identifier is 1024 characters.
 static const unsigned int kESSLMaxIdentifierLength = 1024u;
 
-static const char *kHashedNamePrefix = "webgl_";
+constexpr const ImmutableString kHashedNamePrefix("webgl_");
 
 // Can't prefix with just _ because then we might introduce a double underscore, which is not safe
 // in GLSL (ESSL 3.00.6 section 3.8: All identifiers containing a double underscore are reserved for
 // use by the underlying implementation). u is short for user-defined.
-static const char *kUnhashedNamePrefix              = "_u";
-static const unsigned int kUnhashedNamePrefixLength = 2u;
+constexpr const ImmutableString kUnhashedNamePrefix("_u");
 
-TString HashName(const TString &name, ShHashFunction64 hashFunction)
+ImmutableString HashName(const ImmutableString &name, ShHashFunction64 hashFunction)
 {
     ASSERT(!name.empty());
     ASSERT(hashFunction);
-    khronos_uint64_t number = (*hashFunction)(name.c_str(), name.length());
-    TStringStream stream;
-    stream << kHashedNamePrefix << std::hex << number;
-    TString hashedName = stream.str();
+    khronos_uint64_t number = (*hashFunction)(name.data(), name.length());
+
+    // Build the hashed name in place.
+    static const unsigned int kHexStrMaxLength = sizeof(number) * 2;
+    static const size_t kHashedNameMaxLength   = kHashedNamePrefix.length() + kHexStrMaxLength;
+
+    ImmutableStringBuilder hashedName(kHashedNameMaxLength);
+    hashedName << kHashedNamePrefix;
+
+    hashedName.appendHex(number);
+
     return hashedName;
 }
 
 }  // anonymous namespace
 
-TString HashName(const TString &name, ShHashFunction64 hashFunction, NameMap *nameMap)
+ImmutableString HashName(const ImmutableString &name,
+                         ShHashFunction64 hashFunction,
+                         NameMap *nameMap)
 {
     if (hashFunction == nullptr)
     {
-        if (name.length() + kUnhashedNamePrefixLength > kESSLMaxIdentifierLength)
+        if (name.length() + kUnhashedNamePrefix.length() > kESSLMaxIdentifierLength)
         {
             // If the identifier length is already close to the limit, we can't prefix it. This is
             // not a problem since there are no builtins or ANGLE's internal variables that would
             // have as long names and could conflict.
             return name;
         }
-        return kUnhashedNamePrefix + name;
+        ImmutableStringBuilder prefixedName(kUnhashedNamePrefix.length() + name.length());
+        prefixedName << kUnhashedNamePrefix << name;
+        return prefixedName;
     }
     if (nameMap)
     {
-        NameMap::const_iterator it = nameMap->find(name.c_str());
+        NameMap::const_iterator it = nameMap->find(name.data());
         if (it != nameMap->end())
-            return it->second.c_str();
+        {
+            // TODO(oetuaho): Would be nice if we didn't need to allocate a string here.
+            return ImmutableString(it->second);
+        }
     }
-    TString hashedName = HashName(name, hashFunction);
+    ImmutableString hashedName = HashName(name, hashFunction);
     if (nameMap)
     {
-        (*nameMap)[name.c_str()] = hashedName.c_str();
+        (*nameMap)[name.data()] = hashedName.data();
     }
     return hashedName;
 }
 
-TString HashName(const TSymbol *symbol, ShHashFunction64 hashFunction, NameMap *nameMap)
+ImmutableString HashName(const TSymbol *symbol, ShHashFunction64 hashFunction, NameMap *nameMap)
 {
     if (symbol->symbolType() == SymbolType::Empty)
     {
-        return TString();
+        return ImmutableString("");
     }
     if (symbol->symbolType() == SymbolType::AngleInternal ||
         symbol->symbolType() == SymbolType::BuiltIn)
diff --git a/src/compiler/translator/HashNames.h b/src/compiler/translator/HashNames.h
index 549dab3..132cb59 100644
--- a/src/compiler/translator/HashNames.h
+++ b/src/compiler/translator/HashNames.h
@@ -17,13 +17,16 @@
 
 typedef std::map<TPersistString, TPersistString> NameMap;
 
+class ImmutableString;
 class TSymbol;
 
-TString HashName(const TString &name, ShHashFunction64 hashFunction, NameMap *nameMap);
+ImmutableString HashName(const ImmutableString &name,
+                         ShHashFunction64 hashFunction,
+                         NameMap *nameMap);
 
 // Hash user-defined name for GLSL output, with special handling for internal names.
 // The nameMap parameter is optional and is used to cache hashed names if set.
-TString HashName(const TSymbol *symbol, ShHashFunction64 hashFunction, NameMap *nameMap);
+ImmutableString HashName(const TSymbol *symbol, ShHashFunction64 hashFunction, NameMap *nameMap);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/ImageFunctionHLSL.cpp b/src/compiler/translator/ImageFunctionHLSL.cpp
index 40b5e1f..b3c3931 100644
--- a/src/compiler/translator/ImageFunctionHLSL.cpp
+++ b/src/compiler/translator/ImageFunctionHLSL.cpp
@@ -239,7 +239,7 @@
            std::tie(rhs.image, rhs.imageInternalFormat, rhs.readonly, rhs.method);
 }
 
-TString ImageFunctionHLSL::useImageFunction(const TString &name,
+TString ImageFunctionHLSL::useImageFunction(const ImmutableString &name,
                                             const TBasicType &type,
                                             TLayoutImageInternalFormat imageInternalFormat,
                                             bool readonly)
diff --git a/src/compiler/translator/ImageFunctionHLSL.h b/src/compiler/translator/ImageFunctionHLSL.h
index 9db17a6..4ff0110 100644
--- a/src/compiler/translator/ImageFunctionHLSL.h
+++ b/src/compiler/translator/ImageFunctionHLSL.h
@@ -25,7 +25,7 @@
   public:
     // Returns the name of the image function implementation to caller.
     // The name that's passed in is the name of the GLSL image function that it should implement.
-    TString useImageFunction(const TString &name,
+    TString useImageFunction(const ImmutableString &name,
                              const TBasicType &type,
                              TLayoutImageInternalFormat imageInternalFormat,
                              bool readonly);
diff --git a/src/compiler/translator/ImmutableString.cpp b/src/compiler/translator/ImmutableString.cpp
index 93dad1b..6bb26af 100644
--- a/src/compiler/translator/ImmutableString.cpp
+++ b/src/compiler/translator/ImmutableString.cpp
@@ -13,3 +13,26 @@
 {
     return os.write(str.data(), str.length());
 }
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4309)  // truncation of constant value
+#endif
+
+namespace sh
+{
+
+template <>
+const size_t ImmutableString::FowlerNollVoHash<4>::kFnvPrime = 16777619u;
+
+template <>
+const size_t ImmutableString::FowlerNollVoHash<4>::kFnvOffsetBasis = 0x811c9dc5u;
+
+template <>
+const size_t ImmutableString::FowlerNollVoHash<8>::kFnvPrime =
+    static_cast<size_t>(1099511628211ull);
+
+template <>
+const size_t ImmutableString::FowlerNollVoHash<8>::kFnvOffsetBasis =
+    static_cast<size_t>(0xcbf29ce484222325ull);
+
+}  // namespace sh
diff --git a/src/compiler/translator/ImmutableString.h b/src/compiler/translator/ImmutableString.h
index a33fec9..2caef8d 100644
--- a/src/compiler/translator/ImmutableString.h
+++ b/src/compiler/translator/ImmutableString.h
@@ -12,6 +12,7 @@
 
 #include <string>
 
+#include "common/string_utils.h"
 #include "compiler/translator/Common.h"
 
 namespace sh
@@ -40,20 +41,60 @@
     // The data pointer passed in must be one of:
     //  1. nullptr (only valid with length 0).
     //  2. a null-terminated static char array like a string literal.
-    //  3. a null-terminated pool allocated char array.
+    //  3. a null-terminated pool allocated char array. This can't be c_str() of a local TString,
+    //     since when a TString goes out of scope it clears its first character.
     explicit constexpr ImmutableString(const char *data) : mData(data), mLength(constStrlen(data))
     {
     }
 
     constexpr ImmutableString(const char *data, size_t length) : mData(data), mLength(length) {}
 
+    ImmutableString(const std::string &str)
+        : mData(AllocatePoolCharArray(str.c_str(), str.size())), mLength(str.size())
+    {
+    }
+
     ImmutableString(const ImmutableString &) = default;
     ImmutableString &operator=(const ImmutableString &) = default;
 
-    const char *data() const { return mData ? mData : ""; }
-    size_t length() const { return mLength; }
+    constexpr const char *data() const { return mData ? mData : ""; }
+    constexpr size_t length() const { return mLength; }
 
-    bool operator<(const ImmutableString &b) const
+    char operator[](size_t index) const { return data()[index]; }
+
+    constexpr bool empty() const { return mLength == 0; }
+    bool beginsWith(const char *prefix) const { return angle::BeginsWith(data(), prefix); }
+    constexpr bool beginsWith(const ImmutableString &prefix) const
+    {
+        return mLength >= prefix.length() && memcmp(data(), prefix.data(), prefix.length()) == 0;
+    }
+    bool contains(const char *substr) const { return strstr(data(), substr) != nullptr; }
+
+    constexpr bool operator==(const ImmutableString &b) const
+    {
+        if (mLength != b.mLength)
+        {
+            return false;
+        }
+        return memcmp(data(), b.data(), mLength) == 0;
+    }
+    constexpr bool operator!=(const ImmutableString &b) const { return !(*this == b); }
+    constexpr bool operator==(const char *b) const
+    {
+        if (b == nullptr)
+        {
+            return empty();
+        }
+        return strcmp(data(), b) == 0;
+    }
+    constexpr bool operator!=(const char *b) const { return !(*this == b); }
+    bool operator==(const std::string &b) const
+    {
+        return mLength == b.length() && memcmp(data(), b.c_str(), mLength) == 0;
+    }
+    bool operator!=(const std::string &b) const { return !(*this == b); }
+
+    constexpr bool operator<(const ImmutableString &b) const
     {
         if (mLength < b.mLength)
         {
@@ -66,6 +107,26 @@
         return (memcmp(data(), b.data(), mLength) < 0);
     }
 
+    template <size_t hashBytes>
+    struct FowlerNollVoHash
+    {
+        static const size_t kFnvOffsetBasis;
+        static const size_t kFnvPrime;
+
+        constexpr size_t operator()(const ImmutableString &a) const
+        {
+            const char *data = a.data();
+            size_t hash      = kFnvOffsetBasis;
+            while ((*data) != '\0')
+            {
+                hash = hash ^ (*data);
+                hash = hash * kFnvPrime;
+                ++data;
+            }
+            return hash;
+        }
+    };
+
   private:
     const char *mData;
     size_t mLength;
diff --git a/src/compiler/translator/ImmutableStringBuilder.h b/src/compiler/translator/ImmutableStringBuilder.h
index 94dcc3b..5580f20 100644
--- a/src/compiler/translator/ImmutableStringBuilder.h
+++ b/src/compiler/translator/ImmutableStringBuilder.h
@@ -32,6 +32,28 @@
     // This invalidates the ImmutableStringBuilder, so it should only be called once.
     operator ImmutableString();
 
+    template <typename T>
+    void appendHex(T number)
+    {
+        ASSERT(mData != nullptr);
+        ASSERT(mPos + sizeof(T) * 2u <= mMaxLength);
+        int index = static_cast<int>(sizeof(T)) * 2 - 1;
+        // Loop through leading zeroes.
+        while (((number >> (index * 4)) & 0xfu) == 0 && index > 0)
+        {
+            --index;
+        }
+        // Write the rest of the hex digits.
+        while (index >= 0)
+        {
+            char digit     = static_cast<char>((number >> (index * 4)) & 0xfu);
+            char digitChar = digit < 10 ? digit + '0' : digit + 'a';
+            mData[mPos++]  = digitChar;
+            --index;
+        }
+        return;
+    }
+
   private:
     inline static char *AllocateEmptyPoolCharArray(size_t strLength)
     {
diff --git a/src/compiler/translator/InfoSink.h b/src/compiler/translator/InfoSink.h
index 3f54f4a..ce326ce 100644
--- a/src/compiler/translator/InfoSink.h
+++ b/src/compiler/translator/InfoSink.h
@@ -24,6 +24,8 @@
     return modff(f, &intPart);
 }
 
+class ImmutableString;
+
 //
 // Encapsulate info logs for all objects that have them.
 //
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 91c5d07..0afcf4c 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -748,133 +748,161 @@
     TFieldList *fields       = new TFieldList();
     TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
     auto highpFloat1         = new TType(EbtFloat, EbpHigh, EvqGlobal, 1);
-    TField *near             = new TField(highpFloat1, NewPoolTString("near"), zeroSourceLoc);
-    TField *far              = new TField(highpFloat1, NewPoolTString("far"), zeroSourceLoc);
-    TField *diff             = new TField(highpFloat1, NewPoolTString("diff"), zeroSourceLoc);
+    TField *near             = new TField(highpFloat1, ImmutableString("near"), zeroSourceLoc);
+    TField *far              = new TField(highpFloat1, ImmutableString("far"), zeroSourceLoc);
+    TField *diff             = new TField(highpFloat1, ImmutableString("diff"), zeroSourceLoc);
     fields->push_back(near);
     fields->push_back(far);
     fields->push_back(diff);
     TStructure *depthRangeStruct = new TStructure(
-        &symbolTable, NewPoolTString("gl_DepthRangeParameters"), fields, SymbolType::BuiltIn);
+        &symbolTable, ImmutableString("gl_DepthRangeParameters"), fields, SymbolType::BuiltIn);
     symbolTable.insertStructType(COMMON_BUILTINS, depthRangeStruct);
     TType *depthRangeType = new TType(depthRangeStruct);
     depthRangeType->setQualifier(EvqUniform);
     depthRangeType->realize();
-    symbolTable.insertVariable(COMMON_BUILTINS, "gl_DepthRange", depthRangeType);
+    symbolTable.insertVariable(COMMON_BUILTINS, ImmutableString("gl_DepthRange"), depthRangeType);
 
     //
     // Implementation dependent built-in constants.
     //
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxVertexAttribs",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, ImmutableString("gl_MaxVertexAttribs"),
                                           resources.MaxVertexAttribs);
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxVertexUniformVectors",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS,
+                                          ImmutableString("gl_MaxVertexUniformVectors"),
                                           resources.MaxVertexUniformVectors);
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxVertexTextureImageUnits",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS,
+                                          ImmutableString("gl_MaxVertexTextureImageUnits"),
                                           resources.MaxVertexTextureImageUnits);
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxCombinedTextureImageUnits",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS,
+                                          ImmutableString("gl_MaxCombinedTextureImageUnits"),
                                           resources.MaxCombinedTextureImageUnits);
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxTextureImageUnits",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS,
+                                          ImmutableString("gl_MaxTextureImageUnits"),
                                           resources.MaxTextureImageUnits);
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxFragmentUniformVectors",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS,
+                                          ImmutableString("gl_MaxFragmentUniformVectors"),
                                           resources.MaxFragmentUniformVectors);
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL1_BUILTINS, "gl_MaxVaryingVectors",
+    symbolTable.insertConstInt<EbpMedium>(ESSL1_BUILTINS, ImmutableString("gl_MaxVaryingVectors"),
                                           resources.MaxVaryingVectors);
 
-    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, "gl_MaxDrawBuffers",
+    symbolTable.insertConstInt<EbpMedium>(COMMON_BUILTINS, ImmutableString("gl_MaxDrawBuffers"),
                                           resources.MaxDrawBuffers);
     if (resources.EXT_blend_func_extended)
     {
         symbolTable.insertConstIntExt<EbpMedium>(
-            COMMON_BUILTINS, TExtension::EXT_blend_func_extended, "gl_MaxDualSourceDrawBuffersEXT",
-            resources.MaxDualSourceDrawBuffers);
+            COMMON_BUILTINS, TExtension::EXT_blend_func_extended,
+            ImmutableString("gl_MaxDualSourceDrawBuffersEXT"), resources.MaxDualSourceDrawBuffers);
     }
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS,
+                                          ImmutableString("gl_MaxVertexOutputVectors"),
                                           resources.MaxVertexOutputVectors);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS, "gl_MaxFragmentInputVectors",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS,
+                                          ImmutableString("gl_MaxFragmentInputVectors"),
                                           resources.MaxFragmentInputVectors);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS, "gl_MinProgramTexelOffset",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS,
+                                          ImmutableString("gl_MinProgramTexelOffset"),
                                           resources.MinProgramTexelOffset);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS, "gl_MaxProgramTexelOffset",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_BUILTINS,
+                                          ImmutableString("gl_MaxProgramTexelOffset"),
                                           resources.MaxProgramTexelOffset);
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxImageUnits",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, ImmutableString("gl_MaxImageUnits"),
                                           resources.MaxImageUnits);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxVertexImageUniforms",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxVertexImageUniforms"),
                                           resources.MaxVertexImageUniforms);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxFragmentImageUniforms",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxFragmentImageUniforms"),
                                           resources.MaxFragmentImageUniforms);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxComputeImageUniforms",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeImageUniforms"),
                                           resources.MaxComputeImageUniforms);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxCombinedImageUniforms",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxCombinedImageUniforms"),
                                           resources.MaxCombinedImageUniforms);
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxCombinedShaderOutputResources",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxCombinedShaderOutputResources"),
                                           resources.MaxCombinedShaderOutputResources);
 
-    symbolTable.insertConstIvec3<EbpHigh>(ESSL3_1_BUILTINS, "gl_MaxComputeWorkGroupCount",
+    symbolTable.insertConstIvec3<EbpHigh>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeWorkGroupCount"),
                                           resources.MaxComputeWorkGroupCount);
-    symbolTable.insertConstIvec3<EbpHigh>(ESSL3_1_BUILTINS, "gl_MaxComputeWorkGroupSize",
+    symbolTable.insertConstIvec3<EbpHigh>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeWorkGroupSize"),
                                           resources.MaxComputeWorkGroupSize);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxComputeUniformComponents",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeUniformComponents"),
                                           resources.MaxComputeUniformComponents);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxComputeTextureImageUnits",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeTextureImageUnits"),
                                           resources.MaxComputeTextureImageUnits);
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxComputeAtomicCounters",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeAtomicCounters"),
                                           resources.MaxComputeAtomicCounters);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxComputeAtomicCounterBuffers",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxComputeAtomicCounterBuffers"),
                                           resources.MaxComputeAtomicCounterBuffers);
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxVertexAtomicCounters",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxVertexAtomicCounters"),
                                           resources.MaxVertexAtomicCounters);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxFragmentAtomicCounters",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxFragmentAtomicCounters"),
                                           resources.MaxFragmentAtomicCounters);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxCombinedAtomicCounters",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxCombinedAtomicCounters"),
                                           resources.MaxCombinedAtomicCounters);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBindings",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxAtomicCounterBindings"),
                                           resources.MaxAtomicCounterBindings);
 
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxVertexAtomicCounterBuffers",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxVertexAtomicCounterBuffers"),
                                           resources.MaxVertexAtomicCounterBuffers);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxFragmentAtomicCounterBuffers",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxFragmentAtomicCounterBuffers"),
                                           resources.MaxFragmentAtomicCounterBuffers);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxCombinedAtomicCounterBuffers",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxCombinedAtomicCounterBuffers"),
                                           resources.MaxCombinedAtomicCounterBuffers);
-    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBufferSize",
+    symbolTable.insertConstInt<EbpMedium>(ESSL3_1_BUILTINS,
+                                          ImmutableString("gl_MaxAtomicCounterBufferSize"),
                                           resources.MaxAtomicCounterBufferSize);
 
     if (resources.EXT_geometry_shader)
     {
         TExtension ext = TExtension::EXT_geometry_shader;
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryInputComponents",
+                                                 ImmutableString("gl_MaxGeometryInputComponents"),
                                                  resources.MaxGeometryInputComponents);
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryOutputComponents",
+                                                 ImmutableString("gl_MaxGeometryOutputComponents"),
                                                  resources.MaxGeometryOutputComponents);
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryImageUniforms",
+                                                 ImmutableString("gl_MaxGeometryImageUniforms"),
                                                  resources.MaxGeometryImageUniforms);
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryTextureImageUnits",
+                                                 ImmutableString("gl_MaxGeometryTextureImageUnits"),
                                                  resources.MaxGeometryTextureImageUnits);
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryOutputVertices",
+                                                 ImmutableString("gl_MaxGeometryOutputVertices"),
                                                  resources.MaxGeometryOutputVertices);
+        symbolTable.insertConstIntExt<EbpMedium>(
+            ESSL3_1_BUILTINS, ext, ImmutableString("gl_MaxGeometryTotalOutputComponents"),
+            resources.MaxGeometryTotalOutputComponents);
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryTotalOutputComponents",
-                                                 resources.MaxGeometryTotalOutputComponents);
-        symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryUniformComponents",
+                                                 ImmutableString("gl_MaxGeometryUniformComponents"),
                                                  resources.MaxGeometryUniformComponents);
         symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryAtomicCounters",
+                                                 ImmutableString("gl_MaxGeometryAtomicCounters"),
                                                  resources.MaxGeometryAtomicCounters);
-        symbolTable.insertConstIntExt<EbpMedium>(ESSL3_1_BUILTINS, ext,
-                                                 "gl_MaxGeometryAtomicCounterBuffers",
-                                                 resources.MaxGeometryAtomicCounterBuffers);
+        symbolTable.insertConstIntExt<EbpMedium>(
+            ESSL3_1_BUILTINS, ext, ImmutableString("gl_MaxGeometryAtomicCounterBuffers"),
+            resources.MaxGeometryAtomicCounterBuffers);
     }
 }
 
@@ -891,14 +919,14 @@
     if (resources.OVR_multiview && type != GL_COMPUTE_SHADER)
     {
         const TType *viewIDType = StaticType::Get<EbtUInt, EbpHigh, EvqViewIDOVR, 1, 1>();
-        symbolTable.insertVariableExt(ESSL3_BUILTINS, TExtension::OVR_multiview, "gl_ViewID_OVR",
-                                      viewIDType);
+        symbolTable.insertVariableExt(ESSL3_BUILTINS, TExtension::OVR_multiview,
+                                      ImmutableString("gl_ViewID_OVR"), viewIDType);
 
         // ESSL 1.00 doesn't have unsigned integers, so gl_ViewID_OVR is a signed integer in ESSL
         // 1.00. This is specified in the WEBGL_multiview spec.
         const TType *viewIDIntType = StaticType::Get<EbtInt, EbpHigh, EvqViewIDOVR, 1, 1>();
-        symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::OVR_multiview, "gl_ViewID_OVR",
-                                      viewIDIntType);
+        symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::OVR_multiview,
+                                      ImmutableString("gl_ViewID_OVR"), viewIDIntType);
     }
 
     const TType *positionType    = StaticType::Get<EbtFloat, EbpHigh, EvqPosition, 4, 1>();
@@ -910,15 +938,19 @@
         case GL_FRAGMENT_SHADER:
         {
             const TType *fragCoordType = StaticType::Get<EbtFloat, EbpMedium, EvqFragCoord, 4, 1>();
-            symbolTable.insertVariable(COMMON_BUILTINS, "gl_FragCoord", fragCoordType);
+            symbolTable.insertVariable(COMMON_BUILTINS, ImmutableString("gl_FragCoord"),
+                                       fragCoordType);
             const TType *frontFacingType = StaticType::GetQualified<EbtBool, EvqFrontFacing>();
-            symbolTable.insertVariable(COMMON_BUILTINS, "gl_FrontFacing", frontFacingType);
+            symbolTable.insertVariable(COMMON_BUILTINS, ImmutableString("gl_FrontFacing"),
+                                       frontFacingType);
             const TType *pointCoordType =
                 StaticType::Get<EbtFloat, EbpMedium, EvqPointCoord, 2, 1>();
-            symbolTable.insertVariable(COMMON_BUILTINS, "gl_PointCoord", pointCoordType);
+            symbolTable.insertVariable(COMMON_BUILTINS, ImmutableString("gl_PointCoord"),
+                                       pointCoordType);
 
             const TType *fragColorType = StaticType::Get<EbtFloat, EbpMedium, EvqFragColor, 4, 1>();
-            symbolTable.insertVariable(ESSL1_BUILTINS, "gl_FragColor", fragColorType);
+            symbolTable.insertVariable(ESSL1_BUILTINS, ImmutableString("gl_FragColor"),
+                                       fragColorType);
 
             TType *fragDataType = new TType(EbtFloat, EbpMedium, EvqFragData, 4);
             if (spec != SH_WEBGL2_SPEC && spec != SH_WEBGL3_SPEC)
@@ -930,20 +962,23 @@
                 fragDataType->makeArray(1u);
             }
             fragDataType->realize();
-            symbolTable.insertVariable(ESSL1_BUILTINS, "gl_FragData", fragDataType);
+            symbolTable.insertVariable(ESSL1_BUILTINS, ImmutableString("gl_FragData"),
+                                       fragDataType);
 
             if (resources.EXT_blend_func_extended)
             {
                 const TType *secondaryFragColorType =
                     StaticType::Get<EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4, 1>();
                 symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::EXT_blend_func_extended,
-                                              "gl_SecondaryFragColorEXT", secondaryFragColorType);
+                                              ImmutableString("gl_SecondaryFragColorEXT"),
+                                              secondaryFragColorType);
                 TType *secondaryFragDataType =
                     new TType(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1);
                 secondaryFragDataType->makeArray(resources.MaxDualSourceDrawBuffers);
                 secondaryFragDataType->realize();
                 symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::EXT_blend_func_extended,
-                                              "gl_SecondaryFragDataEXT", secondaryFragDataType);
+                                              ImmutableString("gl_SecondaryFragDataEXT"),
+                                              secondaryFragDataType);
             }
 
             if (resources.EXT_frag_depth)
@@ -953,11 +988,12 @@
                               EvqFragDepthEXT, 1);
                 fragDepthEXTType->realize();
                 symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::EXT_frag_depth,
-                                              "gl_FragDepthEXT", fragDepthEXTType);
+                                              ImmutableString("gl_FragDepthEXT"), fragDepthEXTType);
             }
 
             const TType *fragDepthType = StaticType::Get<EbtFloat, EbpHigh, EvqFragDepth, 1, 1>();
-            symbolTable.insertVariable(ESSL3_BUILTINS, "gl_FragDepth", fragDepthType);
+            symbolTable.insertVariable(ESSL3_BUILTINS, ImmutableString("gl_FragDepth"),
+                                       fragDepthType);
 
             const TType *lastFragColorType =
                 StaticType::Get<EbtFloat, EbpMedium, EvqLastFragColor, 4, 1>();
@@ -970,77 +1006,86 @@
 
                 if (resources.EXT_shader_framebuffer_fetch)
                 {
-                    symbolTable.insertVariableExt(ESSL1_BUILTINS,
-                                                  TExtension::EXT_shader_framebuffer_fetch,
-                                                  "gl_LastFragData", lastFragDataType);
+                    symbolTable.insertVariableExt(
+                        ESSL1_BUILTINS, TExtension::EXT_shader_framebuffer_fetch,
+                        ImmutableString("gl_LastFragData"), lastFragDataType);
                 }
                 else if (resources.NV_shader_framebuffer_fetch)
                 {
-                    symbolTable.insertVariableExt(ESSL1_BUILTINS,
-                                                  TExtension::NV_shader_framebuffer_fetch,
-                                                  "gl_LastFragColor", lastFragColorType);
-                    symbolTable.insertVariableExt(ESSL1_BUILTINS,
-                                                  TExtension::NV_shader_framebuffer_fetch,
-                                                  "gl_LastFragData", lastFragDataType);
+                    symbolTable.insertVariableExt(
+                        ESSL1_BUILTINS, TExtension::NV_shader_framebuffer_fetch,
+                        ImmutableString("gl_LastFragColor"), lastFragColorType);
+                    symbolTable.insertVariableExt(
+                        ESSL1_BUILTINS, TExtension::NV_shader_framebuffer_fetch,
+                        ImmutableString("gl_LastFragData"), lastFragDataType);
                 }
             }
             else if (resources.ARM_shader_framebuffer_fetch)
             {
-                symbolTable.insertVariableExt(ESSL1_BUILTINS,
-                                              TExtension::ARM_shader_framebuffer_fetch,
-                                              "gl_LastFragColorARM", lastFragColorType);
+                symbolTable.insertVariableExt(
+                    ESSL1_BUILTINS, TExtension::ARM_shader_framebuffer_fetch,
+                    ImmutableString("gl_LastFragColorARM"), lastFragColorType);
             }
 
             if (resources.EXT_geometry_shader)
             {
                 TExtension extension = TExtension::EXT_geometry_shader;
-                symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveID",
-                                              primitiveIDType);
-                symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Layer", layerType);
+                symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension,
+                                              ImmutableString("gl_PrimitiveID"), primitiveIDType);
+                symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension,
+                                              ImmutableString("gl_Layer"), layerType);
             }
 
             break;
         }
         case GL_VERTEX_SHADER:
         {
-            symbolTable.insertVariable(COMMON_BUILTINS, "gl_Position", positionType);
+            symbolTable.insertVariable(COMMON_BUILTINS, ImmutableString("gl_Position"),
+                                       positionType);
             const TType *pointSizeType = StaticType::Get<EbtFloat, EbpMedium, EvqPointSize, 1, 1>();
-            symbolTable.insertVariable(COMMON_BUILTINS, "gl_PointSize", pointSizeType);
+            symbolTable.insertVariable(COMMON_BUILTINS, ImmutableString("gl_PointSize"),
+                                       pointSizeType);
             const TType *instanceIDType = StaticType::Get<EbtInt, EbpHigh, EvqInstanceID, 1, 1>();
-            symbolTable.insertVariable(ESSL3_BUILTINS, "gl_InstanceID", instanceIDType);
+            symbolTable.insertVariable(ESSL3_BUILTINS, ImmutableString("gl_InstanceID"),
+                                       instanceIDType);
             const TType *vertexIDType = StaticType::Get<EbtInt, EbpHigh, EvqVertexID, 1, 1>();
-            symbolTable.insertVariable(ESSL3_BUILTINS, "gl_VertexID", vertexIDType);
+            symbolTable.insertVariable(ESSL3_BUILTINS, ImmutableString("gl_VertexID"),
+                                       vertexIDType);
 
             // For internal use by ANGLE - not exposed to the parser.
             const TType *viewportIndexType =
                 StaticType::Get<EbtInt, EbpHigh, EvqViewportIndex, 1, 1>();
-            symbolTable.insertVariable(GLSL_BUILTINS, "gl_ViewportIndex", viewportIndexType);
+            symbolTable.insertVariable(GLSL_BUILTINS, ImmutableString("gl_ViewportIndex"),
+                                       viewportIndexType);
             // gl_Layer exists in other shader stages in ESSL, but not in vertex shader so far.
-            symbolTable.insertVariable(GLSL_BUILTINS, "gl_Layer", layerType);
+            symbolTable.insertVariable(GLSL_BUILTINS, ImmutableString("gl_Layer"), layerType);
             break;
         }
         case GL_COMPUTE_SHADER:
         {
             const TType *numWorkGroupsType =
                 StaticType::Get<EbtUInt, EbpUndefined, EvqNumWorkGroups, 3, 1>();
-            symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_NumWorkGroups", numWorkGroupsType);
+            symbolTable.insertVariable(ESSL3_1_BUILTINS, ImmutableString("gl_NumWorkGroups"),
+                                       numWorkGroupsType);
             const TType *workGroupSizeType =
                 StaticType::Get<EbtUInt, EbpUndefined, EvqWorkGroupSize, 3, 1>();
-            symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_WorkGroupSize", workGroupSizeType);
+            symbolTable.insertVariable(ESSL3_1_BUILTINS, ImmutableString("gl_WorkGroupSize"),
+                                       workGroupSizeType);
             const TType *workGroupIDType =
                 StaticType::Get<EbtUInt, EbpUndefined, EvqWorkGroupID, 3, 1>();
-            symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_WorkGroupID", workGroupIDType);
+            symbolTable.insertVariable(ESSL3_1_BUILTINS, ImmutableString("gl_WorkGroupID"),
+                                       workGroupIDType);
             const TType *localInvocationIDType =
                 StaticType::Get<EbtUInt, EbpUndefined, EvqLocalInvocationID, 3, 1>();
-            symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_LocalInvocationID",
+            symbolTable.insertVariable(ESSL3_1_BUILTINS, ImmutableString("gl_LocalInvocationID"),
                                        localInvocationIDType);
             const TType *globalInvocationIDType =
                 StaticType::Get<EbtUInt, EbpUndefined, EvqGlobalInvocationID, 3, 1>();
-            symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_GlobalInvocationID",
+            symbolTable.insertVariable(ESSL3_1_BUILTINS, ImmutableString("gl_GlobalInvocationID"),
                                        globalInvocationIDType);
             const TType *localInvocationIndexType =
                 StaticType::Get<EbtUInt, EbpUndefined, EvqLocalInvocationIndex, 1, 1>();
-            symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_LocalInvocationIndex",
+            symbolTable.insertVariable(ESSL3_1_BUILTINS, ImmutableString("gl_LocalInvocationIndex"),
                                        localInvocationIndexType);
             break;
         }
@@ -1054,10 +1099,10 @@
             TFieldList *glPerVertexFieldList = new TFieldList();
             TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
             TField *glPositionField =
-                new TField(new TType(*positionType), NewPoolTString("gl_Position"), zeroSourceLoc);
+                new TField(new TType(*positionType), ImmutableString("gl_Position"), zeroSourceLoc);
             glPerVertexFieldList->push_back(glPositionField);
 
-            const TString *glPerVertexString = NewPoolTString("gl_PerVertex");
+            const ImmutableString glPerVertexString("gl_PerVertex");
             TInterfaceBlock *glPerVertexInBlock =
                 new TInterfaceBlock(&symbolTable, glPerVertexString, glPerVertexFieldList,
                                     TLayoutQualifier::Create(), SymbolType::BuiltIn, extension);
@@ -1069,7 +1114,8 @@
                 new TType(glPerVertexInBlock, EvqPerVertexIn, TLayoutQualifier::Create());
             glInType->makeArray(0u);
             glInType->realize();
-            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_in", glInType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, ImmutableString("gl_in"),
+                                          glInType);
 
             TInterfaceBlock *glPerVertexOutBlock =
                 new TInterfaceBlock(&symbolTable, glPerVertexString, glPerVertexFieldList,
@@ -1077,20 +1123,21 @@
             TType *glPositionInBlockType = new TType(EbtFloat, EbpHigh, EvqPosition, 4);
             glPositionInBlockType->setInterfaceBlock(glPerVertexOutBlock);
             glPositionInBlockType->realize();
-            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Position",
-                                          glPositionInBlockType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension,
+                                          ImmutableString("gl_Position"), glPositionInBlockType);
 
             const TType *primitiveIDInType =
                 StaticType::Get<EbtInt, EbpHigh, EvqPrimitiveIDIn, 1, 1>();
-            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveIDIn",
-                                          primitiveIDInType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension,
+                                          ImmutableString("gl_PrimitiveIDIn"), primitiveIDInType);
             const TType *invocationIDType =
                 StaticType::Get<EbtInt, EbpHigh, EvqInvocationID, 1, 1>();
-            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_InvocationID",
-                                          invocationIDType);
-            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveID",
-                                          primitiveIDType);
-            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Layer", layerType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension,
+                                          ImmutableString("gl_InvocationID"), invocationIDType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension,
+                                          ImmutableString("gl_PrimitiveID"), primitiveIDType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, ImmutableString("gl_Layer"),
+                                          layerType);
 
             break;
         }
diff --git a/src/compiler/translator/InitializeVariables.cpp b/src/compiler/translator/InitializeVariables.cpp
index b3035c3..a2f258e 100644
--- a/src/compiler/translator/InitializeVariables.cpp
+++ b/src/compiler/translator/InitializeVariables.cpp
@@ -178,7 +178,8 @@
         TIntermTyped *initializedSymbol = nullptr;
         if (var.isBuiltIn())
         {
-            initializedSymbol = ReferenceBuiltInVariable(name, *symbolTable, shaderVersion);
+            initializedSymbol = ReferenceBuiltInVariable(ImmutableString(name.c_str()),
+                                                         *symbolTable, shaderVersion);
             if (initializedSymbol->getQualifier() == EvqFragData &&
                 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
             {
@@ -194,7 +195,8 @@
         }
         else
         {
-            initializedSymbol = ReferenceGlobalVariable(name, *symbolTable);
+            initializedSymbol =
+                ReferenceGlobalVariable(ImmutableString(name.c_str()), *symbolTable);
         }
         ASSERT(initializedSymbol != nullptr);
 
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 965327c..c40a093 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -18,6 +18,7 @@
 #include "common/mathutil.h"
 #include "common/matrix_utils.h"
 #include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/SymbolTable.h"
 #include "compiler/translator/util.h"
@@ -326,7 +327,7 @@
     return mVariable->uniqueId();
 }
 
-const TString &TIntermSymbol::getName() const
+ImmutableString TIntermSymbol::getName() const
 {
     return mVariable->name();
 }
@@ -503,7 +504,7 @@
     }
     // ESSL 3.0 spec section 8: textureSize always gets highp precision.
     // All other functions that take a sampler are assumed to be texture functions.
-    if (mFunction->name().find("textureSize") == 0)
+    if (mFunction->name() == "textureSize")
         mType.setPrecision(EbpHigh);
     else
         mType.setPrecision(precision);
@@ -517,7 +518,7 @@
         case EOpCallInternalRawFunction:
         case EOpCallBuiltInFunction:
         case EOpCallFunctionInAST:
-            return mFunction->name().c_str();
+            return mFunction->name().data();
         default:
             return GetOperatorString(mOp);
     }
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 764c11e..b7456b7 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -31,6 +31,8 @@
 namespace sh
 {
 
+class ImmutableString;
+
 class TDiagnostics;
 
 class TIntermTraverser;
@@ -248,7 +250,7 @@
     const TType &getType() const override;
 
     const TSymbolUniqueId &uniqueId() const;
-    const TString &getName() const;
+    ImmutableString getName() const;
     const TVariable &variable() const { return *mVariable; }
 
     void traverse(TIntermTraverser *it) override;
diff --git a/src/compiler/translator/IntermNode_util.cpp b/src/compiler/translator/IntermNode_util.cpp
index 1cbbcd4..cbe71ce 100644
--- a/src/compiler/translator/IntermNode_util.cpp
+++ b/src/compiler/translator/IntermNode_util.cpp
@@ -17,13 +17,13 @@
 namespace
 {
 
-const TFunction *LookUpBuiltInFunction(const TString &name,
+const TFunction *LookUpBuiltInFunction(const char *name,
                                        const TIntermSequence *arguments,
                                        const TSymbolTable &symbolTable,
                                        int shaderVersion)
 {
-    const TString &mangledName = TFunctionLookup::GetMangledName(name, *arguments);
-    const TSymbol *symbol = symbolTable.findBuiltIn(mangledName, shaderVersion);
+    const ImmutableString &mangledName = TFunctionLookup::GetMangledName(name, *arguments);
+    const TSymbol *symbol              = symbolTable.findBuiltIn(mangledName, shaderVersion);
     if (symbol)
     {
         ASSERT(symbol->isFunction());
@@ -150,7 +150,7 @@
     ASSERT(symbolTable != nullptr);
     // TODO(oetuaho): Might be useful to sanitize layout qualifier etc. on the type of the created
     // variable. This might need to be done in other places as well.
-    return new TVariable(symbolTable, nullptr, type, SymbolType::AngleInternal);
+    return new TVariable(symbolTable, ImmutableString(""), type, SymbolType::AngleInternal);
 }
 
 TVariable *CreateTempVariable(TSymbolTable *symbolTable, const TType *type, TQualifier qualifier)
@@ -234,14 +234,14 @@
     return blockNode;
 }
 
-TIntermSymbol *ReferenceGlobalVariable(const TString &name, const TSymbolTable &symbolTable)
+TIntermSymbol *ReferenceGlobalVariable(const ImmutableString &name, const TSymbolTable &symbolTable)
 {
     const TVariable *var = reinterpret_cast<const TVariable *>(symbolTable.findGlobal(name));
     ASSERT(var);
     return new TIntermSymbol(var);
 }
 
-TIntermSymbol *ReferenceBuiltInVariable(const TString &name,
+TIntermSymbol *ReferenceBuiltInVariable(const ImmutableString &name,
                                         const TSymbolTable &symbolTable,
                                         int shaderVersion)
 {
@@ -251,7 +251,7 @@
     return new TIntermSymbol(var);
 }
 
-TIntermTyped *CreateBuiltInFunctionCallNode(const TString &name,
+TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
                                             TIntermSequence *arguments,
                                             const TSymbolTable &symbolTable,
                                             int shaderVersion)
diff --git a/src/compiler/translator/IntermNode_util.h b/src/compiler/translator/IntermNode_util.h
index 9f3c0f2..99a6b00 100644
--- a/src/compiler/translator/IntermNode_util.h
+++ b/src/compiler/translator/IntermNode_util.h
@@ -49,14 +49,15 @@
 TIntermBlock *EnsureBlock(TIntermNode *node);
 
 // Should be called from inside Compiler::compileTreeImpl() where the global level is in scope.
-TIntermSymbol *ReferenceGlobalVariable(const TString &name, const TSymbolTable &symbolTable);
+TIntermSymbol *ReferenceGlobalVariable(const ImmutableString &name,
+                                       const TSymbolTable &symbolTable);
 
 // Note: this can access desktop GLSL built-ins that are hidden from the parser.
-TIntermSymbol *ReferenceBuiltInVariable(const TString &name,
+TIntermSymbol *ReferenceBuiltInVariable(const ImmutableString &name,
                                         const TSymbolTable &symbolTable,
                                         int shaderVersion);
 
-TIntermTyped *CreateBuiltInFunctionCallNode(const TString &name,
+TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
                                             TIntermSequence *arguments,
                                             const TSymbolTable &symbolTable,
                                             int shaderVersion);
diff --git a/src/compiler/translator/OutputGLSL.cpp b/src/compiler/translator/OutputGLSL.cpp
index 5e58920..9f8bbce 100644
--- a/src/compiler/translator/OutputGLSL.cpp
+++ b/src/compiler/translator/OutputGLSL.cpp
@@ -49,7 +49,7 @@
     }
 
     // Some built-ins get a special translation.
-    const TString &name = node->getName();
+    const ImmutableString &name = node->getName();
     if (name == "gl_FragDepthEXT")
     {
         out << "gl_FragDepth";
@@ -76,7 +76,7 @@
     }
 }
 
-TString TOutputGLSL::translateTextureFunction(const TString &name)
+ImmutableString TOutputGLSL::translateTextureFunction(const ImmutableString &name)
 {
     static const char *simpleRename[] = {"texture2DLodEXT",
                                          "texture2DLod",
@@ -108,7 +108,7 @@
     {
         if (name == mapping[i])
         {
-            return mapping[i + 1];
+            return ImmutableString(mapping[i + 1]);
         }
     }
 
diff --git a/src/compiler/translator/OutputGLSL.h b/src/compiler/translator/OutputGLSL.h
index c80abec..b676a79 100644
--- a/src/compiler/translator/OutputGLSL.h
+++ b/src/compiler/translator/OutputGLSL.h
@@ -28,7 +28,7 @@
   protected:
     bool writeVariablePrecision(TPrecision) override;
     void visitSymbol(TIntermSymbol *node) override;
-    TString translateTextureFunction(const TString &name) override;
+    ImmutableString translateTextureFunction(const ImmutableString &name) override;
 };
 
 }  // namespace sh
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index b3e8476..2e19264 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -1108,17 +1108,18 @@
     }
 }
 
-TString TOutputGLSLBase::getTypeName(const TType &type)
+ImmutableString TOutputGLSLBase::getTypeName(const TType &type)
 {
     return GetTypeName(type, mHashFunction, &mNameMap);
 }
 
-TString TOutputGLSLBase::hashName(const TSymbol *symbol)
+ImmutableString TOutputGLSLBase::hashName(const TSymbol *symbol)
 {
     return HashName(symbol, mHashFunction, &mNameMap);
 }
 
-TString TOutputGLSLBase::hashFieldName(const TSymbol *containingStruct, const TString &fieldName)
+ImmutableString TOutputGLSLBase::hashFieldName(const TSymbol *containingStruct,
+                                               const ImmutableString &fieldName)
 {
     if (containingStruct->symbolType() == SymbolType::UserDefined ||
         containingStruct->symbolType() == SymbolType::Empty)
@@ -1131,7 +1132,7 @@
     }
 }
 
-TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func)
+ImmutableString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func)
 {
     if (func->isMain())
     {
diff --git a/src/compiler/translator/OutputGLSLBase.h b/src/compiler/translator/OutputGLSLBase.h
index 95a7ed8..f2492e9 100644
--- a/src/compiler/translator/OutputGLSLBase.h
+++ b/src/compiler/translator/OutputGLSLBase.h
@@ -34,7 +34,7 @@
     // Return the original name if hash function pointer is NULL;
     // otherwise return the hashed name. Has special handling for internal names and built-ins,
     // which are not hashed.
-    TString hashName(const TSymbol *symbol);
+    ImmutableString hashName(const TSymbol *symbol);
 
   protected:
     TInfoSinkBase &objSink() { return mObjSink; }
@@ -47,7 +47,7 @@
     void writeFunctionParameters(const TIntermSequence &args);
     const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion);
     void writeConstructorTriplet(Visit visit, const TType &type);
-    TString getTypeName(const TType &type);
+    ImmutableString getTypeName(const TType &type);
 
     void visitSymbol(TIntermSymbol *node) override;
     void visitConstantUnion(TIntermConstantUnion *node) override;
@@ -69,11 +69,12 @@
 
     void visitCodeBlock(TIntermBlock *node);
 
-    TString hashFieldName(const TSymbol *containingStruct, const TString &fieldName);
+    ImmutableString hashFieldName(const TSymbol *containingStruct,
+                                  const ImmutableString &fieldName);
     // Same as hashName(), but without hashing "main".
-    TString hashFunctionNameIfNeeded(const TFunction *func);
+    ImmutableString hashFunctionNameIfNeeded(const TFunction *func);
     // Used to translate function names for differences between ESSL and GLSL
-    virtual TString translateTextureFunction(const TString &name) { return name; }
+    virtual ImmutableString translateTextureFunction(const ImmutableString &name) { return name; }
 
   private:
     bool structDeclared(const TStructure *structure) const;
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 55c57c9..6f59129 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -371,7 +371,8 @@
 
             if (mappedStruct.blockDeclarator->variable().symbolType() != SymbolType::Empty)
             {
-                const TString &instanceName = mappedStruct.blockDeclarator->variable().name();
+                const ImmutableString &instanceName =
+                    mappedStruct.blockDeclarator->variable().name();
                 unsigned int instanceStringArrayIndex = GL_INVALID_INDEX;
                 if (isInstanceArray)
                     instanceStringArrayIndex = instanceArrayIndex;
@@ -415,7 +416,7 @@
     for (const auto &varying : mReferencedVaryings)
     {
         const TType &type   = varying.second->getType();
-        const TString &name = varying.second->name();
+        const ImmutableString &name = varying.second->name();
 
         // Program linking depends on this exact format
         varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) +
@@ -426,7 +427,7 @@
     for (const auto &attribute : mReferencedAttributes)
     {
         const TType &type   = attribute.second->getType();
-        const TString &name = attribute.second->name();
+        const ImmutableString &name = attribute.second->name();
 
         attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) +
                       " = " + zeroInitializer(type) + ";\n";
@@ -498,12 +499,11 @@
         {
             for (const auto &outputVariable : mReferencedOutputVariables)
             {
-                const TString &variableName = outputVariable.second->name();
+                const ImmutableString &variableName = outputVariable.second->name();
                 const TType &variableType   = outputVariable.second->getType();
 
-                out << "static " + TypeString(variableType) + " out_" + variableName +
-                           ArrayString(variableType) + " = " + zeroInitializer(variableType) +
-                           ";\n";
+                out << "static " << TypeString(variableType) << " out_" << variableName
+                    << ArrayString(variableType) << " = " << zeroInitializer(variableType) << ";\n";
             }
         }
         else
@@ -883,7 +883,7 @@
         out << "map";
     }
 
-    const TString &name             = variable.name();
+    const ImmutableString &name     = variable.name();
     const TSymbolUniqueId &uniqueId = variable.uniqueId();
 
     if (name == "gl_DepthRange")
@@ -1314,11 +1314,11 @@
             {
                 if (indexingReturnsSampler)
                 {
-                    out << "_" + field->name();
+                    out << "_" << field->name();
                 }
                 else
                 {
-                    out << "." + DecorateField(field->name(), *structure);
+                    out << "." << DecorateField(field->name(), *structure);
                 }
 
                 return false;
@@ -1661,7 +1661,7 @@
     return true;
 }
 
-TString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node)
+ImmutableString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node)
 {
     if (node->getAsSymbolNode())
     {
@@ -1675,9 +1675,9 @@
         {
             int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
 
-            TInfoSinkBase prefixSink;
+            std::stringstream prefixSink;
             prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" << index;
-            return TString(prefixSink.c_str());
+            return ImmutableString(prefixSink.str());
         }
         case EOpIndexDirectStruct:
         {
@@ -1685,14 +1685,14 @@
             int index           = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
             const TField *field = s->fields()[index];
 
-            TInfoSinkBase prefixSink;
+            std::stringstream prefixSink;
             prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_"
                        << field->name();
-            return TString(prefixSink.c_str());
+            return ImmutableString(prefixSink.str());
         }
         default:
             UNREACHABLE();
-            return TString("");
+            return ImmutableString("");
     }
 }
 
@@ -1951,16 +1951,16 @@
             }
             else if (node->getFunction()->isImageFunction())
             {
-                const TString &name       = node->getFunction()->name();
+                const ImmutableString &name = node->getFunction()->name();
                 TType type                = (*arguments)[0]->getAsTyped()->getType();
-                TString imageFunctionName = mImageFunctionHLSL->useImageFunction(
+                TString imageFunctionName   = mImageFunctionHLSL->useImageFunction(
                     name, type.getBasicType(), type.getLayoutQualifier().imageInternalFormat,
                     type.getMemoryQualifier().readonly);
                 out << imageFunctionName << "(";
             }
             else
             {
-                const TString &name    = node->getFunction()->name();
+                const ImmutableString &name = node->getFunction()->name();
                 TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType();
                 int coords = 0;  // textureSize(gsampler2DMS) doesn't have a second argument.
                 if (arguments->size() > 1)
@@ -1988,8 +1988,10 @@
                 {
                     const TType &argType = typedArg->getType();
                     TVector<const TVariable *> samplerSymbols;
-                    TString structName = samplerNamePrefixFromStruct(typedArg);
-                    argType.createSamplerSymbols("angle_" + structName, "", &samplerSymbols,
+                    ImmutableString structName = samplerNamePrefixFromStruct(typedArg);
+                    std::string namePrefix     = "angle_";
+                    namePrefix += structName.data();
+                    argType.createSamplerSymbols(ImmutableString(namePrefix), "", &samplerSymbols,
                                                  nullptr, mSymbolTable);
                     for (const TVariable *sampler : samplerSymbols)
                     {
@@ -2002,7 +2004,7 @@
                         {
                             // In case of HLSL 4.1+, this symbol is the sampler index, and in case
                             // of D3D9, it's the sampler variable.
-                            out << ", " + sampler->name();
+                            out << ", " << sampler->name();
                         }
                     }
                 }
@@ -2683,7 +2685,10 @@
     {
         ASSERT(qualifier != EvqOut && qualifier != EvqInOut);
         TVector<const TVariable *> samplerSymbols;
-        type.createSamplerSymbols("angle" + nameStr, "", &samplerSymbols, nullptr, mSymbolTable);
+        std::string namePrefix = "angle";
+        namePrefix += nameStr.c_str();
+        type.createSamplerSymbols(ImmutableString(namePrefix), "", &samplerSymbols, nullptr,
+                                  mSymbolTable);
         for (const TVariable *sampler : samplerSymbols)
         {
             const TType &samplerType = sampler->getType();
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index 962babf..8b632a8 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -15,6 +15,7 @@
 #include "compiler/translator/ASTMetadataHLSL.h"
 #include "compiler/translator/Compiler.h"
 #include "compiler/translator/FlagStd140Structs.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/IntermTraverse.h"
 
 class BuiltInFunctionEmulator;
@@ -252,7 +253,7 @@
 
   private:
     TString generateStructMapping(const std::vector<MappedStruct> &std140Structs) const;
-    TString samplerNamePrefixFromStruct(TIntermTyped *node);
+    ImmutableString samplerNamePrefixFromStruct(TIntermTyped *node);
     bool ancestorEvaluatesToSamplerInStruct();
 };
 }
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
         {
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 9706236..39f92e0 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -52,6 +52,7 @@
     ShShaderSpec getShaderSpec() const { return mShaderSpec; }
     int numErrors() const { return mDiagnostics->numErrors(); }
     void error(const TSourceLoc &loc, const char *reason, const char *token);
+    void error(const TSourceLoc &loc, const char *reason, const ImmutableString &token);
     void warning(const TSourceLoc &loc, const char *reason, const char *token);
 
     // If isError is false, a warning will be reported instead.
@@ -96,15 +97,15 @@
 
     // This method is guaranteed to succeed, even if no variable with 'name' exists.
     const TVariable *getNamedVariable(const TSourceLoc &location,
-                                      const TString *name,
+                                      const ImmutableString &name,
                                       const TSymbol *symbol);
     TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
-                                          const TString *name,
+                                          const ImmutableString &name,
                                           const TSymbol *symbol);
 
     // Look at a '.' field selector string and change it into offsets for a vector.
     bool parseVectorFields(const TSourceLoc &line,
-                           const TString &compString,
+                           const ImmutableString &compString,
                            int vecSize,
                            TVector<int> *fieldOffsets);
 
@@ -114,7 +115,7 @@
 
     // Check functions - the ones that return bool return false if an error was generated.
 
-    bool checkIsNotReserved(const TSourceLoc &line, const TString &identifier);
+    bool checkIsNotReserved(const TSourceLoc &line, const ImmutableString &identifier);
     void checkPrecisionSpecified(const TSourceLoc &line, TPrecision precision, TBasicType type);
     bool checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node);
     void checkIsConst(TIntermTyped *node);
@@ -128,7 +129,9 @@
     unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr);
     bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier);
     bool checkArrayElementIsNotArray(const TSourceLoc &line, const TPublicType &elementType);
-    bool checkIsNonVoid(const TSourceLoc &line, const TString &identifier, const TBasicType &type);
+    bool checkIsNonVoid(const TSourceLoc &line,
+                        const ImmutableString &identifier,
+                        const TBasicType &type);
     bool checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type);
     void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType);
     bool checkIsNotOpaqueType(const TSourceLoc &line,
@@ -165,7 +168,7 @@
     void emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location);
 
     void checkLayoutQualifierSupported(const TSourceLoc &location,
-                                       const TString &layoutQualifierName,
+                                       const ImmutableString &layoutQualifierName,
                                        int versionRequired);
     bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
                                           const TLayoutQualifier &layoutQualifier);
@@ -193,12 +196,12 @@
     // Returns true on success. *initNode may still be nullptr on success in case the initialization
     // is not needed in the AST.
     bool executeInitializer(const TSourceLoc &line,
-                            const TString &identifier,
+                            const ImmutableString &identifier,
                             TType *type,
                             TIntermTyped *initializer,
                             TIntermBinary **initNode);
     TIntermNode *addConditionInitializer(const TPublicType &pType,
-                                         const TString &identifier,
+                                         const ImmutableString &identifier,
                                          TIntermTyped *initializer,
                                          const TSourceLoc &loc);
     TIntermNode *addLoop(TLoopType type,
@@ -218,15 +221,15 @@
 
     TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
                                                const TSourceLoc &identifierOrTypeLocation,
-                                               const TString &identifier);
+                                               const ImmutableString &identifier);
     TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &elementType,
                                                     const TSourceLoc &identifierLocation,
-                                                    const TString &identifier,
+                                                    const ImmutableString &identifier,
                                                     const TSourceLoc &indexLocation,
                                                     const TVector<unsigned int> &arraySizes);
     TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
                                                    const TSourceLoc &identifierLocation,
-                                                   const TString &identifier,
+                                                   const ImmutableString &identifier,
                                                    const TSourceLoc &initLocation,
                                                    TIntermTyped *initializer);
 
@@ -234,7 +237,7 @@
     // Note that this does not apply to declarations like "type[n] a = initializer"
     TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &elementType,
                                                         const TSourceLoc &identifierLocation,
-                                                        const TString &identifier,
+                                                        const ImmutableString &identifier,
                                                         const TSourceLoc &indexLocation,
                                                         const TVector<unsigned int> &arraySizes,
                                                         const TSourceLoc &initLocation,
@@ -243,22 +246,22 @@
     TIntermInvariantDeclaration *parseInvariantDeclaration(
         const TTypeQualifierBuilder &typeQualifierBuilder,
         const TSourceLoc &identifierLoc,
-        const TString *identifier,
+        const ImmutableString &identifier,
         const TSymbol *symbol);
 
     void parseDeclarator(TPublicType &publicType,
                          const TSourceLoc &identifierLocation,
-                         const TString &identifier,
+                         const ImmutableString &identifier,
                          TIntermDeclaration *declarationOut);
     void parseArrayDeclarator(TPublicType &elementType,
                               const TSourceLoc &identifierLocation,
-                              const TString &identifier,
+                              const ImmutableString &identifier,
                               const TSourceLoc &arrayLocation,
                               const TVector<unsigned int> &arraySizes,
                               TIntermDeclaration *declarationOut);
     void parseInitDeclarator(const TPublicType &publicType,
                              const TSourceLoc &identifierLocation,
-                             const TString &identifier,
+                             const ImmutableString &identifier,
                              const TSourceLoc &initLocation,
                              TIntermTyped *initializer,
                              TIntermDeclaration *declarationOut);
@@ -266,7 +269,7 @@
     // Parse a declarator like "a[n] = initializer"
     void parseArrayInitDeclarator(const TPublicType &elementType,
                                   const TSourceLoc &identifierLocation,
-                                  const TString &identifier,
+                                  const ImmutableString &identifier,
                                   const TSourceLoc &indexLocation,
                                   const TVector<unsigned int> &arraySizes,
                                   const TSourceLoc &initLocation,
@@ -290,15 +293,17 @@
                                        TIntermFunctionPrototype **prototypeOut);
     TFunction *parseFunctionDeclarator(const TSourceLoc &location, TFunction *function);
     TFunction *parseFunctionHeader(const TPublicType &type,
-                                   const TString *name,
+                                   const ImmutableString &name,
                                    const TSourceLoc &location);
-    TFunctionLookup *addNonConstructorFunc(const TString *name);
+
+    TFunctionLookup *addNonConstructorFunc(const ImmutableString &name);
     TFunctionLookup *addConstructorFunc(const TPublicType &publicType);
+
     TParameter parseParameterDeclarator(const TPublicType &publicType,
-                                        const TString *name,
+                                        const ImmutableString &name,
                                         const TSourceLoc &nameLoc);
 
-    TParameter parseParameterArrayDeclarator(const TString *name,
+    TParameter parseParameterArrayDeclarator(const ImmutableString &name,
                                              const TSourceLoc &nameLoc,
                                              const TVector<unsigned int> &arraySizes,
                                              const TSourceLoc &arrayLoc,
@@ -309,18 +314,18 @@
                                      TIntermTyped *indexExpression);
     TIntermTyped *addFieldSelectionExpression(TIntermTyped *baseExpression,
                                               const TSourceLoc &dotLocation,
-                                              const TString &fieldString,
+                                              const ImmutableString &fieldString,
                                               const TSourceLoc &fieldLocation);
 
     // Parse declarator for a single field
-    TDeclarator *parseStructDeclarator(const TString *identifier, const TSourceLoc &loc);
-    TDeclarator *parseStructArrayDeclarator(const TString *identifier,
+    TDeclarator *parseStructDeclarator(const ImmutableString &identifier, const TSourceLoc &loc);
+    TDeclarator *parseStructArrayDeclarator(const ImmutableString &identifier,
                                             const TSourceLoc &loc,
                                             const TVector<unsigned int> *arraySizes);
 
     void checkDoesNotHaveDuplicateFieldName(const TFieldList::const_iterator begin,
                                             const TFieldList::const_iterator end,
-                                            const TString &name,
+                                            const ImmutableString &name,
                                             const TSourceLoc &location);
     TFieldList *addStructFieldList(TFieldList *fields, const TSourceLoc &location);
     TFieldList *combineStructFieldLists(TFieldList *processedFields,
@@ -334,19 +339,19 @@
                                         const TDeclaratorList *declaratorList);
     TTypeSpecifierNonArray addStructure(const TSourceLoc &structLine,
                                         const TSourceLoc &nameLine,
-                                        const TString *structName,
+                                        const ImmutableString &structName,
                                         TFieldList *fieldList);
 
     TIntermDeclaration *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);
 
-    void parseLocalSize(const TString &qualifierType,
+    void parseLocalSize(const ImmutableString &qualifierType,
                         const TSourceLoc &qualifierTypeLine,
                         int intValue,
                         const TSourceLoc &intValueLine,
@@ -365,9 +370,9 @@
                           const TSourceLoc &intValueLine,
                           const std::string &intValueString,
                           int *numMaxVertices);
-    TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
+    TLayoutQualifier parseLayoutQualifier(const ImmutableString &qualifierType,
                                           const TSourceLoc &qualifierTypeLine);
-    TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
+    TLayoutQualifier parseLayoutQualifier(const ImmutableString &qualifierType,
                                           const TSourceLoc &qualifierTypeLine,
                                           int intValue,
                                           const TSourceLoc &intValueLine);
@@ -383,7 +388,7 @@
                                           const TSourceLoc &rightQualifierLocation);
 
     // Performs an error check for embedded struct declarations.
-    void enterStructDeclaration(const TSourceLoc &line, const TString &identifier);
+    void enterStructDeclaration(const TSourceLoc &line, const ImmutableString &identifier);
     void exitStructDeclaration();
 
     void checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field);
@@ -467,16 +472,16 @@
                            const char *reason);
 
     bool declareVariable(const TSourceLoc &line,
-                         const TString &identifier,
+                         const ImmutableString &identifier,
                          const TType *type,
                          TVariable **variable);
 
     void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
-                                              const TString &identifier,
+                                              const ImmutableString &identifier,
                                               TType *type);
 
     TParameter parseParameterDeclarator(TType *type,
-                                        const TString *name,
+                                        const ImmutableString &name,
                                         const TSourceLoc &nameLoc);
 
     bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
@@ -526,14 +531,14 @@
 
     // Will set the size of the outermost array according to geometry shader input layout.
     void checkGeometryShaderInputAndSetArraySize(const TSourceLoc &location,
-                                                 const char *token,
+                                                 const ImmutableString &token,
                                                  TType *type);
 
     // Will size any unsized array type so unsized arrays won't need to be taken into account
     // further along the line in parsing.
     void checkIsNotUnsizedArray(const TSourceLoc &line,
                                 const char *errorMessage,
-                                const char *token,
+                                const ImmutableString &token,
                                 TType *arrayType);
 
     TIntermTyped *addBinaryMathInternal(TOperator op,
diff --git a/src/compiler/translator/PruneNoOps.cpp b/src/compiler/translator/PruneNoOps.cpp
index 260760f..ebba6fa 100644
--- a/src/compiler/translator/PruneNoOps.cpp
+++ b/src/compiler/translator/PruneNoOps.cpp
@@ -114,7 +114,8 @@
                 {
                     type->setQualifier(EvqTemporary);
                 }
-                TVariable *variable = new TVariable(mSymbolTable, nullptr, type, SymbolType::Empty);
+                TVariable *variable =
+                    new TVariable(mSymbolTable, ImmutableString(""), type, SymbolType::Empty);
                 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
                                            OriginalNode::IS_DROPPED);
             }
diff --git a/src/compiler/translator/RegenerateStructNames.cpp b/src/compiler/translator/RegenerateStructNames.cpp
index b841057..536f521 100644
--- a/src/compiler/translator/RegenerateStructNames.cpp
+++ b/src/compiler/translator/RegenerateStructNames.cpp
@@ -4,12 +4,19 @@
 // found in the LICENSE file.
 //
 
-#include "common/debug.h"
 #include "compiler/translator/RegenerateStructNames.h"
 
+#include "common/debug.h"
+#include "compiler/translator/ImmutableStringBuilder.h"
+
 namespace sh
 {
 
+namespace
+{
+constexpr const ImmutableString kPrefix("_webgl_struct_");
+}  // anonymous namespace
+
 void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol)
 {
     ASSERT(symbol);
@@ -49,15 +56,16 @@
     if (mDeclaredGlobalStructs.count(uniqueId) > 0)
         return;
     // Map {name} to _webgl_struct_{uniqueId}_{name}.
-    const char kPrefix[] = "_webgl_struct_";
-    if (userType->name().find(kPrefix) == 0)
+    if (userType->name().beginsWith(kPrefix))
     {
         // The name has already been regenerated.
         return;
     }
-    std::string id = Str(uniqueId);
-    TString tmp    = kPrefix + TString(id.c_str());
-    tmp += "_" + userType->name();
+    ImmutableStringBuilder tmp(kPrefix.length() + sizeof(uniqueId) * 2u + 1u +
+                               userType->name().length());
+    tmp << kPrefix;
+    tmp.appendHex(uniqueId);
+    tmp << '_' << userType->name();
 
     // TODO(oetuaho): Add another mechanism to change symbol names so that the const_cast is not
     // needed.
diff --git a/src/compiler/translator/RemoveDynamicIndexing.cpp b/src/compiler/translator/RemoveDynamicIndexing.cpp
index 184c711..155e3d1 100644
--- a/src/compiler/translator/RemoveDynamicIndexing.cpp
+++ b/src/compiler/translator/RemoveDynamicIndexing.cpp
@@ -25,6 +25,10 @@
 
 const TType *kIndexType = StaticType::Get<EbtInt, EbpHigh, EvqIn, 1, 1>();
 
+constexpr const ImmutableString kBaseName("base");
+constexpr const ImmutableString kIndexName("index");
+constexpr const ImmutableString kValueName("value");
+
 std::string GetIndexFunctionName(const TType &type, bool write)
 {
     TInfoSinkBase nameSink;
@@ -291,10 +295,6 @@
     bool mRemoveIndexSideEffectsInSubtree;
 
     PerformanceDiagnostics *mPerfDiagnostics;
-
-    const TString *mBaseName;
-    const TString *mIndexName;
-    const TString *mValueName;
 };
 
 RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
@@ -303,10 +303,7 @@
     : TLValueTrackingTraverser(true, false, false, symbolTable),
       mUsedTreeInsertion(false),
       mRemoveIndexSideEffectsInSubtree(false),
-      mPerfDiagnostics(perfDiagnostics),
-      mBaseName(NewPoolTString("base")),
-      mIndexName(NewPoolTString("index")),
-      mValueName(NewPoolTString("value"))
+      mPerfDiagnostics(perfDiagnostics)
 {
 }
 
@@ -404,8 +401,7 @@
 #endif
 
             const TType &type = node->getLeft()->getType();
-            TString *indexingFunctionName =
-                NewPoolTString(GetIndexFunctionName(type, false).c_str());
+            ImmutableString indexingFunctionName(GetIndexFunctionName(type, false));
             TFunction *indexingFunction = nullptr;
             if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
             {
@@ -413,8 +409,8 @@
                     new TFunction(mSymbolTable, indexingFunctionName, GetFieldType(type),
                                   SymbolType::AngleInternal, true);
                 indexingFunction->addParameter(
-                    TConstParameter(mBaseName, GetBaseType(type, false)));
-                indexingFunction->addParameter(TConstParameter(mIndexName, kIndexType));
+                    TConstParameter(kBaseName, GetBaseType(type, false)));
+                indexingFunction->addParameter(TConstParameter(kIndexName, kIndexType));
                 mIndexedVecAndMatrixTypes[type] = indexingFunction;
             }
             else
@@ -457,18 +453,18 @@
                 TFunction *indexedWriteFunction = nullptr;
                 if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
                 {
-                    TString *functionName = NewPoolTString(
-                        GetIndexFunctionName(node->getLeft()->getType(), true).c_str());
+                    ImmutableString functionName(
+                        GetIndexFunctionName(node->getLeft()->getType(), true));
                     indexedWriteFunction =
                         new TFunction(mSymbolTable, functionName, StaticType::GetBasic<EbtVoid>(),
                                       SymbolType::AngleInternal, false);
                     indexedWriteFunction->addParameter(
-                        TConstParameter(mBaseName, GetBaseType(type, true)));
-                    indexedWriteFunction->addParameter(TConstParameter(mIndexName, kIndexType));
+                        TConstParameter(kBaseName, GetBaseType(type, true)));
+                    indexedWriteFunction->addParameter(TConstParameter(kIndexName, kIndexType));
                     TType *valueType = GetFieldType(type);
                     valueType->setQualifier(EvqIn);
                     indexedWriteFunction->addParameter(
-                        TConstParameter(mValueName, static_cast<const TType *>(valueType)));
+                        TConstParameter(kValueName, static_cast<const TType *>(valueType)));
                     mWrittenVecAndMatrixTypes[type] = indexedWriteFunction;
                 }
                 else
diff --git a/src/compiler/translator/RemoveUnreferencedVariables.cpp b/src/compiler/translator/RemoveUnreferencedVariables.cpp
index ad14648..6445eb8 100644
--- a/src/compiler/translator/RemoveUnreferencedVariables.cpp
+++ b/src/compiler/translator/RemoveUnreferencedVariables.cpp
@@ -203,8 +203,9 @@
                 // Already an empty declaration - nothing to do.
                 return;
             }
-            TVariable *emptyVariable = new TVariable(
-                mSymbolTable, nullptr, new TType(declarator->getType()), SymbolType::Empty);
+            TVariable *emptyVariable =
+                new TVariable(mSymbolTable, ImmutableString(""), new TType(declarator->getType()),
+                              SymbolType::Empty);
             queueReplacementWithParent(node, declarator, new TIntermSymbol(emptyVariable),
                                        OriginalNode::IS_DROPPED);
             return;
diff --git a/src/compiler/translator/RunAtTheEndOfShader.cpp b/src/compiler/translator/RunAtTheEndOfShader.cpp
index d0a2b82..ca82a07 100644
--- a/src/compiler/translator/RunAtTheEndOfShader.cpp
+++ b/src/compiler/translator/RunAtTheEndOfShader.cpp
@@ -33,6 +33,8 @@
 namespace
 {
 
+constexpr const ImmutableString kMainString("main");
+
 class ContainsReturnTraverser : public TIntermTraverser
 {
   public:
@@ -66,8 +68,8 @@
                        TSymbolTable *symbolTable)
 {
     // Replace main() with main0() with the same body.
-    TFunction *oldMain =
-        new TFunction(symbolTable, nullptr, new TType(EbtVoid), SymbolType::AngleInternal, false);
+    TFunction *oldMain = new TFunction(symbolTable, ImmutableString(""), new TType(EbtVoid),
+                                       SymbolType::AngleInternal, false);
     TIntermFunctionDefinition *oldMainDefinition =
         CreateInternalFunctionDefinitionNode(*oldMain, main->getBody());
 
@@ -75,8 +77,8 @@
     ASSERT(replaced);
 
     // void main()
-    TFunction *newMain = new TFunction(symbolTable, NewPoolTString("main"), new TType(EbtVoid),
-                                       SymbolType::UserDefined, false);
+    TFunction *newMain =
+        new TFunction(symbolTable, kMainString, new TType(EbtVoid), SymbolType::UserDefined, false);
     TIntermFunctionPrototype *newMainProto = new TIntermFunctionPrototype(newMain);
 
     // {
diff --git a/src/compiler/translator/Symbol.cpp b/src/compiler/translator/Symbol.cpp
index 6ec6449..1332798 100644
--- a/src/compiler/translator/Symbol.cpp
+++ b/src/compiler/translator/Symbol.cpp
@@ -12,6 +12,7 @@
 
 #include "compiler/translator/Symbol.h"
 
+#include "compiler/translator/ImmutableStringBuilder.h"
 #include "compiler/translator/SymbolTable.h"
 
 namespace sh
@@ -20,12 +21,17 @@
 namespace
 {
 
+constexpr const ImmutableString kMainName("main");
+constexpr const ImmutableString kImageLoadName("imageLoad");
+constexpr const ImmutableString kImageStoreName("imageStore");
+constexpr const ImmutableString kImageSizeName("imageSize");
+
 static const char kFunctionMangledNameSeparator = '(';
 
 }  // anonymous namespace
 
 TSymbol::TSymbol(TSymbolTable *symbolTable,
-                 const TString *name,
+                 const ImmutableString &name,
                  SymbolType symbolType,
                  TExtension extension)
     : mName(name),
@@ -34,31 +40,32 @@
       mExtension(extension)
 {
     ASSERT(mSymbolType == SymbolType::BuiltIn || mExtension == TExtension::UNDEFINED);
-    ASSERT(mName != nullptr || mSymbolType == SymbolType::AngleInternal ||
+    ASSERT(mName != "" || mSymbolType == SymbolType::AngleInternal ||
            mSymbolType == SymbolType::Empty);
-    ASSERT(mName == nullptr || *mName != "");
 }
 
-const TString &TSymbol::name() const
+ImmutableString TSymbol::name() const
 {
-    if (mName != nullptr)
+    if (mName != "")
     {
-        return *mName;
+        return mName;
     }
     ASSERT(mSymbolType == SymbolType::AngleInternal);
-    TInfoSinkBase symbolNameOut;
-    symbolNameOut << "s" << mUniqueId.get();
-    return *NewPoolTString(symbolNameOut.c_str());
+    int uniqueId = mUniqueId.get();
+    ImmutableStringBuilder symbolNameOut(sizeof(uniqueId) * 2u + 1u);
+    symbolNameOut << 's';
+    symbolNameOut.appendHex(mUniqueId.get());
+    return symbolNameOut;
 }
 
-const TString &TSymbol::getMangledName() const
+ImmutableString TSymbol::getMangledName() const
 {
     ASSERT(mSymbolType != SymbolType::Empty);
     return name();
 }
 
 TVariable::TVariable(TSymbolTable *symbolTable,
-                     const TString *name,
+                     const ImmutableString &name,
                      const TType *type,
                      SymbolType symbolType,
                      TExtension extension)
@@ -68,14 +75,14 @@
 }
 
 TStructure::TStructure(TSymbolTable *symbolTable,
-                       const TString *name,
+                       const ImmutableString &name,
                        const TFieldList *fields,
                        SymbolType symbolType)
     : TSymbol(symbolTable, name, symbolType), TFieldListCollection(fields)
 {
 }
 
-void TStructure::createSamplerSymbols(const TString &namePrefix,
+void TStructure::createSamplerSymbols(const char *namePrefix,
                                       const TString &apiNamePrefix,
                                       TVector<const TVariable *> *outputSymbols,
                                       TMap<const TVariable *, TString> *outputSymbolsToAPINames,
@@ -87,22 +94,24 @@
         const TType *fieldType = field->type();
         if (IsSampler(fieldType->getBasicType()) || fieldType->isStructureContainingSamplers())
         {
-            TString fieldName    = namePrefix + "_" + field->name();
-            TString fieldApiName = apiNamePrefix + "." + field->name();
-            fieldType->createSamplerSymbols(fieldName, fieldApiName, outputSymbols,
-                                            outputSymbolsToAPINames, symbolTable);
+            std::stringstream fieldName;
+            fieldName << namePrefix << "_" << field->name();
+            TString fieldApiName = apiNamePrefix + ".";
+            fieldApiName += field->name().data();
+            fieldType->createSamplerSymbols(ImmutableString(fieldName.str()), fieldApiName,
+                                            outputSymbols, outputSymbolsToAPINames, symbolTable);
         }
     }
 }
 
-void TStructure::setName(const TString &name)
+void TStructure::setName(const ImmutableString &name)
 {
-    TString *mutableName = const_cast<TString *>(mName);
+    ImmutableString *mutableName = const_cast<ImmutableString *>(&mName);
     *mutableName         = name;
 }
 
 TInterfaceBlock::TInterfaceBlock(TSymbolTable *symbolTable,
-                                 const TString *name,
+                                 const ImmutableString &name,
                                  const TFieldList *fields,
                                  const TLayoutQualifier &layoutQualifier,
                                  SymbolType symbolType,
@@ -116,7 +125,7 @@
 }
 
 TFunction::TFunction(TSymbolTable *symbolTable,
-                     const TString *name,
+                     const ImmutableString &name,
                      const TType *retType,
                      SymbolType symbolType,
                      bool knownToNotHaveSideEffects,
@@ -143,7 +152,7 @@
 void TFunction::clearParameters()
 {
     parameters.clear();
-    mangledName = nullptr;
+    mangledName = ImmutableString("");
 }
 
 void TFunction::swapParameters(const TFunction &parametersSource)
@@ -155,27 +164,27 @@
     }
 }
 
-const TString *TFunction::buildMangledName() const
+ImmutableString TFunction::buildMangledName() const
 {
-    std::string newName = name().c_str();
+    std::string newName(name().data(), name().length());
     newName += kFunctionMangledNameSeparator;
 
     for (const auto &p : parameters)
     {
         newName += p.type->getMangledName();
     }
-    return NewPoolTString(newName.c_str());
+    return ImmutableString(newName);
 }
 
 bool TFunction::isMain() const
 {
-    return symbolType() == SymbolType::UserDefined && name() == "main";
+    return symbolType() == SymbolType::UserDefined && name() == kMainName;
 }
 
 bool TFunction::isImageFunction() const
 {
     return symbolType() == SymbolType::BuiltIn &&
-           (name() == "imageSize" || name() == "imageLoad" || name() == "imageStore");
+           (name() == kImageSizeName || name() == kImageLoadName || name() == kImageStoreName);
 }
 
 }  // namespace sh
diff --git a/src/compiler/translator/Symbol.h b/src/compiler/translator/Symbol.h
index cff5d7a..3675d4f 100644
--- a/src/compiler/translator/Symbol.h
+++ b/src/compiler/translator/Symbol.h
@@ -11,6 +11,7 @@
 
 #include "common/angleutils.h"
 #include "compiler/translator/ExtensionBehavior.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/SymbolUniqueId.h"
 
@@ -33,7 +34,7 @@
   public:
     POOL_ALLOCATOR_NEW_DELETE();
     TSymbol(TSymbolTable *symbolTable,
-            const TString *name,
+            const ImmutableString &name,
             SymbolType symbolType,
             TExtension extension = TExtension::UNDEFINED);
 
@@ -43,8 +44,8 @@
     }
 
     // Don't call name() or getMangledName() for empty symbols (symbolType == SymbolType::Empty).
-    const TString &name() const;
-    virtual const TString &getMangledName() const;
+    ImmutableString name() const;
+    virtual ImmutableString getMangledName() const;
 
     virtual bool isFunction() const { return false; }
     virtual bool isVariable() const { return false; }
@@ -55,7 +56,7 @@
     TExtension extension() const { return mExtension; }
 
   protected:
-    const TString *const mName;
+    const ImmutableString mName;
 
   private:
     const TSymbolUniqueId mUniqueId;
@@ -69,7 +70,7 @@
 {
   public:
     TVariable(TSymbolTable *symbolTable,
-              const TString *name,
+              const ImmutableString &name,
               const TType *type,
               SymbolType symbolType,
               TExtension ext = TExtension::UNDEFINED);
@@ -92,13 +93,14 @@
 {
   public:
     TStructure(TSymbolTable *symbolTable,
-               const TString *name,
+               const ImmutableString &name,
                const TFieldList *fields,
                SymbolType symbolType);
 
     bool isStruct() const override { return true; }
 
-    void createSamplerSymbols(const TString &namePrefix,
+    // The char arrays passed in must be pool allocated or static.
+    void createSamplerSymbols(const char *namePrefix,
                               const TString &apiNamePrefix,
                               TVector<const TVariable *> *outputSymbols,
                               TMap<const TVariable *, TString> *outputSymbolsToAPINames,
@@ -112,7 +114,7 @@
     // setName().  At the moment keep this function private so only
     // friend class RegenerateStructNames may call it.
     friend class RegenerateStructNames;
-    void setName(const TString &name);
+    void setName(const ImmutableString &name);
 
     bool mAtGlobalScope;
 };
@@ -123,7 +125,7 @@
 {
   public:
     TInterfaceBlock(TSymbolTable *symbolTable,
-                    const TString *name,
+                    const ImmutableString &name,
                     const TFieldList *fields,
                     const TLayoutQualifier &layoutQualifier,
                     SymbolType symbolType,
@@ -142,17 +144,17 @@
 // Immutable version of TParameter.
 struct TConstParameter
 {
-    TConstParameter() : name(nullptr), type(nullptr) {}
-    explicit TConstParameter(const TString *n) : name(n), type(nullptr) {}
-    explicit TConstParameter(const TType *t) : name(nullptr), type(t) {}
-    TConstParameter(const TString *n, const TType *t) : name(n), type(t) {}
+    TConstParameter() : name(""), type(nullptr) {}
+    explicit TConstParameter(const ImmutableString &n) : name(n), type(nullptr) {}
+    explicit TConstParameter(const TType *t) : name(""), type(t) {}
+    TConstParameter(const ImmutableString &n, const TType *t) : name(n), type(t) {}
 
     // Both constructor arguments must be const.
-    TConstParameter(TString *n, TType *t)       = delete;
-    TConstParameter(const TString *n, TType *t) = delete;
-    TConstParameter(TString *n, const TType *t) = delete;
+    TConstParameter(ImmutableString *n, TType *t)       = delete;
+    TConstParameter(const ImmutableString *n, TType *t) = delete;
+    TConstParameter(ImmutableString *n, const TType *t) = delete;
 
-    const TString *const name;
+    const ImmutableString name;
     const TType *const type;
 };
 
@@ -165,14 +167,14 @@
     // their content cannot be modified after the call.
     TConstParameter turnToConst()
     {
-        const TString *constName = name;
+        const ImmutableString constName(name);
         const TType *constType   = type;
         name                     = nullptr;
         type                     = nullptr;
         return TConstParameter(constName, constType);
     }
 
-    const TString *name;
+    const char *name;  // either pool allocated or static.
     TType *type;
 };
 
@@ -181,31 +183,32 @@
 {
   public:
     TFunction(TSymbolTable *symbolTable,
-              const TString *name,
+              const ImmutableString &name,
               const TType *retType,
               SymbolType symbolType,
               bool knownToNotHaveSideEffects,
               TOperator tOp        = EOpNull,
               TExtension extension = TExtension::UNDEFINED);
 
-    ~TFunction() override;
+    virtual ~TFunction();
+
     bool isFunction() const override { return true; }
 
     void addParameter(const TConstParameter &p)
     {
         parameters.push_back(p);
-        mangledName = nullptr;
+        mangledName = ImmutableString("");
     }
 
     void swapParameters(const TFunction &parametersSource);
 
-    const TString &getMangledName() const override
+    ImmutableString getMangledName() const override
     {
-        if (mangledName == nullptr)
+        if (mangledName == "")
         {
             mangledName = buildMangledName();
         }
-        return *mangledName;
+        return mangledName;
     }
 
     const TType &getReturnType() const { return *returnType; }
@@ -228,12 +231,12 @@
   private:
     void clearParameters();
 
-    const TString *buildMangledName() const;
+    ImmutableString buildMangledName() const;
 
     typedef TVector<TConstParameter> TParamList;
     TParamList parameters;
     const TType *const returnType;
-    mutable const TString *mangledName;
+    mutable ImmutableString mangledName;
     const TOperator op;  // Only set for built-ins
     bool defined;
     bool mHasPrototypeDeclaration;
diff --git a/src/compiler/translator/SymbolTable.cpp b/src/compiler/translator/SymbolTable.cpp
index 4bc29a4..f183edb 100644
--- a/src/compiler/translator/SymbolTable.cpp
+++ b/src/compiler/translator/SymbolTable.cpp
@@ -32,7 +32,7 @@
     // Insert a function using its unmangled name as the key.
     bool insertUnmangled(TFunction *function);
 
-    TSymbol *find(const TString &name) const;
+    TSymbol *find(const ImmutableString &name) const;
 
     void addInvariantVarying(const std::string &name) { mInvariantVaryings.insert(name); }
 
@@ -47,7 +47,9 @@
     bool hasUnmangledBuiltIn(const char *name) const;
 
   private:
-    using tLevel        = TUnorderedMap<TString, TSymbol *>;
+    using tLevel        = TUnorderedMap<ImmutableString,
+                                 TSymbol *,
+                                 ImmutableString::FowlerNollVoHash<sizeof(size_t)>>;
     using tLevelPair    = const tLevel::value_type;
     using tInsertResult = std::pair<tLevel::iterator, bool>;
 
@@ -74,7 +76,7 @@
     return result.second;
 }
 
-TSymbol *TSymbolTable::TSymbolTableLevel::find(const TString &name) const
+TSymbol *TSymbolTable::TSymbolTableLevel::find(const ImmutableString &name) const
 {
     tLevel::const_iterator it = level.find(name);
     if (it == level.end())
@@ -109,7 +111,7 @@
 }
 
 const TFunction *TSymbolTable::markUserDefinedFunctionHasPrototypeDeclaration(
-    const TString &mangledName,
+    const ImmutableString &mangledName,
     bool *hadPrototypeDeclarationOut)
 {
     TFunction *function         = findUserDefinedFunction(mangledName);
@@ -139,7 +141,7 @@
     return firstDeclaration;
 }
 
-const TSymbol *TSymbolTable::find(const TString &name, int shaderVersion) const
+const TSymbol *TSymbolTable::find(const ImmutableString &name, int shaderVersion) const
 {
     int level       = currentLevel();
     TSymbol *symbol = nullptr;
@@ -161,25 +163,25 @@
     return symbol;
 }
 
-TFunction *TSymbolTable::findUserDefinedFunction(const TString &name) const
+TFunction *TSymbolTable::findUserDefinedFunction(const ImmutableString &name) const
 {
     // User-defined functions are always declared at the global level.
     ASSERT(currentLevel() >= GLOBAL_LEVEL);
     return static_cast<TFunction *>(table[GLOBAL_LEVEL]->find(name));
 }
 
-const TSymbol *TSymbolTable::findGlobal(const TString &name) const
+const TSymbol *TSymbolTable::findGlobal(const ImmutableString &name) const
 {
     ASSERT(table.size() > GLOBAL_LEVEL);
     return table[GLOBAL_LEVEL]->find(name);
 }
 
-const TSymbol *TSymbolTable::findBuiltIn(const TString &name, int shaderVersion) const
+const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name, int shaderVersion) const
 {
     return findBuiltIn(name, shaderVersion, false);
 }
 
-const TSymbol *TSymbolTable::findBuiltIn(const TString &name,
+const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name,
                                          int shaderVersion,
                                          bool includeGLSLBuiltins) const
 {
@@ -316,15 +318,17 @@
     table[GLOBAL_LEVEL]->insert(function);
 }
 
-TVariable *TSymbolTable::insertVariable(ESymbolLevel level, const char *name, const TType *type)
+TVariable *TSymbolTable::insertVariable(ESymbolLevel level,
+                                        const ImmutableString &name,
+                                        const TType *type)
 {
     ASSERT(level <= LAST_BUILTIN_LEVEL);
     ASSERT(type->isRealized());
-    return insertVariable(level, NewPoolTString(name), type, SymbolType::BuiltIn);
+    return insertVariable(level, name, type, SymbolType::BuiltIn);
 }
 
 TVariable *TSymbolTable::insertVariable(ESymbolLevel level,
-                                        const TString *name,
+                                        const ImmutableString &name,
                                         const TType *type,
                                         SymbolType symbolType)
 {
@@ -339,12 +343,12 @@
 
 TVariable *TSymbolTable::insertVariableExt(ESymbolLevel level,
                                            TExtension ext,
-                                           const char *name,
+                                           const ImmutableString &name,
                                            const TType *type)
 {
     ASSERT(level <= LAST_BUILTIN_LEVEL);
     ASSERT(type->isRealized());
-    TVariable *var = new TVariable(this, NewPoolTString(name), type, SymbolType::BuiltIn, ext);
+    TVariable *var = new TVariable(this, name, type, SymbolType::BuiltIn, ext);
     if (insert(level, var))
     {
         return var;
@@ -509,7 +513,7 @@
     else
     {
         TFunction *function =
-            new TFunction(this, NewPoolTString(name), rvalue, SymbolType::BuiltIn, false, op, ext);
+            new TFunction(this, ImmutableString(name), rvalue, SymbolType::BuiltIn, false, op, ext);
 
         function->addParameter(TConstParameter(ptype1));
 
@@ -576,7 +580,7 @@
 {
     insertUnmangledBuiltInName(name, level);
     insert(level,
-           new TFunction(this, NewPoolTString(name), rvalue, SymbolType::BuiltIn, false, op));
+           new TFunction(this, ImmutableString(name), rvalue, SymbolType::BuiltIn, false, op));
 }
 
 void TSymbolTable::insertBuiltInFunctionNoParametersExt(ESymbolLevel level,
@@ -587,7 +591,7 @@
 {
     insertUnmangledBuiltInName(name, level);
     insert(level,
-           new TFunction(this, NewPoolTString(name), rvalue, SymbolType::BuiltIn, false, op, ext));
+           new TFunction(this, ImmutableString(name), rvalue, SymbolType::BuiltIn, false, op, ext));
 }
 
 TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index 9a5363e..089e069 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -36,6 +36,7 @@
 
 #include "common/angleutils.h"
 #include "compiler/translator/ExtensionBehavior.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/InfoSink.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/StaticType.h"
@@ -92,23 +93,28 @@
     // The insert* entry points are used when initializing the symbol table with built-ins.
     // They return the created symbol / true in case the declaration was successful, and nullptr /
     // false if the declaration failed due to redefinition.
-    TVariable *insertVariable(ESymbolLevel level, const char *name, const TType *type);
+    TVariable *insertVariable(ESymbolLevel level, const ImmutableString &name, const TType *type);
     TVariable *insertVariableExt(ESymbolLevel level,
                                  TExtension ext,
-                                 const char *name,
+                                 const ImmutableString &name,
                                  const TType *type);
     bool insertVariable(ESymbolLevel level, TVariable *variable);
     bool insertStructType(ESymbolLevel level, TStructure *str);
     bool insertInterfaceBlock(ESymbolLevel level, TInterfaceBlock *interfaceBlock);
 
     template <TPrecision precision>
-    bool insertConstInt(ESymbolLevel level, const char *name, int value);
+    bool insertConstInt(ESymbolLevel level, const ImmutableString &name, int value);
 
     template <TPrecision precision>
-    bool insertConstIntExt(ESymbolLevel level, TExtension ext, const char *name, int value);
+    bool insertConstIntExt(ESymbolLevel level,
+                           TExtension ext,
+                           const ImmutableString &name,
+                           int value);
 
     template <TPrecision precision>
-    bool insertConstIvec3(ESymbolLevel level, const char *name, const std::array<int, 3> &values);
+    bool insertConstIvec3(ESymbolLevel level,
+                          const ImmutableString &name,
+                          const std::array<int, 3> &values);
 
     // Note that for inserted built-in functions the const char *name needs to remain valid for the
     // lifetime of the SymbolTable. SymbolTable does not allocate a copy of it.
@@ -183,18 +189,20 @@
 
     // These return the TFunction pointer to keep using to refer to this function.
     const TFunction *markUserDefinedFunctionHasPrototypeDeclaration(
-        const TString &mangledName,
+        const ImmutableString &mangledName,
         bool *hadPrototypeDeclarationOut);
     const TFunction *setUserDefinedFunctionParameterNamesFromDefinition(const TFunction *function,
                                                                         bool *wasDefinedOut);
 
-    const TSymbol *find(const TString &name, int shaderVersion) const;
+    // find() is guaranteed not to retain a reference to the ImmutableString, so an ImmutableString
+    // with a reference to a short-lived char * is fine to pass here.
+    const TSymbol *find(const ImmutableString &name, int shaderVersion) const;
 
-    const TSymbol *findGlobal(const TString &name) const;
+    const TSymbol *findGlobal(const ImmutableString &name) const;
 
-    const TSymbol *findBuiltIn(const TString &name, int shaderVersion) const;
+    const TSymbol *findBuiltIn(const ImmutableString &name, int shaderVersion) const;
 
-    const TSymbol *findBuiltIn(const TString &name,
+    const TSymbol *findBuiltIn(const ImmutableString &name,
                                int shaderVersion,
                                bool includeGLSLBuiltins) const;
 
@@ -238,13 +246,13 @@
     ESymbolLevel currentLevel() const { return static_cast<ESymbolLevel>(table.size() - 1); }
 
     TVariable *insertVariable(ESymbolLevel level,
-                              const TString *name,
+                              const ImmutableString &name,
                               const TType *type,
                               SymbolType symbolType);
 
     bool insert(ESymbolLevel level, TSymbol *symbol);
 
-    TFunction *findUserDefinedFunction(const TString &name) const;
+    TFunction *findUserDefinedFunction(const ImmutableString &name) const;
 
     // Used to insert unmangled functions to check redeclaration of built-ins in ESSL 3.00 and
     // above.
@@ -265,11 +273,10 @@
 };
 
 template <TPrecision precision>
-bool TSymbolTable::insertConstInt(ESymbolLevel level, const char *name, int value)
+bool TSymbolTable::insertConstInt(ESymbolLevel level, const ImmutableString &name, int value)
 {
-    TVariable *constant =
-        new TVariable(this, NewPoolTString(name),
-                      StaticType::Get<EbtInt, precision, EvqConst, 1, 1>(), SymbolType::BuiltIn);
+    TVariable *constant = new TVariable(
+        this, name, StaticType::Get<EbtInt, precision, EvqConst, 1, 1>(), SymbolType::BuiltIn);
     TConstantUnion *unionArray = new TConstantUnion[1];
     unionArray[0].setIConst(value);
     constant->shareConstPointer(unionArray);
@@ -279,12 +286,11 @@
 template <TPrecision precision>
 bool TSymbolTable::insertConstIntExt(ESymbolLevel level,
                                      TExtension ext,
-                                     const char *name,
+                                     const ImmutableString &name,
                                      int value)
 {
-    TVariable *constant        = new TVariable(this, NewPoolTString(name),
-                                        StaticType::Get<EbtInt, precision, EvqConst, 1, 1>(),
-                                        SymbolType::BuiltIn, ext);
+    TVariable *constant = new TVariable(
+        this, name, StaticType::Get<EbtInt, precision, EvqConst, 1, 1>(), SymbolType::BuiltIn, ext);
     TConstantUnion *unionArray = new TConstantUnion[1];
     unionArray[0].setIConst(value);
     constant->shareConstPointer(unionArray);
@@ -293,12 +299,11 @@
 
 template <TPrecision precision>
 bool TSymbolTable::insertConstIvec3(ESymbolLevel level,
-                                    const char *name,
+                                    const ImmutableString &name,
                                     const std::array<int, 3> &values)
 {
-    TVariable *constantIvec3 =
-        new TVariable(this, NewPoolTString(name),
-                      StaticType::Get<EbtInt, precision, EvqConst, 3, 1>(), SymbolType::BuiltIn);
+    TVariable *constantIvec3 = new TVariable(
+        this, name, StaticType::Get<EbtInt, precision, EvqConst, 3, 1>(), SymbolType::BuiltIn);
 
     TConstantUnion *unionArray = new TConstantUnion[3];
     for (size_t index = 0u; index < 3u; ++index)
diff --git a/src/compiler/translator/TextureFunctionHLSL.cpp b/src/compiler/translator/TextureFunctionHLSL.cpp
index 36898ff..96138a7 100644
--- a/src/compiler/translator/TextureFunctionHLSL.cpp
+++ b/src/compiler/translator/TextureFunctionHLSL.cpp
@@ -1166,7 +1166,7 @@
            std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method);
 }
 
-TString TextureFunctionHLSL::useTextureFunction(const TString &name,
+TString TextureFunctionHLSL::useTextureFunction(const ImmutableString &name,
                                                 TBasicType samplerType,
                                                 int coords,
                                                 size_t argumentCount,
diff --git a/src/compiler/translator/TextureFunctionHLSL.h b/src/compiler/translator/TextureFunctionHLSL.h
index 68bf8c0..04d248b 100644
--- a/src/compiler/translator/TextureFunctionHLSL.h
+++ b/src/compiler/translator/TextureFunctionHLSL.h
@@ -55,7 +55,7 @@
 
     // Returns the name of the texture function implementation to call.
     // The name that's passed in is the name of the GLSL texture function that it should implement.
-    TString useTextureFunction(const TString &name,
+    TString useTextureFunction(const ImmutableString &name,
                                TBasicType samplerType,
                                int coords,
                                size_t argumentCount,
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
index 000c2f2..eec34fc 100644
--- a/src/compiler/translator/TranslatorVulkan.cpp
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -77,8 +77,8 @@
     {
         if (mInDefaultUniform)
         {
-            const TString &name = symbol->variable().name();
-            ASSERT(name.substr(0, 3) != "gl_");
+            const ImmutableString &name = symbol->variable().name();
+            ASSERT(!name.beginsWith("gl_"));
             (*mSink) << HashName(name, mHashFunction, mNameMap);
         }
     }
diff --git a/src/compiler/translator/Types.cpp b/src/compiler/translator/Types.cpp
index 47759bd..929130e 100644
--- a/src/compiler/translator/Types.cpp
+++ b/src/compiler/translator/Types.cpp
@@ -9,6 +9,7 @@
 #endif
 
 #include "compiler/translator/Types.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/InfoSink.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/SymbolTable.h"
@@ -498,13 +499,13 @@
                 mangledName += "struct-";
                 if (mStructure->symbolType() != SymbolType::Empty)
                 {
-                    mangledName += mStructure->name();
+                    mangledName += mStructure->name().data();
                 }
                 mangledName += mStructure->mangledFieldList();
                 break;
             case EbtInterfaceBlock:
                 mangledName += "iblock-";
-                mangledName += mInterfaceBlock->name();
+                mangledName += mInterfaceBlock->name().data();
                 mangledName += mInterfaceBlock->mangledFieldList();
                 break;
             default:
@@ -788,7 +789,7 @@
     mMangledName = nullptr;
 }
 
-void TType::createSamplerSymbols(const TString &namePrefix,
+void TType::createSamplerSymbols(const ImmutableString &namePrefix,
                                  const TString &apiNamePrefix,
                                  TVector<const TVariable *> *outputSymbols,
                                  TMap<const TVariable *, TString> *outputSymbolsToAPINames,
@@ -802,26 +803,26 @@
             elementType.toArrayElementType();
             for (unsigned int arrayIndex = 0u; arrayIndex < getOutermostArraySize(); ++arrayIndex)
             {
-                TStringStream elementName;
+                std::stringstream elementName;
                 elementName << namePrefix << "_" << arrayIndex;
                 TStringStream elementApiName;
                 elementApiName << apiNamePrefix << "[" << arrayIndex << "]";
-                elementType.createSamplerSymbols(elementName.str(), elementApiName.str(),
-                                                 outputSymbols, outputSymbolsToAPINames,
-                                                 symbolTable);
+                elementType.createSamplerSymbols(ImmutableString(elementName.str()),
+                                                 elementApiName.str(), outputSymbols,
+                                                 outputSymbolsToAPINames, symbolTable);
             }
         }
         else
         {
-            mStructure->createSamplerSymbols(namePrefix, apiNamePrefix, outputSymbols,
+            mStructure->createSamplerSymbols(namePrefix.data(), apiNamePrefix, outputSymbols,
                                              outputSymbolsToAPINames, symbolTable);
         }
         return;
     }
 
     ASSERT(IsSampler(type));
-    TVariable *variable = new TVariable(symbolTable, NewPoolTString(namePrefix.c_str()),
-                                        new TType(*this), SymbolType::AngleInternal);
+    TVariable *variable =
+        new TVariable(symbolTable, namePrefix, new TType(*this), SymbolType::AngleInternal);
     outputSymbols->push_back(variable);
     if (outputSymbolsToAPINames)
     {
diff --git a/src/compiler/translator/Types.h b/src/compiler/translator/Types.h
index d56b07c..47f96aa 100644
--- a/src/compiler/translator/Types.h
+++ b/src/compiler/translator/Types.h
@@ -12,6 +12,7 @@
 
 #include "compiler/translator/BaseTypes.h"
 #include "compiler/translator/Common.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/SymbolUniqueId.h"
 
 namespace sh
@@ -30,22 +31,21 @@
 {
   public:
     POOL_ALLOCATOR_NEW_DELETE();
-    TField(TType *type, const TString *name, const TSourceLoc &line)
+    TField(TType *type, const ImmutableString &name, const TSourceLoc &line)
         : mType(type), mName(name), mLine(line)
     {
-        ASSERT(mName);
     }
 
     // TODO(alokp): We should only return const type.
     // Fix it by tweaking grammar.
     TType *type() { return mType; }
     const TType *type() const { return mType; }
-    const TString &name() const { return *mName; }
+    const ImmutableString &name() const { return mName; }
     const TSourceLoc &line() const { return mLine; }
 
   private:
     TType *mType;
-    const TString *mName;
+    const ImmutableString mName;
     const TSourceLoc mLine;
 };
 
@@ -309,7 +309,8 @@
     // several copies of it in the output code is undesirable for performance.
     bool canReplaceWithConstantUnion() const;
 
-    void createSamplerSymbols(const TString &namePrefix,
+    // The char arrays passed in must be pool allocated or static.
+    void createSamplerSymbols(const ImmutableString &namePrefix,
                               const TString &apiNamePrefix,
                               TVector<const TVariable *> *outputSymbols,
                               TMap<const TVariable *, TString> *outputSymbolsToAPINames,
diff --git a/src/compiler/translator/UniformHLSL.cpp b/src/compiler/translator/UniformHLSL.cpp
index 04f0ff7..33a5d88 100644
--- a/src/compiler/translator/UniformHLSL.cpp
+++ b/src/compiler/translator/UniformHLSL.cpp
@@ -10,6 +10,7 @@
 #include "compiler/translator/UniformHLSL.h"
 
 #include "common/utilities.h"
+#include "compiler/translator/ImmutableStringBuilder.h"
 #include "compiler/translator/StructureHLSL.h"
 #include "compiler/translator/UtilsHLSL.h"
 #include "compiler/translator/blocklayoutHLSL.h"
@@ -21,6 +22,8 @@
 namespace
 {
 
+constexpr const ImmutableString kAngleDecorString("angle_");
+
 static const char *UniformRegisterPrefix(const TType &type)
 {
     if (IsSampler(type.getBasicType()))
@@ -119,11 +122,11 @@
     mUniformBlockRegister = registerCount;
 }
 
-const Uniform *UniformHLSL::findUniformByName(const TString &name) const
+const Uniform *UniformHLSL::findUniformByName(const ImmutableString &name) const
 {
     for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); ++uniformIndex)
     {
-        if (mUniforms[uniformIndex].name == name.c_str())
+        if (name == mUniforms[uniformIndex].name)
         {
             return &mUniforms[uniformIndex];
         }
@@ -133,7 +136,7 @@
 }
 
 unsigned int UniformHLSL::assignUniformRegister(const TType &type,
-                                                const TString &name,
+                                                const ImmutableString &name,
                                                 unsigned int *outRegisterCount)
 {
     unsigned int registerIndex;
@@ -210,7 +213,7 @@
     for (const TVariable *uniform : group)
     {
         const TType &type   = uniform->getType();
-        const TString &name = uniform->name();
+        const ImmutableString &name = uniform->name();
         unsigned int registerCount;
 
         // The uniform might be just a regular sampler or one extracted from a struct.
@@ -379,7 +382,11 @@
             {
                 TVector<const TVariable *> samplerSymbols;
                 TMap<const TVariable *, TString> symbolsToAPINames;
-                type.createSamplerSymbols("angle_" + variable.name(), variable.name(),
+                ImmutableStringBuilder namePrefix(kAngleDecorString.length() +
+                                                  variable.name().length());
+                namePrefix << kAngleDecorString;
+                namePrefix << variable.name();
+                type.createSamplerSymbols(namePrefix, TString(variable.name().data()),
                                           &samplerSymbols, &symbolsToAPINames, symbolTable);
                 for (const TVariable *sampler : samplerSymbols)
                 {
@@ -471,7 +478,7 @@
         }
 
         unsigned int activeRegister                             = mUniformBlockRegister;
-        mUniformBlockRegisterMap[interfaceBlock.name().c_str()] = activeRegister;
+        mUniformBlockRegisterMap[interfaceBlock.name().data()]  = activeRegister;
 
         if (instanceVariable != nullptr && instanceVariable->getType().isArray())
         {
@@ -499,9 +506,8 @@
                                         unsigned int registerIndex,
                                         unsigned int arrayIndex)
 {
-    const TString &arrayIndexString =
-        (arrayIndex != GL_INVALID_INDEX ? Decorate(str(arrayIndex)) : "");
-    const TString &blockName = interfaceBlock.name() + arrayIndexString;
+    const TString &arrayIndexString = (arrayIndex != GL_INVALID_INDEX ? str(arrayIndex) : "");
+    const TString &blockName        = TString(interfaceBlock.name().data()) + arrayIndexString;
     TString hlsl;
 
     hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) +
@@ -524,7 +530,7 @@
     return hlsl;
 }
 
-TString UniformHLSL::UniformBlockInstanceString(const TString &instanceName,
+TString UniformHLSL::UniformBlockInstanceString(const ImmutableString &instanceName,
                                                 unsigned int arrayIndex)
 {
     if (arrayIndex != GL_INVALID_INDEX)
diff --git a/src/compiler/translator/UniformHLSL.h b/src/compiler/translator/UniformHLSL.h
index 914d663..aa02fb1 100644
--- a/src/compiler/translator/UniformHLSL.h
+++ b/src/compiler/translator/UniformHLSL.h
@@ -15,6 +15,7 @@
 
 namespace sh
 {
+class ImmutableString;
 class StructureHLSL;
 class TSymbolTable;
 
@@ -40,7 +41,8 @@
     TString uniformBlocksHeader(const ReferencedInterfaceBlocks &referencedInterfaceBlocks);
 
     // Used for direct index references
-    static TString UniformBlockInstanceString(const TString &instanceName, unsigned int arrayIndex);
+    static TString UniformBlockInstanceString(const ImmutableString &instanceName,
+                                              unsigned int arrayIndex);
 
     const std::map<std::string, unsigned int> &getUniformBlockRegisterMap() const
     {
@@ -59,7 +61,7 @@
     TString uniformBlockMembersString(const TInterfaceBlock &interfaceBlock,
                                       TLayoutBlockStorage blockStorage);
     TString uniformBlockStructString(const TInterfaceBlock &interfaceBlock);
-    const Uniform *findUniformByName(const TString &name) const;
+    const Uniform *findUniformByName(const ImmutableString &name) const;
 
     void outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
                                     const TType &type,
@@ -80,7 +82,7 @@
 
     // Returns the uniform's register index
     unsigned int assignUniformRegister(const TType &type,
-                                       const TString &name,
+                                       const ImmutableString &name,
                                        unsigned int *outRegisterCount);
     unsigned int assignSamplerInStructUniformRegister(const TType &type,
                                                       const TString &name,
diff --git a/src/compiler/translator/UseInterfaceBlockFields.cpp b/src/compiler/translator/UseInterfaceBlockFields.cpp
index 40bd42a..df0c2f9 100644
--- a/src/compiler/translator/UseInterfaceBlockFields.cpp
+++ b/src/compiler/translator/UseInterfaceBlockFields.cpp
@@ -43,9 +43,8 @@
                            TIntermSequence *sequence,
                            const TSymbolTable &symbolTable)
 {
-    TString name = TString(var.name.c_str());
-    ASSERT(name.find_last_of('[') == TString::npos);
-    TIntermSymbol *symbol = ReferenceGlobalVariable(name, symbolTable);
+    ASSERT(var.name.find_last_of('[') == TString::npos);
+    TIntermSymbol *symbol = ReferenceGlobalVariable(ImmutableString(var.name), symbolTable);
     AddNodeUseStatements(symbol, sequence);
 }
 
@@ -74,8 +73,8 @@
         }
         else if (block.arraySize > 0u)
         {
-            TString name(block.instanceName.c_str());
-            TIntermSymbol *arraySymbol = ReferenceGlobalVariable(name, symbolTable);
+            TIntermSymbol *arraySymbol =
+                ReferenceGlobalVariable(ImmutableString(block.instanceName), symbolTable);
             for (unsigned int i = 0u; i < block.arraySize; ++i)
             {
                 TIntermBinary *elementSymbol =
@@ -85,8 +84,8 @@
         }
         else
         {
-            TString name(block.instanceName.c_str());
-            TIntermSymbol *blockSymbol = ReferenceGlobalVariable(name, symbolTable);
+            TIntermSymbol *blockSymbol =
+                ReferenceGlobalVariable(ImmutableString(block.instanceName), symbolTable);
             InsertUseCode(block, blockSymbol, sequence);
         }
     }
diff --git a/src/compiler/translator/UtilsHLSL.cpp b/src/compiler/translator/UtilsHLSL.cpp
index 14ba353..f21598e 100644
--- a/src/compiler/translator/UtilsHLSL.cpp
+++ b/src/compiler/translator/UtilsHLSL.cpp
@@ -347,7 +347,7 @@
     return TextureGroupSuffix(TextureGroup(type, imageInternalFormat));
 }
 
-TString TextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+const char *TextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
 {
     switch (type)
     {
@@ -593,7 +593,7 @@
     return RWTextureString(RWTextureGroup(type, imageInternalFormat));
 }
 
-TString RWTextureGroupSuffix(const HLSLRWTextureGroup type)
+const char *RWTextureGroupSuffix(const HLSLRWTextureGroup type)
 {
     switch (type)
     {
@@ -634,12 +634,14 @@
     return "<unknown read and write resource>";
 }
 
-TString RWTextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+const char *RWTextureGroupSuffix(const TBasicType type,
+                                 TLayoutImageInternalFormat imageInternalFormat)
 {
     return RWTextureGroupSuffix(RWTextureGroup(type, imageInternalFormat));
 }
 
-TString RWTextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+const char *RWTextureTypeSuffix(const TBasicType type,
+                                TLayoutImageInternalFormat imageInternalFormat)
 {
     switch (type)
     {
@@ -691,40 +693,40 @@
     }
 }
 
-TString DecorateField(const TString &string, const TStructure &structure)
+TString DecorateField(const ImmutableString &string, const TStructure &structure)
 {
     if (structure.symbolType() != SymbolType::BuiltIn)
     {
         return Decorate(string);
     }
 
-    return string;
+    return TString(string.data());
 }
 
-TString DecoratePrivate(const TString &privateText)
+TString DecoratePrivate(const ImmutableString &privateText)
 {
-    return "dx_" + privateText;
+    return "dx_" + TString(privateText.data());
 }
 
-TString Decorate(const TString &string)
+TString Decorate(const ImmutableString &string)
 {
-    if (string.compare(0, 3, "gl_") != 0)
+    if (!string.beginsWith("gl_"))
     {
-        return "_" + string;
+        return "_" + TString(string.data());
     }
 
-    return string;
+    return TString(string.data());
 }
 
 TString DecorateVariableIfNeeded(const TVariable &variable)
 {
     if (variable.symbolType() == SymbolType::AngleInternal)
     {
-        const TString &name = variable.name();
+        const ImmutableString &name = variable.name();
         // The name should not have a prefix reserved for user-defined variables or functions.
-        ASSERT(name.compare(0, 2, "f_") != 0);
-        ASSERT(name.compare(0, 1, "_") != 0);
-        return name;
+        ASSERT(!name.beginsWith("f_"));
+        ASSERT(!name.beginsWith("_"));
+        return TString(name.data());
     }
     else
     {
@@ -737,15 +739,15 @@
     if (func->symbolType() == SymbolType::AngleInternal)
     {
         // The name should not have a prefix reserved for user-defined variables or functions.
-        ASSERT(func->name().compare(0, 2, "f_") != 0);
-        ASSERT(func->name().compare(0, 1, "_") != 0);
-        return func->name();
+        ASSERT(!func->name().beginsWith("f_"));
+        ASSERT(!func->name().beginsWith("_"));
+        return TString(func->name().data());
     }
-    ASSERT(func->name().compare(0, 3, "gl_") != 0);
+    ASSERT(!func->name().beginsWith("gl_"));
     // Add an additional f prefix to functions so that they're always disambiguated from variables.
     // This is necessary in the corner case where a variable declaration hides a function that it
     // uses in its initializer.
-    return "f_" + func->name();
+    return "f_" + TString(func->name().data());
 }
 
 TString TypeString(const TType &type)
@@ -860,7 +862,7 @@
         return Decorate(structure.name());
     }
 
-    return "ss" + str(structure.uniqueId().get()) + "_" + structure.name();
+    return "ss" + str(structure.uniqueId().get()) + "_" + TString(structure.name().data());
 }
 
 TString QualifiedStructNameString(const TStructure &structure,
diff --git a/src/compiler/translator/UtilsHLSL.h b/src/compiler/translator/UtilsHLSL.h
index a6af60e..18e678d 100644
--- a/src/compiler/translator/UtilsHLSL.h
+++ b/src/compiler/translator/UtilsHLSL.h
@@ -93,25 +93,27 @@
 const char *TextureGroupSuffix(const HLSLTextureGroup type);
 const char *TextureGroupSuffix(const TBasicType type,
                                TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
-TString TextureTypeSuffix(const TBasicType type,
-                          TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+const char *TextureTypeSuffix(const TBasicType type,
+                              TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
 HLSLRWTextureGroup RWTextureGroup(const TBasicType type,
                                   TLayoutImageInternalFormat imageInternalFormat);
 TString RWTextureString(const HLSLRWTextureGroup textureGroup);
 TString RWTextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
-TString RWTextureGroupSuffix(const HLSLRWTextureGroup type);
-TString RWTextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
-TString RWTextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+const char *RWTextureGroupSuffix(const HLSLRWTextureGroup type);
+const char *RWTextureGroupSuffix(const TBasicType type,
+                                 TLayoutImageInternalFormat imageInternalFormat);
+const char *RWTextureTypeSuffix(const TBasicType type,
+                                TLayoutImageInternalFormat imageInternalFormat);
 
 TString SamplerString(const TBasicType type);
 TString SamplerString(HLSLTextureGroup type);
 
 // Adds a prefix to user-defined names to avoid naming clashes.
-TString Decorate(const TString &string);
+TString Decorate(const ImmutableString &string);
 TString DecorateVariableIfNeeded(const TVariable &variable);
 TString DecorateFunctionIfNeeded(const TFunction *func);
-TString DecorateField(const TString &string, const TStructure &structure);
-TString DecoratePrivate(const TString &privateText);
+TString DecorateField(const ImmutableString &string, const TStructure &structure);
+TString DecoratePrivate(const ImmutableString &privateText);
 TString TypeString(const TType &type);
 TString StructNameString(const TStructure &structure);
 TString QualifiedStructNameString(const TStructure &structure,
diff --git a/src/compiler/translator/ValidateLimitations.cpp b/src/compiler/translator/ValidateLimitations.cpp
index 8b9eef3..b9fd73e 100644
--- a/src/compiler/translator/ValidateLimitations.cpp
+++ b/src/compiler/translator/ValidateLimitations.cpp
@@ -80,6 +80,7 @@
 
   private:
     void error(TSourceLoc loc, const char *reason, const char *token);
+    void error(TSourceLoc loc, const char *reason, const ImmutableString &token);
 
     bool isLoopIndex(TIntermSymbol *symbol);
     bool validateLoopType(TIntermLoop *node);
@@ -117,7 +118,7 @@
     {
         error(node->getLine(),
               "Loop index cannot be statically assigned to within the body of the loop",
-              node->getName().c_str());
+              node->getName());
     }
 }
 
@@ -161,6 +162,13 @@
     mDiagnostics->error(loc, reason, token);
 }
 
+void ValidateLimitationsTraverser::error(TSourceLoc loc,
+                                         const char *reason,
+                                         const ImmutableString &token)
+{
+    error(loc, reason, token.data());
+}
+
 bool ValidateLimitationsTraverser::isLoopIndex(TIntermSymbol *symbol)
 {
     return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->uniqueId().get()) !=
@@ -246,7 +254,7 @@
     if (!isConstExpr(declInit->getRight()))
     {
         error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
-              symbol->getName().c_str());
+              symbol->getName());
         return -1;
     }
 
@@ -280,7 +288,7 @@
     }
     if (symbol->uniqueId().get() != indexSymbolId)
     {
-        error(symbol->getLine(), "Expected loop index", symbol->getName().c_str());
+        error(symbol->getLine(), "Expected loop index", symbol->getName());
         return false;
     }
     // Relational operator is one of: > >= < <= == or !=.
@@ -302,7 +310,7 @@
     if (!isConstExpr(binOp->getRight()))
     {
         error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
-              symbol->getName().c_str());
+              symbol->getName());
         return false;
     }
 
@@ -351,7 +359,7 @@
     }
     if (symbol->uniqueId().get() != indexSymbolId)
     {
-        error(symbol->getLine(), "Expected loop index", symbol->getName().c_str());
+        error(symbol->getLine(), "Expected loop index", symbol->getName());
         return false;
     }
 
@@ -379,7 +387,7 @@
         if (!isConstExpr(binOp->getRight()))
         {
             error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
-                  symbol->getName().c_str());
+                  symbol->getName());
             return false;
         }
     }
diff --git a/src/compiler/translator/ValidateOutputs.cpp b/src/compiler/translator/ValidateOutputs.cpp
index 107c845..d77a0da 100644
--- a/src/compiler/translator/ValidateOutputs.cpp
+++ b/src/compiler/translator/ValidateOutputs.cpp
@@ -23,7 +23,7 @@
 
 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
 {
-    diagnostics->error(symbol.getLine(), reason, symbol.getName().c_str());
+    diagnostics->error(symbol.getLine(), reason, symbol.getName().data());
 }
 
 class ValidateOutputsTraverser : public TIntermTraverser
diff --git a/src/compiler/translator/ValidateVaryingLocations.cpp b/src/compiler/translator/ValidateVaryingLocations.cpp
index dbe5c19..728059d 100644
--- a/src/compiler/translator/ValidateVaryingLocations.cpp
+++ b/src/compiler/translator/ValidateVaryingLocations.cpp
@@ -22,7 +22,7 @@
 
 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
 {
-    diagnostics->error(symbol.getLine(), reason, symbol.getName().c_str());
+    diagnostics->error(symbol.getLine(), reason, symbol.getName().data());
 }
 
 int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize)
diff --git a/src/compiler/translator/VersionGLSL.cpp b/src/compiler/translator/VersionGLSL.cpp
index e9d654c..7e634a6 100644
--- a/src/compiler/translator/VersionGLSL.cpp
+++ b/src/compiler/translator/VersionGLSL.cpp
@@ -12,6 +12,11 @@
 namespace sh
 {
 
+namespace
+{
+constexpr const ImmutableString kGlPointCoordString("gl_PointCoord");
+}  // anonymous namespace
+
 int ShaderOutputTypeToGLSLVersion(ShShaderOutput output)
 {
     switch (output)
@@ -77,7 +82,8 @@
 
 void TVersionGLSL::visitSymbol(TIntermSymbol *node)
 {
-    if (node->variable().symbolType() == SymbolType::BuiltIn && node->getName() == "gl_PointCoord")
+    if (node->variable().symbolType() == SymbolType::BuiltIn &&
+        node->getName() == kGlPointCoordString)
     {
         ensureVersionIsAtLeast(GLSL_VERSION_120);
     }
diff --git a/src/compiler/translator/glslang.l b/src/compiler/translator/glslang.l
index 6a52f4b..549787a 100644
--- a/src/compiler/translator/glslang.l
+++ b/src/compiler/translator/glslang.l
@@ -268,17 +268,17 @@
 "isampler2DMSArray" |
 "usampler2DMSArray" { 
     if (context->getShaderVersion() < 300) {
-		yylval->lex.string = NewPoolTString(yytext); 
-	    return check_type(yyscanner); 
-	}
-	return reserved_word(yyscanner);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
+        return check_type(yyscanner);
+    }
+    return reserved_word(yyscanner);
 }
 
     /* Reserved keywords in GLSL ES 1.00 that are not reserved in GLSL ES 3.00 */
 "packed"  {
     if (context->getShaderVersion() >= 300)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -337,7 +337,7 @@
 "using"        { return reserved_word(yyscanner); }
 
 {L}({L}|{D})*       {
-   yylval->lex.string = NewPoolTString(yytext); 
+   yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
 }
 
@@ -406,7 +406,7 @@
 
 <FIELDS>{L}({L}|{D})* {
     BEGIN(INITIAL);
-    yylval->lex.string = NewPoolTString(yytext);
+    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
     return FIELD_SELECTION;
 }
 <FIELDS>[ \t\v\f\r] {}
@@ -441,7 +441,8 @@
     struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
 
     int token = IDENTIFIER;
-    const TSymbol* symbol = yyextra->symbolTable.find(yytext, yyextra->getShaderVersion());
+    // Note that the ImmutableString used here isn't static or pool allocated - but it's fine since yytext is valid for the duration of its use.
+    const TSymbol* symbol = yyextra->symbolTable.find(ImmutableString(yytext, yyleng), yyextra->getShaderVersion());
     if (symbol && symbol->isStruct())
     {
         token = TYPE_NAME;
@@ -488,7 +489,7 @@
 
     if (context->getShaderVersion() < 300)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
     else if (context->getShaderVersion() == 300)
@@ -507,7 +508,7 @@
     // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
     if (context->getShaderVersion() < 300)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -523,7 +524,7 @@
     // except when multiview extension is enabled
     if (context->getShaderVersion() < 300 && !context->isExtensionEnabled(TExtension::OVR_multiview))
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -550,7 +551,7 @@
     // not a reserved word in GLSL ES 1.00 and GLSL ES 3.00, so could be used as an identifier/type name
     if (context->getShaderVersion() < 310)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -568,7 +569,7 @@
         return token;
     }
 
-    yylval->lex.string = NewPoolTString(yytext);
+    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
     return check_type(yyscanner);
 }
 
@@ -641,11 +642,11 @@
     // a reserved word in GLSL ES 3.00 with enabled extension, otherwise could be used as an identifier/type name
     if (context->getShaderVersion() >= 300 && context->isExtensionEnabled(TExtension::EXT_YUV_target))
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return YUVCSCSTANDARDEXTCONSTANT;
     }
 
-    yylval->lex.string = NewPoolTString(yytext);
+    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
     return check_type(yyscanner);
 }
 
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index 897e1e9..1fe17ee 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -63,7 +63,7 @@
 %union {
     struct {
         union {
-            TString *string;
+            const char *string;  // pool allocated.
             float f;
             int i;
             unsigned int u;
@@ -255,10 +255,7 @@
 variable_identifier
     : IDENTIFIER {
         // The symbol table search was done in the lexical phase
-        $$ = context->parseVariableIdentifier(@1, $1.string, $1.symbol);
-
-        // don't delete $1.string, it's used by error recovery, and the pool
-        // pop will reclaim the memory
+        $$ = context->parseVariableIdentifier(@1, ImmutableString($1.string), $1.symbol);
     }
     ;
 
@@ -289,10 +286,10 @@
     | YUVCSCSTANDARDEXTCONSTANT {
         if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
         {
-           context->error(@1, "unsupported value", $1.string->c_str());
+           context->error(@1, "unsupported value", ImmutableString($1.string));
         }
         TConstantUnion *unionArray = new TConstantUnion[1];
-        unionArray->setYuvCscStandardEXTConst(getYuvCscStandardEXT($1.string->c_str()));
+        unionArray->setYuvCscStandardEXTConst(getYuvCscStandardEXT(ImmutableString($1.string)));
         $$ = context->addScalarLiteral(unionArray, @1);
     }
     | LEFT_PAREN expression RIGHT_PAREN {
@@ -311,7 +308,7 @@
         $$ = $1;
     }
     | postfix_expression DOT FIELD_SELECTION {
-        $$ = context->addFieldSelectionExpression($1, @2, *$3.string, @3);
+        $$ = context->addFieldSelectionExpression($1, @2, ImmutableString($3.string), @3);
     }
     | postfix_expression INC_OP {
         $$ = context->addUnaryMathLValue(EOpPostIncrement, $1, @2);
@@ -387,10 +384,10 @@
         $$ = context->addConstructorFunc($1);
     }
     | IDENTIFIER {
-        $$ = context->addNonConstructorFunc($1.string);
+        $$ = context->addNonConstructorFunc(ImmutableString($1.string));
     }
     | FIELD_SELECTION {
-        $$ = context->addNonConstructorFunc($1.string);
+        $$ = context->addNonConstructorFunc(ImmutableString($1.string));
     }
     ;
 
@@ -592,7 +589,7 @@
 
 enter_struct
     : IDENTIFIER LEFT_BRACE {
-        context->enterStructDeclaration(@1, *$1.string);
+        context->enterStructDeclaration(@1, ImmutableString($1.string));
         $$ = $1;
     }
     ;
@@ -609,16 +606,16 @@
         $$ = nullptr;
     }
     | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE SEMICOLON {
-        ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
-        $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, NULL, @$, NULL, @$);
+        ES3_OR_NEWER(ImmutableString($2.string), @1, "interface blocks");
+        $$ = context->addInterfaceBlock(*$1, @2, ImmutableString($2.string), $3, ImmutableString(""), @$, NULL, @$);
     }
     | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON {
-        ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
-        $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, NULL, @$);
+        ES3_OR_NEWER(ImmutableString($2.string), @1, "interface blocks");
+        $$ = context->addInterfaceBlock(*$1, @2, ImmutableString($2.string), $3, ImmutableString($5.string), @5, NULL, @$);
     }
     | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET SEMICOLON {
-        ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
-        $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, $7, @6);
+        ES3_OR_NEWER(ImmutableString($2.string), @1, "interface blocks");
+        $$ = context->addInterfaceBlock(*$1, @2, ImmutableString($2.string), $3, ImmutableString($5.string), @5, $7, @6);
     }
     | type_qualifier SEMICOLON {
         context->parseGlobalLayoutQualifier(*$1);
@@ -626,7 +623,7 @@
     }
     | type_qualifier IDENTIFIER SEMICOLON // e.g. to qualify an existing variable as invariant
     {
-        $$ = context->parseInvariantDeclaration(*$1, @2, $2.string, $2.symbol);
+        $$ = context->parseInvariantDeclaration(*$1, @2, ImmutableString($2.string), $2.symbol);
     }
     ;
 
@@ -674,7 +671,7 @@
 
 function_header
     : fully_specified_type IDENTIFIER LEFT_PAREN {
-        $$ = context->parseFunctionHeader($1, $2.string, @2);
+        $$ = context->parseFunctionHeader($1, ImmutableString($2.string), @2);
 
         context->symbolTable.push();
         context->enterFunctionDeclaration();
@@ -684,10 +681,10 @@
 parameter_declarator
     // Type + name
     : type_specifier identifier {
-        $$ = context->parseParameterDeclarator($1, $2.string, @2);
+        $$ = context->parseParameterDeclarator($1, ImmutableString($2.string), @2);
     }
     | type_specifier identifier array_specifier {
-        $$ = context->parseParameterArrayDeclarator($2.string, @2, *($3), @3, &$1);
+        $$ = context->parseParameterArrayDeclarator(ImmutableString($2.string), @2, *($3), @3, &$1);
     }
     ;
 
@@ -723,44 +720,44 @@
     }
     | init_declarator_list COMMA identifier {
         $$ = $1;
-        context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration);
+        context->parseDeclarator($$.type, @3, ImmutableString($3.string), $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier array_specifier {
         $$ = $1;
-        context->parseArrayDeclarator($$.type, @3, *$3.string, @4, *($4), $$.intermDeclaration);
+        context->parseArrayDeclarator($$.type, @3, ImmutableString($3.string), @4, *($4), $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier array_specifier EQUAL initializer {
         ES3_OR_NEWER("=", @5, "first-class arrays (array initializer)");
         $$ = $1;
-        context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, *($4), @5, $6, $$.intermDeclaration);
+        context->parseArrayInitDeclarator($$.type, @3, ImmutableString($3.string), @4, *($4), @5, $6, $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier EQUAL initializer {
         $$ = $1;
-        context->parseInitDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
+        context->parseInitDeclarator($$.type, @3, ImmutableString($3.string), @4, $5, $$.intermDeclaration);
     }
     ;
 
 single_declaration
     : fully_specified_type {
         $$.type = $1;
-        $$.intermDeclaration = context->parseSingleDeclaration($$.type, @1, "");
+        $$.intermDeclaration = context->parseSingleDeclaration($$.type, @1, ImmutableString(""));
     }
     | fully_specified_type identifier {
         $$.type = $1;
-        $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string);
+        $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, ImmutableString($2.string));
     }
     | fully_specified_type identifier array_specifier {
         $$.type = $1;
-        $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, *($3));
+        $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, ImmutableString($2.string), @3, *($3));
     }
     | fully_specified_type identifier array_specifier EQUAL initializer {
         ES3_OR_NEWER("[]", @3, "first-class arrays (array initializer)");
         $$.type = $1;
-        $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, *($3), @4, $5);
+        $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, ImmutableString($2.string), @3, *($3), @4, $5);
     }
     | fully_specified_type identifier EQUAL initializer {
         $$.type = $1;
-        $$.intermDeclaration = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
+        $$.intermDeclaration = context->parseSingleInitDeclaration($$.type, @2, ImmutableString($2.string), @3, $4);
     }
     ;
 
@@ -914,16 +911,16 @@
 
 layout_qualifier_id
     : IDENTIFIER {
-        $$ = context->parseLayoutQualifier(*$1.string, @1);
+        $$ = context->parseLayoutQualifier(ImmutableString($1.string), @1);
     }
     | IDENTIFIER EQUAL INTCONSTANT {
-        $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
+        $$ = context->parseLayoutQualifier(ImmutableString($1.string), @1, $3.i, @3);
     }
     | IDENTIFIER EQUAL UINTCONSTANT {
-        $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
+        $$ = context->parseLayoutQualifier(ImmutableString($1.string), @1, $3.i, @3);
     }
     | SHARED {
-        $$ = context->parseLayoutQualifier("shared", @1);
+        $$ = context->parseLayoutQualifier(ImmutableString("shared"), @1);
     }
     ;
 
@@ -1200,11 +1197,11 @@
     ;
 
 struct_specifier
-    : STRUCT identifier LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE {
-        $$ = context->addStructure(@1, @2, $2.string, $5);
+    : STRUCT identifier LEFT_BRACE { context->enterStructDeclaration(@2, ImmutableString($2.string)); } struct_declaration_list RIGHT_BRACE {
+        $$ = context->addStructure(@1, @2, ImmutableString($2.string), $5);
     }
-    | STRUCT LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE {
-        $$ = context->addStructure(@1, @$, nullptr, $4);
+    | STRUCT LEFT_BRACE { context->enterStructDeclaration(@2, ImmutableString("")); } struct_declaration_list RIGHT_BRACE {
+        $$ = context->addStructure(@1, @$, ImmutableString(""), $4);
     }
     ;
 
@@ -1239,10 +1236,10 @@
 
 struct_declarator
     : identifier {
-        $$ = context->parseStructDeclarator($1.string, @1);
+        $$ = context->parseStructDeclarator(ImmutableString($1.string), @1);
     }
     | identifier array_specifier {
-        $$ = context->parseStructArrayDeclarator($1.string, @1, $2);
+        $$ = context->parseStructArrayDeclarator(ImmutableString($1.string), @1, $2);
     }
     ;
 
@@ -1361,7 +1358,7 @@
         context->checkIsScalarBool($1->getLine(), $1);
     }
     | fully_specified_type identifier EQUAL initializer {
-        $$ = context->addConditionInitializer($1, *$2.string, $4, @2);
+        $$ = context->addConditionInitializer($1, ImmutableString($2.string), $4, @2);
     }
     ;
 
diff --git a/src/compiler/translator/glslang_lex.cpp b/src/compiler/translator/glslang_lex.cpp
index c1221a8..2bf623f 100644
--- a/src/compiler/translator/glslang_lex.cpp
+++ b/src/compiler/translator/glslang_lex.cpp
@@ -2157,10 +2157,10 @@
 YY_RULE_SETUP
 { 
     if (context->getShaderVersion() < 300) {
-		yylval->lex.string = NewPoolTString(yytext); 
-	    return check_type(yyscanner); 
-	}
-	return reserved_word(yyscanner);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
+        return check_type(yyscanner);
+    }
+    return reserved_word(yyscanner);
 }
 	YY_BREAK
 /* Reserved keywords in GLSL ES 1.00 that are not reserved in GLSL ES 3.00 */
@@ -2169,7 +2169,7 @@
 {
     if (context->getShaderVersion() >= 300)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -2223,7 +2223,7 @@
 case 184:
 YY_RULE_SETUP
 {
-   yylval->lex.string = NewPoolTString(yytext); 
+   yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
 }
 	YY_BREAK
@@ -2459,7 +2459,7 @@
 YY_RULE_SETUP
 {
     BEGIN(INITIAL);
-    yylval->lex.string = NewPoolTString(yytext);
+    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
     return FIELD_SELECTION;
 }
 	YY_BREAK
@@ -3797,7 +3797,8 @@
     struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
 
     int token = IDENTIFIER;
-    const TSymbol* symbol = yyextra->symbolTable.find(yytext, yyextra->getShaderVersion());
+    // Note that the ImmutableString used here isn't static or pool allocated - but it's fine since yytext is valid for the duration of its use.
+    const TSymbol* symbol = yyextra->symbolTable.find(ImmutableString(yytext, yyleng), yyextra->getShaderVersion());
     if (symbol && symbol->isStruct())
     {
         token = TYPE_NAME;
@@ -3844,7 +3845,7 @@
 
     if (context->getShaderVersion() < 300)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
     else if (context->getShaderVersion() == 300)
@@ -3863,7 +3864,7 @@
     // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
     if (context->getShaderVersion() < 300)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -3879,7 +3880,7 @@
     // except when multiview extension is enabled
     if (context->getShaderVersion() < 300 && !context->isExtensionEnabled(TExtension::OVR_multiview))
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -3906,7 +3907,7 @@
     // not a reserved word in GLSL ES 1.00 and GLSL ES 3.00, so could be used as an identifier/type name
     if (context->getShaderVersion() < 310)
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return check_type(yyscanner);
     }
 
@@ -3924,7 +3925,7 @@
         return token;
     }
 
-    yylval->lex.string = NewPoolTString(yytext);
+    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
     return check_type(yyscanner);
 }
 
@@ -3997,11 +3998,11 @@
     // a reserved word in GLSL ES 3.00 with enabled extension, otherwise could be used as an identifier/type name
     if (context->getShaderVersion() >= 300 && context->isExtensionEnabled(TExtension::EXT_YUV_target))
     {
-        yylval->lex.string = NewPoolTString(yytext);
+        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
         return YUVCSCSTANDARDEXTCONSTANT;
     }
 
-    yylval->lex.string = NewPoolTString(yytext);
+    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
     return check_type(yyscanner);
 }
 
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 89bd638..ead5cf5 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -304,7 +304,7 @@
 
     struct {
         union {
-            TString *string;
+            const char *string;  // pool allocated.
             float f;
             int i;
             unsigned int u;
@@ -743,36 +743,36 @@
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint16 yyrline[] =
 {
-       0,   252,   252,   253,   256,   266,   269,   274,   279,   284,
-     289,   298,   304,   307,   310,   313,   316,   319,   325,   332,
-     338,   341,   349,   352,   358,   361,   367,   371,   378,   386,
-     389,   392,   398,   401,   404,   407,   414,   415,   416,   417,
-     425,   426,   429,   432,   439,   440,   443,   449,   450,   454,
-     461,   462,   465,   468,   471,   477,   478,   481,   487,   488,
-     495,   496,   503,   504,   511,   512,   518,   519,   525,   526,
-     532,   533,   539,   540,   546,   547,   548,   549,   553,   554,
-     555,   559,   563,   567,   571,   578,   581,   587,   594,   601,
-     604,   607,   611,   615,   619,   623,   627,   634,   641,   644,
-     651,   659,   676,   686,   689,   695,   699,   703,   707,   714,
-     721,   724,   728,   732,   737,   744,   748,   752,   756,   761,
-     768,   772,   778,   781,   787,   791,   798,   804,   808,   812,
-     815,   818,   827,   832,   836,   839,   842,   845,   848,   852,
-     855,   859,   862,   865,   868,   871,   874,   881,   888,   891,
-     894,   900,   907,   910,   916,   919,   922,   925,   931,   934,
-     941,   946,   953,   958,   969,   972,   975,   978,   981,   984,
-     988,   992,   996,  1000,  1004,  1008,  1012,  1016,  1020,  1024,
-    1028,  1032,  1036,  1040,  1044,  1048,  1052,  1056,  1060,  1064,
-    1068,  1075,  1078,  1081,  1084,  1087,  1090,  1093,  1096,  1099,
-    1102,  1105,  1108,  1111,  1114,  1117,  1120,  1123,  1126,  1129,
-    1139,  1146,  1153,  1156,  1159,  1162,  1165,  1168,  1171,  1174,
-    1177,  1180,  1183,  1186,  1189,  1192,  1195,  1203,  1203,  1206,
-    1206,  1212,  1215,  1221,  1224,  1231,  1235,  1241,  1244,  1250,
-    1254,  1258,  1259,  1265,  1266,  1267,  1268,  1269,  1270,  1271,
-    1275,  1279,  1279,  1279,  1286,  1287,  1291,  1291,  1292,  1292,
-    1297,  1301,  1308,  1312,  1319,  1320,  1324,  1330,  1334,  1343,
-    1343,  1350,  1353,  1359,  1363,  1369,  1369,  1374,  1374,  1378,
-    1378,  1386,  1389,  1395,  1398,  1404,  1408,  1415,  1418,  1421,
-    1424,  1427,  1435,  1441,  1447,  1450,  1456,  1456
+       0,   252,   252,   253,   256,   263,   266,   271,   276,   281,
+     286,   295,   301,   304,   307,   310,   313,   316,   322,   329,
+     335,   338,   346,   349,   355,   358,   364,   368,   375,   383,
+     386,   389,   395,   398,   401,   404,   411,   412,   413,   414,
+     422,   423,   426,   429,   436,   437,   440,   446,   447,   451,
+     458,   459,   462,   465,   468,   474,   475,   478,   484,   485,
+     492,   493,   500,   501,   508,   509,   515,   516,   522,   523,
+     529,   530,   536,   537,   543,   544,   545,   546,   550,   551,
+     552,   556,   560,   564,   568,   575,   578,   584,   591,   598,
+     601,   604,   608,   612,   616,   620,   624,   631,   638,   641,
+     648,   656,   673,   683,   686,   692,   696,   700,   704,   711,
+     718,   721,   725,   729,   734,   741,   745,   749,   753,   758,
+     765,   769,   775,   778,   784,   788,   795,   801,   805,   809,
+     812,   815,   824,   829,   833,   836,   839,   842,   845,   849,
+     852,   856,   859,   862,   865,   868,   871,   878,   885,   888,
+     891,   897,   904,   907,   913,   916,   919,   922,   928,   931,
+     938,   943,   950,   955,   966,   969,   972,   975,   978,   981,
+     985,   989,   993,   997,  1001,  1005,  1009,  1013,  1017,  1021,
+    1025,  1029,  1033,  1037,  1041,  1045,  1049,  1053,  1057,  1061,
+    1065,  1072,  1075,  1078,  1081,  1084,  1087,  1090,  1093,  1096,
+    1099,  1102,  1105,  1108,  1111,  1114,  1117,  1120,  1123,  1126,
+    1136,  1143,  1150,  1153,  1156,  1159,  1162,  1165,  1168,  1171,
+    1174,  1177,  1180,  1183,  1186,  1189,  1192,  1200,  1200,  1203,
+    1203,  1209,  1212,  1218,  1221,  1228,  1232,  1238,  1241,  1247,
+    1251,  1255,  1256,  1262,  1263,  1264,  1265,  1266,  1267,  1268,
+    1272,  1276,  1276,  1276,  1283,  1284,  1288,  1288,  1289,  1289,
+    1294,  1298,  1305,  1309,  1316,  1317,  1321,  1327,  1331,  1340,
+    1340,  1347,  1350,  1356,  1360,  1366,  1366,  1371,  1371,  1375,
+    1375,  1383,  1386,  1392,  1395,  1401,  1405,  1412,  1415,  1418,
+    1421,  1424,  1432,  1438,  1444,  1447,  1453,  1453
 };
 #endif
 
@@ -2498,10 +2498,7 @@
 
     {
         // The symbol table search was done in the lexical phase
-        (yyval.interm.intermTypedNode) = context->parseVariableIdentifier((yylsp[0]), (yyvsp[0].lex).string, (yyvsp[0].lex).symbol);
-
-        // don't delete $1.string, it's used by error recovery, and the pool
-        // pop will reclaim the memory
+        (yyval.interm.intermTypedNode) = context->parseVariableIdentifier((yylsp[0]), ImmutableString((yyvsp[0].lex).string), (yyvsp[0].lex).symbol);
     }
 
     break;
@@ -2559,10 +2556,10 @@
     {
         if (!context->checkCanUseExtension((yylsp[0]), TExtension::EXT_YUV_target))
         {
-           context->error((yylsp[0]), "unsupported value", (yyvsp[0].lex).string->c_str());
+           context->error((yylsp[0]), "unsupported value", ImmutableString((yyvsp[0].lex).string));
         }
         TConstantUnion *unionArray = new TConstantUnion[1];
-        unionArray->setYuvCscStandardEXTConst(getYuvCscStandardEXT((yyvsp[0].lex).string->c_str()));
+        unionArray->setYuvCscStandardEXTConst(getYuvCscStandardEXT(ImmutableString((yyvsp[0].lex).string)));
         (yyval.interm.intermTypedNode) = context->addScalarLiteral(unionArray, (yylsp[0]));
     }
 
@@ -2603,7 +2600,7 @@
   case 15:
 
     {
-        (yyval.interm.intermTypedNode) = context->addFieldSelectionExpression((yyvsp[-2].interm.intermTypedNode), (yylsp[-1]), *(yyvsp[0].lex).string, (yylsp[0]));
+        (yyval.interm.intermTypedNode) = context->addFieldSelectionExpression((yyvsp[-2].interm.intermTypedNode), (yylsp[-1]), ImmutableString((yyvsp[0].lex).string), (yylsp[0]));
     }
 
     break;
@@ -2728,7 +2725,7 @@
   case 30:
 
     {
-        (yyval.interm.functionLookup) = context->addNonConstructorFunc((yyvsp[0].lex).string);
+        (yyval.interm.functionLookup) = context->addNonConstructorFunc(ImmutableString((yyvsp[0].lex).string));
     }
 
     break;
@@ -2736,7 +2733,7 @@
   case 31:
 
     {
-        (yyval.interm.functionLookup) = context->addNonConstructorFunc((yyvsp[0].lex).string);
+        (yyval.interm.functionLookup) = context->addNonConstructorFunc(ImmutableString((yyvsp[0].lex).string));
     }
 
     break;
@@ -3164,7 +3161,7 @@
   case 88:
 
     {
-        context->enterStructDeclaration((yylsp[-1]), *(yyvsp[-1].lex).string);
+        context->enterStructDeclaration((yylsp[-1]), ImmutableString((yyvsp[-1].lex).string));
         (yyval.lex) = (yyvsp[-1].lex);
     }
 
@@ -3198,8 +3195,8 @@
   case 92:
 
     {
-        ES3_OR_NEWER((yyvsp[-3].lex).string->c_str(), (yylsp[-4]), "interface blocks");
-        (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-4].interm.typeQualifierBuilder), (yylsp[-3]), *(yyvsp[-3].lex).string, (yyvsp[-2].interm.fieldList), NULL, (yyloc), NULL, (yyloc));
+        ES3_OR_NEWER(ImmutableString((yyvsp[-3].lex).string), (yylsp[-4]), "interface blocks");
+        (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-4].interm.typeQualifierBuilder), (yylsp[-3]), ImmutableString((yyvsp[-3].lex).string), (yyvsp[-2].interm.fieldList), ImmutableString(""), (yyloc), NULL, (yyloc));
     }
 
     break;
@@ -3207,8 +3204,8 @@
   case 93:
 
     {
-        ES3_OR_NEWER((yyvsp[-4].lex).string->c_str(), (yylsp[-5]), "interface blocks");
-        (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-5].interm.typeQualifierBuilder), (yylsp[-4]), *(yyvsp[-4].lex).string, (yyvsp[-3].interm.fieldList), (yyvsp[-1].lex).string, (yylsp[-1]), NULL, (yyloc));
+        ES3_OR_NEWER(ImmutableString((yyvsp[-4].lex).string), (yylsp[-5]), "interface blocks");
+        (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-5].interm.typeQualifierBuilder), (yylsp[-4]), ImmutableString((yyvsp[-4].lex).string), (yyvsp[-3].interm.fieldList), ImmutableString((yyvsp[-1].lex).string), (yylsp[-1]), NULL, (yyloc));
     }
 
     break;
@@ -3216,8 +3213,8 @@
   case 94:
 
     {
-        ES3_OR_NEWER((yyvsp[-7].lex).string->c_str(), (yylsp[-8]), "interface blocks");
-        (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-8].interm.typeQualifierBuilder), (yylsp[-7]), *(yyvsp[-7].lex).string, (yyvsp[-6].interm.fieldList), (yyvsp[-4].lex).string, (yylsp[-4]), (yyvsp[-2].interm.intermTypedNode), (yylsp[-3]));
+        ES3_OR_NEWER(ImmutableString((yyvsp[-7].lex).string), (yylsp[-8]), "interface blocks");
+        (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-8].interm.typeQualifierBuilder), (yylsp[-7]), ImmutableString((yyvsp[-7].lex).string), (yyvsp[-6].interm.fieldList), ImmutableString((yyvsp[-4].lex).string), (yylsp[-4]), (yyvsp[-2].interm.intermTypedNode), (yylsp[-3]));
     }
 
     break;
@@ -3234,7 +3231,7 @@
   case 96:
 
     {
-        (yyval.interm.intermNode) = context->parseInvariantDeclaration(*(yyvsp[-2].interm.typeQualifierBuilder), (yylsp[-1]), (yyvsp[-1].lex).string, (yyvsp[-1].lex).symbol);
+        (yyval.interm.intermNode) = context->parseInvariantDeclaration(*(yyvsp[-2].interm.typeQualifierBuilder), (yylsp[-1]), ImmutableString((yyvsp[-1].lex).string), (yyvsp[-1].lex).symbol);
     }
 
     break;
@@ -3299,7 +3296,7 @@
   case 102:
 
     {
-        (yyval.interm.function) = context->parseFunctionHeader((yyvsp[-2].interm.type), (yyvsp[-1].lex).string, (yylsp[-1]));
+        (yyval.interm.function) = context->parseFunctionHeader((yyvsp[-2].interm.type), ImmutableString((yyvsp[-1].lex).string), (yylsp[-1]));
 
         context->symbolTable.push();
         context->enterFunctionDeclaration();
@@ -3310,7 +3307,7 @@
   case 103:
 
     {
-        (yyval.interm.param) = context->parseParameterDeclarator((yyvsp[-1].interm.type), (yyvsp[0].lex).string, (yylsp[0]));
+        (yyval.interm.param) = context->parseParameterDeclarator((yyvsp[-1].interm.type), ImmutableString((yyvsp[0].lex).string), (yylsp[0]));
     }
 
     break;
@@ -3318,7 +3315,7 @@
   case 104:
 
     {
-        (yyval.interm.param) = context->parseParameterArrayDeclarator((yyvsp[-1].lex).string, (yylsp[-1]), *((yyvsp[0].interm.arraySizes)), (yylsp[0]), &(yyvsp[-2].interm.type));
+        (yyval.interm.param) = context->parseParameterArrayDeclarator(ImmutableString((yyvsp[-1].lex).string), (yylsp[-1]), *((yyvsp[0].interm.arraySizes)), (yylsp[0]), &(yyvsp[-2].interm.type));
     }
 
     break;
@@ -3380,7 +3377,7 @@
 
     {
         (yyval.interm) = (yyvsp[-2].interm);
-        context->parseDeclarator((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string, (yyval.interm).intermDeclaration);
+        context->parseDeclarator((yyval.interm).type, (yylsp[0]), ImmutableString((yyvsp[0].lex).string), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3389,7 +3386,7 @@
 
     {
         (yyval.interm) = (yyvsp[-3].interm);
-        context->parseArrayDeclarator((yyval.interm).type, (yylsp[-1]), *(yyvsp[-1].lex).string, (yylsp[0]), *((yyvsp[0].interm.arraySizes)), (yyval.interm).intermDeclaration);
+        context->parseArrayDeclarator((yyval.interm).type, (yylsp[-1]), ImmutableString((yyvsp[-1].lex).string), (yylsp[0]), *((yyvsp[0].interm.arraySizes)), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3399,7 +3396,7 @@
     {
         ES3_OR_NEWER("=", (yylsp[-1]), "first-class arrays (array initializer)");
         (yyval.interm) = (yyvsp[-5].interm);
-        context->parseArrayInitDeclarator((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), *((yyvsp[-2].interm.arraySizes)), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
+        context->parseArrayInitDeclarator((yyval.interm).type, (yylsp[-3]), ImmutableString((yyvsp[-3].lex).string), (yylsp[-2]), *((yyvsp[-2].interm.arraySizes)), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3408,7 +3405,7 @@
 
     {
         (yyval.interm) = (yyvsp[-4].interm);
-        context->parseInitDeclarator((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
+        context->parseInitDeclarator((yyval.interm).type, (yylsp[-2]), ImmutableString((yyvsp[-2].lex).string), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3417,7 +3414,7 @@
 
     {
         (yyval.interm).type = (yyvsp[0].interm.type);
-        (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), "");
+        (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), ImmutableString(""));
     }
 
     break;
@@ -3426,7 +3423,7 @@
 
     {
         (yyval.interm).type = (yyvsp[-1].interm.type);
-        (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string);
+        (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), ImmutableString((yyvsp[0].lex).string));
     }
 
     break;
@@ -3435,7 +3432,7 @@
 
     {
         (yyval.interm).type = (yyvsp[-2].interm.type);
-        (yyval.interm).intermDeclaration = context->parseSingleArrayDeclaration((yyval.interm).type, (yylsp[-1]), *(yyvsp[-1].lex).string, (yylsp[0]), *((yyvsp[0].interm.arraySizes)));
+        (yyval.interm).intermDeclaration = context->parseSingleArrayDeclaration((yyval.interm).type, (yylsp[-1]), ImmutableString((yyvsp[-1].lex).string), (yylsp[0]), *((yyvsp[0].interm.arraySizes)));
     }
 
     break;
@@ -3445,7 +3442,7 @@
     {
         ES3_OR_NEWER("[]", (yylsp[-2]), "first-class arrays (array initializer)");
         (yyval.interm).type = (yyvsp[-4].interm.type);
-        (yyval.interm).intermDeclaration = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), *((yyvsp[-2].interm.arraySizes)), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        (yyval.interm).intermDeclaration = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-3]), ImmutableString((yyvsp[-3].lex).string), (yylsp[-2]), *((yyvsp[-2].interm.arraySizes)), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -3454,7 +3451,7 @@
 
     {
         (yyval.interm).type = (yyvsp[-3].interm.type);
-        (yyval.interm).intermDeclaration = context->parseSingleInitDeclaration((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        (yyval.interm).intermDeclaration = context->parseSingleInitDeclaration((yyval.interm).type, (yylsp[-2]), ImmutableString((yyvsp[-2].lex).string), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -3748,7 +3745,7 @@
   case 154:
 
     {
-        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[0].lex).string, (yylsp[0]));
+        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(ImmutableString((yyvsp[0].lex).string), (yylsp[0]));
     }
 
     break;
@@ -3756,7 +3753,7 @@
   case 155:
 
     {
-        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
+        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(ImmutableString((yyvsp[-2].lex).string), (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
     }
 
     break;
@@ -3764,7 +3761,7 @@
   case 156:
 
     {
-        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
+        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(ImmutableString((yyvsp[-2].lex).string), (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
     }
 
     break;
@@ -3772,7 +3769,7 @@
   case 157:
 
     {
-        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier("shared", (yylsp[0]));
+        (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(ImmutableString("shared"), (yylsp[0]));
     }
 
     break;
@@ -4387,28 +4384,28 @@
 
   case 227:
 
-    { context->enterStructDeclaration((yylsp[-1]), *(yyvsp[-1].lex).string); }
+    { context->enterStructDeclaration((yylsp[-1]), ImmutableString((yyvsp[-1].lex).string)); }
 
     break;
 
   case 228:
 
     {
-        (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-5]), (yylsp[-4]), (yyvsp[-4].lex).string, (yyvsp[-1].interm.fieldList));
+        (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-5]), (yylsp[-4]), ImmutableString((yyvsp[-4].lex).string), (yyvsp[-1].interm.fieldList));
     }
 
     break;
 
   case 229:
 
-    { context->enterStructDeclaration((yylsp[0]), *(yyvsp[0].lex).string); }
+    { context->enterStructDeclaration((yylsp[0]), ImmutableString("")); }
 
     break;
 
   case 230:
 
     {
-        (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-4]), (yyloc), nullptr, (yyvsp[-1].interm.fieldList));
+        (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-4]), (yyloc), ImmutableString(""), (yyvsp[-1].interm.fieldList));
     }
 
     break;
@@ -4466,7 +4463,7 @@
   case 237:
 
     {
-        (yyval.interm.declarator) = context->parseStructDeclarator((yyvsp[0].lex).string, (yylsp[0]));
+        (yyval.interm.declarator) = context->parseStructDeclarator(ImmutableString((yyvsp[0].lex).string), (yylsp[0]));
     }
 
     break;
@@ -4474,7 +4471,7 @@
   case 238:
 
     {
-        (yyval.interm.declarator) = context->parseStructArrayDeclarator((yyvsp[-1].lex).string, (yylsp[-1]), (yyvsp[0].interm.arraySizes));
+        (yyval.interm.declarator) = context->parseStructArrayDeclarator(ImmutableString((yyvsp[-1].lex).string), (yylsp[-1]), (yyvsp[0].interm.arraySizes));
     }
 
     break;
@@ -4728,7 +4725,7 @@
   case 274:
 
     {
-        (yyval.interm.intermNode) = context->addConditionInitializer((yyvsp[-3].interm.type), *(yyvsp[-2].lex).string, (yyvsp[0].interm.intermTypedNode), (yylsp[-2]));
+        (yyval.interm.intermNode) = context->addConditionInitializer((yyvsp[-3].interm.type), ImmutableString((yyvsp[-2].lex).string), (yyvsp[0].interm.intermTypedNode), (yylsp[-2]));
     }
 
     break;
diff --git a/src/compiler/translator/glslang_tab.h b/src/compiler/translator/glslang_tab.h
index 1f1d9b7..9aacce4 100644
--- a/src/compiler/translator/glslang_tab.h
+++ b/src/compiler/translator/glslang_tab.h
@@ -215,7 +215,7 @@
 
     struct {
         union {
-            TString *string;
+            const char *string;  // pool allocated.
             float f;
             int i;
             unsigned int u;
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index bd39cd1..9b70217 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -473,12 +473,12 @@
     return arrayString.str();
 }
 
-TString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap)
+ImmutableString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap)
 {
     if (type.getBasicType() == EbtStruct)
         return HashName(type.getStruct(), hashFunction, nameMap);
     else
-        return type.getBuiltInTypeNameString();
+        return ImmutableString(type.getBuiltInTypeNameString());
 }
 
 bool IsVaryingOut(TQualifier qualifier)
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 6d6dc95..4b2d662 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -13,6 +13,7 @@
 #include <GLSLANG/ShaderLang.h>
 
 #include "compiler/translator/HashNames.h"
+#include "compiler/translator/ImmutableString.h"
 #include "compiler/translator/Operator.h"
 #include "compiler/translator/Types.h"
 
@@ -46,7 +47,7 @@
 // 3.10 section 4.1.9.
 TString ArrayString(const TType &type);
 
-TString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap);
+ImmutableString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap);
 
 TType GetShaderVariableBasicType(const sh::ShaderVariable &var);