Introduce StringBuilder utility around raw_string_ostream

Alp Toker alp at nuanti.com
Sat Jun 14 19:57:08 PDT 2014


Good idea Philip, let's break it down:

The proposed string_ostream below is a safe, stack-allocating, string 
builder that disallows unsynchronised reads/writes and doesn't permit 
misuse or inappropriate calls to flush().

small_string_ostream<bytes> additionally allows fine-tuning of the stack 
allocation size.

We don't go through std::string and we don't incur std::string 
allocation overhead.

The result is acquired using str(), a zero-copy flushing StringRef 
accessor which several callers directly benefit from. Otherwise implicit 
conversion at point of use means the class still works fine as a simple 
ostream-compatible std::stringstream.

The class offers a safe reset() function that restores storage to its 
original state, which is guaranteed to be an empty buffer because we own 
the storage.

There's no trade-off and this implementation should be optimal in all 
cases now.

Below is the entire 18 line facility including comments:


/// string_ostream - A raw_ostream that builds a string.  This is a
/// raw_svector_ostream with storage.
template <unsigned InternalLen>
class small_string_ostream : public raw_svector_ostream {
   SmallVector<char, InternalLen> Buffer;
   // There's no need to flush explicitly.
   using raw_svector_ostream::flush;
public:
   small_string_ostream() : raw_svector_ostream(Buffer, nullptr) { init(); }

   void clear() {
     flush();
     Buffer.clear();
   }
};

typedef small_string_ostream<128> string_ostream;


Usage:


string_ostream OS;
OS << foo;
return os.str();


If there's a more elegant way to achieve this, I'd like to see the code.

(Full patch attached.)

Alp.



On 12/06/2014 23:44, Philip Reames wrote:
> Taking a step back, here's the features I've heard that are desired 
> out of whatever we end up creating:
> - An easy clear function which clears the underlying storage. This 
> prevents bugs like:
> std::string Str;
> raw_string_ostream Stream;
> Stream << "some text"; <-- not yet flushed!
> Str.clear();
> Stream << "something"; <-- flushes *both* writes to the String.
>
> - Desire to avoid virtual dispatch on write_impl (and thus buffer in 
> the base class).
> - Desire to preserve a streaming interface for streaming clients.
>
> - A version of raw_string_stream which owns it's own storage. (i.e. 
> can be used as a single stack object.)  This prevents bugs like:
> std::string Str;
> raw_string_ostream Stream;
> Stream << "some text";
> use( Str ); <-- not yet flushed
>
> Is there anything else that the proposed StringBuilder adds?
>
> For the first, we can and should add a clear function to the 
> raw_string_ostream class.  This seems like a good idea regardless.
>
> Do we agree the last is desirable?  Regardless of how that ends up 
> being implemented?
>
> Philip
>
>
>
> On 06/12/2014 01:25 PM, Alp Toker wrote:
>>
>> On 12/06/2014 22:37, Chandler Carruth wrote:
>>> On Thu, Jun 12, 2014 at 8:26 PM, Alp Toker <alp at nuanti.com 
>>> <mailto:alp at nuanti.com>> wrote:
>>>
>>>
>>>     On 12/06/2014 21:31, Chandler Carruth wrote:
>>>
>>>
>>>         On Thu, Jun 12, 2014 at 5:21 PM, James Molloy
>>>         <james at jamesmolloy.co.uk <mailto:james at jamesmolloy.co.uk>
>>>         <mailto:james at jamesmolloy.co.uk
>>>         <mailto:james at jamesmolloy.co.uk>>> wrote:
>>>
>>>             FWIW, I like Alp's suggestion. There are a bunch of ways 
>>> to do
>>>             string building in LLVM (Twine, raw_string_ostream,
>>>             std::stringstream). I'd appreciate a single mechanism for
>>>         making
>>>             strings.
>>>
>>>
>>>         But that's not what we would get.
>>>
>>>
>>>     I think you're taking an extreme and isolated view on this one
>>>     Chandler.
>>>
>>>
>>> Dave independently wrote almost the exact same thing.
>>>
>>>     What James describes is exactly what we get from the proposed 
>>> patch.
>>>
>>>
>>> We disagree here.
>>>
>>>     I've gone ahead and produced examples of invalid string access,
>>>     and demonstrated how the patch fixes each of these:
>>>
>>>
>>> Yes, two bugs in debugging code. Both of which would also be avoided 
>>> by the simpler suggestion I already made.
>>
>> I don't really agree.
>>
>>> I continue to believe the simpler suggestion could be made to work 
>>> equally well with stack based storage.
>>
>> What would happen if every ostream subclass provided integrated 
>> storage? You put an std::string in raw_string_ostream .. but what do 
>> you put in a raw_fd_ostream? This non sequitur highlights the logical 
>> shortcoming of your simpler suggestion.
>>
>> These are streaming interfaces -- the only alternative I see you've 
>> suggested so far is to plug arbitrary storage and provide a weak, 
>> not-really-safe synthesis of what I've proposed, adding inappropriate 
>> complexity to raw_string_ostream with no clear benefit.
>>
>>>
>>>
>>>         , and it's going to be quite far from replacing the use cases
>>>         of Twine.
>>>
>>>
>>>     The latest iteration of the patch is actually a very worthwhile
>>>     Twine alternative if you're hitting string lifetime issues with 
>>> that.
>>>
>>>     Anyway, shouldn't we be working to make the LLVM interfaces safer
>>>     and more efficient for developers?
>>>
>>>
>>> We are. Part of that is code review. Currently, I don't see any 
>>> strong consensus that your proposal is the right tradeoff, and an 
>>> alternative has been proposed in code review that hasn't really been 
>>> explored.
>>
>> Okay. What evidence do you have for that claim? I'm lacking context.
>>
>> Alp.
>>
>>
>

-- 
http://www.nuanti.com
the browser experts

-------------- next part --------------
diff --git a/include/llvm/Analysis/CFGPrinter.h b/include/llvm/Analysis/CFGPrinter.h
index e6d2ed1..759afca 100644
--- a/include/llvm/Analysis/CFGPrinter.h
+++ b/include/llvm/Analysis/CFGPrinter.h
@@ -36,9 +36,7 @@ struct DOTGraphTraits<const Function*> : public DefaultDOTGraphTraits {
     if (!Node->getName().empty())
       return Node->getName().str();
 
-    std::string Str;
-    raw_string_ostream OS(Str);
-
+    string_ostream OS;
     Node->printAsOperand(OS, false);
     return OS.str();
   }
@@ -46,8 +44,7 @@ struct DOTGraphTraits<const Function*> : public DefaultDOTGraphTraits {
   static std::string getCompleteNodeLabel(const BasicBlock *Node,
                                           const Function *) {
     enum { MaxColumns = 80 };
-    std::string Str;
-    raw_string_ostream OS(Str);
+    string_ostream OS;
 
     if (Node->getName().empty()) {
       Node->printAsOperand(OS, false);
@@ -109,8 +106,7 @@ struct DOTGraphTraits<const Function*> : public DefaultDOTGraphTraits {
 
       if (SuccNo == 0) return "def";
 
-      std::string Str;
-      raw_string_ostream OS(Str);
+      string_ostream OS;
       SwitchInst::ConstCaseIt Case =
           SwitchInst::ConstCaseIt::fromSuccessorIndex(SI, SuccNo);
       OS << Case.getCaseValue()->getValue();
diff --git a/include/llvm/Support/GraphWriter.h b/include/llvm/Support/GraphWriter.h
index 2f02aa7..a6bd1af 100644
--- a/include/llvm/Support/GraphWriter.h
+++ b/include/llvm/Support/GraphWriter.h
@@ -184,8 +184,7 @@ public:
         O << "|" << DOT::EscapeString(NodeDesc);
     }
 
-    std::string edgeSourceLabels;
-    raw_string_ostream EdgeSourceLabels(edgeSourceLabels);
+    string_ostream EdgeSourceLabels;
     bool hasEdgeSourceLabels = getEdgeSourceLabels(EdgeSourceLabels, Node);
 
     if (hasEdgeSourceLabels) {
diff --git a/include/llvm/Support/YAMLTraits.h b/include/llvm/Support/YAMLTraits.h
index a23faf6..14ca2db 100644
--- a/include/llvm/Support/YAMLTraits.h
+++ b/include/llvm/Support/YAMLTraits.h
@@ -612,8 +612,7 @@ template<typename T>
 typename std::enable_if<has_ScalarTraits<T>::value,void>::type
 yamlize(IO &io, T &Val, bool) {
   if ( io.outputting() ) {
-    std::string Storage;
-    llvm::raw_string_ostream Buffer(Storage);
+    llvm::string_ostream Buffer;
     ScalarTraits<T>::output(Val, io.getContext(), Buffer);
     StringRef Str = Buffer.str();
     io.scalarString(Str, ScalarTraits<T>::mustQuote(Str));
diff --git a/include/llvm/Support/raw_ostream.h b/include/llvm/Support/raw_ostream.h
index 34fbe08..ae3c7e5 100644
--- a/include/llvm/Support/raw_ostream.h
+++ b/include/llvm/Support/raw_ostream.h
@@ -15,6 +15,7 @@
 #define LLVM_SUPPORT_RAW_OSTREAM_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/DataTypes.h"
 
@@ -438,6 +439,8 @@ class raw_string_ostream : public raw_ostream {
   /// counting the bytes currently in the buffer.
   uint64_t current_pos() const override { return OS.size(); }
 public:
+  // This constructor is specified not to access the string provided for
+  // storage as it may not yet be initialized at construction time.
   explicit raw_string_ostream(std::string &O) : OS(O) {}
   ~raw_string_ostream();
 
@@ -461,6 +464,12 @@ class raw_svector_ostream : public raw_ostream {
   /// current_pos - Return the current position within the stream, not
   /// counting the bytes currently in the buffer.
   uint64_t current_pos() const override;
+
+protected:
+  explicit raw_svector_ostream(SmallVectorImpl<char> &O, std::nullptr_t)
+      : OS(O){};
+  void init();
+
 public:
   /// Construct a new raw_svector_ostream.
   ///
@@ -493,6 +502,25 @@ public:
   ~raw_null_ostream();
 };
 
+/// string_ostream - A raw_ostream that builds a string.  This is a
+/// raw_svector_ostream with storage.
+template <unsigned InternalLen>
+class small_string_ostream : public raw_svector_ostream {
+  SmallVector<char, InternalLen> Buffer;
+  // There's no need to flush explicitly.
+  using raw_svector_ostream::flush;
+
+public:
+  small_string_ostream() : raw_svector_ostream(Buffer, nullptr) { init(); }
+
+  void clear() {
+    flush();
+    Buffer.clear();
+  }
+};
+
+typedef small_string_ostream<128> string_ostream;
+
 } // end llvm namespace
 
 #endif
diff --git a/lib/Analysis/Analysis.cpp b/lib/Analysis/Analysis.cpp
index ade940a..7b63976 100644
--- a/lib/Analysis/Analysis.cpp
+++ b/lib/Analysis/Analysis.cpp
@@ -75,8 +75,7 @@ void LLVMInitializeAnalysis(LLVMPassRegistryRef R) {
 LLVMBool LLVMVerifyModule(LLVMModuleRef M, LLVMVerifierFailureAction Action,
                           char **OutMessages) {
   raw_ostream *DebugOS = Action != LLVMReturnStatusAction ? &errs() : nullptr;
-  std::string Messages;
-  raw_string_ostream MsgsOS(Messages);
+  string_ostream MsgsOS;
 
   LLVMBool Result = verifyModule(*unwrap(M), OutMessages ? &MsgsOS : DebugOS);
 
@@ -88,7 +87,7 @@ LLVMBool LLVMVerifyModule(LLVMModuleRef M, LLVMVerifierFailureAction Action,
     report_fatal_error("Broken module found, compilation aborted!");
 
   if (OutMessages)
-    *OutMessages = strdup(MsgsOS.str().c_str());
+    *OutMessages = strndup(MsgsOS.str().data(), MsgsOS.str().size());
 
   return Result;
 }
diff --git a/lib/Analysis/BlockFrequencyInfo.cpp b/lib/Analysis/BlockFrequencyInfo.cpp
index 8ed8e3e..7e702c3 100644
--- a/lib/Analysis/BlockFrequencyInfo.cpp
+++ b/lib/Analysis/BlockFrequencyInfo.cpp
@@ -82,10 +82,9 @@ struct DOTGraphTraits<BlockFrequencyInfo*> : public DefaultDOTGraphTraits {
 
   std::string getNodeLabel(const BasicBlock *Node,
                            const BlockFrequencyInfo *Graph) {
-    std::string Result;
-    raw_string_ostream OS(Result);
+    string_ostream OS;
 
-    OS << Node->getName().str() << ":";
+    OS << Node->getName() << ":";
     switch (ViewBlockFreqPropagationDAG) {
     case GVDT_Fraction:
       Graph->printBlockFreq(OS, Node);
@@ -98,7 +97,7 @@ struct DOTGraphTraits<BlockFrequencyInfo*> : public DefaultDOTGraphTraits {
                        "never reach this point.");
     }
 
-    return Result;
+    return OS.str();
   }
 };
 
diff --git a/lib/Analysis/Lint.cpp b/lib/Analysis/Lint.cpp
index b14f329..806a1fd 100644
--- a/lib/Analysis/Lint.cpp
+++ b/lib/Analysis/Lint.cpp
@@ -105,11 +105,10 @@ namespace {
     const DataLayout *DL;
     TargetLibraryInfo *TLI;
 
-    std::string Messages;
-    raw_string_ostream MessagesStr;
+    string_ostream MessagesStr;
 
     static char ID; // Pass identification, replacement for typeid
-    Lint() : FunctionPass(ID), MessagesStr(Messages) {
+    Lint() : FunctionPass(ID) {
       initializeLintPass(*PassRegistry::getPassRegistry());
     }
 
@@ -181,7 +180,7 @@ bool Lint::runOnFunction(Function &F) {
   TLI = &getAnalysis<TargetLibraryInfo>();
   visit(F);
   dbgs() << MessagesStr.str();
-  Messages.clear();
+  MessagesStr.clear();
   return false;
 }
 
diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp
index 0c188f9..2176280 100644
--- a/lib/AsmParser/LLParser.cpp
+++ b/lib/AsmParser/LLParser.cpp
@@ -28,10 +28,9 @@
 using namespace llvm;
 
 static std::string getTypeString(Type *T) {
-  std::string Result;
-  raw_string_ostream Tmp(Result);
-  Tmp << *T;
-  return Tmp.str();
+  string_ostream Result;
+  Result << *T;
+  return Result.str();
 }
 
 /// Run: module ::= toplevelentity*
diff --git a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 996dc21..d396791 100644
--- a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1586,8 +1586,7 @@ static const MCExpr *lowerConstant(const Constant *CV, AsmPrinter &AP) {
 
     // Otherwise report the problem to the user.
     {
-      std::string S;
-      raw_string_ostream OS(S);
+      string_ostream OS;
       OS << "Unsupported expression in static initializer: ";
       CE->printAsOperand(OS, /*PrintType=*/false,
                      !AP.MF ? nullptr : AP.MF->getFunction()->getParent());
diff --git a/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 46ee0c8..6573ae0 100644
--- a/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -241,8 +241,7 @@ static void EmitMSInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
         }
       }
       if (Error) {
-        std::string msg;
-        raw_string_ostream Msg(msg);
+        string_ostream Msg;
         Msg << "invalid operand in inline asm: '" << AsmStr << "'";
         MMI->getModule()->getContext().emitError(LocCookie, Msg.str());
       }
@@ -413,8 +412,7 @@ static void EmitGCCInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
           }
         }
         if (Error) {
-          std::string msg;
-          raw_string_ostream Msg(msg);
+          string_ostream Msg;
           Msg << "invalid operand in inline asm: '" << AsmStr << "'";
           MMI->getModule()->getContext().emitError(LocCookie, Msg.str());
         }
@@ -517,8 +515,7 @@ void AsmPrinter::PrintSpecial(const MachineInstr *MI, raw_ostream &OS,
     }
     OS << Counter;
   } else {
-    std::string msg;
-    raw_string_ostream Msg(msg);
+    string_ostream Msg;
     Msg << "Unknown special formatter '" << Code
          << "' for machine instr: " << *MI;
     report_fatal_error(Msg.str());
diff --git a/lib/CodeGen/MachineBlockFrequencyInfo.cpp b/lib/CodeGen/MachineBlockFrequencyInfo.cpp
index 9151d99..35e4a56 100644
--- a/lib/CodeGen/MachineBlockFrequencyInfo.cpp
+++ b/lib/CodeGen/MachineBlockFrequencyInfo.cpp
@@ -89,10 +89,9 @@ struct DOTGraphTraits<MachineBlockFrequencyInfo*> :
 
   std::string getNodeLabel(const MachineBasicBlock *Node,
                            const MachineBlockFrequencyInfo *Graph) {
-    std::string Result;
-    raw_string_ostream OS(Result);
+    string_ostream OS;
 
-    OS << Node->getName().str() << ":";
+    OS << Node->getName() << ":";
     switch (ViewMachineBlockFreqPropagationDAG) {
     case GVDT_Fraction:
       Graph->printBlockFreq(OS, Node);
@@ -105,7 +104,7 @@ struct DOTGraphTraits<MachineBlockFrequencyInfo*> :
                        "never reach this point.");
     }
 
-    return Result;
+    return OS.str();
   }
 };
 
diff --git a/lib/CodeGen/MachineBlockPlacement.cpp b/lib/CodeGen/MachineBlockPlacement.cpp
index 74af1e2..891f605 100644
--- a/lib/CodeGen/MachineBlockPlacement.cpp
+++ b/lib/CodeGen/MachineBlockPlacement.cpp
@@ -264,23 +264,19 @@ INITIALIZE_PASS_END(MachineBlockPlacement, "block-placement2",
 ///
 /// Only used by debug logging.
 static std::string getBlockName(MachineBasicBlock *BB) {
-  std::string Result;
-  raw_string_ostream OS(Result);
+  string_ostream OS;
   OS << "BB#" << BB->getNumber()
      << " (derived from LLVM BB '" << BB->getName() << "')";
-  OS.flush();
-  return Result;
+  return OS.str();
 }
 
 /// \brief Helper to print the number of a MBB.
 ///
 /// Only used by debug logging.
 static std::string getBlockNum(MachineBasicBlock *BB) {
-  std::string Result;
-  raw_string_ostream OS(Result);
+  string_ostream OS;
   OS << "BB#" << BB->getNumber();
-  OS.flush();
-  return Result;
+  return OS.str();
 }
 #endif
 
diff --git a/lib/CodeGen/MachineScheduler.cpp b/lib/CodeGen/MachineScheduler.cpp
index 0baf2a6..f7459bb 100644
--- a/lib/CodeGen/MachineScheduler.cpp
+++ b/lib/CodeGen/MachineScheduler.cpp
@@ -3235,8 +3235,7 @@ struct DOTGraphTraits<ScheduleDAGMI*> : public DefaultDOTGraphTraits {
   }
 
   static std::string getNodeLabel(const SUnit *SU, const ScheduleDAG *G) {
-    std::string Str;
-    raw_string_ostream SS(Str);
+    string_ostream SS;
     const ScheduleDAGMI *DAG = static_cast<const ScheduleDAGMI*>(G);
     const SchedDFSResult *DFS = DAG->hasVRegLiveness() ?
       static_cast<const ScheduleDAGMILive*>(G)->getDFSResult() : nullptr;
diff --git a/lib/CodeGen/ScheduleDAGInstrs.cpp b/lib/CodeGen/ScheduleDAGInstrs.cpp
index 92a9a30..baf4af6 100644
--- a/lib/CodeGen/ScheduleDAGInstrs.cpp
+++ b/lib/CodeGen/ScheduleDAGInstrs.cpp
@@ -1197,8 +1197,7 @@ void ScheduleDAGInstrs::dumpNode(const SUnit *SU) const {
 }
 
 std::string ScheduleDAGInstrs::getGraphNodeLabel(const SUnit *SU) const {
-  std::string s;
-  raw_string_ostream oss(s);
+  string_ostream oss;
   if (SU == &EntrySU)
     oss << "<entry>";
   else if (SU == &ExitSU)
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 57e22e2..a2cc290 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -3245,8 +3245,7 @@ SelectCodeCommon(SDNode *NodeToMatch, const unsigned char *MatcherTable,
 
 
 void SelectionDAGISel::CannotYetSelect(SDNode *N) {
-  std::string msg;
-  raw_string_ostream Msg(msg);
+  string_ostream Msg;
   Msg << "Cannot select: ";
 
   if (N->getOpcode() != ISD::INTRINSIC_W_CHAIN &&
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGPrinter.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGPrinter.cpp
index 4df5ede..680a7ec 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGPrinter.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGPrinter.cpp
@@ -268,8 +268,7 @@ void SelectionDAG::setSubgraphColor(SDNode *N, const char *Color) {
 }
 
 std::string ScheduleDAGSDNodes::getGraphNodeLabel(const SUnit *SU) const {
-  std::string s;
-  raw_string_ostream O(s);
+  string_ostream O;
   O << "SU(" << SU->NodeNum << "): ";
   if (SU->getNode()) {
     SmallVector<SDNode *, 4> GluedNodes;
diff --git a/lib/CodeGen/TargetSchedule.cpp b/lib/CodeGen/TargetSchedule.cpp
index b0f2ca6..7235264 100644
--- a/lib/CodeGen/TargetSchedule.cpp
+++ b/lib/CodeGen/TargetSchedule.cpp
@@ -212,11 +212,10 @@ unsigned TargetSchedModel::computeOperandLatency(
   if (SCDesc->isValid() && !DefMI->getOperand(DefOperIdx).isImplicit()
       && !DefMI->getDesc().OpInfo[DefOperIdx].isOptionalDef()
       && SchedModel.isComplete()) {
-    std::string Err;
-    raw_string_ostream ss(Err);
-    ss << "DefIdx " << DefIdx << " exceeds machine model writes for "
+    string_ostream Err;
+    Err << "DefIdx " << DefIdx << " exceeds machine model writes for "
        << *DefMI;
-    report_fatal_error(ss.str());
+    report_fatal_error(Err.str());
   }
 #endif
   // FIXME: Automatically giving all implicit defs defaultDefLatency is
diff --git a/lib/DebugInfo/DWARFDebugFrame.cpp b/lib/DebugInfo/DWARFDebugFrame.cpp
index a33548e..2227260 100644
--- a/lib/DebugInfo/DWARFDebugFrame.cpp
+++ b/lib/DebugInfo/DWARFDebugFrame.cpp
@@ -353,10 +353,9 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
     Entries.back()->parseInstructions(Data, &Offset, EndStructureOffset);
 
     if (Offset != EndStructureOffset) {
-      std::string Str;
-      raw_string_ostream OS(Str);
-      OS << format("Parsing entry instructions at %lx failed", StartOffset);
-      report_fatal_error(Str);
+      string_ostream Str;
+      Str << format("Parsing entry instructions at %lx failed", StartOffset);
+      report_fatal_error(Str.str());
     }
   }
 }
diff --git a/lib/IR/Core.cpp b/lib/IR/Core.cpp
index 197b6cb..9694344 100644
--- a/lib/IR/Core.cpp
+++ b/lib/IR/Core.cpp
@@ -62,6 +62,11 @@ void LLVMShutdown() {
 
 /*===-- Error handling ----------------------------------------------------===*/
 
+static char *LLVMCreateMessage(StringRef Message) {
+  assert(Message.find('\0') == Message.npos);
+  return strndup(Message.data(), Message.size());
+}
+
 char *LLVMCreateMessage(const char *Message) {
   return strdup(Message);
 }
@@ -110,14 +115,10 @@ unsigned LLVMGetMDKindID(const char* Name, unsigned SLen) {
 }
 
 char *LLVMGetDiagInfoDescription(LLVMDiagnosticInfoRef DI) {
-  std::string MsgStorage;
-  raw_string_ostream Stream(MsgStorage);
-  DiagnosticPrinterRawOStream DP(Stream);
-
+  string_ostream Msg;
+  DiagnosticPrinterRawOStream DP(Msg);
   unwrap(DI)->print(DP);
-  Stream.flush();
-
-  return LLVMCreateMessage(MsgStorage.c_str());
+  return LLVMCreateMessage(Msg.str());
 }
 
 LLVMDiagnosticSeverity LLVMGetDiagInfoSeverity(LLVMDiagnosticInfoRef DI){
@@ -201,13 +202,9 @@ LLVMBool LLVMPrintModuleToFile(LLVMModuleRef M, const char *Filename,
 }
 
 char *LLVMPrintModuleToString(LLVMModuleRef M) {
-  std::string buf;
-  raw_string_ostream os(buf);
-
+  string_ostream os;
   unwrap(M)->print(os, nullptr);
-  os.flush();
-
-  return strdup(buf.c_str());
+  return LLVMCreateMessage(os.str());
 }
 
 /*--.. Operations on inline assembler ......................................--*/
@@ -278,14 +275,10 @@ void LLVMDumpType(LLVMTypeRef Ty) {
 }
 
 char *LLVMPrintTypeToString(LLVMTypeRef Ty) {
-  std::string buf;
-  raw_string_ostream os(buf);
-
+  string_ostream os;
   assert(unwrap(Ty) != nullptr && "Expecting non-null Type");
   unwrap(Ty)->print(os);
-  os.flush();
-
-  return strdup(buf.c_str());
+  return LLVMCreateMessage(os.str());
 }
 
 /*--.. Operations on integer types .........................................--*/
@@ -529,14 +522,10 @@ void LLVMDumpValue(LLVMValueRef Val) {
 }
 
 char* LLVMPrintValueToString(LLVMValueRef Val) {
-  std::string buf;
-  raw_string_ostream os(buf);
-
+  string_ostream os;
   assert(unwrap(Val) != nullptr && "Expecting non-null Value");
   unwrap(Val)->print(os);
-  os.flush();
-
-  return strdup(buf.c_str());
+  return LLVMCreateMessage(os.str());
 }
 
 void LLVMReplaceAllUsesWith(LLVMValueRef OldVal, LLVMValueRef NewVal) {
diff --git a/lib/IR/DataLayout.cpp b/lib/IR/DataLayout.cpp
index dea05fb..3aa7750 100644
--- a/lib/IR/DataLayout.cpp
+++ b/lib/IR/DataLayout.cpp
@@ -519,8 +519,7 @@ const StructLayout *DataLayout::getStructLayout(StructType *Ty) const {
 }
 
 std::string DataLayout::getStringRepresentation() const {
-  std::string Result;
-  raw_string_ostream OS(Result);
+  string_ostream OS;
 
   OS << (LittleEndian ? "e" : "E");
 
diff --git a/lib/IR/LLVMContext.cpp b/lib/IR/LLVMContext.cpp
index de825f0..201b278 100644
--- a/lib/IR/LLVMContext.cpp
+++ b/lib/IR/LLVMContext.cpp
@@ -164,23 +164,22 @@ void LLVMContext::diagnose(const DiagnosticInfo &DI) {
   }
 
   // Otherwise, print the message with a prefix based on the severity.
-  std::string MsgStorage;
-  raw_string_ostream Stream(MsgStorage);
-  DiagnosticPrinterRawOStream DP(Stream);
+  string_ostream Msg;
+  DiagnosticPrinterRawOStream DP(Msg);
   DI.print(DP);
-  Stream.flush();
+
   switch (DI.getSeverity()) {
   case DS_Error:
-    errs() << "error: " << MsgStorage << "\n";
+    errs() << "error: " << Msg.str() << "\n";
     exit(1);
   case DS_Warning:
-    errs() << "warning: " << MsgStorage << "\n";
+    errs() << "warning: " << Msg.str() << "\n";
     break;
   case DS_Remark:
-    errs() << "remark: " << MsgStorage << "\n";
+    errs() << "remark: " << Msg.str() << "\n";
     break;
   case DS_Note:
-    errs() << "note: " << MsgStorage << "\n";
+    errs() << "note: " << Msg.str() << "\n";
     break;
   }
 }
diff --git a/lib/IRReader/IRReader.cpp b/lib/IRReader/IRReader.cpp
index 01aa074..6d389d4 100644
--- a/lib/IRReader/IRReader.cpp
+++ b/lib/IRReader/IRReader.cpp
@@ -108,13 +108,9 @@ LLVMBool LLVMParseIRInContext(LLVMContextRef ContextRef,
 
   if(!*OutM) {
     if (OutMessage) {
-      std::string buf;
-      raw_string_ostream os(buf);
-
+      string_ostream os;
       Diag.print(nullptr, os, false);
-      os.flush();
-
-      *OutMessage = strdup(buf.c_str());
+      *OutMessage = strndup(os.str().data(), os.str().size());
     }
     return 1;
   }
diff --git a/lib/LTO/LTOCodeGenerator.cpp b/lib/LTO/LTOCodeGenerator.cpp
index 9009958..1b76fcb 100644
--- a/lib/LTO/LTOCodeGenerator.cpp
+++ b/lib/LTO/LTOCodeGenerator.cpp
@@ -565,16 +565,17 @@ void LTOCodeGenerator::DiagnosticHandler2(const DiagnosticInfo &DI) {
     break;
   }
   // Create the string that will be reported to the external diagnostic handler.
-  std::string MsgStorage;
-  raw_string_ostream Stream(MsgStorage);
-  DiagnosticPrinterRawOStream DP(Stream);
+  string_ostream Msg;
+  DiagnosticPrinterRawOStream DP(Msg);
   DI.print(DP);
-  Stream.flush();
+
+  // Null-terminate the C string.
+  Msg << '\0';
 
   // If this method has been called it means someone has set up an external
   // diagnostic handler. Assert on that.
   assert(DiagHandler && "Invalid diagnostic handler");
-  (*DiagHandler)(Severity, MsgStorage.c_str(), DiagContext);
+  (*DiagHandler)(Severity, Msg.str().drop_back().data(), DiagContext);
 }
 
 void
diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp
index cbff7be..e5d74aa 100644
--- a/lib/MC/MCParser/AsmParser.cpp
+++ b/lib/MC/MCParser/AsmParser.cpp
@@ -4534,8 +4534,7 @@ bool AsmParser::parseMSInlineAsm(
   }
 
   // Build the IR assembly string.
-  std::string AsmStringIR;
-  raw_string_ostream OS(AsmStringIR);
+  string_ostream OS;
   const char *AsmStart = SrcMgr.getMemoryBuffer(0)->getBufferStart();
   const char *AsmEnd = SrcMgr.getMemoryBuffer(0)->getBufferEnd();
   array_pod_sort(AsmStrRewrites.begin(), AsmStrRewrites.end(), rewritesSort);
@@ -4603,8 +4602,7 @@ bool AsmParser::parseMSInlineAsm(
     }
     case AOK_DotOperator:
       // Insert the dot if the user omitted it.
-      OS.flush();
-      if (AsmStringIR.back() != '.')
+      if (OS.str().back() != '.')
         OS << '.';
       OS << (*I).Val;
       break;
diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp
index f7c213a..b3f3056 100644
--- a/lib/Support/raw_ostream.cpp
+++ b/lib/Support/raw_ostream.cpp
@@ -704,6 +704,10 @@ void raw_string_ostream::write_impl(const char *Ptr, size_t Size) {
 // and we only need to set the vector size when the data is flushed.
 
 raw_svector_ostream::raw_svector_ostream(SmallVectorImpl<char> &O) : OS(O) {
+  init();
+}
+
+void raw_svector_ostream::init() {
   // Set up the initial external buffer. We make sure that the buffer has at
   // least 128 bytes free; raw_ostream itself only requires 64, but we want to
   // make sure that we don't grow the buffer unnecessarily on destruction (when
diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp
index 0550692..76ef696 100644
--- a/lib/TableGen/TGParser.cpp
+++ b/lib/TableGen/TGParser.cpp
@@ -1307,8 +1307,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
     if (ItemType) {
       ListRecTy *ListType = dyn_cast<ListRecTy>(ItemType);
       if (!ListType) {
-        std::string s;
-        raw_string_ostream ss(s);
+        string_ostream ss;
         ss << "Type mismatch for list, expected list type, got "
            << ItemType->getAsString();
         TokError(ss.str());
diff --git a/lib/Target/NVPTX/NVPTXAsmPrinter.cpp b/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
index e6cbf64..e1ee247 100644
--- a/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
+++ b/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
@@ -146,8 +146,7 @@ const MCExpr *nvptx::LowerConstant(const Constant *CV, AsmPrinter &AP) {
 
     // Otherwise report the problem to the user.
     {
-      std::string S;
-      raw_string_ostream OS(S);
+      string_ostream OS;
       OS << "Unsupported expression in static initializer: ";
       CE->printAsOperand(OS, /*PrintType=*/ false,
                          !AP.MF ? nullptr : AP.MF->getFunction()->getParent());
diff --git a/lib/Target/TargetMachineC.cpp b/lib/Target/TargetMachineC.cpp
index 20923c9..0b0a09a 100644
--- a/lib/Target/TargetMachineC.cpp
+++ b/lib/Target/TargetMachineC.cpp
@@ -237,15 +237,13 @@ LLVMBool LLVMTargetMachineEmitToFile(LLVMTargetMachineRef T, LLVMModuleRef M,
 LLVMBool LLVMTargetMachineEmitToMemoryBuffer(LLVMTargetMachineRef T,
   LLVMModuleRef M, LLVMCodeGenFileType codegen, char** ErrorMessage,
   LLVMMemoryBufferRef *OutMemBuf) {
-  std::string CodeString;
-  raw_string_ostream OStream(CodeString);
-  formatted_raw_ostream Out(OStream);
+  string_ostream Code;
+  formatted_raw_ostream Out(Code);
   bool Result = LLVMTargetMachineEmit(T, M, Out, codegen, ErrorMessage);
-  OStream.flush();
 
-  std::string &Data = OStream.str();
-  *OutMemBuf = LLVMCreateMemoryBufferWithMemoryRangeCopy(Data.c_str(),
-                                                     Data.length(), "");
+  StringRef Buffer = Code.str();
+  *OutMemBuf = LLVMCreateMemoryBufferWithMemoryRangeCopy(Buffer.data(),
+                                                         Buffer.size(), "");
   return Result;
 }
 
diff --git a/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index cfeb62e..5af938b 100644
--- a/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -316,11 +316,9 @@ namespace {
       }
       ReturnBlock = new GCOVBlock(i++, os);
 
-      std::string FunctionNameAndLine;
-      raw_string_ostream FNLOS(FunctionNameAndLine);
-      FNLOS << getFunctionName(SP) << SP.getLineNumber();
-      FNLOS.flush();
-      FuncChecksum = hash_value(FunctionNameAndLine);
+      string_ostream FnNameLine;
+      FnNameLine << getFunctionName(SP) << SP.getLineNumber();
+      FuncChecksum = hash_value(FnNameLine.str());
     }
 
     ~GCOVFunction() {
@@ -337,15 +335,14 @@ namespace {
     }
 
     std::string getEdgeDestinations() {
-      std::string EdgeDestinations;
-      raw_string_ostream EDOS(EdgeDestinations);
+      string_ostream EdgeDestinations;
       Function *F = Blocks.begin()->first->getParent();
       for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
         GCOVBlock &Block = *Blocks[I];
         for (int i = 0, e = Block.OutEdges.size(); i != e; ++i)
-          EDOS << Block.OutEdges[i]->Number;
+          EdgeDestinations << Block.OutEdges[i]->Number;
       }
-      return EdgeDestinations;
+      return EdgeDestinations.str();
     }
 
     uint32_t getFuncChecksum() {
diff --git a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
index dd4dd50..744fb24 100644
--- a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
+++ b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
@@ -835,8 +835,7 @@ static MDString *AppendMDNodeToSourcePtr(unsigned NodeId,
       // of line at the module level and to provide a very simple format
       // encoding the information herein. Both of these makes it simpler to
       // parse the annotations by a simple external program.
-      std::string Str;
-      raw_string_ostream os(Str);
+      string_ostream os;
       os << "(" << Inst->getParent()->getParent()->getName() << ",%"
          << Inst->getName() << ")";
 
@@ -849,8 +848,7 @@ static MDString *AppendMDNodeToSourcePtr(unsigned NodeId,
       Hash = cast<MDString>(Node->getOperand(0));
     }
   } else if (Argument *Arg = dyn_cast<Argument>(Ptr)) {
-    std::string str;
-    raw_string_ostream os(str);
+    string_ostream os;
     os << "(" << Arg->getParent()->getName() << ",%" << Arg->getName()
        << ")";
     Hash = MDString::get(Arg->getContext(), os.str());
@@ -860,8 +858,7 @@ static MDString *AppendMDNodeToSourcePtr(unsigned NodeId,
 }
 
 static std::string SequenceToString(Sequence A) {
-  std::string str;
-  raw_string_ostream os(str);
+  string_ostream os;
   os << A;
   return os.str();
 }
diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp
index f5f28cf..7b7433d 100644
--- a/tools/llvm-ar/llvm-ar.cpp
+++ b/tools/llvm-ar/llvm-ar.cpp
@@ -687,8 +687,7 @@ static void writeSymbolTable(
     std::vector<std::pair<unsigned, unsigned> > &MemberOffsetRefs) {
   unsigned StartOffset = 0;
   unsigned MemberNum = 0;
-  std::string NameBuf;
-  raw_string_ostream NameOS(NameBuf);
+  string_ostream NameOS;
   unsigned NumSyms = 0;
   std::vector<object::SymbolicFile *> DeleteIt;
   LLVMContext &Context = getGlobalContext();
diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp
index e12ff3d..59d23d8 100644
--- a/tools/llvm-objdump/llvm-objdump.cpp
+++ b/tools/llvm-objdump/llvm-objdump.cpp
@@ -222,8 +222,7 @@ static void emitDOTFile(const char *FileName, const MCFunction &f,
         Out << "<o>";
 
       // Escape special chars and print the instruction in mnemonic form.
-      std::string Str;
-      raw_string_ostream OS(Str);
+      string_ostream OS;
       IP->printInst(&(*i)->getInsts()->at(ii).Inst, OS, "");
       Out << DOT::EscapeString(OS.str());
     }
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp
index f6675bd..7138438 100644
--- a/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -95,8 +95,7 @@ raw_ostream &operator<<(raw_ostream &OS, const ARM::WinEH::ReturnType &RT) {
 
 static std::string formatSymbol(StringRef Name, uint64_t Address,
                                 uint64_t Offset = 0) {
-  std::string Buffer;
-  raw_string_ostream OS(Buffer);
+  string_ostream OS;
 
   if (!Name.empty())
     OS << Name << " ";
diff --git a/tools/llvm-readobj/Win64EHDumper.cpp b/tools/llvm-readobj/Win64EHDumper.cpp
index f058632..2d67843 100644
--- a/tools/llvm-readobj/Win64EHDumper.cpp
+++ b/tools/llvm-readobj/Win64EHDumper.cpp
@@ -115,8 +115,7 @@ static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
 static std::string formatSymbol(const Dumper::Context &Ctx,
                                 const coff_section *Section, uint64_t Offset,
                                 uint32_t Displacement) {
-  std::string Buffer;
-  raw_string_ostream OS(Buffer);
+  string_ostream OS;
 
   StringRef Name;
   SymbolRef Symbol;
@@ -131,6 +130,7 @@ static std::string formatSymbol(const Dumper::Context &Ctx,
     OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset);
   else
     OS << format(" (0x%" PRIX64 ")", Offset);
+
   return OS.str();
 }
 
diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp
index 817d207..aa79bdc 100644
--- a/unittests/ExecutionEngine/JIT/JITTest.cpp
+++ b/unittests/ExecutionEngine/JIT/JITTest.cpp
@@ -70,9 +70,9 @@ Function *makeReturnGlobal(std::string Name, GlobalVariable *G, Module *M) {
 }
 
 std::string DumpFunction(const Function *F) {
-  std::string Result;
-  raw_string_ostream(Result) << "" << *F;
-  return Result;
+  string_ostream Result;
+  Result << "" << *F;
+  return Result.str();
 }
 
 class RecordingJITMemoryManager : public JITMemoryManager {
@@ -170,10 +170,9 @@ bool LoadAssemblyInto(Module *M, const char *assembly) {
   SMDiagnostic Error;
   bool success =
     nullptr != ParseAssemblyString(assembly, M, Error, M->getContext());
-  std::string errMsg;
-  raw_string_ostream os(errMsg);
-  Error.print("", os);
-  EXPECT_TRUE(success) << os.str();
+  string_ostream errMsg;
+  Error.print("", errMsg);
+  EXPECT_TRUE(success) << errMsg.str();
   return success;
 }
 
diff --git a/utils/TableGen/CodeEmitterGen.cpp b/utils/TableGen/CodeEmitterGen.cpp
index 4d0c0ca..5e27d45 100644
--- a/utils/TableGen/CodeEmitterGen.cpp
+++ b/utils/TableGen/CodeEmitterGen.cpp
@@ -319,8 +319,7 @@ void CodeEmitterGen::run(raw_ostream &o) {
 
   // Default case: unhandled opcode
   o << "  default:\n"
-    << "    std::string msg;\n"
-    << "    raw_string_ostream Msg(msg);\n"
+    << "    string_ostream Msg;\n"
     << "    Msg << \"Not supported instr: \" << MI;\n"
     << "    report_fatal_error(Msg.str());\n"
     << "  }\n"
diff --git a/utils/TableGen/SetTheory.cpp b/utils/TableGen/SetTheory.cpp
index 5ead7ed..92c8409 100644
--- a/utils/TableGen/SetTheory.cpp
+++ b/utils/TableGen/SetTheory.cpp
@@ -209,13 +209,12 @@ struct SequenceOp : public SetTheory::Operator {
         break;
       else if (Step < 0 && From < To)
         break;
-      std::string Name;
-      raw_string_ostream OS(Name);
-      OS << format(Format.c_str(), unsigned(From));
-      Record *Rec = Records.getDef(OS.str());
+      string_ostream Name;
+      Name << format(Format.c_str(), unsigned(From));
+      Record *Rec = Records.getDef(Name.str());
       if (!Rec)
-        PrintFatalError(Loc, "No def named '" + Name + "': " +
-          Expr->getAsString());
+        PrintFatalError(Loc, "No def named '" + Name.str() + "': " +
+                                 Expr->getAsString());
       // Try to reevaluate Rec in case it is a set.
       if (const RecVec *Result = ST.expand(Rec))
         Elts.insert(Result->begin(), Result->end());
diff --git a/utils/yaml-bench/YAMLBench.cpp b/utils/yaml-bench/YAMLBench.cpp
index 39b8f72..15bee20 100644
--- a/utils/yaml-bench/YAMLBench.cpp
+++ b/utils/yaml-bench/YAMLBench.cpp
@@ -166,23 +166,21 @@ static void benchmark( llvm::TimerGroup &Group
 }
 
 static std::string createJSONText(size_t MemoryMB, unsigned ValueSize) {
-  std::string JSONText;
-  llvm::raw_string_ostream Stream(JSONText);
+  llvm::string_ostream Stream;
   Stream << "[\n";
   size_t MemoryBytes = MemoryMB * 1024 * 1024;
-  while (JSONText.size() < MemoryBytes) {
+  while (Stream.tell() < MemoryBytes) {
     Stream << " {\n"
            << "  \"key1\": \"" << std::string(ValueSize, '*') << "\",\n"
            << "  \"key2\": \"" << std::string(ValueSize, '*') << "\",\n"
            << "  \"key3\": \"" << std::string(ValueSize, '*') << "\"\n"
            << " }";
-    Stream.flush();
-    if (JSONText.size() < MemoryBytes) Stream << ",";
+    if (Stream.tell() < MemoryBytes)
+      Stream << ",";
     Stream << "\n";
   }
   Stream << "]\n";
-  Stream.flush();
-  return JSONText;
+  return Stream.str();
 }
 
 int main(int argc, char **argv) {


More information about the llvm-commits mailing list