Subzero: Remove IceString.

The purpose is to get control over excess string creation and deletion, especially when the strings are long enough to involve malloc/free.

Strings that interface with the outside world, or used for dump/debug, are now explicitly std::string.

Variable names and node names are represented as string IDs and pooled locally in the CFG.  (In a non-DUMP build, this pool should always be empty.)

Other strings that are used across functions are represented as string IDs and pooled globally in the GlobalContext.

The --dump-strings flag allows these strings to be dumped for sanity checking, even in a MINIMAL build.  In a MINIMAL build, the set of strings includes external symbol names, intrinsic names, helper function names, and label names of pooled constants to facilitate deterministic ELF string table output.  For now, it also includes jump table entry names until that gets sorted out.

Constants are fixed so that the name and pooled fields are properly immutable after the constant is fully initialized.

BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4360
R=jpp@chromium.org

Review URL: https://codereview.chromium.org/1838753002 .
diff --git a/src/IceAssembler.h b/src/IceAssembler.h
index 57c9493..4e57a0c 100644
--- a/src/IceAssembler.h
+++ b/src/IceAssembler.h
@@ -28,6 +28,7 @@
 
 #include "IceDefs.h"
 #include "IceFixups.h"
+#include "IceStringPool.h"
 #include "IceUtils.h"
 
 #include "llvm/Support/Allocator.h"
@@ -323,8 +324,8 @@
   void emitIASBytes(GlobalContext *Ctx) const;
   bool getInternal() const { return IsInternal; }
   void setInternal(bool Internal) { IsInternal = Internal; }
-  const IceString &getFunctionName() { return FunctionName; }
-  void setFunctionName(const IceString &NewName) { FunctionName = NewName; }
+  GlobalString getFunctionName() const { return FunctionName; }
+  void setFunctionName(GlobalString NewName) { FunctionName = NewName; }
   intptr_t getBufferSize() const { return Buffer.size(); }
   /// Roll back to a (smaller) size.
   void setBufferSize(intptr_t NewSize) { Buffer.setSize(NewSize); }
@@ -347,7 +348,7 @@
   /// FunctionName and IsInternal are transferred from the original Cfg object,
   /// since the Cfg object may be deleted by the time the assembler buffer is
   /// emitted.
-  IceString FunctionName = "";
+  GlobalString FunctionName;
   bool IsInternal = false;
   /// Preliminary indicates whether a preliminary pass is being made for
   /// calculating bundle padding (Preliminary=true), versus the final pass where
diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
index 613fa77..746a39f 100644
--- a/src/IceAssemblerARM32.cpp
+++ b/src/IceAssemblerARM32.cpp
@@ -593,7 +593,7 @@
   IValueT Inst = Asm.load<IValueT>(position());
   const bool IsMovw = kind() == llvm::ELF::R_ARM_MOVW_ABS_NC ||
                       kind() == llvm::ELF::R_ARM_MOVW_PREL_NC;
-  const IceString Symbol = symbol();
+  const auto Symbol = symbol().toString();
   const bool NeedsPCRelSuffix =
       (Asm.fixupIsPCRel(kind()) || Symbol == GlobalOffsetTable);
   Str << "\t"
@@ -619,7 +619,7 @@
     return 3;
   default:
     llvm::report_fatal_error("SIMD op: Don't understand element type " +
-                             typeIceString(ElmtTy));
+                             typeStdString(ElmtTy));
   }
 }
 
@@ -1042,7 +1042,7 @@
   default:
     llvm::report_fatal_error(std::string(InstName) +
                              ": Unable to process type " +
-                             typeIceString(OpRt->getType()));
+                             typeStdString(OpRt->getType()));
   case 8:
     assert(Index < 16);
     Dn = Dn | mask(Index, 3, 1);
diff --git a/src/IceAssemblerARM32.h b/src/IceAssemblerARM32.h
index 1cf07ad..0bfcac6 100644
--- a/src/IceAssemblerARM32.h
+++ b/src/IceAssemblerARM32.h
@@ -152,7 +152,7 @@
     if (BuildDefs::dump() &&
         !Func->getContext()->getFlags().getDisableHybridAssembly()) {
       constexpr SizeT InstSize = 0;
-      emitTextInst(InstL->getName(Func) + ":", InstSize);
+      emitTextInst(InstL->getLabelName() + ":", InstSize);
     }
     Label *L = getOrCreateLocalLabel(Number);
     if (!getPreliminary())
diff --git a/src/IceBrowserCompileServer.cpp b/src/IceBrowserCompileServer.cpp
index 5064d30..6baf62d 100644
--- a/src/IceBrowserCompileServer.cpp
+++ b/src/IceBrowserCompileServer.cpp
@@ -186,7 +186,7 @@
              NumBytes) != NumBytes;
 }
 
-void BrowserCompileServer::setFatalError(const IceString &Reason) {
+void BrowserCompileServer::setFatalError(const std::string &Reason) {
   HadError.store(true);
   Ctx->getStrError() << Reason;
   // Make sure that the QueueStreamer is not stuck by signaling an early end.
diff --git a/src/IceBrowserCompileServer.h b/src/IceBrowserCompileServer.h
index 1729bf8..ecacdec 100644
--- a/src/IceBrowserCompileServer.h
+++ b/src/IceBrowserCompileServer.h
@@ -77,13 +77,13 @@
 
   StringStream &getErrorStream() { return *ErrorStream; }
 
-  void setFatalError(const IceString &Reason);
+  void setFatalError(const std::string &Reason);
 
 private:
   class StringStream {
   public:
     StringStream() : StrBuf(Buffer) {}
-    const IceString &getContents() { return StrBuf.str(); }
+    const std::string &getContents() { return StrBuf.str(); }
     Ostream &getStream() { return StrBuf; }
 
   private:
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp
index e97eebf..85be8b9 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -32,9 +32,11 @@
 
 Cfg::Cfg(GlobalContext *Ctx, uint32_t SequenceNumber)
     : Ctx(Ctx), SequenceNumber(SequenceNumber),
-      VMask(Ctx->getFlags().getVerbose()), NextInstNumber(Inst::NumberInitial),
-      Live(nullptr) {
+      VMask(Ctx->getFlags().getVerbose()), FunctionName(),
+      NextInstNumber(Inst::NumberInitial), Live(nullptr) {
   Allocator.reset(new ArenaAllocator());
+  NodeStrings.reset(new StringPool);
+  VarStrings.reset(new StringPool);
   CfgLocalAllocatorScope _(this);
   Target =
       TargetLowering::createLowering(Ctx->getFlags().getTargetArch(), this);
@@ -51,7 +53,15 @@
   }
 }
 
-Cfg::~Cfg() { assert(CfgAllocatorTraits::current() == nullptr); }
+Cfg::~Cfg() {
+  assert(CfgAllocatorTraits::current() == nullptr);
+  if (GlobalContext::getFlags().getDumpStrings()) {
+    OstreamLocker _(Ctx);
+    Ostream &Str = Ctx->getStrDump();
+    getNodeStrings()->dump(Str);
+    getVarStrings()->dump(Str);
+  }
+}
 
 /// Create a string like "foo(i=123:b=9)" indicating the function name, number
 /// of high-level instructions, and number of basic blocks.  This string is only
@@ -59,9 +69,9 @@
 /// functions to debug a problem on, it's easy to find the smallest or simplest
 /// function to attack.  Note that the counts may change somewhat depending on
 /// what point it is called during the translation passes.
-IceString Cfg::getFunctionNameAndSize() const {
+std::string Cfg::getFunctionNameAndSize() const {
   if (!BuildDefs::dump())
-    return getFunctionName();
+    return getFunctionName().toString();
   SizeT NodeCount = 0;
   SizeT InstCount = 0;
   for (CfgNode *Node : getNodes()) {
@@ -77,7 +87,7 @@
          std::to_string(NodeCount) + ")";
 }
 
-void Cfg::setError(const IceString &Message) {
+void Cfg::setError(const std::string &Message) {
   HasError = true;
   ErrorMessage = Message;
 }
@@ -124,9 +134,9 @@
 constexpr char BlockStatsGlobalPrefix[] = ".L$profiler$block_info$";
 } // end of anonymous namespace
 
-void Cfg::createNodeNameDeclaration(const IceString &NodeAsmName) {
+void Cfg::createNodeNameDeclaration(const std::string &NodeAsmName) {
   auto *Var = VariableDeclaration::create(GlobalInits.get());
-  Var->setName(BlockNameGlobalPrefix + NodeAsmName);
+  Var->setName(Ctx, BlockNameGlobalPrefix + NodeAsmName);
   Var->setIsConstant(true);
   Var->addInitializer(VariableDeclaration::DataInitializer::create(
       GlobalInits.get(), NodeAsmName.data(), NodeAsmName.size() + 1));
@@ -136,9 +146,9 @@
 }
 
 void Cfg::createBlockProfilingInfoDeclaration(
-    const IceString &NodeAsmName, VariableDeclaration *NodeNameDeclaration) {
+    const std::string &NodeAsmName, VariableDeclaration *NodeNameDeclaration) {
   auto *Var = VariableDeclaration::create(GlobalInits.get());
-  Var->setName(BlockStatsGlobalPrefix + NodeAsmName);
+  Var->setName(Ctx, BlockStatsGlobalPrefix + NodeAsmName);
   const SizeT Int64ByteSize = typeWidthInBytes(IceType_i64);
   Var->addInitializer(VariableDeclaration::ZeroInitializer::create(
       GlobalInits.get(), Int64ByteSize));
@@ -156,7 +166,7 @@
     GlobalInits.reset(new VariableDeclarationList());
 
   for (CfgNode *Node : Nodes) {
-    const IceString NodeAsmName = Node->getAsmName();
+    const std::string NodeAsmName = Node->getAsmName();
     createNodeNameDeclaration(NodeAsmName);
     createBlockProfilingInfoDeclaration(NodeAsmName, GlobalInits->back());
     Node->profileExecutionCount(GlobalInits->back());
@@ -164,7 +174,9 @@
 }
 
 bool Cfg::isProfileGlobal(const VariableDeclaration &Var) {
-  return Var.getName().find(BlockStatsGlobalPrefix) == 0;
+  if (!Var.getName().hasStdString())
+    return false;
+  return Var.getName().toString().find(BlockStatsGlobalPrefix) == 0;
 }
 
 void Cfg::addCallToProfileSummary() {
@@ -172,7 +184,7 @@
   // that cause the program to exit. This function is defined in
   // runtime/szrt_profiler.c.
   Constant *ProfileSummarySym =
-      Ctx->getConstantExternSym("__Sz_profile_summary");
+      Ctx->getConstantExternSym(Ctx->getGlobalString("__Sz_profile_summary"));
   constexpr SizeT NumArgs = 0;
   constexpr Variable *Void = nullptr;
   constexpr bool HasTailCall = false;
@@ -185,9 +197,9 @@
   if (hasError())
     return;
   if (BuildDefs::dump()) {
-    const IceString &TimingFocusOn =
+    const std::string TimingFocusOn =
         getContext()->getFlags().getTimingFocusOn();
-    const IceString &Name = getFunctionName();
+    const std::string Name = getFunctionName().toString();
     if (TimingFocusOn == "*" || TimingFocusOn == Name) {
       setFocusedTiming();
       getContext()->resetTimer(GlobalContext::TSK_Default);
@@ -198,7 +210,7 @@
                                  << getFunctionNameAndSize() << "\n";
     }
   }
-  TimerMarker T_func(getContext(), getFunctionName());
+  TimerMarker T_func(getContext(), getFunctionName().toStringOrEmpty());
   TimerMarker T(TimerStack::TT_translate, this);
 
   dump("Initial CFG");
@@ -966,7 +978,7 @@
 // emitTextHeader() is not target-specific (apart from what is abstracted by
 // the Assembler), so it is defined here rather than in the target lowering
 // class.
-void Cfg::emitTextHeader(const IceString &Name, GlobalContext *Ctx,
+void Cfg::emitTextHeader(GlobalString Name, GlobalContext *Ctx,
                          const Assembler *Asm) {
   if (!BuildDefs::dump())
     return;
diff --git a/src/IceCfg.h b/src/IceCfg.h
index 95d79bc..f6f3309 100644
--- a/src/IceCfg.h
+++ b/src/IceCfg.h
@@ -20,6 +20,7 @@
 #include "IceClFlags.h"
 #include "IceDefs.h"
 #include "IceGlobalContext.h"
+#include "IceStringPool.h"
 #include "IceTypes.h"
 
 namespace Ice {
@@ -53,9 +54,9 @@
 
   /// \name Manage the name and return type of the function being translated.
   /// @{
-  void setFunctionName(const IceString &Name) { FunctionName = Name; }
-  const IceString &getFunctionName() const { return FunctionName; }
-  IceString getFunctionNameAndSize() const;
+  void setFunctionName(GlobalString Name) { FunctionName = Name; }
+  GlobalString getFunctionName() const { return FunctionName; }
+  std::string getFunctionNameAndSize() const;
   void setReturnType(Type Ty) { ReturnType = Ty; }
   Type getReturnType() const { return ReturnType; }
   /// @}
@@ -73,9 +74,9 @@
   /// missing, instead of an assertion failure, setError() should be called and
   /// the error should be propagated back up. This way, we can gracefully fail
   /// to translate and let a fallback translator handle the function.
-  void setError(const IceString &Message);
+  void setError(const std::string &Message);
   bool hasError() const { return HasError; }
-  IceString getError() const { return ErrorMessage; }
+  std::string getError() const { return ErrorMessage; }
   /// @}
 
   /// \name Manage nodes (a.k.a. basic blocks, CfgNodes).
@@ -93,20 +94,10 @@
   void swapNodes(NodeList &NewNodes);
   /// @}
 
-  using IdentifierIndexType = int32_t;
-  /// Adds a name to the list and returns its index, suitable for the argument
-  /// to getIdentifierName(). No checking for duplicates is done. This is
-  /// generally used for node names and variable names to avoid embedding a
-  /// std::string inside an arena-allocated object.
-  IdentifierIndexType addIdentifierName(const IceString &Name) {
-    IdentifierIndexType Index = IdentifierNames.size();
-    IdentifierNames.push_back(Name);
-    return Index;
-  }
-  const IceString &getIdentifierName(IdentifierIndexType Index) const {
-    return IdentifierNames[Index];
-  }
-  enum { IdentifierIndexInvalid = -1 };
+  /// String pool for CfgNode::Name values.
+  StringPool *getNodeStrings() const { return NodeStrings.get(); }
+  /// String pool for Variable::Name values.
+  StringPool *getVarStrings() const { return VarStrings.get(); }
 
   /// \name Manage instruction numbering.
   /// @{
@@ -241,7 +232,7 @@
 
   void emit();
   void emitIAS();
-  static void emitTextHeader(const IceString &Name, GlobalContext *Ctx,
+  static void emitTextHeader(GlobalString Name, GlobalContext *Ctx,
                              const Assembler *Asm);
   void dump(const char *Message = "");
 
@@ -277,9 +268,9 @@
   /// code needs to be defined.
   void profileBlocks();
 
-  void createNodeNameDeclaration(const IceString &NodeAsmName);
+  void createNodeNameDeclaration(const std::string &NodeAsmName);
   void
-  createBlockProfilingInfoDeclaration(const IceString &NodeAsmName,
+  createBlockProfilingInfoDeclaration(const std::string &NodeAsmName,
                                       VariableDeclaration *NodeNameDeclaration);
 
   /// Delete registered jump table placeholder instructions. This should only be
@@ -302,20 +293,23 @@
   uint32_t SequenceNumber;             /// output order for emission
   uint32_t ConstantBlindingCookie = 0; /// cookie for constant blinding
   VerboseMask VMask;
-  IceString FunctionName = "";
+  GlobalString FunctionName;
   Type ReturnType = IceType_void;
   bool IsInternalLinkage = false;
   bool HasError = false;
   bool FocusedTiming = false;
-  IceString ErrorMessage = "";
+  std::string ErrorMessage = "";
   CfgNode *Entry = nullptr; /// entry basic block
   NodeList Nodes;           /// linearized node list; Entry should be first
-  std::vector<IceString> IdentifierNames;
   InstNumberT NextInstNumber;
   VarList Variables;
   VarList Args;         /// subset of Variables, in argument order
   VarList ImplicitArgs; /// subset of Variables
   std::unique_ptr<ArenaAllocator> Allocator;
+  // Separate string pools for CfgNode and Variable names, due to a combination
+  // of the uniqueness requirement, and assumptions in lit tests.
+  std::unique_ptr<StringPool> NodeStrings;
+  std::unique_ptr<StringPool> VarStrings;
   std::unique_ptr<Liveness> Live;
   std::unique_ptr<TargetLowering> Target;
   std::unique_ptr<VariablesMetadata> VMetadata;
@@ -336,6 +330,22 @@
 
 template <> Variable *Cfg::makeVariable<Variable>(Type Ty);
 
+struct NodeStringPoolTraits {
+  using OwnerType = Cfg;
+  static StringPool *getStrings(const OwnerType *PoolOwner) {
+    return PoolOwner->getNodeStrings();
+  }
+};
+using NodeString = StringID<NodeStringPoolTraits>;
+
+struct VariableStringPoolTraits {
+  using OwnerType = Cfg;
+  static StringPool *getStrings(const OwnerType *PoolOwner) {
+    return PoolOwner->getVarStrings();
+  }
+};
+using VariableString = StringID<VariableStringPoolTraits>;
+
 } // end of namespace Ice
 
 #endif // SUBZERO_SRC_ICECFG_H
diff --git a/src/IceCfgNode.cpp b/src/IceCfgNode.cpp
index 212f742..ca3897e 100644
--- a/src/IceCfgNode.cpp
+++ b/src/IceCfgNode.cpp
@@ -26,15 +26,13 @@
 
 namespace Ice {
 
-CfgNode::CfgNode(Cfg *Func, SizeT LabelNumber)
-    : Func(Func), Number(LabelNumber), LabelNumber(LabelNumber) {}
-
-// Returns the name the node was created with. If no name was given, it
-// synthesizes a (hopefully) unique name.
-IceString CfgNode::getName() const {
-  if (NameIndex >= 0)
-    return Func->getIdentifierName(NameIndex);
-  return "__" + std::to_string(LabelNumber);
+CfgNode::CfgNode(Cfg *Func, SizeT Number) : Func(Func), Number(Number) {
+  if (BuildDefs::dump()) {
+    Name =
+        NodeString::createWithString(Func, "__" + std::to_string(getIndex()));
+  } else {
+    Name = NodeString::createWithoutString(Func);
+  }
 }
 
 // Adds an instruction to either the Phi list or the regular instruction list.
@@ -1428,23 +1426,22 @@
 }
 
 void CfgNode::profileExecutionCount(VariableDeclaration *Var) {
-  constexpr char RMW_I64[] = "llvm.nacl.atomic.rmw.i64";
-
-  GlobalContext *Context = Func->getContext();
+  GlobalContext *Ctx = Func->getContext();
+  GlobalString RMW_I64 = Ctx->getGlobalString("llvm.nacl.atomic.rmw.i64");
 
   bool BadIntrinsic = false;
   const Intrinsics::FullIntrinsicInfo *Info =
-      Context->getIntrinsicsInfo().find(RMW_I64, BadIntrinsic);
+      Ctx->getIntrinsicsInfo().find(RMW_I64, BadIntrinsic);
   assert(!BadIntrinsic);
   assert(Info != nullptr);
 
-  Operand *RMWI64Name = Context->getConstantExternSym(RMW_I64);
+  Operand *RMWI64Name = Ctx->getConstantExternSym(RMW_I64);
   constexpr RelocOffsetT Offset = 0;
-  Constant *Counter = Context->getConstantSym(Offset, Var->getName());
-  Constant *AtomicRMWOp = Context->getConstantInt32(Intrinsics::AtomicAdd);
-  Constant *One = Context->getConstantInt64(1);
+  Constant *Counter = Ctx->getConstantSym(Offset, Var->getName());
+  Constant *AtomicRMWOp = Ctx->getConstantInt32(Intrinsics::AtomicAdd);
+  Constant *One = Ctx->getConstantInt64(1);
   Constant *OrderAcquireRelease =
-      Context->getConstantInt32(Intrinsics::MemoryOrderAcquireRelease);
+      Ctx->getConstantInt32(Intrinsics::MemoryOrderAcquireRelease);
 
   auto *Instr = InstIntrinsicCall::create(
       Func, 5, Func->makeVariable(IceType_i64), RMWI64Name, Info->Info);
diff --git a/src/IceCfgNode.h b/src/IceCfgNode.h
index f7e7b0e..b81c5c2 100644
--- a/src/IceCfgNode.h
+++ b/src/IceCfgNode.h
@@ -18,6 +18,7 @@
 
 #include "IceDefs.h"
 #include "IceInst.h" // InstList traits
+#include "IceStringPool.h"
 
 namespace Ice {
 
@@ -27,8 +28,8 @@
   CfgNode &operator=(const CfgNode &) = delete;
 
 public:
-  static CfgNode *create(Cfg *Func, SizeT LabelIndex) {
-    return new (Func->allocate<CfgNode>()) CfgNode(Func, LabelIndex);
+  static CfgNode *create(Cfg *Func, SizeT Number) {
+    return new (Func->allocate<CfgNode>()) CfgNode(Func, Number);
   }
 
   Cfg *getCfg() const { return Func; }
@@ -36,14 +37,13 @@
   /// Access the label number and name for this node.
   SizeT getIndex() const { return Number; }
   void resetIndex(SizeT NewNumber) { Number = NewNumber; }
-  IceString getName() const;
-  void setName(const IceString &NewName) {
-    // Make sure that the name can only be set once.
-    assert(NameIndex == Cfg::IdentifierIndexInvalid);
-    if (!NewName.empty())
-      NameIndex = Func->addIdentifierName(NewName);
+  NodeString getName() const { return Name; }
+  void setName(const std::string &NewName) {
+    if (NewName.empty())
+      return;
+    Name = NodeString::createWithString(Func, NewName);
   }
-  IceString getAsmName() const {
+  std::string getAsmName() const {
     return ".L" + Func->getFunctionName() + "$" + getName();
   }
 
@@ -111,15 +111,13 @@
   void profileExecutionCount(VariableDeclaration *Var);
 
 private:
-  CfgNode(Cfg *Func, SizeT LabelIndex);
+  CfgNode(Cfg *Func, SizeT Number);
   bool livenessValidateIntervals(Liveness *Liveness) const;
   Cfg *const Func;
-  SizeT Number;            /// invariant: Func->Nodes[Number]==this
-  const SizeT LabelNumber; /// persistent number for label generation
-  Cfg::IdentifierIndexType NameIndex =
-      Cfg::IdentifierIndexInvalid; /// index into Cfg::NodeNames table
-  SizeT LoopNestDepth = 0;         /// the loop nest depth of this node
-  bool HasReturn = false;          /// does this block need an epilog?
+  SizeT Number; /// invariant: Func->Nodes[Number]==this
+  NodeString Name;
+  SizeT LoopNestDepth = 0; /// the loop nest depth of this node
+  bool HasReturn = false;  /// does this block need an epilog?
   bool NeedsPlacement = false;
   bool NeedsAlignment = false;       /// is sandboxing required?
   InstNumberT InstCountEstimate = 0; /// rough instruction count estimate
diff --git a/src/IceClFlags.cpp b/src/IceClFlags.cpp
index 4c8bc5e..5befeff 100644
--- a/src/IceClFlags.cpp
+++ b/src/IceClFlags.cpp
@@ -106,7 +106,7 @@
 
 void ClFlags::parseFlags(int argc, char **argv) {
   cl::ParseCommandLineOptions(argc, argv);
-  AppNameObj = IceString(argv[0]);
+  AppNameObj = argv[0];
 }
 
 namespace {
diff --git a/src/IceClFlags.def b/src/IceClFlags.def
index 684fcb8..4908e66 100644
--- a/src/IceClFlags.def
+++ b/src/IceClFlags.def
@@ -132,6 +132,11 @@
   X(DumpStats, bool, dev_opt_flag, "szstats",                                  \
     cl::desc("Print statistics after translating each function"))              \
                                                                                \
+  X(DumpStrings, bool, dev_opt_flag,                                           \
+    "dump-strings",                                                            \
+    cl::desc("Dump string pools during compilation"),                          \
+    cl::init(false))                                                           \
+                                                                               \
   X(EnableBlockProfile, bool, dev_opt_flag, "enable-block-profile",            \
     cl::desc("Instrument basic blocks, and output profiling "                  \
              "information to stdout at the end of program execution."),        \
diff --git a/src/IceCompileServer.cpp b/src/IceCompileServer.cpp
index 4a0da28..e62f268 100644
--- a/src/IceCompileServer.cpp
+++ b/src/IceCompileServer.cpp
@@ -50,7 +50,8 @@
 public:
   TextDataStreamer() = default;
   ~TextDataStreamer() final = default;
-  static TextDataStreamer *create(const IceString &Filename, std::string *Err);
+  static TextDataStreamer *create(const std::string &Filename,
+                                  std::string *Err);
   size_t GetBytes(unsigned char *Buf, size_t Len) final;
 
 private:
@@ -58,7 +59,7 @@
   size_t Cursor = 0;
 };
 
-TextDataStreamer *TextDataStreamer::create(const IceString &Filename,
+TextDataStreamer *TextDataStreamer::create(const std::string &Filename,
                                            std::string *Err) {
   TextDataStreamer *Streamer = new TextDataStreamer();
   llvm::raw_string_ostream ErrStrm(*Err);
@@ -84,7 +85,7 @@
   return Len;
 }
 
-std::unique_ptr<Ostream> makeStream(const IceString &Filename,
+std::unique_ptr<Ostream> makeStream(const std::string &Filename,
                                     std::error_code &EC) {
   if (Filename == "-") {
     return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout));
@@ -210,7 +211,7 @@
     llvm::report_fatal_error("Can't specify 'bitcode-as-text' flag in "
                              "minimal build");
 
-  IceString StrError;
+  std::string StrError;
   std::unique_ptr<llvm::DataStreamer> InputStream(
       (!BuildDefs::minimal() && Flags.getBitcodeAsText())
           ? TextDataStreamer::create(Flags.getIRFilename(), &StrError)
@@ -240,6 +241,7 @@
   transferErrorCode(
       getReturnValue(static_cast<ErrorCodes>(Ctx->getErrorStatus()->value())));
   Ctx->dumpConstantLookupCounts();
+  Ctx->dumpStrings();
 }
 
 } // end of namespace Ice
diff --git a/src/IceCompiler.cpp b/src/IceCompiler.cpp
index bd5e834..4ad7730 100644
--- a/src/IceCompiler.cpp
+++ b/src/IceCompiler.cpp
@@ -22,7 +22,6 @@
 #include "IceBuildDefs.h"
 #include "IceCfg.h"
 #include "IceClFlags.h"
-#include "IceClFlags.h"
 #include "IceConverter.h"
 #include "IceELFObjectWriter.h"
 #include "PNaClTranslator.h"
@@ -50,7 +49,7 @@
 
 namespace {
 
-bool llvmIRInput(const IceString &Filename) {
+bool llvmIRInput(const std::string &Filename) {
   return BuildDefs::llvmIrAsInput() &&
          std::regex_match(Filename, std::regex(".*\\.ll"));
 }
@@ -77,7 +76,7 @@
   Ctx.startWorkerThreads();
 
   std::unique_ptr<Translator> Translator;
-  const IceString &IRFilename = Flags.getIRFilename();
+  const std::string IRFilename = Flags.getIRFilename();
   const bool BuildOnRead = Flags.getBuildOnRead() && !llvmIRInput(IRFilename);
   if (BuildOnRead) {
     std::unique_ptr<PNaClTranslator> PTranslator(new PNaClTranslator(&Ctx));
diff --git a/src/IceConverter.cpp b/src/IceConverter.cpp
index 7dc666f..e1900c3 100644
--- a/src/IceConverter.cpp
+++ b/src/IceConverter.cpp
@@ -102,7 +102,8 @@
 
       VarMap.clear();
       NodeMap.clear();
-      Func->setFunctionName(Ice::mangleName(F->getName()));
+      Func->setFunctionName(
+          Ctx->getGlobalString(Ice::mangleName(F->getName())));
       Func->setReturnType(convertToIceType(F->getReturnType()));
       Func->setInternal(F->hasInternalLinkage());
       Ice::TimerMarker T(Ice::TimerStack::TT_llvmConvert, Func.get());
@@ -142,7 +143,8 @@
         return Ctx->getConstantExternSym(Decl->getName());
       else {
         const Ice::RelocOffsetT Offset = 0;
-        return Ctx->getConstantSym(Offset, Decl->getName());
+        return Ctx->getConstantSym(
+            Offset, Ctx->getGlobalString(Decl->getName().toString()));
       }
     } else if (const auto CI = dyn_cast<ConstantInt>(Const)) {
       Ice::Type Ty = convertToIceType(CI->getType());
@@ -805,7 +807,7 @@
 namespace Ice {
 
 void Converter::nameUnnamedGlobalVariables(Module *Mod) {
-  const IceString &GlobalPrefix = Ctx->getFlags().getDefaultGlobalPrefix();
+  const std::string GlobalPrefix = Ctx->getFlags().getDefaultGlobalPrefix();
   if (GlobalPrefix.empty())
     return;
   uint32_t NameIndex = 0;
@@ -820,7 +822,7 @@
 }
 
 void Converter::nameUnnamedFunctions(Module *Mod) {
-  const IceString &FunctionPrefix = Ctx->getFlags().getDefaultFunctionPrefix();
+  const std::string FunctionPrefix = Ctx->getFlags().getDefaultFunctionPrefix();
   if (FunctionPrefix.empty())
     return;
   uint32_t NameIndex = 0;
@@ -868,7 +870,7 @@
     }
     auto *IceFunc = FunctionDeclaration::create(
         Ctx, Signature, Func.getCallingConv(), Func.getLinkage(), Func.empty());
-    IceFunc->setName(Func.getName());
+    IceFunc->setName(Ctx, Func.getName());
     if (!IceFunc->verifyLinkageCorrect(Ctx)) {
       std::string Buffer;
       raw_string_ostream StrBuf(Buffer);
@@ -892,7 +894,7 @@
         GlobalDeclarationsPool.get(), NoSuppressMangling, GV->getLinkage());
     Var->setAlignment(GV->getAlignment());
     Var->setIsConstant(GV->isConstant());
-    Var->setName(GV->getName());
+    Var->setName(Ctx, GV->getName());
     if (!Var->verifyLinkageCorrect(Ctx)) {
       std::string Buffer;
       raw_string_ostream StrBuf(Buffer);
diff --git a/src/IceDefs.h b/src/IceDefs.h
index 5893ef4..2055990 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -119,7 +119,6 @@
 
 #define ENABLE_MAKE_UNIQUE friend struct ::Ice::Internal::MakeUniqueEnabler
 
-using IceString = std::string;
 using InstList = llvm::ilist<Inst>;
 // Ideally PhiList would be llvm::ilist<InstPhi>, and similar for AssignList,
 // but this runs into issues with SFINAE.
@@ -357,6 +356,32 @@
 
 using GlobalLockType = std::mutex;
 
+/// LockedPtr is an RAII wrapper that allows automatically locked access to a
+/// given pointer, automatically unlocking it when when the LockedPtr goes out
+/// of scope.
+template <typename T> class LockedPtr {
+  LockedPtr() = delete;
+  LockedPtr(const LockedPtr &) = delete;
+  LockedPtr &operator=(const LockedPtr &) = delete;
+
+public:
+  LockedPtr(T *Value, GlobalLockType *Lock) : Value(Value), Lock(Lock) {
+    Lock->lock();
+  }
+  LockedPtr(LockedPtr &&Other) : Value(Other.Value), Lock(Other.Lock) {
+    Other.Value = nullptr;
+    Other.Lock = nullptr;
+  }
+  ~LockedPtr() { Lock->unlock(); }
+  T *operator->() const { return Value; }
+  T &operator*() const { return *Value; }
+  T *get() { return Value; }
+
+private:
+  T *Value;
+  GlobalLockType *Lock;
+};
+
 enum ErrorCodes { EC_None = 0, EC_Args, EC_Bitcode, EC_Translation };
 
 /// Wrapper around std::error_code for allowing multiple errors to be folded
diff --git a/src/IceELFObjectWriter.cpp b/src/IceELFObjectWriter.cpp
index 98b774b..c3ba9b0 100644
--- a/src/IceELFObjectWriter.cpp
+++ b/src/IceELFObjectWriter.cpp
@@ -76,16 +76,16 @@
 ELFObjectWriter::ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out)
     : Ctx(Ctx), Str(Out), ELF64(isELF64(Ctx.getFlags())) {
   // Create the special bookkeeping sections now.
-  const IceString NullSectionName("");
+  constexpr char NullSectionName[] = "";
   NullSection = new (Ctx.allocate<ELFSection>())
       ELFSection(NullSectionName, SHT_NULL, 0, 0, 0);
 
-  const IceString ShStrTabName(".shstrtab");
+  constexpr char ShStrTabName[] = ".shstrtab";
   ShStrTab = new (Ctx.allocate<ELFStringTableSection>())
       ELFStringTableSection(ShStrTabName, SHT_STRTAB, 0, 1, 0);
   ShStrTab->add(ShStrTabName);
 
-  const IceString SymTabName(".symtab");
+  constexpr char SymTabName[] = ".symtab";
   const Elf64_Xword SymTabAlign = ELF64 ? 8 : 4;
   const Elf64_Xword SymTabEntSize =
       ELF64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
@@ -93,15 +93,15 @@
                 "Elf_Sym sizes cannot be derived from sizeof");
   SymTab = createSection<ELFSymbolTableSection>(SymTabName, SHT_SYMTAB, 0,
                                                 SymTabAlign, SymTabEntSize);
-  SymTab->createNullSymbol(NullSection);
+  SymTab->createNullSymbol(NullSection, &Ctx);
 
-  const IceString StrTabName(".strtab");
+  constexpr char StrTabName[] = ".strtab";
   StrTab =
       createSection<ELFStringTableSection>(StrTabName, SHT_STRTAB, 0, 1, 0);
 }
 
 template <typename T>
-T *ELFObjectWriter::createSection(const IceString &Name, Elf64_Word ShType,
+T *ELFObjectWriter::createSection(const std::string &Name, Elf64_Word ShType,
                                   Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
                                   Elf64_Xword ShEntsize) {
   assert(!SectionNumbersAssigned);
@@ -117,8 +117,8 @@
   // practice we've only had .rela for elf64 (x86-64). In the future, the two
   // properties may need to be decoupled and the ShEntSize can vary more.
   const Elf64_Word ShType = ELF64 ? SHT_RELA : SHT_REL;
-  const IceString RelPrefix = ELF64 ? ".rela" : ".rel";
-  const IceString RelSectionName = RelPrefix + RelatedSection->getName();
+  const std::string RelPrefix = ELF64 ? ".rela" : ".rel";
+  const std::string RelSectionName = RelPrefix + RelatedSection->getName();
   const Elf64_Xword ShAlign = ELF64 ? 8 : 4;
   const Elf64_Xword ShEntSize = ELF64 ? sizeof(Elf64_Rela) : sizeof(Elf32_Rel);
   static_assert(sizeof(Elf64_Rela) == 24 && sizeof(Elf32_Rel) == 8,
@@ -216,16 +216,16 @@
   return OffsetInFile;
 }
 
-void ELFObjectWriter::writeFunctionCode(const IceString &FuncName,
-                                        bool IsInternal, Assembler *Asm) {
+void ELFObjectWriter::writeFunctionCode(GlobalString FuncName, bool IsInternal,
+                                        Assembler *Asm) {
   assert(!SectionNumbersAssigned);
-  TimerMarker T_func(&Ctx, FuncName);
+  TimerMarker T_func(&Ctx, FuncName.toStringOrEmpty());
   TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
   ELFTextSection *Section = nullptr;
   ELFRelocationSection *RelSection = nullptr;
   const bool FunctionSections = Ctx.getFlags().getFunctionSections();
   if (TextSections.empty() || FunctionSections) {
-    IceString SectionName = ".text";
+    std::string SectionName = ".text";
     if (FunctionSections)
       SectionName += "." + FuncName;
     constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_EXECINSTR;
@@ -289,7 +289,7 @@
 // is non-empty, then only the TranslateOnly variable is kept for emission.
 void partitionGlobalsBySection(const VariableDeclarationList &Vars,
                                VariableDeclarationPartition VarsBySection[],
-                               const IceString &TranslateOnly) {
+                               const std::string &TranslateOnly) {
   for (VariableDeclaration *Var : Vars) {
     if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
       size_t Section = classifyGlobalSection(Var);
@@ -303,7 +303,7 @@
 
 void ELFObjectWriter::writeDataSection(const VariableDeclarationList &Vars,
                                        FixupKind RelocationKind,
-                                       const IceString &SectionSuffix,
+                                       const std::string &SectionSuffix,
                                        bool IsPIC) {
   TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
   assert(!SectionNumbersAssigned);
@@ -320,7 +320,7 @@
 }
 
 namespace {
-IceString MangleSectionName(const char Base[], const IceString &Suffix) {
+std::string MangleSectionName(const char Base[], const std::string &Suffix) {
   if (Suffix.empty())
     return Base;
   return Base + ("." + Suffix);
@@ -331,7 +331,7 @@
 void ELFObjectWriter::writeDataOfType(SectionType ST,
                                       const VariableDeclarationPartition &Vars,
                                       FixupKind RelocationKind,
-                                      const IceString &SectionSuffix,
+                                      const std::string &SectionSuffix,
                                       bool IsPIC) {
   if (Vars.empty())
     return;
@@ -346,7 +346,7 @@
   // Lift this out, so it can be re-used if we do fdata-sections?
   switch (ST) {
   case ROData: {
-    const IceString SectionName =
+    const std::string SectionName =
         MangleSectionName(IsPIC ? ".data.rel.ro" : ".rodata", SectionSuffix);
     const Elf64_Xword ShFlags = IsPIC ? (SHF_ALLOC | SHF_WRITE) : SHF_ALLOC;
     Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
@@ -358,7 +358,7 @@
     break;
   }
   case Data: {
-    const IceString SectionName = MangleSectionName(".data", SectionSuffix);
+    const std::string SectionName = MangleSectionName(".data", SectionSuffix);
     constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
     Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
                                             ShAddralign, ShEntsize);
@@ -369,7 +369,7 @@
     break;
   }
   case BSS: {
-    const IceString SectionName = MangleSectionName(".bss", SectionSuffix);
+    const std::string SectionName = MangleSectionName(".bss", SectionSuffix);
     constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
     Section = createSection<ELFDataSection>(SectionName, SHT_NOBITS, ShFlags,
                                             ShAddralign, ShEntsize);
@@ -394,7 +394,7 @@
     SizeT SymbolSize = Var->getNumBytes();
     bool IsExternal = Var->isExternal() || Ctx.getFlags().getDisableInternal();
     const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
-    const IceString &Name = Var->getName();
+    GlobalString Name = Var->getName();
     SymTab->createDefinedSym(Name, SymbolType, SymbolBinding, Section,
                              Section->getCurrentSize(), SymbolSize);
     StrTab->add(Name);
@@ -545,10 +545,7 @@
     if (!C->getShouldBePooled())
       continue;
     auto *Const = llvm::cast<ConstType>(C);
-    std::string SymBuffer;
-    llvm::raw_string_ostream SymStrBuf(SymBuffer);
-    Const->emitPoolLabel(SymStrBuf);
-    std::string &SymName = SymStrBuf.str();
+    GlobalString SymName = Const->getLabelName();
     SymTab->createDefinedSym(SymName, STT_NOTYPE, STB_LOCAL, Section,
                              OffsetInSection, SymbolSize);
     StrTab->add(SymName);
@@ -585,8 +582,12 @@
   const Elf64_Xword PointerSize = typeWidthInBytes(getPointerType());
   const Elf64_Xword ShAddralign = PointerSize;
   const Elf64_Xword ShEntsize = PointerSize;
-  const IceString SectionName = MangleSectionName(
-      IsPIC ? ".data.rel.ro" : ".rodata", JT.getFunctionName() + "$jumptable");
+  const GlobalString JTName = JT.getFunctionName();
+  const std::string SectionName = MangleSectionName(
+      IsPIC ? ".data.rel.ro" : ".rodata",
+      (JTName.hasStdString() ? JTName.toString()
+                             : std::to_string(JTName.getID())) +
+          "$jumptable");
   Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, SHF_ALLOC,
                                           ShAddralign, ShEntsize);
   Section->setFileOffset(alignFileOffset(ShAddralign));
@@ -598,8 +599,8 @@
   Section->padToAlignment(Str, PointerSize);
   const bool IsExternal = Ctx.getFlags().getDisableInternal();
   const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
-  const IceString JumpTableName =
-      InstJumpTable::makeName(JT.getFunctionName(), JT.getId());
+  GlobalString JumpTableName = Ctx.getGlobalString(
+      InstJumpTable::makeName(JT.getFunctionName(), JT.getId()));
   SymTab->createDefinedSym(JumpTableName, SymbolType, SymbolBinding, Section,
                            Section->getCurrentSize(), PointerSize);
   StrTab->add(JumpTableName);
@@ -618,7 +619,8 @@
   TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
   for (const Constant *S : UndefSyms) {
     const auto *Sym = llvm::cast<ConstantRelocatable>(S);
-    const IceString &Name = Sym->getName();
+    GlobalString Name = Sym->getName();
+    assert(Name.hasStdString());
     bool BadIntrinsic;
     const Intrinsics::FullIntrinsicInfo *Info =
         Ctx.getIntrinsicsInfo().find(Name, BadIntrinsic);
diff --git a/src/IceELFObjectWriter.h b/src/IceELFObjectWriter.h
index 94e60fd..6c979e1 100644
--- a/src/IceELFObjectWriter.h
+++ b/src/IceELFObjectWriter.h
@@ -64,13 +64,13 @@
   /// RelocationKind for any relocations.
   void writeDataSection(const VariableDeclarationList &Vars,
                         FixupKind RelocationKind,
-                        const IceString &SectionSuffix, bool IsPIC);
+                        const std::string &SectionSuffix, bool IsPIC);
 
   /// Copy data of a function's text section to file and note the offset of the
   /// symbol's definition in the symbol table. Copy the text fixups for use
   /// after all functions are written. The text buffer and fixups are extracted
   /// from the Assembler object.
-  void writeFunctionCode(const IceString &FuncName, bool IsInternal,
+  void writeFunctionCode(GlobalString FuncName, bool IsInternal,
                          Assembler *Asm);
 
   /// Queries the GlobalContext for constant pools of the given type and writes
@@ -119,7 +119,7 @@
   ELFStringTableSection *StrTab;
 
   template <typename T>
-  T *createSection(const IceString &Name, Elf64_Word ShType,
+  T *createSection(const std::string &Name, Elf64_Word ShType,
                    Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
                    Elf64_Xword ShEntsize);
 
@@ -156,8 +156,8 @@
   /// SectionType.
   void writeDataOfType(SectionType SectionType,
                        const VariableDeclarationPartition &Vars,
-                       FixupKind RelocationKind, const IceString &SectionSuffix,
-                       bool IsPIC);
+                       FixupKind RelocationKind,
+                       const std::string &SectionSuffix, bool IsPIC);
 
   /// Write the final relocation sections given the final symbol table. May also
   /// be able to seek around the file and resolve function calls that are for
diff --git a/src/IceELFSection.cpp b/src/IceELFSection.cpp
index 89c7369..d9539bb 100644
--- a/src/IceELFSection.cpp
+++ b/src/IceELFSection.cpp
@@ -94,17 +94,20 @@
 
 // Symbol tables.
 
-void ELFSymbolTableSection::createNullSymbol(ELFSection *NullSection) {
+void ELFSymbolTableSection::createNullSymbol(ELFSection *NullSection,
+                                             GlobalContext *Ctx) {
   // The first entry in the symbol table should be a NULL entry, so make sure
   // the map is still empty.
   assert(LocalSymbols.empty());
-  const IceString NullSymName("");
-  createDefinedSym(NullSymName, STT_NOTYPE, STB_LOCAL, NullSection, 0, 0);
-  NullSymbol = findSymbol(NullSymName);
+  // Explicitly set the null symbol name to the empty string, so that
+  // GlobalString::operator<() orders the null string first.
+  NullSymbolName = GlobalString::createWithString(Ctx, "");
+  createDefinedSym(NullSymbolName, STT_NOTYPE, STB_LOCAL, NullSection, 0, 0);
+  NullSymbol = findSymbol(NullSymbolName);
 }
 
-void ELFSymbolTableSection::createDefinedSym(const IceString &Name,
-                                             uint8_t Type, uint8_t Binding,
+void ELFSymbolTableSection::createDefinedSym(GlobalString Name, uint8_t Type,
+                                             uint8_t Binding,
                                              ELFSection *Section,
                                              RelocOffsetT Offset, SizeT Size) {
   ELFSym NewSymbol = ELFSym();
@@ -122,7 +125,7 @@
   (void)Unique;
 }
 
-void ELFSymbolTableSection::noteUndefinedSym(const IceString &Name,
+void ELFSymbolTableSection::noteUndefinedSym(GlobalString Name,
                                              ELFSection *NullSection) {
   ELFSym NewSymbol = ELFSym();
   NewSymbol.Sym.setBindingAndType(STB_GLOBAL, STT_NOTYPE);
@@ -138,7 +141,7 @@
   (void)Unique;
 }
 
-const ELFSym *ELFSymbolTableSection::findSymbol(const IceString &Name) const {
+const ELFSym *ELFSymbolTableSection::findSymbol(GlobalString Name) const {
   auto I = LocalSymbols.find(Name);
   if (I != LocalSymbols.end())
     return &I->second;
@@ -151,16 +154,16 @@
 void ELFSymbolTableSection::updateIndices(const ELFStringTableSection *StrTab) {
   SizeT SymNumber = 0;
   for (auto &KeyValue : LocalSymbols) {
-    const IceString &Name = KeyValue.first;
+    GlobalString Name = KeyValue.first;
     ELFSection *Section = KeyValue.second.Section;
     Elf64_Sym &SymInfo = KeyValue.second.Sym;
-    if (!Name.empty())
-      SymInfo.st_name = StrTab->getIndex(Name);
+    if (Name != NullSymbolName && Name.hasStdString())
+      SymInfo.st_name = StrTab->getIndex(Name.toString());
     SymInfo.st_shndx = Section->getNumber();
     KeyValue.second.setNumber(SymNumber++);
   }
   for (auto &KeyValue : GlobalSymbols) {
-    const IceString &Name = KeyValue.first;
+    const std::string &Name = KeyValue.first.toString();
     ELFSection *Section = KeyValue.second.Section;
     Elf64_Sym &SymInfo = KeyValue.second.Sym;
     if (!Name.empty())
@@ -182,24 +185,24 @@
 
 // String tables.
 
-void ELFStringTableSection::add(const IceString &Str) {
+void ELFStringTableSection::add(const std::string &Str) {
   assert(!isLaidOut());
   assert(!Str.empty());
   StringToIndexMap.insert(std::make_pair(Str, UnknownIndex));
 }
 
-size_t ELFStringTableSection::getIndex(const IceString &Str) const {
+size_t ELFStringTableSection::getIndex(const std::string &Str) const {
   assert(isLaidOut());
   StringToIndexType::const_iterator It = StringToIndexMap.find(Str);
   if (It == StringToIndexMap.end()) {
-    llvm_unreachable("String index not found");
+    llvm::report_fatal_error("String index not found: " + Str);
     return UnknownIndex;
   }
   return It->second;
 }
 
 bool ELFStringTableSection::SuffixComparator::
-operator()(const IceString &StrA, const IceString &StrB) const {
+operator()(const std::string &StrA, const std::string &StrB) const {
   size_t LenA = StrA.size();
   size_t LenB = StrB.size();
   size_t CommonLen = std::min(LenA, LenB);
diff --git a/src/IceELFSection.h b/src/IceELFSection.h
index c57b821..b5a52f4 100644
--- a/src/IceELFSection.h
+++ b/src/IceELFSection.h
@@ -19,6 +19,7 @@
 #include "IceELFStreamer.h"
 #include "IceFixups.h"
 #include "IceOperand.h"
+#include "IceStringPool.h"
 
 using namespace llvm::ELF;
 
@@ -45,7 +46,7 @@
   /// Constructs an ELF section, filling in fields that will be known once the
   /// *type* of section is decided. Other fields may be updated incrementally or
   /// only after the program is completely defined.
-  ELFSection(const IceString &Name, Elf64_Word ShType, Elf64_Xword ShFlags,
+  ELFSection(const std::string &Name, Elf64_Word ShType, Elf64_Xword ShFlags,
              Elf64_Xword ShAddralign, Elf64_Xword ShEntsize)
       : Name(Name), Header() {
     Header.sh_type = ShType;
@@ -70,7 +71,7 @@
 
   void setNameStrIndex(Elf64_Word sh_name) { Header.sh_name = sh_name; }
 
-  const IceString &getName() const { return Name; }
+  const std::string &getName() const { return Name; }
 
   void setLinkNum(Elf64_Word sh_link) { Header.sh_link = sh_link; }
 
@@ -86,7 +87,7 @@
 protected:
   /// Name of the section in convenient string form (instead of a index into the
   /// Section Header String Table, which is not known till later).
-  const IceString Name;
+  const std::string Name;
 
   // The fields of the header. May only be partially initialized, but should
   // be fully initialized before writing.
@@ -164,26 +165,26 @@
   ELFSymbolTableSection &operator=(const ELFSymbolTableSection &) = delete;
 
 public:
-  ELFSymbolTableSection(const IceString &Name, Elf64_Word ShType,
+  ELFSymbolTableSection(const std::string &Name, Elf64_Word ShType,
                         Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
                         Elf64_Xword ShEntsize)
       : ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize),
-        NullSymbol(nullptr) {}
+        NullSymbolName(), NullSymbol(nullptr) {}
 
   /// Create initial entry for a symbol when it is defined. Each entry should
   /// only be defined once. We might want to allow Name to be a dummy name
   /// initially, then get updated to the real thing, since Data initializers are
   /// read before the bitcode's symbol table is read.
-  void createDefinedSym(const IceString &Name, uint8_t Type, uint8_t Binding,
+  void createDefinedSym(GlobalString Name, uint8_t Type, uint8_t Binding,
                         ELFSection *Section, RelocOffsetT Offset, SizeT Size);
 
   /// Note that a symbol table entry needs to be created for the given symbol
   /// because it is undefined.
-  void noteUndefinedSym(const IceString &Name, ELFSection *NullSection);
+  void noteUndefinedSym(GlobalString Name, ELFSection *NullSection);
 
-  const ELFSym *findSymbol(const IceString &Name) const;
+  const ELFSym *findSymbol(GlobalString Name) const;
 
-  void createNullSymbol(ELFSection *NullSection);
+  void createNullSymbol(ELFSection *NullSection, GlobalContext *Ctx);
   const ELFSym *getNullSymbol() const { return NullSymbol; }
 
   size_t getSectionDataSize() const {
@@ -199,12 +200,13 @@
 private:
   // Map from symbol name to its symbol information. This assumes symbols are
   // unique across all sections.
-  using SymtabKey = IceString;
+  using SymtabKey = GlobalString;
   using SymMap = std::map<SymtabKey, ELFSym>;
 
   template <bool IsELF64>
   void writeSymbolMap(ELFStreamer &Str, const SymMap &Map);
 
+  GlobalString NullSymbolName;
   const ELFSym *NullSymbol;
   // Keep Local and Global symbols separate, since the sh_info needs to know
   // the index of the last LOCAL.
@@ -219,7 +221,7 @@
   ELFRelocationSection &operator=(const ELFRelocationSection &) = delete;
 
 public:
-  ELFRelocationSection(const IceString &Name, Elf64_Word ShType,
+  ELFRelocationSection(const std::string &Name, Elf64_Word ShType,
                        Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
                        Elf64_Xword ShEntsize)
       : ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize),
@@ -264,7 +266,11 @@
   using ELFSection::ELFSection;
 
   /// Add a string to the table, in preparation for final layout.
-  void add(const IceString &Str);
+  void add(const std::string &Str);
+  void add(GlobalString Str) {
+    if (Str.hasStdString())
+      add(Str.toString());
+  }
 
   /// Finalizes the layout of the string table and fills in the section Data.
   void doLayout();
@@ -275,7 +281,7 @@
 
   /// Grabs the final index of a string after layout. Returns UnknownIndex if
   /// the string's index is not found.
-  size_t getIndex(const IceString &Str) const;
+  size_t getIndex(const std::string &Str) const;
 
   llvm::StringRef getSectionData() const {
     assert(isLaidOut());
@@ -294,10 +300,10 @@
   /// "unpop" share "pop" as the suffix, "pop" can only share the characters
   /// with one of them.
   struct SuffixComparator {
-    bool operator()(const IceString &StrA, const IceString &StrB) const;
+    bool operator()(const std::string &StrA, const std::string &StrB) const;
   };
 
-  using StringToIndexType = std::map<IceString, size_t, SuffixComparator>;
+  using StringToIndexType = std::map<std::string, size_t, SuffixComparator>;
 
   /// Track strings to their index. Index will be UnknownIndex if not yet laid
   /// out.
@@ -357,7 +363,7 @@
     } else if (Fixup.valueIsSymbol()) {
       Symbol = Fixup.getSymbolValue();
     } else {
-      const IceString Name = Fixup.symbol();
+      GlobalString Name = Fixup.symbol();
       Symbol = SymTab->findSymbol(Name);
       if (!Symbol)
         llvm::report_fatal_error(Name + ": Missing symbol mentioned in reloc");
diff --git a/src/IceFixups.cpp b/src/IceFixups.cpp
index 27d6eab..3d3b1b4 100644
--- a/src/IceFixups.cpp
+++ b/src/IceFixups.cpp
@@ -31,7 +31,7 @@
   return addend_;
 }
 
-IceString AssemblerFixup::symbol() const {
+GlobalString AssemblerFixup::symbol() const {
   assert(!isNullSymbol());
   assert(!ValueIsSymbol);
   const Constant *C = ConstValue;
@@ -41,10 +41,7 @@
   // NOTE: currently only float/doubles are put into constant pools. In the
   // future we may put integers as well.
   assert(llvm::isa<ConstantFloat>(C) || llvm::isa<ConstantDouble>(C));
-  std::string Buffer;
-  llvm::raw_string_ostream Str(Buffer);
-  C->emitPoolLabel(Str);
-  return Str.str();
+  return C->getLabelName();
 }
 
 size_t AssemblerFixup::emit(GlobalContext *Ctx, const Assembler &Asm) const {
@@ -53,17 +50,17 @@
     return FixupSize;
   Ostream &Str = Ctx->getStrEmit();
   Str << "\t.long ";
-  IceString Symbol;
+  std::string Symbol;
   if (isNullSymbol()) {
     Str << "__Sz_AbsoluteZero";
   } else {
-    Symbol = symbol();
+    Symbol = symbol().toString();
     Str << Symbol;
     assert(!ValueIsSymbol);
     if (const auto *CR = llvm::dyn_cast<ConstantRelocatable>(ConstValue)) {
       if (!Asm.fixupIsPCRel(kind()) &&
           GlobalContext::getFlags().getUseNonsfi() &&
-          CR->getName() != GlobalOffsetTable) {
+          CR->getName().toString() != GlobalOffsetTable) {
         Str << "@GOTOFF";
       }
     }
diff --git a/src/IceFixups.h b/src/IceFixups.h
index 2232a67..b6ba62a 100644
--- a/src/IceFixups.h
+++ b/src/IceFixups.h
@@ -16,6 +16,7 @@
 #define SUBZERO_SRC_ICEFIXUPS_H
 
 #include "IceDefs.h"
+#include "IceStringPool.h"
 
 namespace Ice {
 
@@ -41,7 +42,7 @@
   void set_kind(FixupKind Kind) { kind_ = Kind; }
 
   RelocOffsetT offset() const;
-  IceString symbol() const;
+  GlobalString symbol() const;
 
   static const Constant *NullSymbol;
   bool isNullSymbol() const { return ConstValue == NullSymbol; }
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index d353bfe..8ba5b92 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -43,16 +43,11 @@
 namespace std {
 template <> struct hash<Ice::RelocatableTuple> {
   size_t operator()(const Ice::RelocatableTuple &Key) const {
-    if (!Key.EmitString.empty()) {
-      return hash<Ice::IceString>()(Key.EmitString);
-    }
-
-    // If there's no emit string, then we use the relocatable's name, plus the
-    // hash of a combination of the number of OffsetExprs and the known, fixed
-    // offset for the reloc. We left shift the known relocatable by 5 trying to
-    // minimize the interaction between the bits in OffsetExpr.size() and
-    // Key.Offset.
-    return hash<Ice::IceString>()(Key.Name) +
+    // Use the relocatable's name, plus the hash of a combination of the number
+    // of OffsetExprs and the known, fixed offset for the reloc. We left shift
+    // the known relocatable by 5 trying to minimize the interaction between the
+    // bits in OffsetExpr.size() and Key.Offset.
+    return hash<Ice::SizeT>()(Key.Name.getID()) +
            hash<std::size_t>()(Key.OffsetExpr.size() + (Key.Offset << 5));
   }
 };
@@ -218,7 +213,8 @@
   UndefPool Undefs;
 };
 
-void GlobalContext::CodeStats::dump(const IceString &Name, GlobalContext *Ctx) {
+void GlobalContext::CodeStats::dump(const std::string &Name,
+                                    GlobalContext *Ctx) {
   if (!BuildDefs::dump())
     return;
   OstreamLocker _(Ctx);
@@ -253,10 +249,10 @@
 
 GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
                              ELFStreamer *ELFStr)
-    : ConstPool(new ConstantPool()), ErrorStatus(), StrDump(OsDump),
-      StrEmit(OsEmit), StrError(OsError), ObjectWriter(),
-      OptQ(/*Sequential=*/Flags.isSequential(),
-           /*MaxSize=*/Flags.getNumTranslationThreads()),
+    : Strings(new StringPool()), ConstPool(new ConstantPool()), ErrorStatus(),
+      StrDump(OsDump), StrEmit(OsEmit), StrError(OsError), IntrinsicsInfo(this),
+      ObjectWriter(), OptQ(/*Sequential=*/Flags.isSequential(),
+                           /*MaxSize=*/Flags.getNumTranslationThreads()),
       // EmitQ is allowed unlimited size.
       EmitQ(/*Sequential=*/Flags.isSequential()),
       DataLowering(TargetDataLowering::createLowering(this)) {
@@ -300,7 +296,7 @@
 // Define runtime helper functions.
 #define X(Tag, Name)                                                           \
   RuntimeHelperFunc[static_cast<size_t>(RuntimeHelper::H_##Tag)] =             \
-      getConstantExternSym(Name);
+      getConstantExternSym(getGlobalString(Name));
   RUNTIME_HELPER_FUNCTIONS_TABLE
 #undef X
 
@@ -348,7 +344,8 @@
         // The Cfg has already emitted into the assembly buffer, so
         // stats have been fully collected into this thread's TLS.
         // Dump them before TLS is reset for the next Cfg.
-        dumpStats(Func->getFunctionNameAndSize());
+        if (BuildDefs::dump())
+          dumpStats(Func->getFunctionNameAndSize());
         auto Asm = Func->releaseAssembler();
         // Copy relevant fields into Asm before Func is deleted.
         Asm->setFunctionName(Func->getFunctionName());
@@ -409,7 +406,7 @@
   }
 }
 
-void GlobalContext::lowerGlobals(const IceString &SectionSuffix) {
+void GlobalContext::lowerGlobals(const std::string &SectionSuffix) {
   TimerMarker T(TimerStack::TT_emitGlobalInitializers, this);
   const bool DumpGlobalVariables = BuildDefs::dump() &&
                                    (Flags.getVerbose() & IceV_GlobalInit) &&
@@ -455,7 +452,7 @@
 
   // Note: if you change this symbol, make sure to update
   // runtime/szrt_profiler.c as well.
-  ProfileBlockInfoVarDecl->setName("__Sz_block_profile_info");
+  ProfileBlockInfoVarDecl->setName(this, "__Sz_block_profile_info");
 
   for (const VariableDeclaration *PBI : ProfileBlockInfos) {
     if (Cfg::isProfileGlobal(*PBI)) {
@@ -478,6 +475,11 @@
   lowerGlobals(ProfileDataSection);
 }
 
+bool GlobalContext::matchSymbolName(const GlobalString &SymbolName,
+                                    const std::string &Match) {
+  return Match.empty() || Match == SymbolName.toString();
+}
+
 void GlobalContext::emitItems() {
   const bool Threaded = !getFlags().isSequential();
   // Pending is a vector containing the reassembled, ordered list of
@@ -568,7 +570,7 @@
 
         std::unique_ptr<Assembler> Asm = Item->getAsm();
         Asm->alignFunction();
-        const IceString &Name = Asm->getFunctionName();
+        GlobalString Name = Asm->getFunctionName();
         switch (getFlags().getOutFileType()) {
         case FT_Elf:
           getObjectWriter()->writeFunctionCode(Name, Asm->getInternal(),
@@ -618,6 +620,15 @@
     Dtor();
 }
 
+void GlobalContext::dumpStrings() {
+  if (!getFlags().getDumpStrings())
+    return;
+  OstreamLocker _(this);
+  Ostream &Str = getStrDump();
+  Str << "GlobalContext strings:\n";
+  getStrings()->dump(Str);
+}
+
 void GlobalContext::dumpConstantLookupCounts() {
   if (!BuildDefs::dump())
     return;
@@ -698,21 +709,20 @@
   return getConstPool()->Doubles.getOrAdd(this, ConstantDouble);
 }
 
-Constant *GlobalContext::getConstantSym(const RelocOffsetT Offset,
-                                        const RelocOffsetArray &OffsetExpr,
-                                        const IceString &Name,
-                                        const IceString &EmitString) {
+Constant *GlobalContext::getConstantSymWithEmitString(
+    const RelocOffsetT Offset, const RelocOffsetArray &OffsetExpr,
+    GlobalString Name, const std::string &EmitString) {
   return getConstPool()->Relocatables.getOrAdd(
       this, RelocatableTuple(Offset, OffsetExpr, Name, EmitString));
 }
 
 Constant *GlobalContext::getConstantSym(RelocOffsetT Offset,
-                                        const IceString &Name) {
+                                        GlobalString Name) {
   constexpr char EmptyEmitString[] = "";
-  return getConstantSym(Offset, {}, Name, EmptyEmitString);
+  return getConstantSymWithEmitString(Offset, {}, Name, EmptyEmitString);
 }
 
-Constant *GlobalContext::getConstantExternSym(const IceString &Name) {
+Constant *GlobalContext::getConstantExternSym(GlobalString Name) {
   constexpr RelocOffsetT Offset = 0;
   return getConstPool()->ExternRelocatables.getOrAdd(
       this, RelocatableTuple(Offset, {}, Name));
@@ -725,7 +735,7 @@
 Constant *GlobalContext::getConstantZero(Type Ty) {
   Constant *Zero = ConstZeroForType[Ty];
   if (Zero == nullptr)
-    llvm::report_fatal_error("Unsupported constant type: " + typeIceString(Ty));
+    llvm::report_fatal_error("Unsupported constant type: " + typeStdString(Ty));
   return Zero;
 }
 
@@ -772,12 +782,9 @@
   case IceType_v16i8:
   case IceType_v8i16:
   case IceType_v4i32:
-  case IceType_v4f32: {
-    IceString Str;
-    llvm::raw_string_ostream BaseOS(Str);
-    BaseOS << "Unsupported constant type: " << Ty;
-    llvm_unreachable(BaseOS.str().c_str());
-  } break;
+  case IceType_v4f32:
+    llvm::report_fatal_error("Unsupported constant type: " + typeStdString(Ty));
+    break;
   case IceType_void:
   case IceType_NUM:
     break;
@@ -789,6 +796,10 @@
   return getConstPool()->ExternRelocatables.getConstantPool();
 }
 
+GlobalString GlobalContext::getGlobalString(const std::string &Name) {
+  return GlobalString::createWithString(this, Name);
+}
+
 JumpTableDataList GlobalContext::getJumpTables() {
   JumpTableDataList JumpTables(*getJumpTableList());
   // Make order deterministic by sorting into functions and then ID of the jump
@@ -815,14 +826,14 @@
 }
 
 JumpTableData &
-GlobalContext::addJumpTable(const IceString &FuncName, SizeT Id,
+GlobalContext::addJumpTable(GlobalString FuncName, SizeT Id,
                             const JumpTableData::TargetList &TargetList) {
   auto JumpTableList = getJumpTableList();
   JumpTableList->emplace_back(FuncName, Id, TargetList);
   return JumpTableList->back();
 }
 
-TimerStackIdT GlobalContext::newTimerStackID(const IceString &Name) {
+TimerStackIdT GlobalContext::newTimerStackID(const std::string &Name) {
   if (!BuildDefs::timers())
     return 0;
   auto Timers = getTimers();
@@ -832,7 +843,7 @@
 }
 
 TimerIdT GlobalContext::getTimerID(TimerStackIdT StackID,
-                                   const IceString &Name) {
+                                   const std::string &Name) {
   auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
   assert(StackID < Timers->size());
   return Timers->at(StackID).getTimerID(Name);
@@ -857,7 +868,7 @@
 }
 
 void GlobalContext::setTimerName(TimerStackIdT StackID,
-                                 const IceString &NewName) {
+                                 const std::string &NewName) {
   auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
   assert(StackID < Timers->size());
   Timers->at(StackID).setName(NewName);
@@ -898,7 +909,7 @@
   return EmitQ.blockingPop();
 }
 
-void GlobalContext::dumpStats(const IceString &Name, bool Final) {
+void GlobalContext::dumpStats(const std::string &Name, bool Final) {
   if (!getFlags().getDumpStats())
     return;
   if (Final) {
@@ -917,10 +928,15 @@
   Timers->at(StackID).dump(getStrDump(), DumpCumulative);
 }
 
+LockedPtr<StringPool>
+GlobalStringPoolTraits::getStrings(const GlobalContext *PoolOwner) {
+  return PoolOwner->getStrings();
+}
+
 ClFlags GlobalContext::Flags;
 
 TimerIdT TimerMarker::getTimerIdFromFuncName(GlobalContext *Ctx,
-                                             const IceString &FuncName) {
+                                             const std::string &FuncName) {
   if (!BuildDefs::timers())
     return 0;
   if (!Ctx->getFlags().getTimeEachFunction())
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 6c6c222..05c4e27 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -20,6 +20,7 @@
 #include "IceClFlags.h"
 #include "IceIntrinsics.h"
 #include "IceRNG.h"
+#include "IceStringPool.h"
 #include "IceSwitchLowering.h"
 #include "IceTargetLowering.def"
 #include "IceThreading.h"
@@ -53,30 +54,6 @@
       H_Num
 };
 
-/// LockedPtr is a way to provide automatically locked access to some object.
-template <typename T> class LockedPtr {
-  LockedPtr() = delete;
-  LockedPtr(const LockedPtr &) = delete;
-  LockedPtr &operator=(const LockedPtr &) = delete;
-
-public:
-  LockedPtr(T *Value, GlobalLockType *Lock) : Value(Value), Lock(Lock) {
-    Lock->lock();
-  }
-  LockedPtr(LockedPtr &&Other) : Value(Other.Value), Lock(Other.Lock) {
-    Other.Value = nullptr;
-    Other.Lock = nullptr;
-  }
-  ~LockedPtr() { Lock->unlock(); }
-  T *operator->() const { return Value; }
-  T &operator*() const { return *Value; }
-  T *get() { return Value; }
-
-private:
-  T *Value;
-  GlobalLockType *Lock;
-};
-
 class GlobalContext {
   GlobalContext() = delete;
   GlobalContext(const GlobalContext &) = delete;
@@ -113,7 +90,7 @@
       for (uint32_t i = 0; i < Stats.size(); ++i)
         Stats[i] += Other.Stats[i];
     }
-    void dump(const IceString &Name, GlobalContext *Ctx);
+    void dump(const std::string &Name, GlobalContext *Ctx);
 
   private:
     std::array<uint32_t, CS_NUM> Stats;
@@ -172,6 +149,7 @@
                 ELFStreamer *ELFStreamer);
   ~GlobalContext();
 
+  void dumpStrings();
   ///
   /// The dump, error, and emit streams need to be used by only one
   /// thread at a time.  This is done by exclusively reserving the
@@ -247,11 +225,12 @@
   Constant *getConstantFloat(float Value);
   Constant *getConstantDouble(double Value);
   /// Returns a symbolic constant.
-  Constant *getConstantSym(const RelocOffsetT Offset,
-                           const RelocOffsetArray &OffsetExpr,
-                           const IceString &Name, const IceString &EmitString);
-  Constant *getConstantSym(RelocOffsetT Offset, const IceString &Name);
-  Constant *getConstantExternSym(const IceString &Name);
+  Constant *getConstantSymWithEmitString(const RelocOffsetT Offset,
+                                         const RelocOffsetArray &OffsetExpr,
+                                         GlobalString Name,
+                                         const std::string &EmitString);
+  Constant *getConstantSym(RelocOffsetT Offset, GlobalString Name);
+  Constant *getConstantExternSym(GlobalString Name);
   /// Returns an undef.
   Constant *getConstantUndef(Type Ty);
   /// Returns a zero value.
@@ -268,11 +247,12 @@
     assert(Result != nullptr && "No such runtime helper function");
     return Result;
   }
+  GlobalString getGlobalString(const std::string &Name);
 
   /// Return a locked pointer to the registered jump tables.
   JumpTableDataList getJumpTables();
   /// Create a new jump table entry and return a reference to it.
-  JumpTableData &addJumpTable(const IceString &FuncName, SizeT Id,
+  JumpTableData &addJumpTable(GlobalString FuncName, SizeT Id,
                               const JumpTableData::TargetList &TargetList);
 
   static const ClFlags &getFlags() { return Flags; }
@@ -305,7 +285,7 @@
     if (BuildDefs::dump())
       ICE_TLS_GET_FIELD(TLS)->StatsFunction.reset();
   }
-  void dumpStats(const IceString &Name, bool Final = false);
+  void dumpStats(const std::string &Name, bool Final = false);
   void statsUpdateEmitted(uint32_t InstCount) {
     if (!getFlags().getDumpStats())
       return;
@@ -356,24 +336,24 @@
 
   /// newTimerStackID() creates a new TimerStack in the global space. It does
   /// not affect any TimerStack objects in TLS.
-  TimerStackIdT newTimerStackID(const IceString &Name);
+  TimerStackIdT newTimerStackID(const std::string &Name);
   /// dumpTimers() dumps the global timer data. As such, one probably wants to
   /// call mergeTimerStacks() as a prerequisite.
   void dumpTimers(TimerStackIdT StackID = TSK_Default,
                   bool DumpCumulative = true);
   /// The following methods affect only the calling thread's TLS timer data.
-  TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name);
+  TimerIdT getTimerID(TimerStackIdT StackID, const std::string &Name);
   void pushTimer(TimerIdT ID, TimerStackIdT StackID);
   void popTimer(TimerIdT ID, TimerStackIdT StackID);
   void resetTimer(TimerStackIdT StackID);
-  void setTimerName(TimerStackIdT StackID, const IceString &NewName);
+  void setTimerName(TimerStackIdT StackID, const std::string &NewName);
 
   /// This is the first work item sequence number that the parser produces, and
   /// correspondingly the first sequence number that the emitter thread will
   /// wait for. Start numbering at 1 to leave room for a sentinel, in case e.g.
   /// we wish to inject items with a special sequence number that may be
   /// executed out of order.
-  static uint32_t getFirstSequenceNumber() { return 1; }
+  static constexpr uint32_t getFirstSequenceNumber() { return 1; }
   /// Adds a newly parsed and constructed function to the Cfg work queue.
   /// Notifies any idle workers that a new function is available for
   /// translating. May block if the work queue is too large, in order to control
@@ -474,7 +454,7 @@
   /// Uses DataLowering to lower Globals. Side effects:
   ///  - discards the initializer list for the global variable in Globals.
   ///  - clears the Globals array.
-  void lowerGlobals(const IceString &SectionSuffix);
+  void lowerGlobals(const std::string &SectionSuffix);
 
   /// Lowers the profile information.
   void lowerProfileData();
@@ -486,10 +466,8 @@
   /// function or symbol based on a command-line argument, such as changing the
   /// verbose level for a particular function. An empty Match argument means
   /// match everything. Returns true if there is a match.
-  static bool matchSymbolName(const IceString &SymbolName,
-                              const IceString &Match) {
-    return Match.empty() || Match == SymbolName;
-  }
+  static bool matchSymbolName(const GlobalString &SymbolName,
+                              const std::string &Match);
 
   static ClFlags Flags;
 
@@ -505,49 +483,58 @@
   }
   /// @}
 
+  LockedPtr<StringPool> getStrings() const {
+    return LockedPtr<StringPool>(Strings.get(), &StringsLock);
+  }
+
 private:
   // Try to ensure mutexes are allocated on separate cache lines.
 
   // Destructors collaborate with Allocator
   ICE_CACHELINE_BOUNDARY;
   // Managed by getAllocator()
-  GlobalLockType AllocLock;
+  mutable GlobalLockType AllocLock;
   ArenaAllocator Allocator;
 
   ICE_CACHELINE_BOUNDARY;
   // Managed by getInitializerAllocator()
-  GlobalLockType InitAllocLock;
+  mutable GlobalLockType InitAllocLock;
   VariableDeclarationList Globals;
 
   ICE_CACHELINE_BOUNDARY;
   // Managed by getDestructors()
   using DestructorArray = std::vector<std::function<void()>>;
-  GlobalLockType DestructorsLock;
+  mutable GlobalLockType DestructorsLock;
   DestructorArray Destructors;
 
   ICE_CACHELINE_BOUNDARY;
-  // Managed by getConstantPool()
-  GlobalLockType ConstPoolLock;
+  // Managed by getStrings()
+  mutable GlobalLockType StringsLock;
+  std::unique_ptr<StringPool> Strings;
+
+  ICE_CACHELINE_BOUNDARY;
+  // Managed by getConstPool()
+  mutable GlobalLockType ConstPoolLock;
   std::unique_ptr<ConstantPool> ConstPool;
 
   ICE_CACHELINE_BOUNDARY;
   // Managed by getJumpTableList()
-  GlobalLockType JumpTablesLock;
+  mutable GlobalLockType JumpTablesLock;
   JumpTableDataList JumpTableList;
 
   ICE_CACHELINE_BOUNDARY;
   // Managed by getErrorStatus()
-  GlobalLockType ErrorStatusLock;
+  mutable GlobalLockType ErrorStatusLock;
   ErrorCode ErrorStatus;
 
   ICE_CACHELINE_BOUNDARY;
   // Managed by getStatsCumulative()
-  GlobalLockType StatsLock;
+  mutable GlobalLockType StatsLock;
   CodeStats StatsCumulative;
 
   ICE_CACHELINE_BOUNDARY;
   // Managed by getTimers()
-  GlobalLockType TimerLock;
+  mutable GlobalLockType TimerLock;
   TimerList Timers;
 
   ICE_CACHELINE_BOUNDARY;
@@ -663,7 +650,7 @@
     if (BuildDefs::timers())
       pushCfg(Func);
   }
-  TimerMarker(GlobalContext *Ctx, const IceString &FuncName)
+  TimerMarker(GlobalContext *Ctx, const std::string &FuncName)
       : ID(getTimerIdFromFuncName(Ctx, FuncName)), Ctx(Ctx),
         StackID(GlobalContext::TSK_Funcs) {
     if (BuildDefs::timers())
@@ -679,7 +666,7 @@
   void push();
   void pushCfg(const Cfg *Func);
   static TimerIdT getTimerIdFromFuncName(GlobalContext *Ctx,
-                                         const IceString &FuncName);
+                                         const std::string &FuncName);
   const TimerIdT ID;
   GlobalContext *Ctx;
   const TimerStackIdT StackID;
diff --git a/src/IceGlobalInits.cpp b/src/IceGlobalInits.cpp
index d2f9285..a21980b 100644
--- a/src/IceGlobalInits.cpp
+++ b/src/IceGlobalInits.cpp
@@ -92,7 +92,8 @@
   return Signature.getReturnType() == Info->getReturnType();
 }
 
-IceString FunctionDeclaration::getTypeSignatureError(const GlobalContext *Ctx) {
+std::string
+FunctionDeclaration::getTypeSignatureError(const GlobalContext *Ctx) {
   std::string Buffer;
   llvm::raw_string_ostream StrBuf(Buffer);
   StrBuf << "Invalid";
diff --git a/src/IceGlobalInits.h b/src/IceGlobalInits.h
index dac91f7..862cc1d 100644
--- a/src/IceGlobalInits.h
+++ b/src/IceGlobalInits.h
@@ -61,11 +61,16 @@
     VariableDeclarationKind
   };
   GlobalDeclarationKind getKind() const { return Kind; }
-  const IceString &getName() const { return Name; }
-  void setName(const IceString &NewName) {
-    Name = getSuppressMangling() ? NewName : mangleName(NewName);
+  GlobalString getName() const { return Name; }
+  void setName(GlobalContext *Ctx, const std::string &NewName) {
+    Name = Ctx->getGlobalString(getSuppressMangling() ? NewName
+                                                      : mangleName(NewName));
   }
-  bool hasName() const { return !Name.empty(); }
+  void setName(GlobalString NewName) { Name = NewName; }
+  void setName(GlobalContext *Ctx) {
+    Name = GlobalString::createWithoutString(Ctx);
+  }
+  bool hasName() const { return Name.hasStdString(); }
   bool isInternal() const {
     return Linkage == llvm::GlobalValue::InternalLinkage;
   }
@@ -95,7 +100,7 @@
 
   /// Returns true if the name of this GlobalDeclaration indicates that it
   /// should have ExternalLinkage (as a special case).
-  virtual bool isPNaClABIExternalName(const IceString &Name) const = 0;
+  virtual bool isPNaClABIExternalName(const std::string &Name) const = 0;
 
 protected:
   GlobalDeclaration(GlobalDeclarationKind Kind,
@@ -117,7 +122,7 @@
 
   const GlobalDeclarationKind Kind;
   llvm::GlobalValue::LinkageTypes Linkage;
-  IceString Name;
+  GlobalString Name;
 };
 
 /// Models a function declaration. This includes the type signature of the
@@ -149,11 +154,13 @@
 
   /// Returns true if linkage is correct for the function declaration.
   bool verifyLinkageCorrect(const GlobalContext *Ctx) const {
-    if (isPNaClABIExternalName(getName()) || isIntrinsicName(Ctx)) {
-      return Linkage == llvm::GlobalValue::ExternalLinkage;
-    } else {
-      return verifyLinkageDefault(Ctx);
+    if (getName().hasStdString()) {
+      if (isPNaClABIExternalName(getName().toString()) ||
+          isIntrinsicName(Ctx)) {
+        return Linkage == llvm::GlobalValue::ExternalLinkage;
+      }
     }
+    return verifyLinkageDefault(Ctx);
   }
 
   /// Validates that the type signature of the function is correct. Returns true
@@ -168,7 +175,7 @@
 
   /// Generates an error message describing why validateTypeSignature returns
   /// false.
-  IceString getTypeSignatureError(const GlobalContext *Ctx);
+  std::string getTypeSignatureError(const GlobalContext *Ctx);
 
   /// Returns corresponding PNaCl intrisic information.
   const Intrinsics::FullIntrinsicInfo *
@@ -193,7 +200,7 @@
       : GlobalDeclaration(FunctionDeclarationKind, Linkage),
         Signature(Signature), CallingConv(CallingConv), IsProto(IsProto) {}
 
-  bool isPNaClABIExternalName(const IceString &Name) const override {
+  bool isPNaClABIExternalName(const std::string &Name) const override {
     return Name == "_start";
   }
 
@@ -443,7 +450,7 @@
     // faulty SuppressMangling logic.
     const bool SameMangling = (OldSuppressMangling == getSuppressMangling());
     (void)SameMangling;
-    assert(!Name.empty() || SameMangling);
+    assert(Name.hasStdString() || SameMangling);
   }
 
   /// Prints out type for initializer associated with the declaration to Stream.
@@ -455,8 +462,10 @@
 
   /// Returns true if linkage is correct for the variable declaration.
   bool verifyLinkageCorrect(const GlobalContext *Ctx) const {
-    if (isPNaClABIExternalName(getName())) {
-      return Linkage == llvm::GlobalValue::ExternalLinkage;
+    if (getName().hasStdString()) {
+      if (isPNaClABIExternalName(getName().toString())) {
+        return Linkage == llvm::GlobalValue::ExternalLinkage;
+      }
     }
     return verifyLinkageDefault(Ctx);
   }
@@ -473,7 +482,7 @@
 
   void discardInitializers() { Initializers.clear(); }
 
-  bool isPNaClABIExternalName(const IceString &Name) const override {
+  bool isPNaClABIExternalName(const std::string &Name) const override {
     return Name == "__pnacl_pso_root";
   }
 
diff --git a/src/IceInst.cpp b/src/IceInst.cpp
index 1442c82..5a308d8 100644
--- a/src/IceInst.cpp
+++ b/src/IceInst.cpp
@@ -77,7 +77,7 @@
     : Kind(Kind), Number(Func->newInstNumber()), Dest(Dest), MaxSrcs(MaxSrcs),
       Srcs(Func->allocateArrayOf<Operand *>(MaxSrcs)), LiveRangesEnded(0) {}
 
-IceString Inst::getInstName() const {
+const char *Inst::getInstName() const {
   if (!BuildDefs::dump())
     return "???";
 
@@ -272,7 +272,7 @@
   addSource(Source2);
 }
 
-IceString InstArithmetic::getInstName() const {
+const char *InstArithmetic::getInstName() const {
   if (!BuildDefs::dump())
     return "???";
 
diff --git a/src/IceInst.h b/src/IceInst.h
index 6c62d0b..4fb74ae 100644
--- a/src/IceInst.h
+++ b/src/IceInst.h
@@ -76,7 +76,7 @@
   };
   static_assert(Target <= Target_Max, "Must not be above max.");
   InstKind getKind() const { return Kind; }
-  virtual IceString getInstName() const;
+  virtual const char *getInstName() const;
 
   InstNumberT getNumber() const { return Number; }
   void renumber(Cfg *Func);
@@ -290,7 +290,7 @@
   }
   OpKind getOp() const { return Op; }
 
-  virtual IceString getInstName() const override;
+  virtual const char *getInstName() const override;
 
   static const char *getOpName(OpKind Op);
   bool isCommutative() const;
@@ -942,8 +942,11 @@
     return Instr->getKind() == JumpTable;
   }
 
-  static IceString makeName(const IceString &FuncName, SizeT Id) {
-    return ".L" + FuncName + "$jumptable$__" + std::to_string(Id);
+  // TODO(stichnot): Should this create&save GlobalString values?
+  static std::string makeName(GlobalString FuncName, SizeT Id) {
+    if (FuncName.hasStdString())
+      return ".L" + FuncName + "$jumptable$__" + std::to_string(Id);
+    return ".L" + std::to_string(FuncName.getID()) + "_" + std::to_string(Id);
   }
 
 private:
diff --git a/src/IceInstARM32.cpp b/src/IceInstARM32.cpp
index 698ec94..a025da5 100644
--- a/src/IceInstARM32.cpp
+++ b/src/IceInstARM32.cpp
@@ -627,7 +627,7 @@
   switch (DestTy) {
   default:
     llvm::report_fatal_error("Vadd not defined on type " +
-                             typeIceString(DestTy));
+                             typeStdString(DestTy));
   case IceType_v16i8:
   case IceType_v8i16:
   case IceType_v4i32:
@@ -652,7 +652,7 @@
   switch (Dest->getType()) {
   default:
     llvm::report_fatal_error("Vand not defined on type " +
-                             typeIceString(Dest->getType()));
+                             typeStdString(Dest->getType()));
   case IceType_v4i1:
   case IceType_v8i1:
   case IceType_v16i1:
@@ -743,7 +743,7 @@
   switch (Dest->getType()) {
   default:
     llvm::report_fatal_error("Vorr not defined on type " +
-                             typeIceString(Dest->getType()));
+                             typeStdString(Dest->getType()));
   case IceType_v4i1:
   case IceType_v8i1:
   case IceType_v16i1:
@@ -762,7 +762,7 @@
   switch (DestTy) {
   default:
     llvm::report_fatal_error("Vsub not defined on type " +
-                             typeIceString(DestTy));
+                             typeStdString(DestTy));
   case IceType_v16i8:
   case IceType_v8i16:
   case IceType_v4i32:
@@ -788,7 +788,7 @@
   switch (DestTy) {
   default:
     llvm::report_fatal_error("Vmul not defined on type " +
-                             typeIceString(DestTy));
+                             typeStdString(DestTy));
 
   case IceType_v16i8:
   case IceType_v8i16:
@@ -815,12 +815,14 @@
 
 InstARM32Label::InstARM32Label(Cfg *Func, TargetARM32 *Target)
     : InstARM32(Func, InstARM32::Label, 0, nullptr),
-      Number(Target->makeNextLabelNumber()) {}
-
-IceString InstARM32Label::getName(const Cfg *Func) const {
-  if (!BuildDefs::dump())
-    return "";
-  return ".L" + Func->getFunctionName() + "$local$__" + std::to_string(Number);
+      Number(Target->makeNextLabelNumber()) {
+  if (BuildDefs::dump()) {
+    Name = GlobalString::createWithString(
+        Func->getContext(),
+        ".L" + Func->getFunctionName() + "$local$__" + std::to_string(Number));
+  } else {
+    Name = GlobalString::createWithoutString(Func->getContext());
+  }
 }
 
 namespace {
@@ -1582,8 +1584,8 @@
     return;
   }
   llvm::report_fatal_error("Mov: don't know how to move " +
-                           typeIceString(SrcTy) + " to " +
-                           typeIceString(DestTy));
+                           typeStdString(SrcTy) + " to " +
+                           typeStdString(DestTy));
 }
 
 void InstARM32Mov::dump(const Cfg *Func) const {
@@ -1612,7 +1614,7 @@
   Str << "\t"
          "b" << getPredicate() << "\t";
   if (Label) {
-    Str << Label->getName(Func);
+    Str << Label->getLabelName();
   } else {
     if (isUnconditionalBranch()) {
       Str << getTargetFalse()->getAsmName();
@@ -1652,13 +1654,16 @@
   Str << "br ";
 
   if (getPredicate() == CondARM32::AL) {
-    Str << "label %"
-        << (Label ? Label->getName(Func) : getTargetFalse()->getName());
+    if (Label) {
+      Str << "label %" << Label->getLabelName();
+    } else {
+      Str << "label %" << getTargetFalse()->getName();
+    }
     return;
   }
 
   if (Label) {
-    Str << getPredicate() << ", label %" << Label->getName(Func);
+    Str << getPredicate() << ", label %" << Label->getLabelName();
   } else {
     Str << getPredicate() << ", label %" << getTargetTrue()->getName();
     if (getTargetFalse()) {
@@ -1731,7 +1736,7 @@
   if (auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>())
     Asm->decEmitTextSize(InstSize);
   Ostream &Str = Func->getContext()->getStrEmit();
-  Str << getName(Func) << ":";
+  Str << getLabelName() << ":";
 }
 
 void InstARM32Label::emitIAS(const Cfg *Func) const {
@@ -1748,7 +1753,7 @@
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Func->getContext()->getStrDump();
-  Str << getName(Func) << ":";
+  Str << getLabelName() << ":";
 }
 
 template <InstARM32::InstKindARM32 K>
@@ -1791,7 +1796,7 @@
   const Type DestTy = Dest->getType();
   switch (DestTy) {
   default:
-    llvm::report_fatal_error("Ldr on unknown type: " + typeIceString(DestTy));
+    llvm::report_fatal_error("Ldr on unknown type: " + typeStdString(DestTy));
   case IceType_i1:
   case IceType_i8:
   case IceType_i16:
@@ -2139,7 +2144,7 @@
   Type Ty = Src0->getType();
   switch (Ty) {
   default:
-    llvm::report_fatal_error("Str on unknown type: " + typeIceString(Ty));
+    llvm::report_fatal_error("Str on unknown type: " + typeStdString(Ty));
   case IceType_i1:
   case IceType_i8:
   case IceType_i16:
@@ -2469,7 +2474,7 @@
   switch (Dest->getType()) {
   default:
     llvm::report_fatal_error("fabs not defined on type " +
-                             typeIceString(Dest->getType()));
+                             typeStdString(Dest->getType()));
   case IceType_f32:
     Asm->vabss(Dest, getSrc(0), getPredicate());
     break;
diff --git a/src/IceInstARM32.h b/src/IceInstARM32.h
index c939246..e31fce5 100644
--- a/src/IceInstARM32.h
+++ b/src/IceInstARM32.h
@@ -347,7 +347,7 @@
 
 public:
   static StackVariable *create(Cfg *Func, Type Ty, SizeT Index) {
-    return new (Func->allocate<StackVariable>()) StackVariable(Ty, Index);
+    return new (Func->allocate<StackVariable>()) StackVariable(Func, Ty, Index);
   }
   constexpr static auto StackVariableKind =
       static_cast<OperandKind>(kVariable_Target);
@@ -359,8 +359,8 @@
   // Inherit dump() and emit() from Variable.
 
 private:
-  StackVariable(Type Ty, SizeT Index)
-      : Variable(StackVariableKind, Ty, Index) {}
+  StackVariable(const Cfg *Func, Type Ty, SizeT Index)
+      : Variable(Func, StackVariableKind, Ty, Index) {}
   RegNumT BaseRegNum;
 };
 
@@ -968,7 +968,7 @@
     return new (Func->allocate<InstARM32Label>()) InstARM32Label(Func, Target);
   }
   uint32_t getEmitInstCount() const override { return 0; }
-  IceString getName(const Cfg *Func) const;
+  GlobalString getLabelName() const { return Name; }
   SizeT getNumber() const { return Number; }
   void emit(const Cfg *Func) const override;
   void emitIAS(const Cfg *Func) const override;
@@ -979,8 +979,8 @@
   InstARM32Label(Cfg *Func, TargetARM32 *Target);
 
   RelocOffset *OffsetReloc = nullptr;
-
   SizeT Number; // used for unique label generation.
+  GlobalString Name;
 };
 
 /// Direct branch instruction.
diff --git a/src/IceInstMIPS32.cpp b/src/IceInstMIPS32.cpp
index dcd977d..1b0ab4c 100644
--- a/src/IceInstMIPS32.cpp
+++ b/src/IceInstMIPS32.cpp
@@ -114,26 +114,28 @@
 
 InstMIPS32Label::InstMIPS32Label(Cfg *Func, TargetMIPS32 *Target)
     : InstMIPS32(Func, InstMIPS32::Label, 0, nullptr),
-      Number(Target->makeNextLabelNumber()) {}
-
-IceString InstMIPS32Label::getName(const Cfg *Func) const {
-  if (!BuildDefs::dump())
-    return "";
-  return ".L" + Func->getFunctionName() + "$local$__" + std::to_string(Number);
+      Number(Target->makeNextLabelNumber()) {
+  if (BuildDefs::dump()) {
+    Name = GlobalString::createWithString(
+        Func->getContext(),
+        ".L" + Func->getFunctionName() + "$local$__" + std::to_string(Number));
+  } else {
+    Name = GlobalString::createWithoutString(Func->getContext());
+  }
 }
 
 void InstMIPS32Label::dump(const Cfg *Func) const {
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Func->getContext()->getStrDump();
-  Str << getName(Func) << ":";
+  Str << getLabelName() << ":";
 }
 
 void InstMIPS32Label::emit(const Cfg *Func) const {
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Func->getContext()->getStrEmit();
-  Str << getName(Func) << ":";
+  Str << getLabelName() << ":";
 }
 
 void InstMIPS32Label::emitIAS(const Cfg *Func) const {
@@ -268,7 +270,7 @@
          "b"
       << "\t";
   if (Label) {
-    Str << Label->getName(Func);
+    Str << Label->getLabelName();
   } else {
     if (isUnconditionalBranch()) {
       Str << getTargetFalse()->getAsmName();
diff --git a/src/IceInstMIPS32.h b/src/IceInstMIPS32.h
index 25b48b5..a981473 100644
--- a/src/IceInstMIPS32.h
+++ b/src/IceInstMIPS32.h
@@ -308,7 +308,7 @@
         InstMIPS32Label(Func, Target);
   }
   uint32_t getEmitInstCount() const override { return 0; }
-  IceString getName(const Cfg *Func) const;
+  GlobalString getLabelName() const { return Name; }
   SizeT getNumber() const { return Number; }
   void emit(const Cfg *Func) const override;
   void emitIAS(const Cfg *Func) const override;
@@ -320,8 +320,8 @@
   InstMIPS32Label(Cfg *Func, TargetMIPS32 *Target);
 
   // RelocOffset *OffsetReloc = nullptr;
-
   SizeT Number; // used for unique label generation.
+  GlobalString Name;
 };
 
 /// Direct branch instruction.
diff --git a/src/IceInstX8664.cpp b/src/IceInstX8664.cpp
index bbcb080..ec2d140 100644
--- a/src/IceInstX8664.cpp
+++ b/src/IceInstX8664.cpp
@@ -138,12 +138,11 @@
     CR->emitWithoutPrefix(Target, UseNonsfi ? "@GOTOFF" : "");
     assert(!UseNonsfi);
     if (Base == nullptr && Index == nullptr) {
-      if (CR->getName() != "") { // rip-relative addressing.
-        if (NeedSandboxing) {
-          Str << "(%rip)";
-        } else {
-          Str << "(%eip)";
-        }
+      // rip-relative addressing.
+      if (NeedSandboxing) {
+        Str << "(%rip)";
+      } else {
+        Str << "(%eip)";
       }
     }
   } else {
@@ -167,8 +166,8 @@
         assert(Base->getRegNum() == RegX8664::Encoded_Reg_rsp ||
                Base->getRegNum() == RegX8664::Encoded_Reg_rbp ||
                getType() == IceType_void);
-        B = B->asType(IceType_i32, X8664::Traits::getGprForType(
-                                       IceType_i32, Base->getRegNum()));
+        B = B->asType(Func, IceType_i32, X8664::Traits::getGprForType(
+                                             IceType_i32, Base->getRegNum()));
       }
     }
 
@@ -267,13 +266,11 @@
       Disp += static_cast<int32_t>(CI->getValue());
     } else if (const auto *CR =
                    llvm::dyn_cast<ConstantRelocatable>(getOffset())) {
-      if (CR->getName() != "") {
-        const auto FixupKind =
-            (getBase() != nullptr || getIndex() != nullptr) ? FK_Abs : FK_PcRel;
-        const RelocOffsetT DispAdjustment = FixupKind == FK_PcRel ? 4 : 0;
-        Fixup = Asm->createFixup(FixupKind, CR);
-        Fixup->set_addend(-DispAdjustment);
-      }
+      const auto FixupKind =
+          (getBase() != nullptr || getIndex() != nullptr) ? FK_Abs : FK_PcRel;
+      const RelocOffsetT DispAdjustment = FixupKind == FK_PcRel ? 4 : 0;
+      Fixup = Asm->createFixup(FixupKind, CR);
+      Fixup->set_addend(-DispAdjustment);
       Disp = CR->getOffset();
     } else {
       llvm_unreachable("Unexpected offset type");
diff --git a/src/IceInstX86Base.h b/src/IceInstX86Base.h
index 42e1558..761d412 100644
--- a/src/IceInstX86Base.h
+++ b/src/IceInstX86Base.h
@@ -332,8 +332,8 @@
       return new (Func->allocate<InstX86Label>()) InstX86Label(Func, Target);
     }
     uint32_t getEmitInstCount() const override { return 0; }
-    IceString getName(const Cfg *Func) const;
-    SizeT getNumber() const { return Number; }
+    GlobalString getLabelName() const { return Name; }
+    SizeT getLabelNumber() const { return LabelNumber; }
     void emit(const Cfg *Func) const override;
     void emitIAS(const Cfg *Func) const override;
     void dump(const Cfg *Func) const override;
@@ -342,8 +342,9 @@
   private:
     InstX86Label(Cfg *Func, TargetLowering *Target);
 
-    SizeT Number; // used for unique label generation.
+    SizeT LabelNumber; // used for unique label generation.
     RelocOffset *OffsetReloc = nullptr;
+    GlobalString Name;
   };
 
   /// Conditional and unconditional branch instruction.
diff --git a/src/IceInstX86BaseImpl.h b/src/IceInstX86BaseImpl.h
index a6ce132..3de3296 100644
--- a/src/IceInstX86BaseImpl.h
+++ b/src/IceInstX86BaseImpl.h
@@ -94,16 +94,14 @@
 InstImpl<TraitsType>::InstX86Label::InstX86Label(Cfg *Func,
                                                  TargetLowering *Target)
     : InstX86Base(Func, InstX86Base::Label, 0, nullptr),
-      Number(Target->makeNextLabelNumber()) {}
-
-template <typename TraitsType>
-IceString InstImpl<TraitsType>::InstX86Label::getName(const Cfg *Func) const {
-  // TODO(stichnot): Returning an empty string in a non-DUMP build can cause a
-  // huge degradation in ConstantRelocatable hashing.  Investigate and fix, but
-  // for now return something reasonably unique.
-  if (!BuildDefs::dump())
-    return Func->getFunctionName() + std::to_string(Number);
-  return ".L" + Func->getFunctionName() + "$local$__" + std::to_string(Number);
+      LabelNumber(Target->makeNextLabelNumber()) {
+  if (BuildDefs::dump()) {
+    Name = GlobalString::createWithString(
+        Func->getContext(), ".L" + Func->getFunctionName() + "$local$__" +
+                                std::to_string(LabelNumber));
+  } else {
+    Name = GlobalString::createWithoutString(Func->getContext());
+  }
 }
 
 template <typename TraitsType>
@@ -441,13 +439,13 @@
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Func->getContext()->getStrEmit();
-  Str << getName(Func) << ":";
+  Str << getLabelName() << ":";
 }
 
 template <typename TraitsType>
 void InstImpl<TraitsType>::InstX86Label::emitIAS(const Cfg *Func) const {
   Assembler *Asm = Func->getAssembler<Assembler>();
-  Asm->bindLocalLabel(Number);
+  Asm->bindLocalLabel(LabelNumber);
   if (OffsetReloc != nullptr) {
     Asm->bindRelocOffset(OffsetReloc);
   }
@@ -458,7 +456,7 @@
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Func->getContext()->getStrDump();
-  Str << getName(Func) << ":";
+  Str << getLabelName() << ":";
 }
 
 template <typename TraitsType>
@@ -475,7 +473,7 @@
   }
 
   if (Label) {
-    Str << "\t" << Label->getName(Func);
+    Str << "\t" << Label->getLabelName();
   } else {
     if (Condition == Cond::Br_None) {
       Str << "\t" << getTargetFalse()->getAsmName();
@@ -493,7 +491,7 @@
 void InstImpl<TraitsType>::InstX86Br::emitIAS(const Cfg *Func) const {
   Assembler *Asm = Func->getAssembler<Assembler>();
   if (Label) {
-    auto *L = Asm->getOrCreateLocalLabel(Label->getNumber());
+    auto *L = Asm->getOrCreateLocalLabel(Label->getLabelNumber());
     if (Condition == Cond::Br_None) {
       Asm->jmp(L, isNear());
     } else {
@@ -523,14 +521,17 @@
   Str << "br ";
 
   if (Condition == Cond::Br_None) {
-    Str << "label %"
-        << (Label ? Label->getName(Func) : getTargetFalse()->getName());
+    if (Label) {
+      Str << "label %" << Label->getLabelName();
+    } else {
+      Str << "label %" << getTargetFalse()->getName();
+    }
     return;
   }
 
   Str << Traits::InstBrAttributes[Condition].DisplayString;
   if (Label) {
-    Str << ", label %" << Label->getName(Func);
+    Str << ", label %" << Label->getLabelName();
   } else {
     Str << ", label %" << getTargetTrue()->getName();
     if (getTargetFalse()) {
@@ -661,7 +662,7 @@
   getCallTarget()->dump(Func);
 }
 
-// The this->Opcode parameter needs to be char* and not IceString because of
+// The this->Opcode parameter needs to be char* and not std::string because of
 // template issues.
 template <typename TraitsType>
 void InstImpl<TraitsType>::InstX86Base::emitTwoAddress(
@@ -734,7 +735,8 @@
   } else if (const auto *Imm = llvm::dyn_cast<ConstantInteger32>(Src)) {
     (Asm->*(Emitter.GPRImm))(Ty, VarReg, AssemblerImmediate(Imm->getValue()));
   } else if (const auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Src)) {
-    const auto FixupKind = Reloc->getName() == GlobalOffsetTable
+    const auto FixupKind = (Reloc->getName().hasStdString() &&
+                            Reloc->getName().toString() == GlobalOffsetTable)
                                ? Traits::FK_GotPC
                                : Traits::TargetLowering::getAbsFixup();
     AssemblerFixup *Fixup = Asm->createFixup(FixupKind, Reloc);
@@ -760,7 +762,8 @@
   } else if (const auto *Imm = llvm::dyn_cast<ConstantInteger32>(Src)) {
     (Asm->*(Emitter.AddrImm))(Ty, Addr, AssemblerImmediate(Imm->getValue()));
   } else if (const auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Src)) {
-    const auto FixupKind = Reloc->getName() == GlobalOffsetTable
+    const auto FixupKind = (Reloc->getName().hasStdString() &&
+                            Reloc->getName().toString() == GlobalOffsetTable)
                                ? Traits::FK_GotPC
                                : Traits::TargetLowering::getAbsFixup();
     AssemblerFixup *Fixup = Asm->createFixup(FixupKind, Reloc);
@@ -1992,7 +1995,8 @@
     Type Ty = Src0Var->getType();
     // lea on x86-32 doesn't accept mem128 operands, so cast VSrc0 to an
     // acceptable type.
-    Src0Var->asType(isVectorType(Ty) ? IceType_i32 : Ty, RegNumT())->emit(Func);
+    Src0Var->asType(Func, isVectorType(Ty) ? IceType_i32 : Ty, RegNumT())
+        ->emit(Func);
   } else {
     Src0->emit(Func);
   }
@@ -2033,7 +2037,7 @@
     if (SrcVar->hasReg())
       NewRegNum = Traits::getGprForType(DestTy, SrcVar->getRegNum());
     if (SrcTy != DestTy)
-      NewSrc = SrcVar->asType(DestTy, NewRegNum);
+      NewSrc = SrcVar->asType(Func, DestTy, NewRegNum);
   }
   NewSrc->emit(Func);
   Str << ", ";
@@ -2311,7 +2315,7 @@
                "\t";
         Src->emit(Func);
         Str << ", ";
-        Dest->asType(IceType_i32,
+        Dest->asType(Func, IceType_i32,
                      Traits::getGprForType(IceType_i32, Dest->getRegNum()))
             ->emit(Func);
         Str << " /* movzx */";
@@ -2519,7 +2523,7 @@
   // memory dest, but we aren't using it. For uniformity, just restrict them
   // all to have a register dest for now.
   assert(Dest->hasReg());
-  Dest->asType(IceType_i32, Dest->getRegNum())->emit(Func);
+  Dest->asType(Func, IceType_i32, Dest->getRegNum())->emit(Func);
 }
 
 template <typename TraitsType>
@@ -2556,7 +2560,7 @@
     // If src1 is a register, it should always be r32.
     if (Src1Var->hasReg()) {
       const auto NewRegNum = Traits::getBaseReg(Src1Var->getRegNum());
-      const Variable *NewSrc = Src1Var->asType(IceType_i32, NewRegNum);
+      const Variable *NewSrc = Src1Var->asType(Func, IceType_i32, NewRegNum);
       NewSrc->emit(Func);
     } else {
       Src1Var->emit(Func);
diff --git a/src/IceIntrinsics.cpp b/src/IceIntrinsics.cpp
index 748aeb1..4555b21 100644
--- a/src/IceIntrinsics.cpp
+++ b/src/IceIntrinsics.cpp
@@ -20,6 +20,7 @@
 #include "IceInst.h"
 #include "IceLiveness.h"
 #include "IceOperand.h"
+#include "IceStringPool.h"
 
 #include <utility>
 
@@ -51,7 +52,7 @@
                                                              IceType_i32},     \
           6                                                                    \
     }                                                                          \
-    , "nacl.atomic.cmpxchg." NameSuffix                                        \
+    , "llvm.nacl.atomic.cmpxchg." NameSuffix                                   \
   }
     AtomicCmpxchgInit(IceType_i8, "i8"),
     AtomicCmpxchgInit(IceType_i16, "i16"),
@@ -62,13 +63,13 @@
     {{INTRIN(AtomicFence, SideEffects_T, ReturnsTwice_F),
       {IceType_void, IceType_i32},
       2},
-     "nacl.atomic.fence"},
+     "llvm.nacl.atomic.fence"},
     {{INTRIN(AtomicFenceAll, SideEffects_T, ReturnsTwice_F), {IceType_void}, 1},
-     "nacl.atomic.fence.all"},
+     "llvm.nacl.atomic.fence.all"},
     {{INTRIN(AtomicIsLockFree, SideEffects_F, ReturnsTwice_F),
       {IceType_i1, IceType_i32, IceType_i32},
       3},
-     "nacl.atomic.is.lock.free"},
+     "llvm.nacl.atomic.is.lock.free"},
 
 #define AtomicLoadInit(Overload, NameSuffix)                                   \
   {                                                                            \
@@ -76,7 +77,7 @@
       INTRIN(AtomicLoad, SideEffects_T, ReturnsTwice_F),                       \
           {Overload, IceType_i32, IceType_i32}, 3                              \
     }                                                                          \
-    , "nacl.atomic.load." NameSuffix                                           \
+    , "llvm.nacl.atomic.load." NameSuffix                                      \
   }
     AtomicLoadInit(IceType_i8, "i8"),
     AtomicLoadInit(IceType_i16, "i16"),
@@ -90,7 +91,7 @@
       INTRIN(AtomicRMW, SideEffects_T, ReturnsTwice_F)                         \
       , {Overload, IceType_i32, IceType_i32, Overload, IceType_i32}, 5         \
     }                                                                          \
-    , "nacl.atomic.rmw." NameSuffix                                            \
+    , "llvm.nacl.atomic.rmw." NameSuffix                                       \
   }
     AtomicRMWInit(IceType_i8, "i8"),
     AtomicRMWInit(IceType_i16, "i16"),
@@ -104,7 +105,7 @@
       INTRIN(AtomicStore, SideEffects_T, ReturnsTwice_F)                       \
       , {IceType_void, Overload, IceType_i32, IceType_i32}, 4                  \
     }                                                                          \
-    , "nacl.atomic.store." NameSuffix                                          \
+    , "llvm.nacl.atomic.store." NameSuffix                                     \
   }
     AtomicStoreInit(IceType_i8, "i8"),
     AtomicStoreInit(IceType_i16, "i16"),
@@ -118,7 +119,7 @@
       INTRIN(Bswap, SideEffects_F, ReturnsTwice_F)                             \
       , {Overload, Overload}, 2                                                \
     }                                                                          \
-    , "bswap." NameSuffix                                                      \
+    , "llvm.bswap." NameSuffix                                                 \
   }
     BswapInit(IceType_i16, "i16"),
     BswapInit(IceType_i32, "i32"),
@@ -131,7 +132,7 @@
       INTRIN(Ctlz, SideEffects_F, ReturnsTwice_F)                              \
       , {Overload, Overload, IceType_i1}, 3                                    \
     }                                                                          \
-    , "ctlz." NameSuffix                                                       \
+    , "llvm.ctlz." NameSuffix                                                  \
   }
     CtlzInit(IceType_i32, "i32"),
     CtlzInit(IceType_i64, "i64"),
@@ -143,7 +144,7 @@
       INTRIN(Ctpop, SideEffects_F, ReturnsTwice_F)                             \
       , {Overload, Overload}, 2                                                \
     }                                                                          \
-    , "ctpop." NameSuffix                                                      \
+    , "llvm.ctpop." NameSuffix                                                 \
   }
     CtpopInit(IceType_i32, "i32"),
     CtpopInit(IceType_i64, "i64"),
@@ -155,7 +156,7 @@
       INTRIN(Cttz, SideEffects_F, ReturnsTwice_F)                              \
       , {Overload, Overload, IceType_i1}, 3                                    \
     }                                                                          \
-    , "cttz." NameSuffix                                                       \
+    , "llvm.cttz." NameSuffix                                                  \
   }
     CttzInit(IceType_i32, "i32"),
     CttzInit(IceType_i64, "i64"),
@@ -164,7 +165,7 @@
 #define FabsInit(Overload, NameSuffix)                                         \
   {                                                                            \
     { INTRIN(Fabs, SideEffects_F, ReturnsTwice_F), {Overload, Overload}, 2 }   \
-    , "fabs." NameSuffix                                                       \
+    , "llvm.fabs." NameSuffix                                                  \
   }
     FabsInit(IceType_f32, "f32"),
     FabsInit(IceType_f64, "f64"),
@@ -174,75 +175,74 @@
     {{INTRIN(Longjmp, SideEffects_T, ReturnsTwice_F),
       {IceType_void, IceType_i32, IceType_i32},
       3},
-     "nacl.longjmp"},
+     "llvm.nacl.longjmp"},
     {{INTRIN(Memcpy, SideEffects_T, ReturnsTwice_F),
       {IceType_void, IceType_i32, IceType_i32, IceType_i32, IceType_i32,
        IceType_i1},
       6},
-     "memcpy.p0i8.p0i8.i32"},
+     "llvm.memcpy.p0i8.p0i8.i32"},
     {{INTRIN(Memmove, SideEffects_T, ReturnsTwice_F),
       {IceType_void, IceType_i32, IceType_i32, IceType_i32, IceType_i32,
        IceType_i1},
       6},
-     "memmove.p0i8.p0i8.i32"},
+     "llvm.memmove.p0i8.p0i8.i32"},
     {{INTRIN(Memset, SideEffects_T, ReturnsTwice_F),
       {IceType_void, IceType_i32, IceType_i8, IceType_i32, IceType_i32,
        IceType_i1},
       6},
-     "memset.p0i8.i32"},
+     "llvm.memset.p0i8.i32"},
     {{INTRIN(NaClReadTP, SideEffects_F, ReturnsTwice_F), {IceType_i32}, 1},
-     "nacl.read.tp"},
+     "llvm.nacl.read.tp"},
     {{INTRIN(Setjmp, SideEffects_T, ReturnsTwice_T),
       {IceType_i32, IceType_i32},
       2},
-     "nacl.setjmp"},
+     "llvm.nacl.setjmp"},
 
 #define SqrtInit(Overload, NameSuffix)                                         \
   {                                                                            \
     { INTRIN(Sqrt, SideEffects_F, ReturnsTwice_F), {Overload, Overload}, 2 }   \
-    , "sqrt." NameSuffix                                                       \
+    , "llvm.sqrt." NameSuffix                                                  \
   }
     SqrtInit(IceType_f32, "f32"),
     SqrtInit(IceType_f64, "f64"),
 #undef SqrtInit
 
     {{INTRIN(Stacksave, SideEffects_T, ReturnsTwice_F), {IceType_i32}, 1},
-     "stacksave"},
+     "llvm.stacksave"},
     {{INTRIN(Stackrestore, SideEffects_T, ReturnsTwice_F),
       {IceType_void, IceType_i32},
       2},
-     "stackrestore"},
-    {{INTRIN(Trap, SideEffects_T, ReturnsTwice_F), {IceType_void}, 1}, "trap"}};
+     "llvm.stackrestore"},
+    {{INTRIN(Trap, SideEffects_T, ReturnsTwice_F), {IceType_void}, 1},
+     "llvm.trap"}};
 const size_t IceIntrinsicsTableSize = llvm::array_lengthof(IceIntrinsicsTable);
 
 #undef INTRIN
 
 } // end of anonymous namespace
 
-Intrinsics::Intrinsics() {
+Intrinsics::Intrinsics(GlobalContext *Ctx) {
   for (size_t I = 0; I < IceIntrinsicsTableSize; ++I) {
     const struct IceIntrinsicsEntry_ &Entry = IceIntrinsicsTable[I];
     assert(Entry.Info.NumTypes <= kMaxIntrinsicParameters);
-    Map.insert(std::make_pair(IceString(Entry.IntrinsicName), Entry.Info));
+    Map.insert(
+        std::make_pair(Ctx->getGlobalString(Entry.IntrinsicName), Entry.Info));
   }
 }
 
-Intrinsics::~Intrinsics() = default;
-
-const Intrinsics::FullIntrinsicInfo *Intrinsics::find(const IceString &Name,
+const Intrinsics::FullIntrinsicInfo *Intrinsics::find(GlobalString Name,
                                                       bool &Error) const {
-  static const char LLVMPrefix[] = "llvm.";
-  const size_t LLVMPrefixLen = strlen(LLVMPrefix);
+  static constexpr char LLVMPrefix[] = "llvm.";
+  constexpr size_t LLVMPrefixLen = llvm::array_lengthof(LLVMPrefix) - 1;
   Error = false;
-  if (Name.substr(0, LLVMPrefixLen) != LLVMPrefix)
+  if (Name.toString().substr(0, LLVMPrefixLen) != LLVMPrefix)
     return nullptr;
-  const IceString NameSuffix = Name.substr(LLVMPrefixLen);
-  auto it = Map.find(NameSuffix);
-  if (it == Map.end()) {
+  auto Iter = Map.find(Name);
+  if (Iter == Map.end()) {
     Error = true;
     return nullptr;
   }
-  return &it->second;
+  return &Iter->second;
 }
 
 namespace {
diff --git a/src/IceIntrinsics.h b/src/IceIntrinsics.h
index 2bd1a68..aea0bd2 100644
--- a/src/IceIntrinsics.h
+++ b/src/IceIntrinsics.h
@@ -16,6 +16,7 @@
 #define SUBZERO_SRC_ICEINTRINSICS_H
 
 #include "IceDefs.h"
+#include "IceStringPool.h"
 #include "IceTypes.h"
 
 namespace Ice {
@@ -29,8 +30,8 @@
   Intrinsics &operator=(const Intrinsics &) = delete;
 
 public:
-  Intrinsics();
-  ~Intrinsics();
+  explicit Intrinsics(GlobalContext *Ctx);
+  ~Intrinsics() = default;
 
   /// Some intrinsics allow overloading by type. This enum collapses all
   /// overloads into a single ID, but the type can still be recovered by the
@@ -160,11 +161,11 @@
   /// found, sets Error to false and returns the reference. If not found, sets
   /// Error to true and returns nullptr (indicating an unknown "llvm.foo"
   /// intrinsic).
-  const FullIntrinsicInfo *find(const IceString &Name, bool &Error) const;
+  const FullIntrinsicInfo *find(GlobalString Name, bool &Error) const;
 
 private:
   // TODO(jvoung): May want to switch to something like LLVM's StringMap.
-  using IntrinsicMap = std::map<IceString, FullIntrinsicInfo>;
+  using IntrinsicMap = std::unordered_map<GlobalString, FullIntrinsicInfo>;
   IntrinsicMap Map;
 };
 
diff --git a/src/IceMangling.cpp b/src/IceMangling.cpp
index 685b3d9..28275c1 100644
--- a/src/IceMangling.cpp
+++ b/src/IceMangling.cpp
@@ -117,7 +117,7 @@
 // In this context, name mangling means to rewrite a symbol using a given
 // prefix. For a C++ symbol, nest the original symbol inside the "prefix"
 // namespace. For other symbols, just prepend the prefix.
-IceString mangleName(const IceString &Name) {
+std::string mangleName(const std::string &Name) {
   // An already-nested name like foo::bar() gets pushed down one level, making
   // it equivalent to Prefix::foo::bar().
   //   _ZN3foo3barExyz ==> _ZN6Prefix3foo3barExyz
@@ -129,7 +129,7 @@
   if (!BuildDefs::dump() || GlobalContext::getFlags().getTestPrefix().empty())
     return Name;
 
-  const IceString &TestPrefix = GlobalContext::getFlags().getTestPrefix();
+  const std::string TestPrefix = GlobalContext::getFlags().getTestPrefix();
   unsigned PrefixLength = TestPrefix.length();
   ManglerVector NameBase(1 + Name.length());
   const size_t BufLen = 30 + Name.length() + PrefixLength;
diff --git a/src/IceMangling.h b/src/IceMangling.h
index 3c838a1..d1f12fb 100644
--- a/src/IceMangling.h
+++ b/src/IceMangling.h
@@ -15,11 +15,11 @@
 #ifndef SUBZERO_SRC_ICEMANGLING_H
 #define SUBZERO_SRC_ICEMANGLING_H
 
-#include "IceDefs.h"
+#include <string>
 
 namespace Ice {
 
-IceString mangleName(const IceString &Name);
+std::string mangleName(const std::string &Name);
 
 } // end of namespace Ice
 
diff --git a/src/IceOperand.cpp b/src/IceOperand.cpp
index 2133185..870ca17 100644
--- a/src/IceOperand.cpp
+++ b/src/IceOperand.cpp
@@ -24,6 +24,10 @@
 
 namespace Ice {
 
+void Constant::initShouldBePooled() {
+  ShouldBePooled = TargetLowering::shouldBePooled(this);
+}
+
 bool operator==(const RelocatableTuple &A, const RelocatableTuple &B) {
   // A and B are the same if:
   //   (1) they have the same name; and
@@ -187,21 +191,22 @@
     ++TrimmedBegin;
 }
 
-IceString Variable::getName(const Cfg *Func) const {
-  if (Func && NameIndex >= 0)
-    return Func->getIdentifierName(NameIndex);
-  return "__" + std::to_string(getIndex());
+std::string Variable::getName(const Cfg *Func) const {
+  if (Func == nullptr)
+    return "__" + std::to_string(getIndex());
+  return Name.toString();
 }
 
-const Variable *Variable::asType(Type Ty, RegNumT NewRegNum) const {
+const Variable *Variable::asType(const Cfg *Func, Type Ty,
+                                 RegNumT NewRegNum) const {
   // Note: This returns a Variable, even if the "this" object is a subclass of
   // Variable.
   if (!BuildDefs::dump() || getType() == Ty)
     return this;
   static constexpr SizeT One = 1;
   Variable *V = new (CfgLocalAllocator<Variable>().allocate(One))
-      Variable(kVariable, Ty, Number);
-  V->NameIndex = NameIndex;
+      Variable(Func, kVariable, Ty, Number);
+  V->Name = Name;
   V->RegNum = NewRegNum.hasValue() ? NewRegNum : RegNum;
   V->StackOffset = StackOffset;
   return V;
@@ -605,10 +610,11 @@
 // =========== Immediate Randomization and Pooling routines ==============
 // Specialization of the template member function for ConstantInteger32
 // TODO(stichnot): try to move this specialization into a target-specific file.
-template <>
-bool ConstantInteger32::shouldBeRandomizedOrPooled(const GlobalContext *Ctx) {
-  uint32_t Threshold = Ctx->getFlags().getRandomizeAndPoolImmediatesThreshold();
-  if (Ctx->getFlags().getRandomizeAndPoolImmediatesOption() == RPI_None)
+template <> bool ConstantInteger32::shouldBeRandomizedOrPooled() const {
+  uint32_t Threshold =
+      GlobalContext::getFlags().getRandomizeAndPoolImmediatesThreshold();
+  if (GlobalContext::getFlags().getRandomizeAndPoolImmediatesOption() ==
+      RPI_None)
     return false;
   if (getType() != IceType_i32 && getType() != IceType_i16 &&
       getType() != IceType_i8)
diff --git a/src/IceOperand.h b/src/IceOperand.h
index bc48b74..327c348 100644
--- a/src/IceOperand.h
+++ b/src/IceOperand.h
@@ -22,6 +22,7 @@
 #include "IceDefs.h"
 #include "IceCfg.h"
 #include "IceGlobalContext.h"
+#include "IceStringPool.h"
 #include "IceTypes.h"
 
 #include "llvm/Support/Format.h"
@@ -121,10 +122,6 @@
   Constant &operator=(const Constant &) = delete;
 
 public:
-  virtual void emitPoolLabel(Ostream &Str) const {
-    (void)Str;
-    llvm::report_fatal_error("emitPoolLabel not defined for type");
-  };
   // Declare the lookup counter to take minimal space in a non-DUMP build.
   using CounterType =
       std::conditional<BuildDefs::dump(), uint64_t, uint8_t>::type;
@@ -136,15 +133,13 @@
     return Kind >= kConst_Base && Kind <= kConst_Max;
   }
 
+  const GlobalString getLabelName() const { return LabelName; }
+
   /// Judge if this given immediate should be randomized or pooled By default
   /// should return false, only constant integers should truly go through this
   /// method.
-  virtual bool shouldBeRandomizedOrPooled(const GlobalContext *Ctx) {
-    (void)Ctx;
-    return false;
-  }
+  virtual bool shouldBeRandomizedOrPooled() const { return false; }
 
-  void setShouldBePooled(bool R) { ShouldBePooled = R; }
   bool getShouldBePooled() const { return ShouldBePooled; }
 
   // This should be thread-safe because the constant pool lock is acquired
@@ -161,8 +156,13 @@
     Vars = nullptr;
     NumVars = 0;
   }
+  /// Set the ShouldBePooled field to the proper value after the object is fully
+  /// initialized.
+  void initShouldBePooled();
+  GlobalString LabelName;
   /// Whether we should pool this constant. Usually Float/Double and pooled
-  /// Integers should be flagged true.
+  /// Integers should be flagged true.  Ideally this field would be const, but
+  /// it needs to be initialized only after the subclass is fully constructed.
   bool ShouldBePooled = false;
   /// Note: If ShouldBePooled is ever removed from the base class, we will want
   /// to completely disable LookupCount in a non-DUMP build to save space.
@@ -181,11 +181,34 @@
 
   static ConstantPrimitive *create(GlobalContext *Ctx, Type Ty,
                                    PrimType Value) {
-    return new (Ctx->allocate<ConstantPrimitive>())
-        ConstantPrimitive(Ty, Value);
+    auto *Const =
+        new (Ctx->allocate<ConstantPrimitive>()) ConstantPrimitive(Ty, Value);
+    Const->initShouldBePooled();
+    if (Const->getShouldBePooled())
+      Const->initName(Ctx);
+    return Const;
   }
   PrimType getValue() const { return Value; }
-  void emitPoolLabel(Ostream &Str) const final {
+  using Constant::emit;
+  void emit(TargetLowering *Target) const final;
+  using Constant::dump;
+  void dump(const Cfg *, Ostream &Str) const override {
+    if (BuildDefs::dump())
+      Str << getValue();
+  }
+
+  static bool classof(const Operand *Operand) {
+    return Operand->getKind() == K;
+  }
+
+  virtual bool shouldBeRandomizedOrPooled() const override { return false; }
+
+private:
+  ConstantPrimitive(Type Ty, PrimType Value) : Constant(K, Ty), Value(Value) {}
+
+  void initName(GlobalContext *Ctx) {
+    std::string Buffer;
+    llvm::raw_string_ostream Str(Buffer);
     Str << ".L$" << getType() << "$";
     // Print hex characters byte by byte, starting from the most significant
     // byte.  NOTE: This ordering assumes Subzero runs on a little-endian
@@ -212,26 +235,9 @@
       }
       Str << Buf;
     }
-  }
-  using Constant::emit;
-  void emit(TargetLowering *Target) const final;
-  using Constant::dump;
-  void dump(const Cfg *, Ostream &Str) const override {
-    if (BuildDefs::dump())
-      Str << getValue();
+    LabelName = GlobalString::createWithString(Ctx, Str.str());
   }
 
-  static bool classof(const Operand *Operand) {
-    return Operand->getKind() == K;
-  }
-
-  virtual bool shouldBeRandomizedOrPooled(const GlobalContext *Ctx) override {
-    (void)Ctx;
-    return false;
-  }
-
-private:
-  ConstantPrimitive(Type Ty, PrimType Value) : Constant(K, Ty), Value(Value) {}
   const PrimType Value;
 };
 
@@ -251,8 +257,7 @@
 }
 
 /// Specialization of the template member function for ConstantInteger32
-template <>
-bool ConstantInteger32::shouldBeRandomizedOrPooled(const GlobalContext *Ctx);
+template <> bool ConstantInteger32::shouldBeRandomizedOrPooled() const;
 
 template <>
 inline void ConstantInteger64::dump(const Cfg *, Ostream &Str) const {
@@ -315,12 +320,12 @@
 
 public:
   RelocatableTuple(const RelocOffsetT Offset,
-                   const RelocOffsetArray &OffsetExpr, const IceString &Name)
+                   const RelocOffsetArray &OffsetExpr, GlobalString Name)
       : Offset(Offset), OffsetExpr(OffsetExpr), Name(Name) {}
 
   RelocatableTuple(const RelocOffsetT Offset,
-                   const RelocOffsetArray &OffsetExpr, const IceString &Name,
-                   const IceString &EmitString)
+                   const RelocOffsetArray &OffsetExpr, GlobalString Name,
+                   const std::string &EmitString)
       : Offset(Offset), OffsetExpr(OffsetExpr), Name(Name),
         EmitString(EmitString) {}
 
@@ -328,8 +333,8 @@
 
   const RelocOffsetT Offset;
   const RelocOffsetArray OffsetExpr;
-  const IceString Name;
-  const IceString EmitString;
+  const GlobalString Name;
+  const std::string EmitString;
 };
 
 bool operator==(const RelocatableTuple &A, const RelocatableTuple &B);
@@ -358,9 +363,9 @@
     return Ret;
   }
 
-  const IceString &getEmitString() const { return EmitString; }
+  const std::string &getEmitString() const { return EmitString; }
 
-  const IceString &getName() const { return Name; }
+  GlobalString getName() const { return Name; }
   using Constant::emit;
   void emit(TargetLowering *Target) const final;
   void emitWithoutPrefix(const TargetLowering *Target,
@@ -375,15 +380,15 @@
 
 private:
   ConstantRelocatable(Type Ty, const RelocOffsetT Offset,
-                      const RelocOffsetArray &OffsetExpr, const IceString &Name,
-                      const IceString &EmitString)
+                      const RelocOffsetArray &OffsetExpr, GlobalString Name,
+                      const std::string &EmitString)
       : Constant(kConstRelocatable, Ty), Offset(Offset), OffsetExpr(OffsetExpr),
         Name(Name), EmitString(EmitString) {}
 
   const RelocOffsetT Offset;         /// fixed, known offset to add
   const RelocOffsetArray OffsetExpr; /// fixed, unknown offset to add
-  const IceString Name;              /// optional for debug/dump
-  const IceString EmitString;        /// optional for textual emission
+  const GlobalString Name;           /// optional for debug/dump
+  const std::string EmitString;      /// optional for textual emission
 };
 
 /// ConstantUndef represents an unspecified bit pattern. Although it is legal to
@@ -637,16 +642,17 @@
 
 public:
   static Variable *create(Cfg *Func, Type Ty, SizeT Index) {
-    return new (Func->allocate<Variable>()) Variable(kVariable, Ty, Index);
+    return new (Func->allocate<Variable>())
+        Variable(Func, kVariable, Ty, Index);
   }
 
   SizeT getIndex() const { return Number; }
-  IceString getName(const Cfg *Func) const;
-  virtual void setName(Cfg *Func, const IceString &NewName) {
-    // Make sure that the name can only be set once.
-    assert(NameIndex == Cfg::IdentifierIndexInvalid);
-    if (!NewName.empty())
-      NameIndex = Func->addIdentifierName(NewName);
+  std::string getName(const Cfg *Func) const;
+  virtual void setName(const Cfg *Func, const std::string &NewName) {
+    (void)Func;
+    if (NewName.empty())
+      return;
+    Name = VariableString::createWithString(Func, NewName);
   }
 
   bool getIsArg() const { return IsArgument; }
@@ -661,7 +667,7 @@
   void setStackOffset(int32_t Offset) { StackOffset = Offset; }
   /// Returns the variable's stack offset in symbolic form, to improve
   /// readability in DecorateAsm mode.
-  IceString getSymbolicStackOffset(const Cfg *Func) const {
+  std::string getSymbolicStackOffset(const Cfg *Func) const {
     if (!BuildDefs::dump())
       return "";
     return "lv$" + getName(Func);
@@ -725,7 +731,7 @@
   /// IsImplicitArgument, IgnoreLiveness, RegNumTmp, Live, LoVar, HiVar,
   /// VarsReal. If NewRegNum.hasValue(), then that register assignment is made
   /// instead of copying the existing assignment.
-  const Variable *asType(Type Ty, RegNumT NewRegNum) const;
+  const Variable *asType(const Cfg *Func, Type Ty, RegNumT NewRegNum) const;
 
   void emit(const Cfg *Func) const override;
   using Operand::dump;
@@ -740,17 +746,24 @@
   }
 
 protected:
-  Variable(OperandKind K, Type Ty, SizeT Index)
+  Variable(const Cfg *Func, OperandKind K, Type Ty, SizeT Index)
       : Operand(K, Ty), Number(Index),
+        Name(VariableString::createWithoutString(Func)),
         RegisterClass(static_cast<RegClass>(Ty)) {
     Vars = VarsReal;
     Vars[0] = this;
     NumVars = 1;
+    if (BuildDefs::dump()) {
+      Name = VariableString::createWithString(
+          Func, "__" + std::to_string(getIndex()));
+    } else {
+      Name = VariableString::createWithoutString(Func);
+    }
   }
   /// Number is unique across all variables, and is used as a (bit)vector index
   /// for liveness analysis.
   const SizeT Number;
-  Cfg::IdentifierIndexType NameIndex = Cfg::IdentifierIndexInvalid;
+  VariableString Name;
   bool IsArgument = false;
   bool IsImplicitArgument = false;
   /// IgnoreLiveness means that the variable should be ignored when constructing
@@ -784,10 +797,10 @@
 public:
   static Variable64On32 *create(Cfg *Func, Type Ty, SizeT Index) {
     return new (Func->allocate<Variable64On32>())
-        Variable64On32(kVariable64On32, Ty, Index);
+        Variable64On32(Func, kVariable64On32, Ty, Index);
   }
 
-  void setName(Cfg *Func, const IceString &NewName) override {
+  void setName(const Cfg *Func, const std::string &NewName) override {
     Variable::setName(Func, NewName);
     if (LoVar && HiVar) {
       LoVar->setName(Func, getName(Func) + "__lo");
@@ -819,8 +832,10 @@
     HiVar = Func->makeVariable(IceType_i32);
     LoVar->setIsArg(getIsArg());
     HiVar->setIsArg(getIsArg());
-    LoVar->setName(Func, getName(Func) + "__lo");
-    HiVar->setName(Func, getName(Func) + "__hi");
+    if (BuildDefs::dump()) {
+      LoVar->setName(Func, getName(Func) + "__lo");
+      HiVar->setName(Func, getName(Func) + "__hi");
+    }
   }
 
   static bool classof(const Operand *Operand) {
@@ -829,7 +844,8 @@
   }
 
 protected:
-  Variable64On32(OperandKind K, Type Ty, SizeT Index) : Variable(K, Ty, Index) {
+  Variable64On32(const Cfg *Func, OperandKind K, Type Ty, SizeT Index)
+      : Variable(Func, K, Ty, Index) {
     assert(typeWidthInBytes(Ty) == 8);
   }
 
diff --git a/src/IceRegistersARM32.h b/src/IceRegistersARM32.h
index 8941a9e..6c091c5 100644
--- a/src/IceRegistersARM32.h
+++ b/src/IceRegistersARM32.h
@@ -207,7 +207,7 @@
   return QRegister(RegTable[RegNum].Encoding);
 }
 
-static inline IceString getRegName(RegNumT RegNum) {
+static inline const char *getRegName(RegNumT RegNum) {
   RegNum.assertIsValid();
   return RegTable[RegNum].Name;
 }
diff --git a/src/IceStringPool.h b/src/IceStringPool.h
new file mode 100644
index 0000000..e6b3d11
--- /dev/null
+++ b/src/IceStringPool.h
@@ -0,0 +1,174 @@
+//===- subzero/src/IceStringPool.h - String pooling -------------*- C++ -*-===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines unique pooled strings with short unique IDs.  This makes
+/// hashing, equality testing, and ordered comparison faster, and avoids a lot
+/// of memory allocation compared to directly using std::string.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICESTRINGPOOL_H
+#define SUBZERO_SRC_ICESTRINGPOOL_H
+
+#include <cstdint> // uintptr_t
+#include <string>
+
+namespace Ice {
+
+class StringPool {
+  StringPool(const StringPool &) = delete;
+  StringPool &operator=(const StringPool &) = delete;
+
+public:
+  using IDType = uintptr_t;
+
+  StringPool() = default;
+  ~StringPool() = default;
+  IDType getNewID() {
+    // TODO(stichnot): Make it so that the GlobalString ctor doesn't have to
+    // grab the lock, and instead does an atomic increment of NextID.
+    auto NewID = NextID;
+    NextID += IDIncrement;
+    return NewID;
+  }
+  IDType getOrAddString(const std::string &Value) {
+    auto Iter = StringToId.find(Value);
+    if (Iter == StringToId.end()) {
+      auto *NewStr = new std::string(Value);
+      auto ID = reinterpret_cast<IDType>(NewStr);
+      StringToId[Value].reset(NewStr);
+      return ID;
+    }
+    return reinterpret_cast<IDType>(Iter->second.get());
+  }
+  void dump(Ostream &Str) const {
+    if (StringToId.empty())
+      return;
+    Str << "String pool (NumStrings=" << StringToId.size()
+        << " NumIDs=" << ((NextID - FirstID) / IDIncrement) << "):";
+    for (const auto &Tuple : StringToId) {
+      Str << " " << Tuple.first;
+    }
+    Str << "\n";
+  }
+
+private:
+  static constexpr IDType FirstID = 1;
+  static constexpr IDType IDIncrement = 2;
+  IDType NextID = FirstID;
+  std::unordered_map<std::string, std::unique_ptr<std::string>> StringToId;
+};
+
+template <typename Traits> class StringID {
+public:
+  using IDType = StringPool::IDType;
+
+  StringID() = default; // Create a default, invalid StringID.
+  StringID(const StringID &) = default;
+  StringID &operator=(const StringID &) = default;
+
+  /// Create a unique StringID without an actual string, by grabbing the next
+  /// unique integral ID from the Owner.
+  static StringID createWithoutString(const typename Traits::OwnerType *Owner) {
+    return StringID(Owner);
+  }
+  /// Create a unique StringID that holds an actual string, by fetching or
+  /// adding the string from the Owner's pool.
+  static StringID createWithString(const typename Traits::OwnerType *Owner,
+                                   const std::string &Value) {
+    return StringID(Owner, Value);
+  }
+
+  /// Tests whether the StringID was initialized with respect to an actual
+  /// std::string value, i.e. via StringID::createWithString().
+  bool hasStdString() const { return isValid() && ((ID & 0x1) == 0); }
+
+  IDType getID() const {
+    assert(isValid());
+    return ID;
+  }
+  const std::string &toString() const {
+    if (!hasStdString())
+      llvm::report_fatal_error(
+          "toString() called when hasStdString() is false");
+    return *reinterpret_cast<std::string *>(ID);
+  }
+  std::string toStringOrEmpty() const {
+    if (hasStdString())
+      return toString();
+    return "";
+  }
+
+  bool operator==(const StringID &Other) const { return ID == Other.ID; }
+  bool operator!=(const StringID &Other) const { return !(*this == Other); }
+  bool operator<(const StringID &Other) const {
+    const bool ThisHasString = hasStdString();
+    const bool OtherHasString = Other.hasStdString();
+    // Do a normal string comparison if both have strings.
+    if (ThisHasString && OtherHasString)
+      return this->toString() < Other.toString();
+    // Use the ID as a tiebreaker if neither has a string.
+    if (!ThisHasString && !OtherHasString)
+      return ID < Other.ID;
+    // If exactly one has a string, then that one comes first.
+    assert(!OtherHasString);
+    return ThisHasString;
+  }
+
+private:
+  static constexpr IDType InvalidID = 0;
+  IDType ID = InvalidID;
+
+  explicit StringID(const typename Traits::OwnerType *Owner)
+      : ID(Traits::getStrings(Owner)->getNewID()) {}
+  StringID(const typename Traits::OwnerType *Owner, const std::string &Value)
+      : ID(Traits::getStrings(Owner)->getOrAddString(Value)) {
+    assert(hasStdString());
+  }
+  bool isValid() const { return ID != InvalidID; }
+};
+
+// TODO(stichnot, jpp): Move GlobalStringPoolTraits definition into
+// IceGlobalContext.h, once the include order issues are solved.
+struct GlobalStringPoolTraits {
+  using OwnerType = GlobalContext;
+  static LockedPtr<StringPool> getStrings(const OwnerType *Owner);
+};
+
+using GlobalString = StringID<struct GlobalStringPoolTraits>;
+
+template <typename T>
+Ostream &operator<<(Ostream &Str, const StringID<T> &Name) {
+  return Str << Name.toString();
+}
+
+template <typename T>
+std::string operator+(const std::string &A, const StringID<T> &B) {
+  return A + B.toString();
+}
+
+template <typename T>
+std::string operator+(const StringID<T> &A, const std::string &B) {
+  return A.toString() + B;
+}
+
+} // end of namespace Ice
+
+namespace std {
+template <typename T> struct hash<Ice::StringID<T>> {
+  size_t operator()(const Ice::StringID<T> &Key) const {
+    if (Key.hasStdString())
+      return hash<std::string>()(Key.toString());
+    return hash<Ice::StringPool::IDType>()(Key.getID());
+  }
+};
+} // end of namespace std
+
+#endif // SUBZERO_SRC_ICESTRINGPOOL_H
diff --git a/src/IceSwitchLowering.h b/src/IceSwitchLowering.h
index 5b9f673..d4f5291 100644
--- a/src/IceSwitchLowering.h
+++ b/src/IceSwitchLowering.h
@@ -16,6 +16,7 @@
 #define SUBZERO_SRC_ICESWITCHLOWERING_H
 
 #include "IceDefs.h"
+#include "IceStringPool.h"
 
 namespace Ice {
 
@@ -84,19 +85,19 @@
 public:
   using TargetList = std::vector<intptr_t>;
 
-  JumpTableData(const IceString &FuncName, SizeT Id,
+  JumpTableData(GlobalString FuncName, SizeT Id,
                 const TargetList &TargetOffsets)
       : FuncName(FuncName), Id(Id), TargetOffsets(TargetOffsets) {}
   JumpTableData(const JumpTableData &) = default;
   JumpTableData(JumpTableData &&) = default;
   JumpTableData &operator=(JumpTableData &&) = default;
 
-  const IceString &getFunctionName() const { return FuncName; }
+  const GlobalString getFunctionName() const { return FuncName; }
   SizeT getId() const { return Id; }
   const TargetList &getTargetOffsets() const { return TargetOffsets; }
 
 private:
-  IceString FuncName;
+  GlobalString FuncName;
   SizeT Id;
   TargetList TargetOffsets;
 };
diff --git a/src/IceTargetLowering.cpp b/src/IceTargetLowering.cpp
index 6f104fe..94c8ecc 100644
--- a/src/IceTargetLowering.cpp
+++ b/src/IceTargetLowering.cpp
@@ -56,6 +56,7 @@
   std::unique_ptr<::Ice::TargetHeaderLowering>                                 \
   createTargetHeaderLowering(::Ice::GlobalContext *Ctx);                       \
   void staticInit(::Ice::GlobalContext *Ctx);                                  \
+  bool shouldBePooled(const ::Ice::Constant *C);                               \
   } // end of namespace X
 #include "SZTargets.def"
 #undef SUBZERO_TARGET
@@ -125,8 +126,8 @@
 namespace {
 
 void printRegisterSet(Ostream &Str, const SmallBitVector &Bitset,
-                      std::function<IceString(RegNumT)> getRegName,
-                      const IceString &LineIndentString) {
+                      std::function<std::string(RegNumT)> getRegName,
+                      const std::string &LineIndentString) {
   constexpr size_t RegistersPerLine = 16;
   size_t Count = 0;
   for (RegNumT RegNum : RegNumBVIter(Bitset)) {
@@ -146,8 +147,8 @@
 
 // Splits "<class>:<reg>" into "<class>" plus "<reg>".  If there is no <class>
 // component, the result is "" plus "<reg>".
-void splitToClassAndName(const IceString &RegName, IceString *SplitRegClass,
-                         IceString *SplitRegName) {
+void splitToClassAndName(const std::string &RegName, std::string *SplitRegClass,
+                         std::string *SplitRegName) {
   constexpr const char Separator[] = ":";
   constexpr size_t SeparatorWidth = llvm::array_lengthof(Separator) - 1;
   size_t Pos = RegName.find(Separator);
@@ -169,14 +170,15 @@
 
 void TargetLowering::filterTypeToRegisterSet(
     GlobalContext *Ctx, int32_t NumRegs, SmallBitVector TypeToRegisterSet[],
-    size_t TypeToRegisterSetSize, std::function<IceString(RegNumT)> getRegName,
-    std::function<IceString(RegClass)> getRegClassName) {
+    size_t TypeToRegisterSetSize,
+    std::function<std::string(RegNumT)> getRegName,
+    std::function<const char *(RegClass)> getRegClassName) {
   std::vector<SmallBitVector> UseSet(TypeToRegisterSetSize,
                                      SmallBitVector(NumRegs));
   std::vector<SmallBitVector> ExcludeSet(TypeToRegisterSetSize,
                                          SmallBitVector(NumRegs));
 
-  std::unordered_map<IceString, RegNumT> RegNameToIndex;
+  std::unordered_map<std::string, RegNumT> RegNameToIndex;
   for (int32_t RegIndex = 0; RegIndex < NumRegs; ++RegIndex) {
     const auto RegNum = RegNumT::fromInt(RegIndex);
     RegNameToIndex[getRegName(RegNum)] = RegNum;
@@ -191,9 +193,9 @@
   // for all classes.
   auto processRegList = [&](const std::vector<std::string> &RegNames,
                             std::vector<SmallBitVector> &RegSet) {
-    for (const IceString &RegClassAndName : RegNames) {
-      IceString RClass;
-      IceString RName;
+    for (const std::string &RegClassAndName : RegNames) {
+      std::string RClass;
+      std::string RName;
       splitToClassAndName(RegClassAndName, &RClass, &RName);
       if (!RegNameToIndex.count(RName)) {
         BadRegNames.push_back(RName);
@@ -236,8 +238,8 @@
   if (BuildDefs::dump() && NumRegs &&
       (Ctx->getFlags().getVerbose() & IceV_AvailableRegs)) {
     Ostream &Str = Ctx->getStrDump();
-    const IceString Indent = "  ";
-    const IceString IndentTwice = Indent + Indent;
+    const std::string Indent = "  ";
+    const std::string IndentTwice = Indent + Indent;
     Str << "Registers available for register allocation:\n";
     for (size_t TypeIndex = 0; TypeIndex < TypeToRegisterSetSize; ++TypeIndex) {
       Str << Indent << getRegClassName(static_cast<RegClass>(TypeIndex))
@@ -282,6 +284,19 @@
   }
 }
 
+bool TargetLowering::shouldBePooled(const Constant *C) {
+  const TargetArch Target = GlobalContext::getFlags().getTargetArch();
+  switch (Target) {
+  default:
+    return false;
+#define SUBZERO_TARGET(X)                                                      \
+  case TARGET_LOWERING_CLASS_FOR(X):                                           \
+    return ::X::shouldBePooled(C);
+#include "SZTargets.def"
+#undef SUBZERO_TARGET
+  }
+}
+
 TargetLowering::SandboxType
 TargetLowering::determineSandboxTypeFromFlags(const ClFlags &Flags) {
   assert(!Flags.getUseSandboxing() || !Flags.getUseNonsfi());
@@ -728,7 +743,7 @@
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Ctx->getStrEmit();
-  const IceString &EmitStr = C->getEmitString();
+  const std::string &EmitStr = C->getEmitString();
   if (!EmitStr.empty()) {
     // C has a custom emit string, so we use it instead of the canonical
     // Name + Offset form.
@@ -765,8 +780,9 @@
 // dataSectionSuffix decides whether to use SectionSuffix or VarName as data
 // section suffix. Essentially, when using separate data sections for globals
 // SectionSuffix is not necessary.
-IceString dataSectionSuffix(const IceString &SectionSuffix,
-                            const IceString &VarName, const bool DataSections) {
+std::string dataSectionSuffix(const std::string &SectionSuffix,
+                              const std::string &VarName,
+                              const bool DataSections) {
   if (SectionSuffix.empty() && !DataSections) {
     return "";
   }
@@ -783,7 +799,7 @@
 } // end of anonymous namespace
 
 void TargetDataLowering::emitGlobal(const VariableDeclaration &Var,
-                                    const IceString &SectionSuffix) {
+                                    const std::string &SectionSuffix) {
   if (!BuildDefs::dump())
     return;
 
@@ -798,13 +814,13 @@
   const bool HasNonzeroInitializer = Var.hasNonzeroInitializer();
   const bool IsConstant = Var.getIsConstant();
   const SizeT Size = Var.getNumBytes();
-  const IceString &Name = Var.getName();
+  const std::string Name = Var.getName().toString();
 
   Str << "\t.type\t" << Name << ",%object\n";
 
   const bool UseDataSections = Ctx->getFlags().getDataSections();
   const bool UseNonsfi = Ctx->getFlags().getUseNonsfi();
-  const IceString Suffix =
+  const std::string Suffix =
       dataSectionSuffix(SectionSuffix, Name, UseDataSections);
   if (IsConstant && UseNonsfi)
     Str << "\t.section\t.data.rel.ro" << Suffix << ",\"aw\",%progbits\n";
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index b775d92..03a8cb2 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -57,7 +57,8 @@
       /* Use llvm_unreachable instead of report_fatal_error, which gives       \
          better stack traces. */                                               \
       llvm_unreachable(                                                        \
-          ("Not yet implemented: " + Instr->getInstName()).c_str());           \
+          (std::string("Not yet implemented: ") + Instr->getInstName())        \
+              .c_str());                                                       \
       abort();                                                                 \
     }                                                                          \
   } while (0)
@@ -172,6 +173,7 @@
   static void staticInit(GlobalContext *Ctx);
   // Each target must define a public static method:
   //   static void staticInit(GlobalContext *Ctx);
+  static bool shouldBePooled(const class Constant *C);
 
   static std::unique_ptr<TargetLowering> createLowering(TargetArch Target,
                                                         Cfg *Func);
@@ -237,7 +239,7 @@
   virtual Variable *getPhysicalRegister(RegNumT RegNum,
                                         Type Ty = IceType_void) = 0;
   /// Returns a printable name for the register.
-  virtual IceString getRegName(RegNumT RegNum, Type Ty) const = 0;
+  virtual const char *getRegName(RegNumT RegNum, Type Ty) const = 0;
 
   virtual bool hasFramePointer() const { return false; }
   virtual void setHasFramePointer() = 0;
@@ -364,12 +366,11 @@
 
   explicit TargetLowering(Cfg *Func);
   // Applies command line filters to TypeToRegisterSet array.
-  static void
-  filterTypeToRegisterSet(GlobalContext *Ctx, int32_t NumRegs,
-                          SmallBitVector TypeToRegisterSet[],
-                          size_t TypeToRegisterSetSize,
-                          std::function<IceString(RegNumT)> getRegName,
-                          std::function<IceString(RegClass)> getRegClassName);
+  static void filterTypeToRegisterSet(
+      GlobalContext *Ctx, int32_t NumRegs, SmallBitVector TypeToRegisterSet[],
+      size_t TypeToRegisterSetSize,
+      std::function<std::string(RegNumT)> getRegName,
+      std::function<const char *(RegClass)> getRegClassName);
   virtual void lowerAlloca(const InstAlloca *Instr) = 0;
   virtual void lowerArithmetic(const InstArithmetic *Instr) = 0;
   virtual void lowerAssign(const InstAssign *Instr) = 0;
@@ -583,13 +584,13 @@
   virtual ~TargetDataLowering();
 
   virtual void lowerGlobals(const VariableDeclarationList &Vars,
-                            const IceString &SectionSuffix) = 0;
+                            const std::string &SectionSuffix) = 0;
   virtual void lowerConstants() = 0;
   virtual void lowerJumpTables() = 0;
 
 protected:
   void emitGlobal(const VariableDeclaration &Var,
-                  const IceString &SectionSuffix);
+                  const std::string &SectionSuffix);
 
   /// For now, we assume .long is the right directive for emitting 4 byte emit
   /// global relocations. However, LLVM MIPS usually uses .4byte instead.
diff --git a/src/IceTargetLoweringARM32.cpp b/src/IceTargetLoweringARM32.cpp
index 1d2211f..d3313e5 100644
--- a/src/IceTargetLoweringARM32.cpp
+++ b/src/IceTargetLoweringARM32.cpp
@@ -58,10 +58,14 @@
     // pexe) so we need to register it as such so that ELF emission won't barf
     // on an "unknown" symbol. The GOT is added to the External symbols list
     // here because staticInit() is invoked in a single-thread context.
-    Ctx->getConstantExternSym(::Ice::GlobalOffsetTable);
+    Ctx->getConstantExternSym(Ctx->getGlobalString(::Ice::GlobalOffsetTable));
   }
 }
 
+bool shouldBePooled(const ::Ice::Constant *C) {
+  return ::Ice::ARM32::TargetARM32::shouldBePooled(C);
+}
+
 } // end of namespace ARM32
 
 namespace Ice {
@@ -279,7 +283,7 @@
     ;
 std::array<RegNumT, NumVec128Args> Vec128ArgInitializer;
 
-IceString getRegClassName(RegClass C) {
+const char *getRegClassName(RegClass C) {
   auto ClassNum = static_cast<RegARM32::RegClassARM32>(C);
   assert(ClassNum < RegARM32::RCARM32_NUM);
   switch (ClassNum) {
@@ -361,20 +365,23 @@
   for (size_t i = 0; i < llvm::array_lengthof(TypeToRegisterSet); ++i)
     TypeToRegisterSetUnfiltered[i] = TypeToRegisterSet[i];
 
-  filterTypeToRegisterSet(
-      Ctx, RegARM32::Reg_NUM, TypeToRegisterSet,
-      llvm::array_lengthof(TypeToRegisterSet), [](RegNumT RegNum) -> IceString {
-        // This function simply removes ", " from the register name.
-        IceString Name = RegARM32::getRegName(RegNum);
-        constexpr const char RegSeparator[] = ", ";
-        constexpr size_t RegSeparatorWidth =
-            llvm::array_lengthof(RegSeparator) - 1;
-        for (size_t Pos = Name.find(RegSeparator); Pos != std::string::npos;
-             Pos = Name.find(RegSeparator)) {
-          Name.replace(Pos, RegSeparatorWidth, "");
-        }
-        return Name;
-      }, getRegClassName);
+  filterTypeToRegisterSet(Ctx, RegARM32::Reg_NUM, TypeToRegisterSet,
+                          llvm::array_lengthof(TypeToRegisterSet),
+                          [](RegNumT RegNum) -> std::string {
+                            // This function simply removes ", " from the
+                            // register name.
+                            std::string Name = RegARM32::getRegName(RegNum);
+                            constexpr const char RegSeparator[] = ", ";
+                            constexpr size_t RegSeparatorWidth =
+                                llvm::array_lengthof(RegSeparator) - 1;
+                            for (size_t Pos = Name.find(RegSeparator);
+                                 Pos != std::string::npos;
+                                 Pos = Name.find(RegSeparator)) {
+                              Name.replace(Pos, RegSeparatorWidth, "");
+                            }
+                            return Name;
+                          },
+                          getRegClassName);
 }
 
 namespace {
@@ -790,7 +797,8 @@
       static constexpr SizeT MaxArgs = 0;
       Operand *TargetHelper =
           SandboxingType == ST_Nonsfi
-              ? Ctx->getConstantExternSym("__aeabi_read_tp")
+              ? Ctx->getConstantExternSym(
+                    Ctx->getGlobalString("__aeabi_read_tp"))
               : Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_read_tp);
       Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, NoTailCall,
                                IsTargetHelperCall);
@@ -909,10 +917,11 @@
   Context.insert<InstFakeDef>(GotPtr, T);
 }
 
-IceString TargetARM32::createGotoffRelocation(const ConstantRelocatable *CR) {
-  const IceString &CRName = CR->getName();
-  const IceString CRGotoffName =
-      "GOTOFF$" + Func->getFunctionName() + "$" + CRName;
+GlobalString
+TargetARM32::createGotoffRelocation(const ConstantRelocatable *CR) {
+  GlobalString CRName = CR->getName();
+  GlobalString CRGotoffName =
+      Ctx->getGlobalString("GOTOFF$" + Func->getFunctionName() + "$" + CRName);
   if (KnownGotoffs.count(CRGotoffName) == 0) {
     constexpr bool SuppressMangling = true;
     auto *Global =
@@ -965,14 +974,14 @@
   Context.setInsertPoint(DefGotPtr);
   assert(DefGotPtr->getSrcSize() == 1);
   auto *T = llvm::cast<Variable>(DefGotPtr->getSrc(0));
-  loadNamedConstantRelocatablePIC(GlobalOffsetTable, T,
+  loadNamedConstantRelocatablePIC(Ctx->getGlobalString(GlobalOffsetTable), T,
                                   [this, T](Variable *PC) { _add(T, PC, T); });
   _mov(GotPtr, T);
   DefGotPtr->setDeleted();
 }
 
 void TargetARM32::loadNamedConstantRelocatablePIC(
-    const IceString &Name, Variable *Register,
+    GlobalString Name, Variable *Register,
     std::function<void(Variable *PC)> Finish) {
   assert(SandboxingType == ST_Nonsfi);
   // We makeReg() here instead of getPhysicalRegister() because the latter ends
@@ -1001,10 +1010,10 @@
   //
   // relocations.
   static constexpr RelocOffsetT PcOffset = -8;
-  auto *CRLower = Ctx->getConstantSym(PcOffset, {MovwReloc, AddPcReloc}, Name,
-                                      Name + " -16");
-  auto *CRUpper = Ctx->getConstantSym(PcOffset, {MovtReloc, AddPcReloc}, Name,
-                                      Name + " -12");
+  auto *CRLower = Ctx->getConstantSymWithEmitString(
+      PcOffset, {MovwReloc, AddPcReloc}, Name, Name + " -16");
+  auto *CRUpper = Ctx->getConstantSymWithEmitString(
+      PcOffset, {MovtReloc, AddPcReloc}, Name, Name + " -12");
 
   Context.insert(MovwLabel);
   _movw(Register, CRLower);
@@ -1207,7 +1216,7 @@
   return false;
 }
 
-IceString TargetARM32::getRegName(RegNumT RegNum, Type Ty) const {
+const char *TargetARM32::getRegName(RegNumT RegNum, Type Ty) const {
   (void)Ty;
   return RegARM32::getRegName(RegNum);
 }
@@ -2313,7 +2322,7 @@
   switch (Ty) {
   default:
     llvm_unreachable(
-        ("Unexpected type in div0Check: " + typeIceString(Ty)).c_str());
+        ("Unexpected type in div0Check: " + typeStdString(Ty)).c_str());
   case IceType_i8:
   case IceType_i16: {
     Operand *ShAmtImm = shAmtImm(32 - getScalarIntBitWidth(Ty));
@@ -5963,7 +5972,7 @@
         _movt(Reg, C);
       } else {
         auto *GotAddr = legalizeToReg(GotPtr);
-        const IceString CGotoffName = createGotoffRelocation(C);
+        GlobalString CGotoffName = createGotoffRelocation(C);
         loadNamedConstantRelocatablePIC(
             CGotoffName, Reg, [this, Reg](Variable *PC) {
               _ldr(Reg, OperandARM32Mem::create(Func, IceType_i32, PC, Reg));
@@ -5991,11 +6000,9 @@
       }
 
       // Load floats/doubles from literal pool.
-      std::string Buffer;
-      llvm::raw_string_ostream StrBuf(Buffer);
-      llvm::cast<Constant>(From)->emitPoolLabel(StrBuf);
-      llvm::cast<Constant>(From)->setShouldBePooled(true);
-      Constant *Offset = Ctx->getConstantSym(0, StrBuf.str());
+      auto *CFrom = llvm::cast<Constant>(From);
+      assert(CFrom->getShouldBePooled());
+      Constant *Offset = Ctx->getConstantSym(0, CFrom->getLabelName());
       Variable *BaseReg = nullptr;
       if (SandboxingType == ST_Nonsfi) {
         // vldr does not support the [base, index] addressing mode, so we need
@@ -6713,7 +6720,7 @@
     : TargetDataLowering(Ctx) {}
 
 void TargetDataARM32::lowerGlobals(const VariableDeclarationList &Vars,
-                                   const IceString &SectionSuffix) {
+                                   const std::string &SectionSuffix) {
   const bool IsPIC = Ctx->getFlags().getUseNonsfi();
   switch (Ctx->getFlags().getOutFileType()) {
   case FT_Elf: {
@@ -6723,7 +6730,7 @@
   } break;
   case FT_Asm:
   case FT_Iasm: {
-    const IceString &TranslateOnly = Ctx->getFlags().getTranslateOnly();
+    const std::string TranslateOnly = Ctx->getFlags().getTranslateOnly();
     OstreamLocker _(Ctx);
     for (const VariableDeclaration *Var : Vars) {
       if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
@@ -6780,7 +6787,7 @@
     Ostream &Str,
     const typename ConstantPoolEmitterTraits<T>::ConstantType *Const) {
   using Traits = ConstantPoolEmitterTraits<T>;
-  Const->emitPoolLabel(Str);
+  Str << Const->getLabelName();
   Str << ":\n\t" << Traits::AsmTag << "\t0x";
   T Value = Const->getValue();
   Str.write_hex(Traits::bitcastToUint64(Value));
diff --git a/src/IceTargetLoweringARM32.h b/src/IceTargetLoweringARM32.h
index 36f5cfd..fd40b38 100644
--- a/src/IceTargetLoweringARM32.h
+++ b/src/IceTargetLoweringARM32.h
@@ -58,6 +58,16 @@
 
 public:
   static void staticInit(GlobalContext *Ctx);
+
+  static bool shouldBePooled(const Constant *C) {
+    if (auto *ConstDouble = llvm::dyn_cast<ConstantDouble>(C)) {
+      return !Utils::isPositiveZero(ConstDouble->getValue());
+    }
+    if (llvm::isa<ConstantFloat>(C))
+      return true;
+    return false;
+  }
+
   // TODO(jvoung): return a unique_ptr.
   static std::unique_ptr<::Ice::TargetLowering> create(Cfg *Func) {
     return makeUnique<TargetARM32>(Func);
@@ -81,7 +91,7 @@
   SizeT getNumRegisters() const override { return RegARM32::Reg_NUM; }
   Variable *getPhysicalRegister(RegNumT RegNum,
                                 Type Ty = IceType_void) override;
-  IceString getRegName(RegNumT RegNum, Type Ty) const override;
+  const char *getRegName(RegNumT RegNum, Type Ty) const override;
   SmallBitVector getRegisterSet(RegSetMask Include,
                                 RegSetMask Exclude) const override;
   const SmallBitVector &
@@ -914,8 +924,8 @@
   /// @{
   // TODO(jpp): if the same global G is used in different functions, then this
   // method will emit one G(gotoff) relocation per function.
-  IceString createGotoffRelocation(const ConstantRelocatable *CR);
-  CfgUnorderedSet<IceString> KnownGotoffs;
+  GlobalString createGotoffRelocation(const ConstantRelocatable *CR);
+  CfgUnorderedSet<GlobalString> KnownGotoffs;
   /// @}
 
   /// Loads the constant relocatable Name to Register. Then invoke Finish to
@@ -936,7 +946,7 @@
   // The -8 in movw/movt above is to account for the PC value that the first
   // instruction emitted by Finish(PC) will read.
   void
-  loadNamedConstantRelocatablePIC(const IceString &Name, Variable *Register,
+  loadNamedConstantRelocatablePIC(GlobalString Name, Variable *Register,
                                   std::function<void(Variable *PC)> Finish);
 
   /// Sandboxer defines methods for ensuring that "dangerous" operations are
@@ -1296,7 +1306,7 @@
   }
 
   void lowerGlobals(const VariableDeclarationList &Vars,
-                    const IceString &SectionSuffix) override;
+                    const std::string &SectionSuffix) override;
   void lowerConstants() override;
   void lowerJumpTables() override;
 
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
index df92b88..5f1f8e6 100644
--- a/src/IceTargetLoweringMIPS32.cpp
+++ b/src/IceTargetLoweringMIPS32.cpp
@@ -48,6 +48,10 @@
 void staticInit(::Ice::GlobalContext *Ctx) {
   ::Ice::MIPS32::TargetMIPS32::staticInit(Ctx);
 }
+
+bool shouldBePooled(const ::Ice::Constant *C) {
+  return ::Ice::MIPS32::TargetMIPS32::shouldBePooled(C);
+}
 } // end of namespace MIPS32
 
 namespace Ice {
@@ -60,7 +64,7 @@
 // The maximum number of arguments to pass in GPR registers.
 constexpr uint32_t MIPS32_MAX_GPR_ARG = 4;
 
-IceString getRegClassName(RegClass C) {
+const char *getRegClassName(RegClass C) {
   auto ClassNum = static_cast<RegClassMIPS32>(C);
   assert(ClassNum < RCMIPS32_NUM);
   switch (ClassNum) {
@@ -296,7 +300,7 @@
   return RegNames[RegNum];
 }
 
-IceString TargetMIPS32::getRegName(RegNumT RegNum, Type Ty) const {
+const char *TargetMIPS32::getRegName(RegNumT RegNum, Type Ty) const {
   (void)Ty;
   return RegMIPS32::getRegName(RegNum);
 }
@@ -1196,7 +1200,7 @@
     : TargetDataLowering(Ctx) {}
 
 void TargetDataMIPS32::lowerGlobals(const VariableDeclarationList &Vars,
-                                    const IceString &SectionSuffix) {
+                                    const std::string &SectionSuffix) {
   const bool IsPIC = Ctx->getFlags().getUseNonsfi();
   switch (Ctx->getFlags().getOutFileType()) {
   case FT_Elf: {
@@ -1206,7 +1210,7 @@
   } break;
   case FT_Asm:
   case FT_Iasm: {
-    const IceString &TranslateOnly = Ctx->getFlags().getTranslateOnly();
+    const std::string TranslateOnly = Ctx->getFlags().getTranslateOnly();
     OstreamLocker L(Ctx);
     for (const VariableDeclaration *Var : Vars) {
       if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
diff --git a/src/IceTargetLoweringMIPS32.h b/src/IceTargetLoweringMIPS32.h
index 5dffe61..3216058 100644
--- a/src/IceTargetLoweringMIPS32.h
+++ b/src/IceTargetLoweringMIPS32.h
@@ -34,6 +34,10 @@
   ~TargetMIPS32() override = default;
 
   static void staticInit(GlobalContext *Ctx);
+  static bool shouldBePooled(const Constant *C) {
+    (void)C;
+    return false;
+  }
   static std::unique_ptr<::Ice::TargetLowering> create(Cfg *Func) {
     return makeUnique<TargetMIPS32>(Func);
   }
@@ -49,7 +53,7 @@
   SizeT getNumRegisters() const override { return RegMIPS32::Reg_NUM; }
   Variable *getPhysicalRegister(RegNumT RegNum,
                                 Type Ty = IceType_void) override;
-  IceString getRegName(RegNumT RegNum, Type Ty) const override;
+  const char *getRegName(RegNumT RegNum, Type Ty) const override;
   SmallBitVector getRegisterSet(RegSetMask Include,
                                 RegSetMask Exclude) const override;
   const SmallBitVector &
@@ -335,7 +339,7 @@
   }
 
   void lowerGlobals(const VariableDeclarationList &Vars,
-                    const IceString &SectionSuffix) override;
+                    const std::string &SectionSuffix) override;
   void lowerConstants() override;
   void lowerJumpTables() override;
 
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index ebf55f5..0721168 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -41,9 +41,13 @@
     // pexe) so we need to register it as such so that ELF emission won't barf
     // on an "unknown" symbol. The GOT is added to the External symbols list
     // here because staticInit() is invoked in a single-thread context.
-    Ctx->getConstantExternSym(::Ice::GlobalOffsetTable);
+    Ctx->getConstantExternSym(Ctx->getGlobalString(::Ice::GlobalOffsetTable));
   }
 }
+
+bool shouldBePooled(const class ::Ice::Constant *C) {
+  return ::Ice::X8632::TargetX8632::shouldBePooled(C);
+}
 } // end of namespace X8632
 
 namespace Ice {
@@ -252,9 +256,10 @@
 
     const RelocOffsetT ImmSize = -typeWidthInBytes(IceType_i32);
 
-    auto *GotFromPc = llvm::cast<ConstantRelocatable>(
-        Ctx->getConstantSym(ImmSize, {AfterAddReloc, BeforeAddReloc},
-                            GlobalOffsetTable, GlobalOffsetTable));
+    auto *GotFromPc =
+        llvm::cast<ConstantRelocatable>(Ctx->getConstantSymWithEmitString(
+            ImmSize, {AfterAddReloc, BeforeAddReloc},
+            Ctx->getGlobalString(GlobalOffsetTable), GlobalOffsetTable));
 
     // Insert a new version of InstX86GetIP.
     Context.insert<Traits::Insts::GetIP>(CallDest);
diff --git a/src/IceTargetLoweringX8632Traits.h b/src/IceTargetLoweringX8632Traits.h
index 797b731..12129c3 100644
--- a/src/IceTargetLoweringX8632Traits.h
+++ b/src/IceTargetLoweringX8632Traits.h
@@ -276,7 +276,7 @@
   static const char *TargetName;
   static constexpr Type WordType = IceType_i32;
 
-  static IceString getRegName(RegNumT RegNum) {
+  static const char *getRegName(RegNumT RegNum) {
     static const char *const RegNames[RegisterSet::Reg_NUM] = {
 #define X(val, encode, name, base, scratch, preserved, stackptr, frameptr,     \
           isGPR, is64, is32, is16, is8, isXmm, is64To8, is32To8, is16To8,      \
@@ -947,7 +947,8 @@
 
   public:
     static SpillVariable *create(Cfg *Func, Type Ty, SizeT Index) {
-      return new (Func->allocate<SpillVariable>()) SpillVariable(Ty, Index);
+      return new (Func->allocate<SpillVariable>())
+          SpillVariable(Func, Ty, Index);
     }
     constexpr static auto SpillVariableKind =
         static_cast<OperandKind>(kVariable_Target);
@@ -959,8 +960,8 @@
     // Inherit dump() and emit() from Variable.
 
   private:
-    SpillVariable(Type Ty, SizeT Index)
-        : Variable(SpillVariableKind, Ty, Index), LinkedTo(nullptr) {}
+    SpillVariable(const Cfg *Func, Type Ty, SizeT Index)
+        : Variable(Func, SpillVariableKind, Ty, Index), LinkedTo(nullptr) {}
     Variable *LinkedTo;
   };
 
diff --git a/src/IceTargetLoweringX8664.cpp b/src/IceTargetLoweringX8664.cpp
index 5aaf2c9..6ac908c 100644
--- a/src/IceTargetLoweringX8664.cpp
+++ b/src/IceTargetLoweringX8664.cpp
@@ -36,6 +36,11 @@
 void staticInit(::Ice::GlobalContext *Ctx) {
   ::Ice::X8664::TargetX8664::staticInit(Ctx);
 }
+
+bool shouldBePooled(const class ::Ice::Constant *C) {
+  return ::Ice::X8664::TargetX8664::shouldBePooled(C);
+}
+
 } // end of namespace X8664
 
 namespace Ice {
@@ -344,11 +349,9 @@
 
   bool AbsoluteAddress = false;
   if (Base == nullptr && Index == nullptr) {
-    if (const auto *CR = llvm::dyn_cast<ConstantRelocatable>(Offset)) {
-      if (CR->getName() != "") {
-        // Mem is RIP-relative. There's no need to rebase it.
-        return Mem;
-      }
+    if (llvm::isa<ConstantRelocatable>(Offset)) {
+      // Mem is RIP-relative. There's no need to rebase it.
+      return Mem;
     }
     // Offset is an absolute address, so we need to emit
     //   Offset(%r15)
@@ -411,7 +414,7 @@
   bool NeedsLea = false;
   if (Offset != nullptr) {
     if (const auto *CR = llvm::dyn_cast<ConstantRelocatable>(Offset)) {
-      NeedsLea = CR->getName() != "" || CR->getOffset() < 0;
+      NeedsLea = CR->getOffset() < 0;
     } else if (const auto *Imm = llvm::dyn_cast<ConstantInteger32>(Offset)) {
       NeedsLea = Imm->getValue() < 0;
     } else {
@@ -626,7 +629,8 @@
     auto *ReturnRelocOffset = RelocOffset::create(Func->getAssembler());
     ReturnAddress->setRelocOffset(ReturnRelocOffset);
     constexpr RelocOffsetT NoFixedOffset = 0;
-    const IceString EmitString = ReturnAddress->getName(Func);
+    const std::string EmitString =
+        BuildDefs::dump() ? ReturnAddress->getLabelName().toString() : "";
     auto *ReturnReloc = ConstantRelocatable::create(
         Func->getAssembler(), IceType_i32,
         RelocatableTuple(NoFixedOffset, {ReturnRelocOffset},
diff --git a/src/IceTargetLoweringX8664Traits.h b/src/IceTargetLoweringX8664Traits.h
index b739f1a..0feaa20 100644
--- a/src/IceTargetLoweringX8664Traits.h
+++ b/src/IceTargetLoweringX8664Traits.h
@@ -301,7 +301,7 @@
   static const char *TargetName;
   static constexpr Type WordType = IceType_i64;
 
-  static IceString getRegName(RegNumT RegNum) {
+  static const char *getRegName(RegNumT RegNum) {
     static const char *const RegNames[RegisterSet::Reg_NUM] = {
 #define X(val, encode, name, base, scratch, preserved, stackptr, frameptr,     \
           sboxres, isGPR, is64, is32, is16, is8, isXmm, is64To8, is32To8,      \
@@ -992,7 +992,8 @@
 
   public:
     static SpillVariable *create(Cfg *Func, Type Ty, SizeT Index) {
-      return new (Func->allocate<SpillVariable>()) SpillVariable(Ty, Index);
+      return new (Func->allocate<SpillVariable>())
+          SpillVariable(Func, Ty, Index);
     }
     constexpr static auto SpillVariableKind =
         static_cast<OperandKind>(kVariable_Target);
@@ -1004,8 +1005,8 @@
     // Inherit dump() and emit() from Variable.
 
   private:
-    SpillVariable(Type Ty, SizeT Index)
-        : Variable(SpillVariableKind, Ty, Index), LinkedTo(nullptr) {}
+    SpillVariable(const Cfg *Func, Type Ty, SizeT Index)
+        : Variable(Func, SpillVariableKind, Ty, Index), LinkedTo(nullptr) {}
     Variable *LinkedTo;
   };
 
diff --git a/src/IceTargetLoweringX86Base.h b/src/IceTargetLoweringX86Base.h
index 0c2523f..1014493 100644
--- a/src/IceTargetLoweringX86Base.h
+++ b/src/IceTargetLoweringX86Base.h
@@ -79,6 +79,7 @@
   ~TargetX86Base() override = default;
 
   static void staticInit(GlobalContext *Ctx);
+  static bool shouldBePooled(const Constant *C);
 
   static FixupKind getPcRelFixup() { return PcRelFixup; }
   static FixupKind getAbsFixup() { return AbsFixup; }
@@ -95,8 +96,8 @@
   }
   Variable *getPhysicalRegister(RegNumT RegNum,
                                 Type Ty = IceType_void) override;
-  IceString getRegName(RegNumT RegNum, Type Ty) const override;
-  static IceString getRegClassName(RegClass C) {
+  const char *getRegName(RegNumT RegNum, Type Ty) const override;
+  static const char *getRegClassName(RegClass C) {
     auto ClassNum = static_cast<RegClassX86>(C);
     assert(ClassNum < RCX86_NUM);
     switch (ClassNum) {
@@ -174,9 +175,9 @@
 
   ConstantRelocatable *createGetIPForRegister(const Variable *Dest) {
     assert(Dest->hasReg());
-    const IceString RegName = Traits::getRegName(Dest->getRegNum());
-    return llvm::cast<ConstantRelocatable>(
-        Ctx->getConstantExternSym(H_getIP_prefix + RegName));
+    const std::string RegName = Traits::getRegName(Dest->getRegNum());
+    return llvm::cast<ConstantRelocatable>(Ctx->getConstantExternSym(
+        Ctx->getGlobalString(H_getIP_prefix + RegName)));
   }
 
   SizeT getMinJumpTableSize() const override { return 4; }
@@ -1097,7 +1098,7 @@
   }
 
   void lowerGlobals(const VariableDeclarationList &Vars,
-                    const IceString &SectionSuffix) override;
+                    const std::string &SectionSuffix) override;
   void lowerConstants() override;
   void lowerJumpTables() override;
 
diff --git a/src/IceTargetLoweringX86BaseImpl.h b/src/IceTargetLoweringX86BaseImpl.h
index 0819078..7394ab1 100644
--- a/src/IceTargetLoweringX86BaseImpl.h
+++ b/src/IceTargetLoweringX86BaseImpl.h
@@ -376,6 +376,21 @@
       Ctx->getFlags().getUseNonsfi() ? Traits::FK_Gotoff : Traits::FK_Abs;
 }
 
+template <typename TraitsType>
+bool TargetX86Base<TraitsType>::shouldBePooled(const Constant *C) {
+  if (auto *ConstFloat = llvm::dyn_cast<ConstantFloat>(C)) {
+    return !Utils::isPositiveZero(ConstFloat->getValue());
+  }
+  if (auto *ConstDouble = llvm::dyn_cast<ConstantDouble>(C)) {
+    return !Utils::isPositiveZero(ConstDouble->getValue());
+  }
+  if (GlobalContext::getFlags().getRandomizeAndPoolImmediatesOption() !=
+      RPI_Pool) {
+    return false;
+  }
+  return C->shouldBeRandomizedOrPooled();
+}
+
 template <typename TraitsType> void TargetX86Base<TraitsType>::translateO2() {
   TimerMarker T(TimerStack::TT_O2, Func);
 
@@ -855,7 +870,8 @@
 }
 
 template <typename TraitsType>
-IceString TargetX86Base<TraitsType>::getRegName(RegNumT RegNum, Type Ty) const {
+const char *TargetX86Base<TraitsType>::getRegName(RegNumT RegNum,
+                                                  Type Ty) const {
   return Traits::getRegName(Traits::getGprForType(Ty, RegNum));
 }
 
@@ -5868,10 +5884,11 @@
     }
 
     constexpr RelocOffsetT RelocOffset = 0;
-    const IceString &FunctionName = Func->getFunctionName();
+    GlobalString FunctionName = Func->getFunctionName();
     constexpr Variable *NoBase = nullptr;
-    Constant *Offset = Ctx->getConstantSym(
-        RelocOffset, InstJumpTable::makeName(FunctionName, JumpTable->getId()));
+    auto JTName = GlobalString::createWithString(
+        Ctx, InstJumpTable::makeName(FunctionName, JumpTable->getId()));
+    Constant *Offset = Ctx->getConstantSym(RelocOffset, JTName);
     uint16_t Shift = typeWidthInBytesLog2(PointerType);
     constexpr auto Segment = X86OperandMem::SegmentRegisters::DefaultSegment;
 
@@ -6818,11 +6835,9 @@
           return makeZeroedRegister(Ty, RegNum);
       }
 
-      std::string Buffer;
-      llvm::raw_string_ostream StrBuf(Buffer);
-      llvm::cast<Constant>(From)->emitPoolLabel(StrBuf);
-      llvm::cast<Constant>(From)->setShouldBePooled(true);
-      Constant *Offset = Ctx->getConstantSym(0, StrBuf.str());
+      auto *CFrom = llvm::cast<Constant>(From);
+      assert(CFrom->getShouldBePooled());
+      Constant *Offset = Ctx->getConstantSym(0, CFrom->getLabelName());
       auto *Mem = X86OperandMem::create(Func, Ty, nullptr, Offset);
       From = Mem;
     }
@@ -7039,7 +7054,7 @@
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Ctx->getStrEmit();
-  C->emitPoolLabel(Str);
+  Str << C->getLabelName();
 }
 
 template <typename TraitsType>
@@ -7047,7 +7062,7 @@
   if (!BuildDefs::dump())
     return;
   Ostream &Str = Ctx->getStrEmit();
-  C->emitPoolLabel(Str);
+  Str << C->getLabelName();
 }
 
 template <typename TraitsType>
@@ -7059,7 +7074,8 @@
 void TargetX86Base<Machine>::emit(const ConstantRelocatable *C) const {
   if (!BuildDefs::dump())
     return;
-  assert(!Ctx->getFlags().getUseNonsfi() || C->getName() == GlobalOffsetTable);
+  assert(!Ctx->getFlags().getUseNonsfi() ||
+         C->getName().toString() == GlobalOffsetTable);
   Ostream &Str = Ctx->getStrEmit();
   Str << "$";
   emitWithoutPrefix(C);
@@ -7086,7 +7102,7 @@
     return Immediate;
   }
 
-  if (!Immediate->shouldBeRandomizedOrPooled(Ctx)) {
+  if (!Immediate->shouldBeRandomizedOrPooled()) {
     // the constant Immediate is not eligible for blinding/pooling
     return Immediate;
   }
@@ -7129,17 +7145,14 @@
     //  insert: mov $label, Reg
     //  => Reg
     assert(Ctx->getFlags().getRandomizeAndPoolImmediatesOption() == RPI_Pool);
-    Immediate->setShouldBePooled(true);
+    assert(Immediate->getShouldBePooled());
     // if we have already assigned a phy register, we must come from
     // advancedPhiLowering()=>lowerAssign(). In this case we should reuse the
     // assigned register as this assignment is that start of its use-def
     // chain. So we add RegNum argument here.
     Variable *Reg = makeReg(Immediate->getType(), RegNum);
-    IceString Label;
-    llvm::raw_string_ostream Label_stream(Label);
-    Immediate->emitPoolLabel(Label_stream);
     constexpr RelocOffsetT Offset = 0;
-    Constant *Symbol = Ctx->getConstantSym(Offset, Label_stream.str());
+    Constant *Symbol = Ctx->getConstantSym(Offset, Immediate->getLabelName());
     constexpr Variable *NoBase = nullptr;
     X86OperandMem *MemOperand =
         X86OperandMem::create(Func, Immediate->getType(), NoBase, Symbol);
@@ -7179,7 +7192,7 @@
     return MemOperand;
   }
 
-  if (!C->shouldBeRandomizedOrPooled(Ctx)) {
+  if (!C->shouldBeRandomizedOrPooled()) {
     return MemOperand;
   }
 
@@ -7240,12 +7253,10 @@
     if (RegNum.hasValue())
       return MemOperand;
     Variable *RegTemp = makeReg(IceType_i32);
-    IceString Label;
-    llvm::raw_string_ostream Label_stream(Label);
-    MemOperand->getOffset()->emitPoolLabel(Label_stream);
-    MemOperand->getOffset()->setShouldBePooled(true);
+    assert(MemOperand->getOffset()->getShouldBePooled());
     constexpr RelocOffsetT SymOffset = 0;
-    Constant *Symbol = Ctx->getConstantSym(SymOffset, Label_stream.str());
+    Constant *Symbol =
+        Ctx->getConstantSym(SymOffset, MemOperand->getOffset()->getLabelName());
     constexpr Variable *NoBase = nullptr;
     X86OperandMem *SymbolOperand = X86OperandMem::create(
         Func, MemOperand->getOffset()->getType(), NoBase, Symbol);
@@ -7274,8 +7285,8 @@
     return;
   Ostream &Str = Ctx->getStrEmit();
   const bool UseNonsfi = Ctx->getFlags().getUseNonsfi();
-  const IceString &FunctionName = Func->getFunctionName();
-  const IceString Prefix = UseNonsfi ? ".data.rel.ro." : ".rodata.";
+  GlobalString FunctionName = Func->getFunctionName();
+  const char *Prefix = UseNonsfi ? ".data.rel.ro." : ".rodata.";
   Str << "\t.section\t" << Prefix << FunctionName
       << "$jumptable,\"a\",@progbits\n";
   Str << "\t.align\t" << typeWidthInBytes(getPointerType()) << "\n";
@@ -7328,7 +7339,7 @@
     assert(CharsPrinted >= 0);
     assert((size_t)CharsPrinted < llvm::array_lengthof(buf));
     (void)CharsPrinted; // avoid warnings if asserts are disabled
-    Const->emitPoolLabel(Str);
+    Str << Const->getLabelName();
     Str << ":\n\t" << T::AsmTag << "\t" << buf << "\t/* " << T::TypeName << " "
         << Value << " */\n";
   }
@@ -7379,7 +7390,7 @@
     if (!BuildDefs::dump())
       return;
     Ostream &Str = Ctx->getStrEmit();
-    const IceString Prefix = IsPIC ? ".data.rel.ro." : ".rodata.";
+    const char *Prefix = IsPIC ? ".data.rel.ro." : ".rodata.";
     for (const JumpTableData &JT : Ctx->getJumpTables()) {
       Str << "\t.section\t" << Prefix << JT.getFunctionName()
           << "$jumptable,\"a\",@progbits\n";
@@ -7397,7 +7408,7 @@
 
 template <typename TraitsType>
 void TargetDataX86<TraitsType>::lowerGlobals(
-    const VariableDeclarationList &Vars, const IceString &SectionSuffix) {
+    const VariableDeclarationList &Vars, const std::string &SectionSuffix) {
   const bool IsPIC = Ctx->getFlags().getUseNonsfi();
   switch (Ctx->getFlags().getOutFileType()) {
   case FT_Elf: {
@@ -7406,7 +7417,7 @@
   } break;
   case FT_Asm:
   case FT_Iasm: {
-    const IceString &TranslateOnly = Ctx->getFlags().getTranslateOnly();
+    const std::string TranslateOnly = Ctx->getFlags().getTranslateOnly();
     OstreamLocker L(Ctx);
     for (const VariableDeclaration *Var : Vars) {
       if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
diff --git a/src/IceTimerTree.cpp b/src/IceTimerTree.cpp
index 1ae4e53..6cba289 100644
--- a/src/IceTimerTree.cpp
+++ b/src/IceTimerTree.cpp
@@ -30,7 +30,7 @@
 
 namespace Ice {
 
-TimerStack::TimerStack(const IceString &Name)
+TimerStack::TimerStack(const std::string &Name)
     : Name(Name), FirstTimestamp(timestamp()), LastTimestamp(FirstTimestamp) {
   if (!BuildDefs::timers())
     return;
@@ -48,7 +48,7 @@
 }
 
 // Returns the unique timer ID for the given Name, creating a new ID if needed.
-TimerIdT TimerStack::getTimerID(const IceString &Name) {
+TimerIdT TimerStack::getTimerID(const std::string &Name) {
   if (!BuildDefs::timers())
     return 0;
   if (IDsIndex.find(Name) == IDsIndex.end()) {
@@ -224,7 +224,7 @@
 
 namespace {
 
-using DumpMapType = std::multimap<double, IceString>;
+using DumpMapType = std::multimap<double, std::string>;
 
 // Dump the Map items in reverse order of their time contribution.
 void dumpHelper(Ostream &Str, const DumpMapType &Map, double TotalTime) {
@@ -275,7 +275,7 @@
     DumpMapType CumulativeMap;
     for (TTindex i = 1; i < Nodes.size(); ++i) {
       TTindex Prefix = i;
-      IceString Suffix = "";
+      std::string Suffix = "";
       while (Prefix) {
         if (Suffix.empty())
           Suffix = IDs[Nodes[Prefix].Interior];
diff --git a/src/IceTimerTree.h b/src/IceTimerTree.h
index fb21f84..5f068c1 100644
--- a/src/IceTimerTree.h
+++ b/src/IceTimerTree.h
@@ -62,12 +62,12 @@
 #undef X
         TT__num
   };
-  explicit TimerStack(const IceString &Name);
+  explicit TimerStack(const std::string &Name);
   TimerStack(const TimerStack &) = default;
-  TimerIdT getTimerID(const IceString &Name);
+  TimerIdT getTimerID(const std::string &Name);
   void mergeFrom(const TimerStack &Src);
-  void setName(const IceString &NewName) { Name = NewName; }
-  const IceString &getName() const { return Name; }
+  void setName(const std::string &NewName) { Name = NewName; }
+  const std::string &getName() const { return Name; }
   void push(TimerIdT ID);
   void pop(TimerIdT ID);
   void reset();
@@ -80,13 +80,13 @@
   PathType getPath(TTindex Index, const TranslationType &Mapping) const;
   TTindex getChildIndex(TTindex Parent, TimerIdT ID);
   TTindex findPath(const PathType &Path);
-  IceString Name;
+  std::string Name;
   double FirstTimestamp;
   double LastTimestamp;
   uint64_t StateChangeCount = 0;
   /// IDsIndex maps a symbolic timer name to its integer ID.
-  std::map<IceString, TimerIdT> IDsIndex;
-  std::vector<IceString> IDs;       /// indexed by TimerIdT
+  std::map<std::string, TimerIdT> IDsIndex;
+  std::vector<std::string> IDs;     /// indexed by TimerIdT
   std::vector<TimerTreeNode> Nodes; /// indexed by TTindex
   std::vector<double> LeafTimes;    /// indexed by TimerIdT
   std::vector<size_t> LeafCounts;   /// indexed by TimerIdT
diff --git a/src/IceTranslator.cpp b/src/IceTranslator.cpp
index ce54c21..04e2ffd 100644
--- a/src/IceTranslator.cpp
+++ b/src/IceTranslator.cpp
@@ -28,7 +28,8 @@
     : Ctx(Ctx), NextSequenceNumber(GlobalContext::getFirstSequenceNumber()),
       ErrorStatus() {}
 
-IceString Translator::createUnnamedName(const IceString &Prefix, SizeT Index) {
+std::string Translator::createUnnamedName(const std::string &Prefix,
+                                          SizeT Index) {
   if (Index == 0)
     return Prefix;
   std::string Buffer;
@@ -37,8 +38,9 @@
   return StrBuf.str();
 }
 
-bool Translator::checkIfUnnamedNameSafe(const IceString &Name, const char *Kind,
-                                        const IceString &Prefix) {
+bool Translator::checkIfUnnamedNameSafe(const std::string &Name,
+                                        const char *Kind,
+                                        const std::string &Prefix) {
   if (Name.find(Prefix) == 0) {
     for (size_t i = Prefix.size(); i < Name.size(); ++i) {
       if (!isdigit(Name[i])) {
diff --git a/src/IceTranslator.h b/src/IceTranslator.h
index 5c62c04..5e7dfad 100644
--- a/src/IceTranslator.h
+++ b/src/IceTranslator.h
@@ -59,13 +59,13 @@
   lowerGlobals(std::unique_ptr<VariableDeclarationList> VariableDeclarations);
 
   /// Creates a name using the given prefix and corresponding index.
-  std::string createUnnamedName(const IceString &Prefix, SizeT Index);
+  std::string createUnnamedName(const std::string &Prefix, SizeT Index);
 
   /// Reports if there is a (potential) conflict between Name, and using Prefix
   /// to name unnamed names. Errors are put on Ostream. Returns true if there
   /// isn't a potential conflict.
-  bool checkIfUnnamedNameSafe(const IceString &Name, const char *Kind,
-                              const IceString &Prefix);
+  bool checkIfUnnamedNameSafe(const std::string &Name, const char *Kind,
+                              const std::string &Prefix);
 
   uint32_t getNextSequenceNumber() { return NextSequenceNumber++; }
 
diff --git a/src/IceTypes.h b/src/IceTypes.h
index e6e6f40..5376e73 100644
--- a/src/IceTypes.h
+++ b/src/IceTypes.h
@@ -82,7 +82,7 @@
 size_t typeNumElements(Type Ty);
 Type typeElementType(Type Ty);
 const char *typeString(Type Ty);
-inline IceString typeIceString(Type Ty) { return typeString(Ty); }
+inline std::string typeStdString(Type Ty) { return typeString(Ty); }
 const char *regClassString(RegClass C);
 
 inline Type getPointerType() { return IceType_i32; }
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index 27260c3..1040a4f 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -473,17 +473,22 @@
 
   // Gives Decl a name if it doesn't already have one. Prefix and NameIndex are
   // used to generate the name. NameIndex is automatically incremented if a new
-  // name is created. DeclType is literal text describing the type of name
-  // being created. Also generates warning if created names may conflict with
-  // named declarations.
+  // name is created. DeclType is literal text describing the type of name being
+  // created. Also generates a warning if created names may conflict with named
+  // declarations.
   void installDeclarationName(Ice::GlobalDeclaration *Decl,
-                              const Ice::IceString &Prefix,
-                              const char *DeclType,
+                              const std::string &Prefix, const char *DeclType,
                               NaClBcIndexSize_t &NameIndex) {
     if (Decl->hasName()) {
-      Translator.checkIfUnnamedNameSafe(Decl->getName(), DeclType, Prefix);
+      Translator.checkIfUnnamedNameSafe(Decl->getName().toString(), DeclType,
+                                        Prefix);
     } else {
-      Decl->setName(Translator.createUnnamedName(Prefix, NameIndex));
+      Ice::GlobalContext *Ctx = Translator.getContext();
+      if (Ice::BuildDefs::dump() || !Decl->isInternal()) {
+        Decl->setName(Ctx, Translator.createUnnamedName(Prefix, NameIndex));
+      } else {
+        Decl->setName(Ctx);
+      }
       ++NameIndex;
     }
   }
@@ -491,7 +496,7 @@
   // Installs names for global variables without names.
   void installGlobalVarNames() {
     assert(VariableDeclarations);
-    const Ice::IceString &GlobalPrefix =
+    const std::string &GlobalPrefix =
         getTranslator().getFlags().getDefaultGlobalPrefix();
     if (!GlobalPrefix.empty()) {
       NaClBcIndexSize_t NameIndex = 0;
@@ -503,7 +508,7 @@
 
   // Installs names for functions without names.
   void installFunctionNames() {
-    const Ice::IceString &FunctionPrefix =
+    const std::string &FunctionPrefix =
         getTranslator().getFlags().getDefaultFunctionPrefix();
     if (!FunctionPrefix.empty()) {
       NaClBcIndexSize_t NameIndex = 0;
@@ -515,13 +520,13 @@
 
   // Builds a constant symbol named Name.  IsExternal is true iff the symbol is
   // external.
-  Ice::Constant *getConstantSym(const Ice::IceString &Name,
-                                bool IsExternal) const {
+  Ice::Constant *getConstantSym(Ice::GlobalString Name, bool IsExternal) const {
+    Ice::GlobalContext *Ctx = getTranslator().getContext();
     if (IsExternal) {
-      return getTranslator().getContext()->getConstantExternSym(Name);
+      return Ctx->getConstantExternSym(Name);
     } else {
       const Ice::RelocOffsetT Offset = 0;
-      return getTranslator().getContext()->getConstantSym(Offset, Name);
+      return Ctx->getConstantSym(Offset, Name);
     }
   }
 
@@ -1353,11 +1358,12 @@
   bool convertFunction() {
     bool ParserResult;
     {
-      Ice::TimerMarker T(getTranslator().getContext(), FuncDecl->getName());
+      Ice::TimerMarker T(getTranslator().getContext(),
+                         FuncDecl->getName().toStringOrEmpty());
       // Note: The Cfg is created, even when IR generation is disabled. This is
       // done to install a CfgLocalAllocator for various internal containers.
-      Func = Ice::Cfg::create(getTranslator().getContext(),
-                              getTranslator().getNextSequenceNumber());
+      Ice::GlobalContext *Ctx = getTranslator().getContext();
+      Func = Ice::Cfg::create(Ctx, getTranslator().getNextSequenceNumber());
 
       Ice::CfgLocalAllocatorScope _(Func.get());
 
@@ -2102,9 +2108,9 @@
     }
   }
 
-  const Ice::IceString printName(Ice::FunctionDeclaration *Fcn) {
+  const std::string printName(Ice::FunctionDeclaration *Fcn) {
     if (Fcn)
-      return Fcn->getName();
+      return Fcn->getName().toString();
     return "function";
   }
 };
@@ -3056,7 +3062,12 @@
     Decl->setLinkage(llvm::GlobalValue::ExternalLinkage);
   }
 
-  Decl->setName(StringRef(Name.data(), Name.size()));
+  // Unconditionally capture the name if it is provided in the input file,
+  // regardless of whether dump is enabled or whether the symbol is internal vs
+  // external.  This fits in well with the lit tests, and most symbols in a
+  // conforming pexe are nameless and don't take this path.
+  Decl->setName(getTranslator().getContext(),
+                StringRef(Name.data(), Name.size()));
 }
 
 void ModuleValuesymtabParser::setBbName(NaClBcIndexSize_t Index,
diff --git a/unittest/IceELFSectionTest.cpp b/unittest/IceELFSectionTest.cpp
index 57e43c1..3cfbd44 100644
--- a/unittest/IceELFSectionTest.cpp
+++ b/unittest/IceELFSectionTest.cpp
@@ -14,6 +14,8 @@
 #include "IceDefs.h"
 #include "IceELFSection.h"
 
+#include "llvm/Support/raw_os_ostream.h"
+
 namespace Ice {
 namespace {
 
@@ -21,23 +23,23 @@
 // lollipop, and lipop are able to share data, while the other strings do not.
 void CheckStringTablePermLayout(const ELFStringTableSection &Strtab) {
   size_t pop_index = Strtab.getIndex("pop");
-  size_t pop_size = IceString("pop").size();
+  size_t pop_size = std::string("pop").size();
   size_t lollipop_index = Strtab.getIndex("lollipop");
-  size_t lollipop_size = IceString("lollipop").size();
+  size_t lollipop_size = std::string("lollipop").size();
   size_t lipop_index = Strtab.getIndex("lipop");
-  size_t lipop_size = IceString("lipop").size();
+  size_t lipop_size = std::string("lipop").size();
   size_t pops_index = Strtab.getIndex("pops");
-  size_t pops_size = IceString("pops").size();
+  size_t pops_size = std::string("pops").size();
   size_t unpop_index = Strtab.getIndex("unpop");
-  size_t unpop_size = IceString("unpop").size();
+  size_t unpop_size = std::string("unpop").size();
   size_t popular_index = Strtab.getIndex("popular");
-  size_t popular_size = IceString("popular").size();
+  size_t popular_size = std::string("popular").size();
   size_t strtab_index = Strtab.getIndex(".strtab");
-  size_t strtab_size = IceString(".strtab").size();
+  size_t strtab_size = std::string(".strtab").size();
   size_t shstrtab_index = Strtab.getIndex(".shstrtab");
-  size_t shstrtab_size = IceString(".shstrtab").size();
+  size_t shstrtab_size = std::string(".shstrtab").size();
   size_t symtab_index = Strtab.getIndex(".symtab");
-  size_t symtab_size = IceString(".symtab").size();
+  size_t symtab_size = std::string(".symtab").size();
 
   // Check that some sharing exists.
   EXPECT_EQ(pop_index, lollipop_index + (lollipop_size - pop_size));
@@ -73,7 +75,7 @@
 
 // Test that the order in which strings are added doesn't matter.
 TEST(IceELFSectionTest, StringTableBuilderPermSeveral) {
-  std::vector<IceString> Strings;
+  std::vector<std::string> Strings;
   Strings.push_back("pop");
   Strings.push_back("lollipop");
   Strings.push_back("lipop");
@@ -94,6 +96,7 @@
   RandomNumberGenerator R(RandomSeed);
   RandomNumberGeneratorWrapper RNG(R);
   for (SizeT i = 0; i < NumTests; ++i) {
+    auto Str = std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout));
     RandomShuffle(Strings.begin(), Strings.end(), RNG);
     ELFStringTableSection Strtab(".strtab", SHT_STRTAB, 0, 1, 0);
     for (auto &S : Strings) {
@@ -106,6 +109,7 @@
 
 // Test that adding duplicate strings is fine.
 TEST(IceELFSectionTest, StringTableBuilderDuplicates) {
+  auto Str = std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout));
   ELFStringTableSection Strtab(".strtab", SHT_STRTAB, 0, 1, 0);
   Strtab.add("unpop");
   Strtab.add("pop");