[Remarks] Restructure bitstream remarks to be fully standalone (#156715)

Currently there are two serialization modes for bitstream Remarks:
standalone and separate. The separate mode splits remark metadata (e.g.
the string table) from actual remark data. The metadata is written into
the object file by the AsmPrinter, while the remark data is stored in a
separate remarks file. This means we can't use bitstream remarks with
tools like opt that don't generate an object file. Also, it is confusing
to post-process bitstream remarks files, because only the standalone
files can be read by llvm-remarkutil. We always need to use dsymutil
to convert the separate files to standalone files, which only works for
MachO. It is not possible for clang/opt to directly emit bitstream
remark files in standalone mode, because the string table can only be
serialized after all remarks were emitted.

Therefore, this change completely removes the separate serialization
mode. Instead, the remark string table is now always written to the end
of the remarks file. This requires us to tell the serializer when to
finalize remark serialization. This automatically happens when the
serializer goes out of scope. However, often the remark file goes out of
scope before the serializer is destroyed. To diagnose this, I have added
an assert to alert users that they need to explicitly call
finalizeLLVMOptimizationRemarks.

This change paves the way for further improvements to the remark
infrastructure, including more tooling (e.g. #159784), size optimizations
for bitstream remarks, and more.

Pull Request: https://github.com/llvm/llvm-project/pull/156715
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 77bf0c8..106363fa 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -29,6 +29,7 @@
 #include "llvm/Frontend/Driver/CodeGenOptions.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/ModuleSummaryIndex.h"
@@ -1384,6 +1385,10 @@
     Conf.CGFileType = getCodeGenFileType(Action);
     break;
   }
+
+  // FIXME: Both ExecuteAction and thinBackend set up optimization remarks for
+  // the same context.
+  finalizeLLVMOptimizationRemarks(M->getContext());
   if (Error E =
           thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
                       ModuleToDefinedGVSummaries[M->getModuleIdentifier()],
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index dc54c97..8e32349 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -29,6 +29,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Serialization/ASTWriter.h"
 #include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
 #include "llvm/Demangle/Demangle.h"
@@ -259,19 +260,18 @@
   Ctx.setDefaultTargetCPU(TargetOpts.CPU);
   Ctx.setDefaultTargetFeatures(llvm::join(TargetOpts.Features, ","));
 
-  Expected<std::unique_ptr<llvm::ToolOutputFile>> OptRecordFileOrErr =
-    setupLLVMOptimizationRemarks(
-      Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses,
-      CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness,
-      CodeGenOpts.DiagnosticsHotnessThreshold);
+  Expected<LLVMRemarkFileHandle> OptRecordFileOrErr =
+      setupLLVMOptimizationRemarks(
+          Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses,
+          CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness,
+          CodeGenOpts.DiagnosticsHotnessThreshold);
 
   if (Error E = OptRecordFileOrErr.takeError()) {
     reportOptRecordError(std::move(E), Diags, CodeGenOpts);
     return;
   }
 
-  std::unique_ptr<llvm::ToolOutputFile> OptRecordFile =
-    std::move(*OptRecordFileOrErr);
+  LLVMRemarkFileHandle OptRecordFile = std::move(*OptRecordFileOrErr);
 
   if (OptRecordFile && CodeGenOpts.getProfileUse() !=
                            llvm::driver::ProfileInstrKind::ProfileNone)
@@ -1173,7 +1173,7 @@
   Ctx.setDefaultTargetCPU(TargetOpts.CPU);
   Ctx.setDefaultTargetFeatures(llvm::join(TargetOpts.Features, ","));
 
-  Expected<std::unique_ptr<llvm::ToolOutputFile>> OptRecordFileOrErr =
+  Expected<LLVMRemarkFileHandle> OptRecordFileOrErr =
       setupLLVMOptimizationRemarks(
           Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses,
           CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness,
@@ -1183,8 +1183,7 @@
     reportOptRecordError(std::move(E), Diagnostics, CodeGenOpts);
     return;
   }
-  std::unique_ptr<llvm::ToolOutputFile> OptRecordFile =
-      std::move(*OptRecordFileOrErr);
+  LLVMRemarkFileHandle OptRecordFile = std::move(*OptRecordFileOrErr);
 
   emitBackendOutput(CI, CI.getCodeGenOpts(),
                     CI.getTarget().getDataLayoutString(), TheModule.get(), BA,
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index c3c53d5..db6b989 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -1352,7 +1352,7 @@
       std::make_unique<BackendRemarkConsumer>(remarkConsumer));
 
   // write optimization-record
-  llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
+  llvm::Expected<llvm::LLVMRemarkFileHandle> optRecordFileOrErr =
       setupLLVMOptimizationRemarks(
           llvmModule->getContext(), codeGenOpts.OptRecordFile,
           codeGenOpts.OptRecordPasses, codeGenOpts.OptRecordFormat,
@@ -1364,8 +1364,7 @@
     return;
   }
 
-  std::unique_ptr<llvm::ToolOutputFile> optRecordFile =
-      std::move(*optRecordFileOrErr);
+  llvm::LLVMRemarkFileHandle optRecordFile = std::move(*optRecordFileOrErr);
 
   if (optRecordFile) {
     optRecordFile->keep();
diff --git a/llvm/docs/Remarks.rst b/llvm/docs/Remarks.rst
index c89940f..67ed94d 100644
--- a/llvm/docs/Remarks.rst
+++ b/llvm/docs/Remarks.rst
@@ -152,26 +152,6 @@
     .. option:: -opt-remarks-format=<format>
     .. option:: -opt-remarks-with-hotness
 
-Serialization modes
-===================
-
-There are two modes available for serializing remarks:
-
-``Separate``
-
-    In this mode, the remarks and the metadata are serialized separately. The
-    client is responsible for parsing the metadata first, then use the metadata
-    to correctly parse the remarks.
-
-``Standalone``
-
-    In this mode, the remarks and the metadata are serialized to the same
-    stream. The metadata will always come before the remarks.
-
-    The compiler does not support emitting standalone remarks. This mode is
-    more suited for post-processing tools like linkers, that can merge the
-    remarks for one whole project.
-
 .. _yamlremarks:
 
 YAML remarks
@@ -374,27 +354,11 @@
 The remark container
 --------------------
 
-Bitstream remarks are designed to be used in two different modes:
+The bitstream remark container supports multiple types:
 
-``The separate mode``
+.. _bitstreamremarksfileexternal:
 
-    The separate mode is the mode that is typically used during compilation. It
-    provides a way to serialize the remark entries to a stream while some
-    metadata is kept in memory to be emitted in the product of the compilation
-    (typically, an object file).
-
-``The standalone mode``
-
-    The standalone mode is typically stored and used after the distribution of
-    a program. It contains all the information that allows the parsing of all
-    the remarks without having any external dependencies.
-
-In order to support multiple modes, the format introduces the concept of a
-bitstream remark container type.
-
-.. _bitstreamremarksseparateremarksmeta:
-
-``SeparateRemarksMeta: the metadata emitted separately``
+``RemarksFileExternal: a link to an external remarks file``
 
     This container type expects only a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
 
@@ -406,84 +370,33 @@
     clients to retrieve remarks and their associated metadata directly from
     intermediate products.
 
-``SeparateRemarksFile: the remark entries emitted separately``
+    The container versions of the external separate container should match in order to
+    have a well-formed file.
 
-    This container type expects only a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
+.. _bitstreamremarksfile:
+
+``RemarksFile: a standalone remarks file``
+
+    This container type expects a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
 
     * :ref:`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>`
     * :ref:`RECORD_META_REMARK_VERSION <bitstreamremarksrecordmetaremarkversion>`
 
-    This container type expects 0 or more :ref:`REMARK_BLOCK <bitstreamremarksremarkblock>`.
+    Then, this container type expects 1 or more :ref:`REMARK_BLOCK <bitstreamremarksremarkblock>`.
+    If no remarks are emitted, the meta blocks are also not emitted, so the file is empty.
 
-    Typically, this is emitted in a side-file alongside an object file, and is
-    made to be able to stream to without increasing the memory consumption of
-    the compiler. This is referenced by the :ref:`RECORD_META_EXTERNAL_FILE
-    <bitstreamremarksrecordmetaexternalfile>` entry in the
-    :ref:`SeparateRemarksMeta <bitstreamremarksseparateremarksmeta>` container.
-
-When the parser tries to parse a container that contains the metadata for the
-separate remarks, it should parse the version and type, then keep the string
-table in memory while opening the external file, validating its metadata and
-parsing the remark entries.
-
-The container versions from the separate container should match in order to
-have a well-formed file.
-
-``Standalone: the metadata and the remark entries emitted together``
-
-    This container type expects only a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
-
-    * :ref:`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>`
-    * :ref:`RECORD_META_REMARK_VERSION <bitstreamremarksrecordmetaremarkversion>`
+    After the remark blocks, another :ref:`META_BLOCK <bitstreamremarksmetablock>` is emitted, containing:
     * :ref:`RECORD_META_STRTAB <bitstreamremarksrecordmetastrtab>`
 
-    This container type expects 0 or more :ref:`REMARK_BLOCK <bitstreamremarksremarkblock>`.
+    When the parser reads this container type, it jumps to the end of the file
+    to read the string table before parsing the individual remarks.
 
-A complete output of :program:`llvm-bcanalyzer` on the different container types:
+    Standalone remarks files can be referenced by the
+    :ref:`RECORD_META_EXTERNAL_FILE <bitstreamremarksrecordmetaexternalfile>`
+    entry in the :ref:`RemarksFileExternal
+    <bitstreamremarksfileexternal>` container.
 
-``SeparateRemarksMeta``
-
-.. code-block:: none
-
-    <BLOCKINFO_BLOCK/>
-    <Meta BlockID=8 NumWords=13 BlockCodeSize=3>
-      <Container info codeid=1 abbrevid=4 op0=5 op1=0/>
-      <String table codeid=3 abbrevid=5/> blob data = 'pass\\x00key\\x00value\\x00'
-      <External File codeid=4 abbrevid=6/> blob data = '/path/to/file/name'
-    </Meta>
-
-``SeparateRemarksFile``
-
-.. code-block:: none
-
-    <BLOCKINFO_BLOCK/>
-    <Meta BlockID=8 NumWords=3 BlockCodeSize=3>
-      <Container info codeid=1 abbrevid=4 op0=0 op1=1/>
-      <Remark version codeid=2 abbrevid=5 op0=0/>
-    </Meta>
-    <Remark BlockID=9 NumWords=8 BlockCodeSize=4>
-      <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>
-      <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>
-      <Remark hotness codeid=7 abbrevid=6 op0=999999999/>
-      <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 op3=11 op4=66/>
-    </Remark>
-
-``Standalone``
-
-.. code-block:: none
-
-    <BLOCKINFO_BLOCK/>
-    <Meta BlockID=8 NumWords=15 BlockCodeSize=3>
-      <Container info codeid=1 abbrevid=4 op0=5 op1=2/>
-      <Remark version codeid=2 abbrevid=5 op0=30/>
-      <String table codeid=3 abbrevid=6/> blob data = 'pass\\x00remark\\x00function\\x00path\\x00key\\x00value\\x00argpath\\x00'
-    </Meta>
-    <Remark BlockID=9 NumWords=8 BlockCodeSize=4>
-      <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>
-      <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>
-      <Remark hotness codeid=7 abbrevid=6 op0=999999999/>
-      <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 op3=11 op4=66/>
-    </Remark>
+.. FIXME: Add complete output of :program:`llvm-bcanalyzer` on the different container types (once format changes are completed)
 
 opt-viewer
 ==========
diff --git a/llvm/include/llvm/IR/LLVMRemarkStreamer.h b/llvm/include/llvm/IR/LLVMRemarkStreamer.h
index 376acde..96ccceb 100644
--- a/llvm/include/llvm/IR/LLVMRemarkStreamer.h
+++ b/llvm/include/llvm/IR/LLVMRemarkStreamer.h
@@ -17,6 +17,7 @@
 #include "llvm/Remarks/Remark.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/ToolOutputFile.h"
 #include <memory>
 #include <optional>
 #include <string>
@@ -82,20 +83,81 @@
       LLVMRemarkSetupFormatError>::LLVMRemarkSetupErrorInfo;
 };
 
-/// Setup optimization remarks that output to a file.
-LLVM_ABI Expected<std::unique_ptr<ToolOutputFile>> setupLLVMOptimizationRemarks(
+/// RAII handle that manages the lifetime of the ToolOutputFile used to output
+/// remarks. On destruction (or when calling releaseFile()), this handle ensures
+/// that the optimization remarks are finalized and the RemarkStreamer is
+/// correctly deregistered from the LLVMContext.
+class LLVMRemarkFileHandle final {
+  struct Finalizer {
+    LLVMContext *Context;
+
+    Finalizer(LLVMContext *Ctx) : Context(Ctx) {}
+
+    Finalizer(const Finalizer &) = delete;
+    Finalizer &operator=(const Finalizer &) = delete;
+
+    Finalizer(Finalizer &&Other) : Context(Other.Context) {
+      Other.Context = nullptr;
+    }
+
+    Finalizer &operator=(Finalizer &&Other) {
+      std::swap(Context, Other.Context);
+      return *this;
+    }
+
+    ~Finalizer() { finalize(); }
+
+    LLVM_ABI void finalize();
+  };
+
+  std::unique_ptr<ToolOutputFile> OutputFile;
+  Finalizer Finalize;
+
+public:
+  LLVMRemarkFileHandle() : OutputFile(nullptr), Finalize(nullptr) {}
+
+  LLVMRemarkFileHandle(std::unique_ptr<ToolOutputFile> OutputFile,
+                       LLVMContext &Ctx)
+      : OutputFile(std::move(OutputFile)), Finalize(&Ctx) {}
+
+  ToolOutputFile *get() { return OutputFile.get(); }
+  explicit operator bool() { return bool(OutputFile); }
+
+  /// Finalize remark emission and release the underlying ToolOutputFile.
+  std::unique_ptr<ToolOutputFile> releaseFile() {
+    finalize();
+    return std::move(OutputFile);
+  }
+
+  void finalize() { Finalize.finalize(); }
+
+  ToolOutputFile &operator*() { return *OutputFile; }
+  ToolOutputFile *operator->() { return &*OutputFile; }
+};
+
+/// Set up optimization remarks that output to a file. The LLVMRemarkFileHandle
+/// manages the lifetime of the underlying ToolOutputFile to ensure \ref
+/// finalizeLLVMOptimizationRemarks() is called before the file is destroyed or
+/// released from the handle. The handle must be kept alive until all remarks
+/// were emitted through the remark streamer.
+LLVM_ABI Expected<LLVMRemarkFileHandle> setupLLVMOptimizationRemarks(
     LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
     StringRef RemarksFormat, bool RemarksWithHotness,
     std::optional<uint64_t> RemarksHotnessThreshold = 0);
 
-/// Setup optimization remarks that output directly to a raw_ostream.
-/// \p OS is managed by the caller and should be open for writing as long as \p
-/// Context is streaming remarks to it.
+/// Set up optimization remarks that output directly to a raw_ostream.
+/// \p OS is managed by the caller and must be open for writing until
+/// \ref finalizeLLVMOptimizationRemarks() is called.
 LLVM_ABI Error setupLLVMOptimizationRemarks(
     LLVMContext &Context, raw_ostream &OS, StringRef RemarksPasses,
     StringRef RemarksFormat, bool RemarksWithHotness,
     std::optional<uint64_t> RemarksHotnessThreshold = 0);
 
+/// Finalize optimization remarks and deregister the RemarkStreamer from the \p
+/// Context. This must be called before closing the (file) stream that was used
+/// to set up the remarks.
+LLVM_ABI void finalizeLLVMOptimizationRemarks(LLVMContext &Context);
+
 } // end namespace llvm
 
 #endif // LLVM_IR_LLVMREMARKSTREAMER_H
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 323c478..3a9a7f7 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -15,6 +15,7 @@
 #ifndef LLVM_LTO_LTO_H
 #define LLVM_LTO_LTO_H
 
+#include "llvm/IR/LLVMRemarkStreamer.h"
 #include "llvm/Support/Compiler.h"
 #include <memory>
 
@@ -91,7 +92,7 @@
                                           StringRef NewPrefix);
 
 /// Setup optimization remarks.
-LLVM_ABI Expected<std::unique_ptr<ToolOutputFile>> setupLLVMOptimizationRemarks(
+LLVM_ABI Expected<LLVMRemarkFileHandle> setupLLVMOptimizationRemarks(
     LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
     StringRef RemarksFormat, bool RemarksWithHotness,
     std::optional<uint64_t> RemarksHotnessThreshold = 0, int Count = -1);
@@ -579,7 +580,7 @@
   DenseSet<GlobalValue::GUID> DynamicExportSymbols;
 
   // Diagnostic optimization remarks file
-  std::unique_ptr<ToolOutputFile> DiagnosticOutputFile;
+  LLVMRemarkFileHandle DiagnosticOutputFile;
 };
 
 /// The resolution for a symbol. The linker must provide a SymbolResolution for
diff --git a/llvm/include/llvm/LTO/LTOBackend.h b/llvm/include/llvm/LTO/LTOBackend.h
index 86b488c..48ad5aa 100644
--- a/llvm/include/llvm/LTO/LTOBackend.h
+++ b/llvm/include/llvm/LTO/LTOBackend.h
@@ -65,8 +65,7 @@
             AddStreamFn IRAddStream = nullptr,
             const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>());
 
-LLVM_ABI Error
-finalizeOptimizationRemarks(std::unique_ptr<ToolOutputFile> DiagOutputFile);
+LLVM_ABI Error finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile);
 
 /// Returns the BitcodeModule that is ThinLTO.
 LLVM_ABI BitcodeModule *findThinLTOModule(MutableArrayRef<BitcodeModule> BMs);
diff --git a/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h b/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h
index 806d3c5..caff1983 100644
--- a/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h
+++ b/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h
@@ -244,7 +244,7 @@
   bool ShouldInternalize = EnableLTOInternalization;
   bool ShouldEmbedUselists = false;
   bool ShouldRestoreGlobalsLinkage = false;
-  std::unique_ptr<ToolOutputFile> DiagnosticOutputFile;
+  LLVMRemarkFileHandle DiagnosticOutputFile;
   std::unique_ptr<ToolOutputFile> StatsFile = nullptr;
   std::string SaveIRBeforeOptPath;
 
diff --git a/llvm/include/llvm/Remarks/BitstreamRemarkContainer.h b/llvm/include/llvm/Remarks/BitstreamRemarkContainer.h
index 48a148a..d4b70e5 100644
--- a/llvm/include/llvm/Remarks/BitstreamRemarkContainer.h
+++ b/llvm/include/llvm/Remarks/BitstreamRemarkContainer.h
@@ -23,35 +23,35 @@
 
 /// The current version of the remark container.
 /// Note: this is different from the version of the remark entry.
-constexpr uint64_t CurrentContainerVersion = 0;
+constexpr uint64_t CurrentContainerVersion = 1;
 /// The magic number used for identifying remark blocks.
 constexpr StringLiteral ContainerMagic("RMRK");
 
 /// Type of the remark container.
-/// The remark container has two modes:
-/// * separate: the metadata is separate from the remarks and points to the
-///             auxiliary file that contains the remarks.
-/// * standalone: the metadata and the remarks are emitted together.
 enum class BitstreamRemarkContainerType {
-  /// The metadata emitted separately.
-  /// This will contain the following:
-  /// * Container version and type
-  /// * String table
-  /// * External file
-  SeparateRemarksMeta,
-  /// The remarks emitted separately.
-  /// This will contain the following:
-  /// * Container version and type
-  /// * Remark version
-  SeparateRemarksFile,
-  /// Everything is emitted together.
-  /// This will contain the following:
-  /// * Container version and type
-  /// * Remark version
-  /// * String table
-  Standalone,
-  First = SeparateRemarksMeta,
-  Last = Standalone,
+  /// Emit a link to an external remarks file
+  /// (usually as a section of the object file, to enable discovery of all
+  /// remarks files from the final linked object file)
+  /// RemarksFileExternal:
+  ///   | Meta:
+  ///   | | Container info
+  ///   | | External file
+  RemarksFileExternal,
+  /// Emit metadata and remarks into a file
+  /// RemarksFile:
+  ///   | Meta:
+  ///   | | Container info
+  ///   | | Remark version
+  ///   | Remarks:
+  ///   | | Remark0
+  ///   | | Remark1
+  ///   | | Remark2
+  ///   | | ...
+  ///   | Late Meta:
+  ///   | | String table
+  RemarksFile,
+  First = RemarksFileExternal,
+  Last = RemarksFile
 };
 
 /// The possible blocks that will be encountered in a bitstream remark
diff --git a/llvm/include/llvm/Remarks/BitstreamRemarkSerializer.h b/llvm/include/llvm/Remarks/BitstreamRemarkSerializer.h
index 6236800..76e2d5b 100644
--- a/llvm/include/llvm/Remarks/BitstreamRemarkSerializer.h
+++ b/llvm/include/llvm/Remarks/BitstreamRemarkSerializer.h
@@ -27,31 +27,7 @@
 /// Serialize the remarks to LLVM bitstream.
 /// This class provides ways to emit remarks in the LLVM bitstream format and
 /// its associated metadata.
-///
-/// * The separate model:
-///   Separate meta:        | Container info
-///                         | String table
-///                         | External file
-///
-///   Separate remarks:     | Container info
-///                         | Remark version
-///                         | Remark0
-///                         | Remark1
-///                         | Remark2
-///                         | ...
-///
-/// * The standalone model: | Container info
-///                         | String table
-///                         | Remark version
-///                         | Remark0
-///                         | Remark1
-///                         | Remark2
-///                         | ...
-///
 struct BitstreamRemarkSerializerHelper {
-  /// Buffer used for encoding the bitstream before writing it to the final
-  /// stream.
-  SmallVector<char, 1024> Encoded;
   /// Buffer used to construct records and pass to the bitstream writer.
   SmallVector<uint64_t, 64> R;
   /// The Bitstream writer.
@@ -73,7 +49,8 @@
   uint64_t RecordRemarkArgWithDebugLocAbbrevID = 0;
   uint64_t RecordRemarkArgWithoutDebugLocAbbrevID = 0;
 
-  BitstreamRemarkSerializerHelper(BitstreamRemarkContainerType ContainerType);
+  BitstreamRemarkSerializerHelper(BitstreamRemarkContainerType ContainerType,
+                                  raw_ostream &OS);
 
   // Disable copy and move: Bitstream points to Encoded, which needs special
   // handling during copy/move, but moving the vectors is probably useless
@@ -104,20 +81,15 @@
   /// The block info for the remarks block.
   void setupRemarkBlockInfo();
 
-  /// Emit the metadata for the remarks.
-  void emitMetaBlock(uint64_t ContainerVersion,
-                     std::optional<uint64_t> RemarkVersion,
-                     std::optional<const StringTable *> StrTab = std::nullopt,
-                     std::optional<StringRef> Filename = std::nullopt);
+  /// Emit the main metadata at the beginning of the file
+  void emitMetaBlock(std::optional<StringRef> Filename = std::nullopt);
+
+  /// Emit the remaining metadata at the end of the file. Here we emit metadata
+  /// that is only known once all remarks were emitted.
+  void emitLateMetaBlock(const StringTable &StrTab);
 
   /// Emit a remark block. The string table is required.
-  void emitRemarkBlock(const Remark &Remark, StringTable &StrTab);
-  /// Finalize the writing to \p OS.
-  void flushToStream(raw_ostream &OS);
-  /// Finalize the writing to a buffer.
-  /// The contents of the buffer remain valid for the lifetime of the object.
-  /// Any call to any other function in this class will invalidate the buffer.
-  StringRef getBuffer();
+  void emitRemark(const Remark &Remark, StringTable &StrTab);
 };
 
 /// Implementation of the remark serializer using LLVM bitstream.
@@ -127,68 +99,57 @@
   /// 2) The metadata block that contains various information about the remarks
   ///    in the file.
   /// 3) A number of remark blocks.
+  /// 4) Another metadata block for metadata that is only finalized once all
+  ///    remarks were emitted (e.g. StrTab)
 
-  /// We need to set up 1) and 2) first, so that we can emit 3) after. This flag
-  /// is used to emit the first two blocks only once.
-  bool DidSetUp = false;
-  /// The helper to emit bitstream.
-  BitstreamRemarkSerializerHelper Helper;
+  /// The helper to emit bitstream. This is nullopt when the Serializer has not
+  /// been setup yet.
+  std::optional<BitstreamRemarkSerializerHelper> Helper;
 
   /// Construct a serializer that will create its own string table.
-  BitstreamRemarkSerializer(raw_ostream &OS, SerializerMode Mode);
+  BitstreamRemarkSerializer(raw_ostream &OS);
   /// Construct a serializer with a pre-filled string table.
-  BitstreamRemarkSerializer(raw_ostream &OS, SerializerMode Mode,
-                            StringTable StrTab);
+  BitstreamRemarkSerializer(raw_ostream &OS, StringTable StrTab);
+
+  ~BitstreamRemarkSerializer() override;
 
   /// Emit a remark to the stream. This also emits the metadata associated to
-  /// the remarks based on the SerializerMode specified at construction.
-  /// This writes the serialized output to the provided stream.
+  /// the remarks. This writes the serialized output to the provided stream.
   void emit(const Remark &Remark) override;
+
+  /// Finalize emission of remarks. This emits the late metadata block and
+  /// flushes internal buffers. It is safe to call this function multiple times,
+  /// and it is automatically executed on destruction of the Serializer.
+  void finalize() override;
+
   /// The metadata serializer associated to this remark serializer. Based on the
   /// container type of the current serializer, the container type of the
   /// metadata serializer will change.
-  std::unique_ptr<MetaSerializer> metaSerializer(
-      raw_ostream &OS,
-      std::optional<StringRef> ExternalFilename = std::nullopt) override;
+  std::unique_ptr<MetaSerializer>
+  metaSerializer(raw_ostream &OS, StringRef ExternalFilename) override;
 
   static bool classof(const RemarkSerializer *S) {
     return S->SerializerFormat == Format::Bitstream;
   }
+
+private:
+  void setup();
 };
 
 /// Serializer of metadata for bitstream remarks.
 struct BitstreamMetaSerializer : public MetaSerializer {
-  /// This class can be used with [1] a pre-constructed
-  /// BitstreamRemarkSerializerHelper, or with [2] one that is owned by the meta
-  /// serializer. In case of [1], we need to be able to store a reference to the
-  /// object, while in case of [2] we need to store the whole object.
-  std::optional<BitstreamRemarkSerializerHelper> TmpHelper;
-  /// The actual helper, that can point to \p TmpHelper or to an external helper
-  /// object.
-  BitstreamRemarkSerializerHelper *Helper = nullptr;
+  std::optional<BitstreamRemarkSerializerHelper> Helper;
 
-  std::optional<const StringTable *> StrTab;
-  std::optional<StringRef> ExternalFilename;
+  StringRef ExternalFilename;
 
   /// Create a new meta serializer based on \p ContainerType.
-  BitstreamMetaSerializer(
-      raw_ostream &OS, BitstreamRemarkContainerType ContainerType,
-      std::optional<const StringTable *> StrTab = std::nullopt,
-      std::optional<StringRef> ExternalFilename = std::nullopt)
-      : MetaSerializer(OS), TmpHelper(std::nullopt), Helper(nullptr),
-        StrTab(StrTab), ExternalFilename(ExternalFilename) {
-    TmpHelper.emplace(ContainerType);
-    Helper = &*TmpHelper;
+  BitstreamMetaSerializer(raw_ostream &OS,
+                          BitstreamRemarkContainerType ContainerType,
+                          StringRef ExternalFilename)
+      : MetaSerializer(OS), ExternalFilename(ExternalFilename) {
+    Helper.emplace(ContainerType, OS);
   }
 
-  /// Create a new meta serializer based on a previously built \p Helper.
-  BitstreamMetaSerializer(
-      raw_ostream &OS, BitstreamRemarkSerializerHelper &Helper,
-      std::optional<const StringTable *> StrTab = std::nullopt,
-      std::optional<StringRef> ExternalFilename = std::nullopt)
-      : MetaSerializer(OS), TmpHelper(std::nullopt), Helper(&Helper),
-        StrTab(StrTab), ExternalFilename(ExternalFilename) {}
-
   void emit() override;
 };
 
diff --git a/llvm/include/llvm/Remarks/RemarkSerializer.h b/llvm/include/llvm/Remarks/RemarkSerializer.h
index 05ef14a..1785152 100644
--- a/llvm/include/llvm/Remarks/RemarkSerializer.h
+++ b/llvm/include/llvm/Remarks/RemarkSerializer.h
@@ -26,16 +26,6 @@
 
 struct Remark;
 
-enum class SerializerMode {
-  Separate,  // A mode where the metadata is serialized separately from the
-             // remarks. Typically, this is used when the remarks need to be
-             // streamed to a side file and the metadata is embedded into the
-             // final result of the compilation.
-  Standalone // A mode where everything can be retrieved in the same
-             // file/buffer. Typically, this is used for storing remarks for
-             // later use.
-};
-
 struct MetaSerializer;
 
 /// This is the base class for a remark serializer.
@@ -45,24 +35,27 @@
   Format SerializerFormat;
   /// The open raw_ostream that the remark diagnostics are emitted to.
   raw_ostream &OS;
-  /// The serialization mode.
-  SerializerMode Mode;
   /// The string table containing all the unique strings used in the output.
   /// The table can be serialized to be consumed after the compilation.
   std::optional<StringTable> StrTab;
 
-  RemarkSerializer(Format SerializerFormat, raw_ostream &OS,
-                   SerializerMode Mode)
-      : SerializerFormat(SerializerFormat), OS(OS), Mode(Mode) {}
+  RemarkSerializer(Format SerializerFormat, raw_ostream &OS)
+      : SerializerFormat(SerializerFormat), OS(OS) {}
 
-  /// This is just an interface.
   virtual ~RemarkSerializer() = default;
+
+  /// Finalize remark emission (e.g. finish writing metadata, flush internal
+  /// buffers). It is safe to call this function multiple times, and it should
+  /// have the same behavior as destructing the RemarkSerializer.
+  /// After finalizing, the behavior of emit is unspecified.
+  virtual void finalize() {}
+
   /// Emit a remark to the stream.
   virtual void emit(const Remark &Remark) = 0;
+
   /// Return the corresponding metadata serializer.
   virtual std::unique_ptr<MetaSerializer>
-  metaSerializer(raw_ostream &OS,
-                 std::optional<StringRef> ExternalFilename = std::nullopt) = 0;
+  metaSerializer(raw_ostream &OS, StringRef ExternalFilename) = 0;
 };
 
 /// This is the base class for a remark metadata serializer.
@@ -79,13 +72,12 @@
 
 /// Create a remark serializer.
 LLVM_ABI Expected<std::unique_ptr<RemarkSerializer>>
-createRemarkSerializer(Format RemarksFormat, SerializerMode Mode,
-                       raw_ostream &OS);
+createRemarkSerializer(Format RemarksFormat, raw_ostream &OS);
 
 /// Create a remark serializer that uses a pre-filled string table.
 LLVM_ABI Expected<std::unique_ptr<RemarkSerializer>>
-createRemarkSerializer(Format RemarksFormat, SerializerMode Mode,
-                       raw_ostream &OS, remarks::StringTable StrTab);
+createRemarkSerializer(Format RemarksFormat, raw_ostream &OS,
+                       remarks::StringTable StrTab);
 
 } // end namespace remarks
 } // end namespace llvm
diff --git a/llvm/include/llvm/Remarks/RemarkStreamer.h b/llvm/include/llvm/Remarks/RemarkStreamer.h
index 5b1cc81..dd5bfcb 100644
--- a/llvm/include/llvm/Remarks/RemarkStreamer.h
+++ b/llvm/include/llvm/Remarks/RemarkStreamer.h
@@ -52,6 +52,7 @@
 public:
   RemarkStreamer(std::unique_ptr<remarks::RemarkSerializer> RemarkSerializer,
                  std::optional<StringRef> Filename = std::nullopt);
+  ~RemarkStreamer();
 
   /// Return the filename that the remark diagnostics are emitted to.
   std::optional<StringRef> getFilename() const {
@@ -61,6 +62,14 @@
   raw_ostream &getStream() { return RemarkSerializer->OS; }
   /// Return the serializer used for this stream.
   remarks::RemarkSerializer &getSerializer() { return *RemarkSerializer; }
+
+  /// Release the underlying RemarkSerializer. Destructing the RemarkStreamer
+  /// will assert that the RemarkStreamer has been released, to ensure that the
+  /// remarks were properly finalized.
+  std::unique_ptr<remarks::RemarkSerializer> releaseSerializer() {
+    return std::move(RemarkSerializer);
+  }
+
   /// Set a pass filter based on a regex \p Filter.
   /// Returns an error if the regex is invalid.
   Error setFilter(StringRef Filter);
diff --git a/llvm/include/llvm/Remarks/YAMLRemarkSerializer.h b/llvm/include/llvm/Remarks/YAMLRemarkSerializer.h
index d80464c..69b8f9f 100644
--- a/llvm/include/llvm/Remarks/YAMLRemarkSerializer.h
+++ b/llvm/include/llvm/Remarks/YAMLRemarkSerializer.h
@@ -36,28 +36,22 @@
   /// The YAML streamer.
   yaml::Output YAMLOutput;
 
-  YAMLRemarkSerializer(raw_ostream &OS, SerializerMode Mode,
-                       std::optional<StringTable> StrTab = std::nullopt);
+  YAMLRemarkSerializer(raw_ostream &OS);
+  YAMLRemarkSerializer(raw_ostream &OS, StringTable StrTabIn);
 
   void emit(const Remark &Remark) override;
-  std::unique_ptr<MetaSerializer> metaSerializer(
-      raw_ostream &OS,
-      std::optional<StringRef> ExternalFilename = std::nullopt) override;
+  std::unique_ptr<MetaSerializer>
+  metaSerializer(raw_ostream &OS, StringRef ExternalFilename) override;
 
   static bool classof(const RemarkSerializer *S) {
     return S->SerializerFormat == Format::YAML;
   }
-
-protected:
-  YAMLRemarkSerializer(Format SerializerFormat, raw_ostream &OS,
-                       SerializerMode Mode,
-                       std::optional<StringTable> StrTab = std::nullopt);
 };
 
 struct LLVM_ABI YAMLMetaSerializer : public MetaSerializer {
-  std::optional<StringRef> ExternalFilename;
+  StringRef ExternalFilename;
 
-  YAMLMetaSerializer(raw_ostream &OS, std::optional<StringRef> ExternalFilename)
+  YAMLMetaSerializer(raw_ostream &OS, StringRef ExternalFilename)
       : MetaSerializer(OS), ExternalFilename(ExternalFilename) {}
 
   void emit() override;
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index cd14a4f..57fdc8b 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -78,6 +78,7 @@
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Instruction.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
@@ -2508,6 +2509,8 @@
 void AsmPrinter::emitRemarksSection(remarks::RemarkStreamer &RS) {
   if (!RS.needsSection())
     return;
+  if (!RS.getFilename())
+    return;
 
   MCSection *RemarksSection =
       OutContext.getObjectFileInfo()->getRemarksSection();
@@ -2518,20 +2521,16 @@
     return;
   }
 
-  remarks::RemarkSerializer &RemarkSerializer = RS.getSerializer();
-
-  std::optional<SmallString<128>> Filename;
-  if (std::optional<StringRef> FilenameRef = RS.getFilename()) {
-    Filename = *FilenameRef;
-    sys::fs::make_absolute(*Filename);
-    assert(!Filename->empty() && "The filename can't be empty.");
-  }
+  SmallString<128> Filename = *RS.getFilename();
+  sys::fs::make_absolute(Filename);
+  assert(!Filename.empty() && "The filename can't be empty.");
 
   std::string Buf;
   raw_string_ostream OS(Buf);
+
+  remarks::RemarkSerializer &RemarkSerializer = RS.getSerializer();
   std::unique_ptr<remarks::MetaSerializer> MetaSerializer =
-      Filename ? RemarkSerializer.metaSerializer(OS, Filename->str())
-               : RemarkSerializer.metaSerializer(OS);
+      RemarkSerializer.metaSerializer(OS, Filename);
   MetaSerializer->emit();
 
   // Switch to the remarks section.
diff --git a/llvm/lib/IR/LLVMRemarkStreamer.cpp b/llvm/lib/IR/LLVMRemarkStreamer.cpp
index 71f8d4a..9e1e459 100644
--- a/llvm/lib/IR/LLVMRemarkStreamer.cpp
+++ b/llvm/lib/IR/LLVMRemarkStreamer.cpp
@@ -92,7 +92,7 @@
 char LLVMRemarkSetupPatternError::ID = 0;
 char LLVMRemarkSetupFormatError::ID = 0;
 
-Expected<std::unique_ptr<ToolOutputFile>> llvm::setupLLVMOptimizationRemarks(
+Expected<LLVMRemarkFileHandle> llvm::setupLLVMOptimizationRemarks(
     LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
     StringRef RemarksFormat, bool RemarksWithHotness,
     std::optional<uint64_t> RemarksHotnessThreshold) {
@@ -102,7 +102,7 @@
   Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold);
 
   if (RemarksFilename.empty())
-    return nullptr;
+    return LLVMRemarkFileHandle();
 
   Expected<remarks::Format> Format = remarks::parseFormat(RemarksFormat);
   if (Error E = Format.takeError())
@@ -119,24 +119,35 @@
     return make_error<LLVMRemarkSetupFileError>(errorCodeToError(EC));
 
   Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer =
-      remarks::createRemarkSerializer(
-          *Format, remarks::SerializerMode::Separate, RemarksFile->os());
+      remarks::createRemarkSerializer(*Format, RemarksFile->os());
   if (Error E = RemarkSerializer.takeError())
     return make_error<LLVMRemarkSetupFormatError>(std::move(E));
 
-  // Create the main remark streamer.
-  Context.setMainRemarkStreamer(std::make_unique<remarks::RemarkStreamer>(
-      std::move(*RemarkSerializer), RemarksFilename));
+  auto RS = std::make_unique<remarks::RemarkStreamer>(
+      std::move(*RemarkSerializer), RemarksFilename);
+
+  if (!RemarksPasses.empty())
+    if (Error E = RS->setFilter(RemarksPasses)) {
+      RS->releaseSerializer();
+      return make_error<LLVMRemarkSetupPatternError>(std::move(E));
+    }
+
+  // Install the main remark streamer. Only install this after setting the
+  // filter, because this might fail.
+  Context.setMainRemarkStreamer(std::move(RS));
 
   // Create LLVM's optimization remarks streamer.
   Context.setLLVMRemarkStreamer(
       std::make_unique<LLVMRemarkStreamer>(*Context.getMainRemarkStreamer()));
 
-  if (!RemarksPasses.empty())
-    if (Error E = Context.getMainRemarkStreamer()->setFilter(RemarksPasses))
-      return make_error<LLVMRemarkSetupPatternError>(std::move(E));
+  return LLVMRemarkFileHandle{std::move(RemarksFile), Context};
+}
 
-  return std::move(RemarksFile);
+void LLVMRemarkFileHandle::Finalizer::finalize() {
+  if (!Context)
+    return;
+  finalizeLLVMOptimizationRemarks(*Context);
+  Context = nullptr;
 }
 
 Error llvm::setupLLVMOptimizationRemarks(
@@ -153,22 +164,34 @@
     return make_error<LLVMRemarkSetupFormatError>(std::move(E));
 
   Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer =
-      remarks::createRemarkSerializer(*Format,
-                                      remarks::SerializerMode::Separate, OS);
+      remarks::createRemarkSerializer(*Format, OS);
   if (Error E = RemarkSerializer.takeError())
     return make_error<LLVMRemarkSetupFormatError>(std::move(E));
 
-  // Create the main remark streamer.
-  Context.setMainRemarkStreamer(
-      std::make_unique<remarks::RemarkStreamer>(std::move(*RemarkSerializer)));
+  auto RS =
+      std::make_unique<remarks::RemarkStreamer>(std::move(*RemarkSerializer));
+
+  if (!RemarksPasses.empty())
+    if (Error E = RS->setFilter(RemarksPasses)) {
+      RS->releaseSerializer();
+      return make_error<LLVMRemarkSetupPatternError>(std::move(E));
+    }
+
+  // Install the main remark streamer. Only install this after setting the
+  // filter, because this might fail.
+  Context.setMainRemarkStreamer(std::move(RS));
 
   // Create LLVM's optimization remarks streamer.
   Context.setLLVMRemarkStreamer(
       std::make_unique<LLVMRemarkStreamer>(*Context.getMainRemarkStreamer()));
 
-  if (!RemarksPasses.empty())
-    if (Error E = Context.getMainRemarkStreamer()->setFilter(RemarksPasses))
-      return make_error<LLVMRemarkSetupPatternError>(std::move(E));
-
   return Error::success();
 }
+
+void llvm::finalizeLLVMOptimizationRemarks(LLVMContext &Context) {
+  Context.setLLVMRemarkStreamer(nullptr);
+  if (auto *RS = Context.getMainRemarkStreamer()) {
+    RS->releaseSerializer();
+    Context.setMainRemarkStreamer(nullptr);
+  }
+}
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index ce9ecc3..7b25262 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1290,11 +1290,11 @@
 
 Error LTO::runRegularLTO(AddStreamFn AddStream) {
   llvm::TimeTraceScope timeScope("Run regular LTO");
+  LLVMContext &CombinedCtx = RegularLTO.CombinedModule->getContext();
   // Setup optimization remarks.
   auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks(
-      RegularLTO.CombinedModule->getContext(), Conf.RemarksFilename,
-      Conf.RemarksPasses, Conf.RemarksFormat, Conf.RemarksWithHotness,
-      Conf.RemarksHotnessThreshold);
+      CombinedCtx, Conf.RemarksFilename, Conf.RemarksPasses, Conf.RemarksFormat,
+      Conf.RemarksWithHotness, Conf.RemarksHotnessThreshold);
   LLVM_DEBUG(dbgs() << "Running regular LTO\n");
   if (!DiagFileOrErr)
     return DiagFileOrErr.takeError();
@@ -2177,7 +2177,7 @@
   return RunBackends(SecondRoundLTO.get());
 }
 
-Expected<std::unique_ptr<ToolOutputFile>> lto::setupLLVMOptimizationRemarks(
+Expected<LLVMRemarkFileHandle> lto::setupLLVMOptimizationRemarks(
     LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
     StringRef RemarksFormat, bool RemarksWithHotness,
     std::optional<uint64_t> RemarksHotnessThreshold, int Count) {
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index ce42fc5..c126e8e 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -540,12 +540,12 @@
   return T;
 }
 
-Error lto::finalizeOptimizationRemarks(
-    std::unique_ptr<ToolOutputFile> DiagOutputFile) {
+Error lto::finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile) {
   // Make sure we flush the diagnostic remarks file in case the linker doesn't
   // call the global destructors before exiting.
   if (!DiagOutputFile)
     return Error::success();
+  DiagOutputFile.finalize();
   DiagOutputFile->keep();
   DiagOutputFile->os().flush();
   return Error::success();
@@ -640,7 +640,7 @@
 
   auto OptimizeAndCodegen =
       [&](Module &Mod, TargetMachine *TM,
-          std::unique_ptr<ToolOutputFile> DiagnosticOutputFile) {
+          LLVMRemarkFileHandle DiagnosticOutputFile) {
         // Perform optimization and code generation for ThinLTO.
         if (!opt(Conf, TM, Task, Mod, /*IsThinLTO=*/true,
                  /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex,
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index d8a96f7..8aa404d 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -545,6 +545,7 @@
   if (DiagnosticOutputFile) {
     DiagnosticOutputFile->keep();
     // FIXME: LTOCodeGenerator dtor is not invoked on Darwin
+    DiagnosticOutputFile.finalize();
     DiagnosticOutputFile->os().flush();
   }
 }
diff --git a/llvm/lib/Remarks/BitstreamRemarkParser.cpp b/llvm/lib/Remarks/BitstreamRemarkParser.cpp
index d40b40d..33eedd6 100644
--- a/llvm/lib/Remarks/BitstreamRemarkParser.cpp
+++ b/llvm/lib/Remarks/BitstreamRemarkParser.cpp
@@ -197,14 +197,9 @@
   Loc.reset();
   Args.clear();
 
-  if (Error E = expectBlock())
-    return E;
   return parseBlock();
 }
 
-BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer)
-    : Stream(Buffer) {}
-
 Error BitstreamParserHelper::expectMagic() {
   std::array<char, 4> Result;
   for (unsigned I = 0; I < 4; ++I)
@@ -244,14 +239,57 @@
   return Error::success();
 }
 
-Error BitstreamParserHelper::advanceToMetaBlock() {
+Error BitstreamParserHelper::parseMeta() {
   if (Error E = expectMagic())
     return E;
   if (Error E = parseBlockInfoBlock())
     return E;
+
+  // Parse early meta block.
+  if (Error E = MetaHelper.expectBlock())
+    return E;
+  if (Error E = MetaHelper.parseBlock())
+    return E;
+
+  // Skip all Remarks blocks.
+  while (!Stream.AtEndOfStream()) {
+    auto MaybeBlockID = expectSubBlock(Stream);
+    if (!MaybeBlockID)
+      return MaybeBlockID.takeError();
+    if (*MaybeBlockID == META_BLOCK_ID)
+      break;
+    if (*MaybeBlockID != REMARK_BLOCK_ID)
+      return error("Unexpected block between meta blocks.");
+    // Remember first remark block.
+    if (!RemarkStartBitPos)
+      RemarkStartBitPos = Stream.GetCurrentBitNo();
+    if (Error E = Stream.SkipBlock())
+      return E;
+  }
+
+  // Late meta block is optional if there are no remarks.
+  if (Stream.AtEndOfStream())
+    return Error::success();
+
+  // Parse late meta block.
+  if (Error E = MetaHelper.parseBlock())
+    return E;
   return Error::success();
 }
 
+Error BitstreamParserHelper::parseRemark() {
+  if (RemarkStartBitPos) {
+    RemarkStartBitPos.reset();
+  } else {
+    auto MaybeBlockID = expectSubBlock(Stream);
+    if (!MaybeBlockID)
+      return MaybeBlockID.takeError();
+    if (*MaybeBlockID != REMARK_BLOCK_ID)
+      return make_error<EndOfFileError>();
+  }
+  return RemarksHelper->parseNext();
+}
+
 Expected<std::unique_ptr<BitstreamRemarkParser>>
 remarks::createBitstreamParserFromMeta(
     StringRef Buf, std::optional<StringRef> ExternalFilePrependPath) {
@@ -263,45 +301,52 @@
   return std::move(Parser);
 }
 
-Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() {
-  if (ParserHelper.atEndOfStream())
-    return make_error<EndOfFileError>();
+BitstreamRemarkParser::BitstreamRemarkParser(StringRef Buf)
+    : RemarkParser(Format::Bitstream), ParserHelper(Buf) {}
 
-  if (!ReadyToParseRemarks) {
+Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() {
+  if (!IsMetaReady) {
+    // Container is completely empty.
+    if (ParserHelper->Stream.AtEndOfStream())
+      return make_error<EndOfFileError>();
+
     if (Error E = parseMeta())
       return std::move(E);
-    ReadyToParseRemarks = true;
+    IsMetaReady = true;
+
+    // Container has meta, but no remarks blocks.
+    if (!ParserHelper->RemarkStartBitPos)
+      return error(
+          "Container is non-empty, but does not contain any remarks blocks.");
+
+    if (Error E =
+            ParserHelper->Stream.JumpToBit(*ParserHelper->RemarkStartBitPos))
+      return std::move(E);
+    ParserHelper->RemarksHelper.emplace(ParserHelper->Stream);
   }
 
-  return parseRemark();
+  if (Error E = ParserHelper->parseRemark())
+    return std::move(E);
+  return processRemark();
 }
 
 Error BitstreamRemarkParser::parseMeta() {
-  if (Error E = ParserHelper.advanceToMetaBlock())
+  if (Error E = ParserHelper->parseMeta())
     return E;
-
-  BitstreamMetaParserHelper MetaHelper(ParserHelper.Stream);
-  if (Error E = MetaHelper.expectBlock())
-    return E;
-  if (Error E = MetaHelper.parseBlock())
-    return E;
-
-  if (Error E = processCommonMeta(MetaHelper))
+  if (Error E = processCommonMeta())
     return E;
 
   switch (ContainerType) {
-  case BitstreamRemarkContainerType::Standalone:
-    return processStandaloneMeta(MetaHelper);
-  case BitstreamRemarkContainerType::SeparateRemarksFile:
-    return processSeparateRemarksFileMeta(MetaHelper);
-  case BitstreamRemarkContainerType::SeparateRemarksMeta:
-    return processSeparateRemarksMetaMeta(MetaHelper);
+  case BitstreamRemarkContainerType::RemarksFileExternal:
+    return processExternalFilePath();
+  case BitstreamRemarkContainerType::RemarksFile:
+    return processFileContainerMeta();
   }
   llvm_unreachable("Unknown BitstreamRemarkContainerType enum");
 }
 
-Error BitstreamRemarkParser::processCommonMeta(
-    BitstreamMetaParserHelper &Helper) {
+Error BitstreamRemarkParser::processCommonMeta() {
+  auto &Helper = ParserHelper->MetaHelper;
   if (!Helper.Container)
     return Helper.error("Missing container info.");
   auto &Container = *Helper.Container;
@@ -313,7 +358,16 @@
   return Error::success();
 }
 
-Error BitstreamRemarkParser::processStrTab(BitstreamMetaParserHelper &Helper) {
+Error BitstreamRemarkParser::processFileContainerMeta() {
+  if (Error E = processRemarkVersion())
+    return E;
+  if (Error E = processStrTab())
+    return E;
+  return Error::success();
+}
+
+Error BitstreamRemarkParser::processStrTab() {
+  auto &Helper = ParserHelper->MetaHelper;
   if (!Helper.StrTabBuf)
     return Helper.error("Missing string table.");
   // Parse and assign the string table.
@@ -321,26 +375,25 @@
   return Error::success();
 }
 
-Error BitstreamRemarkParser::processRemarkVersion(
-    BitstreamMetaParserHelper &Helper) {
+Error BitstreamRemarkParser::processRemarkVersion() {
+  auto &Helper = ParserHelper->MetaHelper;
   if (!Helper.RemarkVersion)
     return Helper.error("Missing remark version.");
   RemarkVersion = *Helper.RemarkVersion;
   return Error::success();
 }
 
-Error BitstreamRemarkParser::processExternalFilePath(
-    BitstreamMetaParserHelper &Helper) {
+Error BitstreamRemarkParser::processExternalFilePath() {
+  auto &Helper = ParserHelper->MetaHelper;
   if (!Helper.ExternalFilePath)
     return Helper.error("Missing external file path.");
-  StringRef ExternalFilePath = *Helper.ExternalFilePath;
 
   SmallString<80> FullPath(ExternalFilePrependPath);
-  sys::path::append(FullPath, ExternalFilePath);
+  sys::path::append(FullPath, *Helper.ExternalFilePath);
 
   // External file: open the external file, parse it, check if its metadata
-  // matches the one from the separate metadata, then replace the current parser
-  // with the one parsing the remarks.
+  // matches the one from the separate metadata, then replace the current
+  // parser with the one parsing the remarks.
   ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
       MemoryBuffer::getFile(FullPath);
   if (std::error_code EC = BufferOrErr.getError())
@@ -353,58 +406,19 @@
     return make_error<EndOfFileError>();
 
   // Create a separate parser used for parsing the separate file.
-  ParserHelper = BitstreamParserHelper(TmpRemarkBuffer->getBuffer());
-  // Advance and check until we can parse the meta block.
-  if (Error E = ParserHelper.advanceToMetaBlock())
-    return E;
-  // Parse the meta from the separate file.
-  // Note: here we overwrite the BlockInfo with the one from the file. This will
-  // be used to parse the rest of the file.
-  BitstreamMetaParserHelper SeparateMetaHelper(ParserHelper.Stream);
-  if (Error E = SeparateMetaHelper.expectBlock())
-    return E;
-  if (Error E = SeparateMetaHelper.parseBlock())
+  ParserHelper.emplace(TmpRemarkBuffer->getBuffer());
+  if (Error E = parseMeta())
     return E;
 
-  if (Error E = processCommonMeta(SeparateMetaHelper))
-    return E;
+  if (ContainerType != BitstreamRemarkContainerType::RemarksFile)
+    return ParserHelper->MetaHelper.error(
+        "Wrong container type in external file.");
 
-  if (ContainerType != BitstreamRemarkContainerType::SeparateRemarksFile)
-    return SeparateMetaHelper.error("Wrong container type in external file.");
-
-  // Process the meta from the separate file.
-  return processSeparateRemarksFileMeta(SeparateMetaHelper);
+  return Error::success();
 }
 
-Error BitstreamRemarkParser::processStandaloneMeta(
-    BitstreamMetaParserHelper &Helper) {
-  if (Error E = processStrTab(Helper))
-    return E;
-  return processRemarkVersion(Helper);
-}
-
-Error BitstreamRemarkParser::processSeparateRemarksFileMeta(
-    BitstreamMetaParserHelper &Helper) {
-  return processRemarkVersion(Helper);
-}
-
-Error BitstreamRemarkParser::processSeparateRemarksMetaMeta(
-    BitstreamMetaParserHelper &Helper) {
-  if (Error E = processStrTab(Helper))
-    return E;
-  return processExternalFilePath(Helper);
-}
-
-Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::parseRemark() {
-  BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream);
-  if (Error E = RemarkHelper.parseNext())
-    return std::move(E);
-
-  return processRemark(RemarkHelper);
-}
-
-Expected<std::unique_ptr<Remark>>
-BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) {
+Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::processRemark() {
+  auto &Helper = *ParserHelper->RemarksHelper;
   std::unique_ptr<Remark> Result = std::make_unique<Remark>();
   Remark &R = *Result;
 
@@ -491,5 +505,3 @@
 
   return std::move(Result);
 }
-llvm::remarks::BitstreamRemarkParser::BitstreamRemarkParser(StringRef Buf)
-    : RemarkParser(Format::Bitstream), ParserHelper(Buf) {}
diff --git a/llvm/lib/Remarks/BitstreamRemarkParser.h b/llvm/lib/Remarks/BitstreamRemarkParser.h
index d756e32..4f66c47 100644
--- a/llvm/lib/Remarks/BitstreamRemarkParser.h
+++ b/llvm/lib/Remarks/BitstreamRemarkParser.h
@@ -187,35 +187,49 @@
   BitstreamCursor Stream;
   /// The block info block.
   BitstreamBlockInfo BlockInfo;
+
+  /// Helper to parse the metadata blocks in this bitstream.
+  BitstreamMetaParserHelper MetaHelper;
+  /// Helper to parse the remark blocks in this bitstream. Only needed
+  /// for ContainerType RemarksFile.
+  std::optional<BitstreamRemarkParserHelper> RemarksHelper;
+  /// The position of the first remark block we encounter after
+  /// the initial metadata block.
+  std::optional<uint64_t> RemarkStartBitPos;
+
   /// Start parsing at \p Buffer.
-  BitstreamParserHelper(StringRef Buffer);
+  BitstreamParserHelper(StringRef Buffer)
+      : Stream(Buffer), MetaHelper(Stream), RemarksHelper(Stream) {}
+
   /// Parse and validate the magic number.
   Error expectMagic();
-  /// Advance to the meta block
-  Error advanceToMetaBlock();
   /// Parse the block info block containing all the abbrevs.
   /// This needs to be called before calling any other parsing function.
   Error parseBlockInfoBlock();
-  /// Return true if the parser reached the end of the stream.
-  bool atEndOfStream() { return Stream.AtEndOfStream(); }
+
+  /// Parse all metadata blocks in the file. This populates the meta helper.
+  Error parseMeta();
+  /// Parse the next remark. This populates the remark helper data.
+  Error parseRemark();
 };
 
 /// Parses and holds the state of the latest parsed remark.
 struct BitstreamRemarkParser : public RemarkParser {
   /// The buffer to parse.
-  BitstreamParserHelper ParserHelper;
+  std::optional<BitstreamParserHelper> ParserHelper;
   /// The string table used for parsing strings.
   std::optional<ParsedStringTable> StrTab;
   /// Temporary remark buffer used when the remarks are stored separately.
   std::unique_ptr<MemoryBuffer> TmpRemarkBuffer;
+  /// Whether the metadata has already been parsed, so we can continue parsing
+  /// remarks.
+  bool IsMetaReady = false;
   /// The common metadata used to decide how to parse the buffer.
   /// This is filled when parsing the metadata block.
   uint64_t ContainerVersion = 0;
   uint64_t RemarkVersion = 0;
   BitstreamRemarkContainerType ContainerType =
-      BitstreamRemarkContainerType::Standalone;
-  /// Wether the parser is ready to parse remarks.
-  bool ReadyToParseRemarks = false;
+      BitstreamRemarkContainerType::RemarksFile;
 
   /// Create a parser that expects to find a string table embedded in the
   /// stream.
@@ -230,20 +244,15 @@
   /// Parse and process the metadata of the buffer.
   Error parseMeta();
 
-  /// Parse a Bitstream remark.
-  Expected<std::unique_ptr<Remark>> parseRemark();
-
 private:
-  Error processCommonMeta(BitstreamMetaParserHelper &Helper);
-  Error processStandaloneMeta(BitstreamMetaParserHelper &Helper);
-  Error processSeparateRemarksFileMeta(BitstreamMetaParserHelper &Helper);
-  Error processSeparateRemarksMetaMeta(BitstreamMetaParserHelper &Helper);
-  Error processExternalFilePath(BitstreamMetaParserHelper &Helper);
-  Error processStrTab(BitstreamMetaParserHelper &Helper);
-  Error processRemarkVersion(BitstreamMetaParserHelper &Helper);
+  Error processCommonMeta();
+  Error processFileContainerMeta();
+  Error processExternalFilePath();
 
-  Expected<std::unique_ptr<Remark>>
-  processRemark(BitstreamRemarkParserHelper &Helper);
+  Expected<std::unique_ptr<Remark>> processRemark();
+
+  Error processStrTab();
+  Error processRemarkVersion();
 };
 
 Expected<std::unique_ptr<BitstreamRemarkParser>> createBitstreamParserFromMeta(
diff --git a/llvm/lib/Remarks/BitstreamRemarkSerializer.cpp b/llvm/lib/Remarks/BitstreamRemarkSerializer.cpp
index b262719..abd436e 100644
--- a/llvm/lib/Remarks/BitstreamRemarkSerializer.cpp
+++ b/llvm/lib/Remarks/BitstreamRemarkSerializer.cpp
@@ -12,25 +12,23 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Remarks/BitstreamRemarkSerializer.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Remarks/Remark.h"
+#include <cassert>
 #include <optional>
 
 using namespace llvm;
 using namespace llvm::remarks;
 
 BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
-    BitstreamRemarkContainerType ContainerType)
-    : Bitstream(Encoded), ContainerType(ContainerType) {}
-
-static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
-  append_range(R, Str);
-}
+    BitstreamRemarkContainerType ContainerType, raw_ostream &OS)
+    : Bitstream(OS), ContainerType(ContainerType) {}
 
 static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream,
                           SmallVectorImpl<uint64_t> &R, StringRef Str) {
   R.clear();
   R.push_back(RecordID);
-  push(R, Str);
+  append_range(R, Str);
   Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, R);
 }
 
@@ -41,7 +39,7 @@
   Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, R);
 
   R.clear();
-  push(R, Str);
+  append_range(R, Str);
   Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, R);
 }
 
@@ -200,75 +198,64 @@
     Bitstream.Emit(static_cast<unsigned>(C), 8);
 
   Bitstream.EnterBlockInfoBlock();
+  auto ExitBlock = make_scope_exit([&] { Bitstream.ExitBlock(); });
 
   // Setup the main metadata. Depending on the container type, we'll setup the
   // required records next.
   setupMetaBlockInfo();
 
   switch (ContainerType) {
-  case BitstreamRemarkContainerType::SeparateRemarksMeta:
-    // Needs a string table that the separate remark file is using.
-    setupMetaStrTab();
+  case BitstreamRemarkContainerType::RemarksFileExternal:
     // Needs to know where the external remarks file is.
     setupMetaExternalFile();
-    break;
-  case BitstreamRemarkContainerType::SeparateRemarksFile:
-    // Contains remarks: emit the version.
-    setupMetaRemarkVersion();
-    // Contains remarks: emit the remark abbrevs.
-    setupRemarkBlockInfo();
-    break;
-  case BitstreamRemarkContainerType::Standalone:
+    return;
+  case BitstreamRemarkContainerType::RemarksFile:
     // Contains remarks: emit the version.
     setupMetaRemarkVersion();
     // Needs a string table.
     setupMetaStrTab();
     // Contains remarks: emit the remark abbrevs.
     setupRemarkBlockInfo();
-    break;
+    return;
   }
-
-  Bitstream.ExitBlock();
+  llvm_unreachable("Unexpected BitstreamRemarkContainerType");
 }
 
 void BitstreamRemarkSerializerHelper::emitMetaBlock(
-    uint64_t ContainerVersion, std::optional<uint64_t> RemarkVersion,
-    std::optional<const StringTable *> StrTab,
     std::optional<StringRef> Filename) {
   // Emit the meta block
   Bitstream.EnterSubblock(META_BLOCK_ID, 3);
+  auto ExitBlock = make_scope_exit([&] { Bitstream.ExitBlock(); });
 
   // The container version and type.
   R.clear();
   R.push_back(RECORD_META_CONTAINER_INFO);
-  R.push_back(ContainerVersion);
+  R.push_back(CurrentContainerVersion);
   R.push_back(static_cast<uint64_t>(ContainerType));
   Bitstream.EmitRecordWithAbbrev(RecordMetaContainerInfoAbbrevID, R);
 
   switch (ContainerType) {
-  case BitstreamRemarkContainerType::SeparateRemarksMeta:
-    assert(StrTab != std::nullopt && *StrTab != nullptr);
-    emitMetaStrTab(**StrTab);
+  case BitstreamRemarkContainerType::RemarksFileExternal:
     assert(Filename != std::nullopt);
     emitMetaExternalFile(*Filename);
-    break;
-  case BitstreamRemarkContainerType::SeparateRemarksFile:
-    assert(RemarkVersion != std::nullopt);
-    emitMetaRemarkVersion(*RemarkVersion);
-    break;
-  case BitstreamRemarkContainerType::Standalone:
-    assert(RemarkVersion != std::nullopt);
-    emitMetaRemarkVersion(*RemarkVersion);
-    assert(StrTab != std::nullopt && *StrTab != nullptr);
-    emitMetaStrTab(**StrTab);
-    break;
+    return;
+  case BitstreamRemarkContainerType::RemarksFile:
+    emitMetaRemarkVersion(CurrentRemarkVersion);
+    return;
   }
+  llvm_unreachable("Unexpected BitstreamRemarkContainerType");
+}
 
+void BitstreamRemarkSerializerHelper::emitLateMetaBlock(
+    const StringTable &StrTab) {
+  // Emit the late meta block (after all remarks are serialized)
+  Bitstream.EnterSubblock(META_BLOCK_ID, 3);
+  emitMetaStrTab(StrTab);
   Bitstream.ExitBlock();
 }
 
-void BitstreamRemarkSerializerHelper::emitRemarkBlock(const Remark &Remark,
-                                                      StringTable &StrTab) {
+void BitstreamRemarkSerializerHelper::emitRemark(const Remark &Remark,
+                                                 StringTable &StrTab) {
   Bitstream.EnterSubblock(REMARK_BLOCK_ID, 4);
 
   R.clear();
@@ -317,73 +304,49 @@
   Bitstream.ExitBlock();
 }
 
-void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
-  OS.write(Encoded.data(), Encoded.size());
-  Encoded.clear();
-}
-
-StringRef BitstreamRemarkSerializerHelper::getBuffer() {
-  return StringRef(Encoded.data(), Encoded.size());
-}
-
-BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS,
-                                                     SerializerMode Mode)
-    : RemarkSerializer(Format::Bitstream, OS, Mode),
-      Helper(BitstreamRemarkContainerType::SeparateRemarksFile) {
-  assert(Mode == SerializerMode::Separate &&
-         "For SerializerMode::Standalone, a pre-filled string table needs to "
-         "be provided.");
-  // We always use a string table with bitstream.
+BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS)
+    : RemarkSerializer(Format::Bitstream, OS) {
   StrTab.emplace();
 }
 
 BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS,
-                                                     SerializerMode Mode,
                                                      StringTable StrTabIn)
-    : RemarkSerializer(Format::Bitstream, OS, Mode),
-      Helper(Mode == SerializerMode::Separate
-                 ? BitstreamRemarkContainerType::SeparateRemarksFile
-                 : BitstreamRemarkContainerType::Standalone) {
+    : RemarkSerializer(Format::Bitstream, OS) {
   StrTab = std::move(StrTabIn);
 }
 
-void BitstreamRemarkSerializer::emit(const Remark &Remark) {
-  if (!DidSetUp) {
-    // Emit the metadata that is embedded in the remark file.
-    // If we're in standalone mode, serialize the string table as well.
-    bool IsStandalone =
-        Helper.ContainerType == BitstreamRemarkContainerType::Standalone;
-    BitstreamMetaSerializer MetaSerializer(
-        OS, Helper,
-        IsStandalone ? &*StrTab
-                     : std::optional<const StringTable *>(std::nullopt));
-    MetaSerializer.emit();
-    DidSetUp = true;
-  }
+BitstreamRemarkSerializer::~BitstreamRemarkSerializer() { finalize(); }
 
-  assert(DidSetUp &&
-         "The Block info block and the meta block were not emitted yet.");
-  Helper.emitRemarkBlock(Remark, *StrTab);
-
-  Helper.flushToStream(OS);
+void BitstreamRemarkSerializer::setup() {
+  if (Helper)
+    return;
+  Helper.emplace(BitstreamRemarkContainerType::RemarksFile, OS);
+  Helper->setupBlockInfo();
+  Helper->emitMetaBlock();
 }
 
-std::unique_ptr<MetaSerializer> BitstreamRemarkSerializer::metaSerializer(
-    raw_ostream &OS, std::optional<StringRef> ExternalFilename) {
-  assert(Helper.ContainerType !=
-         BitstreamRemarkContainerType::SeparateRemarksMeta);
-  bool IsStandalone =
-      Helper.ContainerType == BitstreamRemarkContainerType::Standalone;
+void BitstreamRemarkSerializer::finalize() {
+  if (!Helper)
+    return;
+  Helper->emitLateMetaBlock(*StrTab);
+  Helper = std::nullopt;
+}
+
+void BitstreamRemarkSerializer::emit(const Remark &Remark) {
+  setup();
+  Helper->emitRemark(Remark, *StrTab);
+}
+
+std::unique_ptr<MetaSerializer>
+BitstreamRemarkSerializer::metaSerializer(raw_ostream &OS,
+                                          StringRef ExternalFilename) {
   return std::make_unique<BitstreamMetaSerializer>(
-      OS,
-      IsStandalone ? BitstreamRemarkContainerType::Standalone
-                   : BitstreamRemarkContainerType::SeparateRemarksMeta,
-      &*StrTab, ExternalFilename);
+      OS, BitstreamRemarkContainerType::RemarksFileExternal, ExternalFilename);
 }
 
 void BitstreamMetaSerializer::emit() {
+  assert(Helper && "BitstreamMetaSerializer emitted multiple times");
   Helper->setupBlockInfo();
-  Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
-                        ExternalFilename);
-  Helper->flushToStream(OS);
+  Helper->emitMetaBlock(ExternalFilename);
+  Helper = std::nullopt;
 }
diff --git a/llvm/lib/Remarks/RemarkLinker.cpp b/llvm/lib/Remarks/RemarkLinker.cpp
index b00419b..f0feecc 100644
--- a/llvm/lib/Remarks/RemarkLinker.cpp
+++ b/llvm/lib/Remarks/RemarkLinker.cpp
@@ -108,7 +108,7 @@
 
 Error RemarkLinker::serialize(raw_ostream &OS, Format RemarksFormat) const {
   Expected<std::unique_ptr<RemarkSerializer>> MaybeSerializer =
-      createRemarkSerializer(RemarksFormat, SerializerMode::Standalone, OS,
+      createRemarkSerializer(RemarksFormat, OS,
                              std::move(const_cast<StringTable &>(StrTab)));
   if (!MaybeSerializer)
     return MaybeSerializer.takeError();
diff --git a/llvm/lib/Remarks/RemarkSerializer.cpp b/llvm/lib/Remarks/RemarkSerializer.cpp
index df1da53..80388b4 100644
--- a/llvm/lib/Remarks/RemarkSerializer.cpp
+++ b/llvm/lib/Remarks/RemarkSerializer.cpp
@@ -18,34 +18,32 @@
 using namespace llvm::remarks;
 
 Expected<std::unique_ptr<RemarkSerializer>>
-remarks::createRemarkSerializer(Format RemarksFormat, SerializerMode Mode,
-                                raw_ostream &OS) {
+remarks::createRemarkSerializer(Format RemarksFormat, raw_ostream &OS) {
   switch (RemarksFormat) {
   case Format::Unknown:
   case Format::Auto:
     return createStringError(std::errc::invalid_argument,
                              "Invalid remark serializer format.");
   case Format::YAML:
-    return std::make_unique<YAMLRemarkSerializer>(OS, Mode);
+    return std::make_unique<YAMLRemarkSerializer>(OS);
   case Format::Bitstream:
-    return std::make_unique<BitstreamRemarkSerializer>(OS, Mode);
+    return std::make_unique<BitstreamRemarkSerializer>(OS);
   }
   llvm_unreachable("Unknown remarks::Format enum");
 }
 
 Expected<std::unique_ptr<RemarkSerializer>>
-remarks::createRemarkSerializer(Format RemarksFormat, SerializerMode Mode,
-                                raw_ostream &OS, remarks::StringTable StrTab) {
+remarks::createRemarkSerializer(Format RemarksFormat, raw_ostream &OS,
+                                remarks::StringTable StrTab) {
   switch (RemarksFormat) {
   case Format::Unknown:
   case Format::Auto:
     return createStringError(std::errc::invalid_argument,
                              "Invalid remark serializer format.");
   case Format::YAML:
-    return std::make_unique<YAMLRemarkSerializer>(OS, Mode, std::move(StrTab));
+    return std::make_unique<YAMLRemarkSerializer>(OS, std::move(StrTab));
   case Format::Bitstream:
-    return std::make_unique<BitstreamRemarkSerializer>(OS, Mode,
-                                                       std::move(StrTab));
+    return std::make_unique<BitstreamRemarkSerializer>(OS, std::move(StrTab));
   }
   llvm_unreachable("Unknown remarks::Format enum");
 }
diff --git a/llvm/lib/Remarks/RemarkStreamer.cpp b/llvm/lib/Remarks/RemarkStreamer.cpp
index bb62c8b..d9be2f1 100644
--- a/llvm/lib/Remarks/RemarkStreamer.cpp
+++ b/llvm/lib/Remarks/RemarkStreamer.cpp
@@ -12,6 +12,7 @@
 
 #include "llvm/Remarks/RemarkStreamer.h"
 #include "llvm/Support/CommandLine.h"
+#include <cassert>
 #include <optional>
 
 using namespace llvm;
@@ -31,6 +32,14 @@
       Filename(FilenameIn ? std::optional<std::string>(FilenameIn->str())
                           : std::nullopt) {}
 
+RemarkStreamer::~RemarkStreamer() {
+  // Ensure that llvm::finalizeOptimizationRemarks was called before the
+  // RemarkStreamer is destroyed.
+  assert(!RemarkSerializer &&
+         "RemarkSerializer must be released before RemarkStreamer is "
+         "destroyed. Ensure llvm::finalizeOptimizationRemarks is called.");
+}
+
 Error RemarkStreamer::setFilter(StringRef Filter) {
   Regex R = Regex(Filter);
   std::string RegexError;
@@ -57,16 +66,7 @@
 
   assert(EnableRemarksSection == cl::BOU_UNSET);
 
-  // We only need a section if we're in separate mode.
-  if (RemarkSerializer->Mode != remarks::SerializerMode::Separate)
-    return false;
-
-  // Only some formats need a section:
-  // * bitstream
-  switch (RemarkSerializer->SerializerFormat) {
-  case remarks::Format::Bitstream:
-    return true;
-  default:
-    return false;
-  }
+  // Enable remark sections by default for bitstream remarks (so dsymutil can
+  // find all remarks for a linked binary)
+  return RemarkSerializer->SerializerFormat == Format::Bitstream;
 }
diff --git a/llvm/lib/Remarks/YAMLRemarkParser.cpp b/llvm/lib/Remarks/YAMLRemarkParser.cpp
index 5ff42fe..baad378 100644
--- a/llvm/lib/Remarks/YAMLRemarkParser.cpp
+++ b/llvm/lib/Remarks/YAMLRemarkParser.cpp
@@ -385,7 +385,11 @@
   if (!ValueStr)
     return error("argument value is missing.", *ArgMap);
 
-  return Argument{*KeyStr, *ValueStr, Loc};
+  Argument Arg;
+  Arg.Key = *KeyStr;
+  Arg.Val = *ValueStr;
+  Arg.Loc = Loc;
+  return Arg;
 }
 
 Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() {
diff --git a/llvm/lib/Remarks/YAMLRemarkSerializer.cpp b/llvm/lib/Remarks/YAMLRemarkSerializer.cpp
index 846a721..f8b610d 100644
--- a/llvm/lib/Remarks/YAMLRemarkSerializer.cpp
+++ b/llvm/lib/Remarks/YAMLRemarkSerializer.cpp
@@ -19,8 +19,6 @@
 using namespace llvm;
 using namespace llvm::remarks;
 
-// Use the same keys whether we use a string table or not (respectively, T is an
-// unsigned or a StringRef).
 static void
 mapRemarkHeader(yaml::IO &io, StringRef PassName, StringRef RemarkName,
                 std::optional<RemarkLocation> RL, StringRef FunctionName,
@@ -131,10 +129,13 @@
 
 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument)
 
-YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream &OS, SerializerMode Mode,
-                                           std::optional<StringTable> StrTabIn)
-    : RemarkSerializer(Format::YAML, OS, Mode),
-      YAMLOutput(OS, reinterpret_cast<void *>(this)) {
+YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream &OS)
+    : RemarkSerializer(Format::YAML, OS),
+      YAMLOutput(OS, reinterpret_cast<void *>(this)) {}
+
+YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream &OS,
+                                           StringTable StrTabIn)
+    : YAMLRemarkSerializer(OS) {
   StrTab = std::move(StrTabIn);
 }
 
@@ -145,8 +146,9 @@
   YAMLOutput << R;
 }
 
-std::unique_ptr<MetaSerializer> YAMLRemarkSerializer::metaSerializer(
-    raw_ostream &OS, std::optional<StringRef> ExternalFilename) {
+std::unique_ptr<MetaSerializer>
+YAMLRemarkSerializer::metaSerializer(raw_ostream &OS,
+                                     StringRef ExternalFilename) {
   return std::make_unique<YAMLMetaSerializer>(OS, ExternalFilename);
 }
 
@@ -186,6 +188,5 @@
   support::endian::write64le(StrTabSizeBuf.data(), StrTabSize);
 
   OS.write(StrTabSizeBuf.data(), StrTabSizeBuf.size());
-  if (ExternalFilename)
-    emitExternalFile(OS, *ExternalFilename);
+  emitExternalFile(OS, ExternalFilename);
 }
diff --git a/llvm/test/Transforms/Inline/optimization-remarks-passed-yaml.ll b/llvm/test/Transforms/Inline/optimization-remarks-passed-yaml.ll
index b65ed66..b0a238f 100644
--- a/llvm/test/Transforms/Inline/optimization-remarks-passed-yaml.ll
+++ b/llvm/test/Transforms/Inline/optimization-remarks-passed-yaml.ll
@@ -8,6 +8,11 @@
 ; RUN:    -pass-remarks-with-hotness 2>&1 | FileCheck %s
 ; RUN: cat %t | FileCheck -check-prefixes=YAML,YAML-NO-ANNOTATE %s
 
+; RUN: opt < %s -S -passes=inline -pass-remarks-output=%t.bitstream -pass-remarks=inline \
+; RUN:    -pass-remarks-missed=inline -pass-remarks-analysis=inline \
+; RUN:    -pass-remarks-with-hotness -pass-remarks-format=bitstream 2>&1 | FileCheck %s
+; RUN: llvm-remarkutil bitstream2yaml %t.bitstream | FileCheck -check-prefixes=YAML,YAML-NO-ANNOTATE %s
+
 ; RUN: opt < %s -S -passes=inliner-wrapper -pass-remarks-output=%t -pass-remarks=inline \
 ; RUN:    -pass-remarks-missed=inline -pass-remarks-analysis=inline \
 ; RUN:    -annotate-inline-phase=false \
diff --git a/llvm/test/tools/dsymutil/ARM/remarks-linking-bundle-empty.test b/llvm/test/tools/dsymutil/ARM/remarks-linking-bundle-empty.test
new file mode 100644
index 0000000..0a89fa1
--- /dev/null
+++ b/llvm/test/tools/dsymutil/ARM/remarks-linking-bundle-empty.test
@@ -0,0 +1,13 @@
+RUN: rm -rf %t
+RUN: mkdir -p %t
+RUN: cat %p/../Inputs/remarks/basic.macho.remarks.empty.arm64 > %t/basic.macho.remarks.empty.arm64
+
+RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.empty.arm64
+
+Check that the remark file in the bundle does not exist:
+RUN: not cat %t/basic.macho.remarks.empty.arm64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.arm64 2>&1
+
+RUN: dsymutil --linker parallel -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.empty.arm64
+
+Check that the remark file in the bundle does not exist:
+RUN: not cat %t/basic.macho.remarks.empty.arm64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.empty.arm64 2>&1
diff --git a/llvm/test/tools/dsymutil/ARM/remarks-linking-bundle.test b/llvm/test/tools/dsymutil/ARM/remarks-linking-bundle.test
new file mode 100644
index 0000000..e1b0445
--- /dev/null
+++ b/llvm/test/tools/dsymutil/ARM/remarks-linking-bundle.test
@@ -0,0 +1,81 @@
+RUN: rm -rf %t
+RUN: mkdir -p %t/private/tmp/remarks
+RUN: cat %p/../Inputs/remarks/basic.macho.remarks.arm64> %t/basic.macho.remarks.arm64
+RUN: llvm-remarkutil yaml2bitstream %p/../Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.opt.yaml -o %t/private/tmp/remarks/basic1.macho.remarks.arm64.opt.bitstream
+RUN: llvm-remarkutil yaml2bitstream %p/../Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.opt.yaml -o %t/private/tmp/remarks/basic2.macho.remarks.arm64.opt.bitstream
+RUN: llvm-remarkutil yaml2bitstream %p/../Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.opt.yaml -o %t/private/tmp/remarks/basic3.macho.remarks.arm64.opt.bitstream
+
+RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%t %t/basic.macho.remarks.arm64
+
+Check that the remark file in the bundle exists and is sane:
+RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.arm64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.arm64 | FileCheck %s
+
+RUN: dsymutil --linker parallel -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%t %t/basic.macho.remarks.arm64
+
+Check that the remark file in the bundle exists and is sane:
+RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.arm64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.arm64 | FileCheck %s
+
+Now emit it in a different format: YAML.
+RUN: dsymutil -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%t %t/basic.macho.remarks.arm64
+RUN: cat %t/basic.macho.remarks.arm64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.arm64 | FileCheck %s --check-prefix=CHECK-YAML
+
+RUN: dsymutil --linker parallel -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%t %t/basic.macho.remarks.arm64
+RUN: cat %t/basic.macho.remarks.arm64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.arm64 | FileCheck %s --check-prefix=CHECK-YAML
+
+CHECK: <Meta
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK: <Remark Num
+CHECK-NOT: <Remark Num
+CHECK: <Meta
+
+CHECK-YAML:--- !Passed
+CHECK-YAML:--- !Passed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Missed
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-YAML:--- !Analysis
+CHECK-NOT: --- !
diff --git a/llvm/test/tools/dsymutil/Inputs/basic1.c b/llvm/test/tools/dsymutil/Inputs/basic1.c
index af96081..0aa8cdf 100644
--- a/llvm/test/tools/dsymutil/Inputs/basic1.c
+++ b/llvm/test/tools/dsymutil/Inputs/basic1.c
@@ -22,9 +22,9 @@
 
    Remarks compilation:
       for FILE in basic1.c basic2.c basic3.c; do
-         clang -gline-tables-only -c $FILE -fsave-optimization-record=bitstream -foptimization-record-file=/remarks/${FILE%.c}.macho.remarks.x86_64.opt.bitstream -o ${FILE%.c}.macho.remarks.x86_64.o
+         clang -gline-tables-only -c $FILE -fsave-optimization-record=bitstream -foptimization-record-file=${FILE%.c}.macho.remarks.arm64.opt.bitstream -o ${FILE%.c}.macho.remarks.arm64.o
       done
-      clang basic1.macho.remarks.x86_64.o basic2.macho.remarks.x86_64.o basic3.macho.remarks.x86_64.o -o basic.macho.remarks.x86_64 -Wl,-dead_strip
+      clang basic1.macho.remarks.arm64.o basic2.macho.remarks.arm64.o basic3.macho.remarks.arm64.o -o basic.macho.remarks.arm64 -Wl,-dead_strip
 
     Remarks archive compilation (after remarks compilation):
     ar -q libbasic.a basic1.macho.x86_64.o basic2.macho.x86_64.o basic3.macho.x86_64.o
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.o
new file mode 100644
index 0000000..d2901a7
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.opt.yaml b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.opt.yaml
new file mode 100644
index 0000000..9aa711a
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.arm64.opt.yaml
@@ -0,0 +1,47 @@
+--- !Missed
+Pass:            inline
+Name:            NoDefinition
+DebugLoc:        { File: basic1.c, Line: 38, Column: 10 }
+Function:        main
+Args:
+  - Callee:          foo
+    DebugLoc:        { File: basic1.c, Line: 0, Column: 0 }
+  - String:          ' will not be inlined into '
+  - Caller:          main
+    DebugLoc:        { File: basic1.c, Line: 37, Column: 0 }
+  - String:          ' because its definition is unavailable'
+...
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: basic1.c, Line: 37, Column: 0 }
+Function:        main
+Args:
+  - NumStackBytes:   '0'
+  - String:          ' stack bytes in function '''
+  - Function:        main
+  - String:          ''''
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+DebugLoc:        { File: basic1.c, Line: 38, Column: 10 }
+Function:        main
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          TCRETURNdi
+  - String:          ': '
+  - INST_TCRETURNdi: '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: basic1.c, Line: 37, Column: 0 }
+Function:        main
+Args:
+  - NumInstructions: '1'
+  - String:          ' instructions in function'
+...
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.empty.arm64.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.empty.arm64.o
new file mode 100644
index 0000000..54cd2e2
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.empty.arm64.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.empty.arm64.opt.bitstream
similarity index 100%
rename from llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.opt.bitstream
rename to llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic1.macho.remarks.empty.arm64.opt.bitstream
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.o
new file mode 100644
index 0000000..81a972e
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.opt.yaml b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.opt.yaml
new file mode 100644
index 0000000..4aa5301
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.arm64.opt.yaml
@@ -0,0 +1,194 @@
+--- !Missed
+Pass:            inline
+Name:            NoDefinition
+DebugLoc:        { File: basic2.c, Line: 11, Column: 3 }
+Function:        unused1
+Args:
+  - Callee:          bar
+    DebugLoc:        { File: basic2.c, Line: 0, Column: 0 }
+  - String:          ' will not be inlined into '
+  - Caller:          unused1
+    DebugLoc:        { File: basic2.c, Line: 10, Column: 0 }
+  - String:          ' because its definition is unavailable'
+...
+--- !Missed
+Pass:            inline
+Name:            NoDefinition
+DebugLoc:        { File: basic2.c, Line: 20, Column: 10 }
+Function:        foo
+Args:
+  - Callee:          bar
+    DebugLoc:        { File: basic2.c, Line: 0, Column: 0 }
+  - String:          ' will not be inlined into '
+  - Caller:          foo
+    DebugLoc:        { File: basic2.c, Line: 19, Column: 0 }
+  - String:          ' because its definition is unavailable'
+...
+--- !Passed
+Pass:            inline
+Name:            Inlined
+DebugLoc:        { File: basic2.c, Line: 20, Column: 25 }
+Function:        foo
+Args:
+  - String:          ''''
+  - Callee:          inc
+    DebugLoc:        { File: basic2.c, Line: 14, Column: 0 }
+  - String:          ''' inlined into '''
+  - Caller:          foo
+    DebugLoc:        { File: basic2.c, Line: 19, Column: 0 }
+  - String:          ''''
+  - String:          ' with '
+  - String:          '(cost='
+  - Cost:            '-15015'
+  - String:          ', threshold='
+  - Threshold:       '75'
+  - String:          ')'
+  - String:          ' at callsite '
+  - String:          foo
+  - String:          ':'
+  - Line:            '1'
+  - String:          ':'
+  - Column:          '25'
+  - String:          ';'
+...
+--- !Missed
+Pass:            gvn
+Name:            LoadClobbered
+DebugLoc:        { File: basic2.c, Line: 15, Column: 10 }
+Function:        foo
+Args:
+  - String:          'load of type '
+  - Type:            i32
+  - String:          ' not eliminated'
+  - String:          ' because it is clobbered by '
+  - ClobberedBy:     call
+    DebugLoc:        { File: basic2.c, Line: 20, Column: 10 }
+...
+--- !Missed
+Pass:            gvn
+Name:            LoadClobbered
+DebugLoc:        { File: basic2.c, Line: 20, Column: 36 }
+Function:        foo
+Args:
+  - String:          'load of type '
+  - Type:            i32
+  - String:          ' not eliminated'
+  - String:          ' because it is clobbered by '
+  - ClobberedBy:     call
+    DebugLoc:        { File: basic2.c, Line: 20, Column: 10 }
+...
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: basic2.c, Line: 10, Column: 0 }
+Function:        unused1
+Args:
+  - NumStackBytes:   '0'
+  - String:          ' stack bytes in function '''
+  - Function:        unused1
+  - String:          ''''
+...
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: basic2.c, Line: 19, Column: 0 }
+Function:        foo
+Args:
+  - NumStackBytes:   '16'
+  - String:          ' stack bytes in function '''
+  - Function:        foo
+  - String:          ''''
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+DebugLoc:        { File: basic2.c, Line: 11, Column: 7 }
+Function:        unused1
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          ADRP
+  - String:          ': '
+  - INST_ADRP:       '1'
+  - String:          "\n"
+  - String:          LDRWui
+  - String:          ': '
+  - INST_LDRWui:     '1'
+  - String:          "\n"
+  - String:          TCRETURNdi
+  - String:          ': '
+  - INST_TCRETURNdi: '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: basic2.c, Line: 10, Column: 0 }
+Function:        unused1
+Args:
+  - NumInstructions: '3'
+  - String:          ' instructions in function'
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+Function:        foo
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          ADDWrs
+  - String:          ': '
+  - INST_ADDWrs:     '3'
+  - String:          "\n"
+  - String:          ADRP
+  - String:          ': '
+  - INST_ADRP:       '3'
+  - String:          "\n"
+  - String:          LDRWui
+  - String:          ': '
+  - INST_LDRWui:     '3'
+  - String:          "\n"
+  - String:          ADDWri
+  - String:          ': '
+  - INST_ADDWri:     '2'
+  - String:          "\n"
+  - String:          STRWui
+  - String:          ': '
+  - INST_STRWui:     '2'
+  - String:          "\n"
+  - String:          ADDXri
+  - String:          ': '
+  - INST_ADDXri:     '1'
+  - String:          "\n"
+  - String:          BL
+  - String:          ': '
+  - INST_BL:         '1'
+  - String:          "\n"
+  - String:          LDPXpost
+  - String:          ': '
+  - INST_LDPXpost:   '1'
+  - String:          "\n"
+  - String:          LDRXui
+  - String:          ': '
+  - INST_LDRXui:     '1'
+  - String:          "\n"
+  - String:          RET
+  - String:          ': '
+  - INST_RET:        '1'
+  - String:          "\n"
+  - String:          STPXpre
+  - String:          ': '
+  - INST_STPXpre:    '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: basic2.c, Line: 19, Column: 0 }
+Function:        foo
+Args:
+  - NumInstructions: '19'
+  - String:          ' instructions in function'
+...
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.empty.arm64.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.empty.arm64.o
new file mode 100644
index 0000000..8c55fb3
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.empty.arm64.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.empty.arm64.opt.bitstream
similarity index 100%
copy from llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.opt.bitstream
copy to llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic2.macho.remarks.empty.arm64.opt.bitstream
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.o
new file mode 100644
index 0000000..3497e3a
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.opt.yaml b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.opt.yaml
new file mode 100644
index 0000000..8570049
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.arm64.opt.yaml
@@ -0,0 +1,181 @@
+--- !Missed
+Pass:            inline
+Name:            NoDefinition
+DebugLoc:        { File: basic3.c, Line: 8, Column: 10 }
+Function:        unused2
+Args:
+  - Callee:          foo
+    DebugLoc:        { File: basic3.c, Line: 0, Column: 0 }
+  - String:          ' will not be inlined into '
+  - Caller:          unused2
+    DebugLoc:        { File: basic3.c, Line: 7, Column: 0 }
+  - String:          ' because its definition is unavailable'
+...
+--- !Missed
+Pass:            inline
+Name:            NoDefinition
+DebugLoc:        { File: basic3.c, Line: 19, Column: 10 }
+Function:        bar
+Args:
+  - Callee:          foo
+    DebugLoc:        { File: basic3.c, Line: 0, Column: 0 }
+  - String:          ' will not be inlined into '
+  - Caller:          bar
+    DebugLoc:        { File: basic3.c, Line: 16, Column: 0 }
+  - String:          ' because its definition is unavailable'
+...
+--- !Passed
+Pass:            inline
+Name:            Inlined
+DebugLoc:        { File: basic3.c, Line: 18, Column: 12 }
+Function:        bar
+Args:
+  - String:          ''''
+  - Callee:          inc
+    DebugLoc:        { File: basic3.c, Line: 11, Column: 0 }
+  - String:          ''' inlined into '''
+  - Caller:          bar
+    DebugLoc:        { File: basic3.c, Line: 16, Column: 0 }
+  - String:          ''''
+  - String:          ' with '
+  - String:          '(cost='
+  - Cost:            '-15015'
+  - String:          ', threshold='
+  - Threshold:       '75'
+  - String:          ')'
+  - String:          ' at callsite '
+  - String:          bar
+  - String:          ':'
+  - Line:            '2'
+  - String:          ':'
+  - Column:          '12'
+  - String:          ';'
+...
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: basic3.c, Line: 7, Column: 0 }
+Function:        unused2
+Args:
+  - NumStackBytes:   '0'
+  - String:          ' stack bytes in function '''
+  - Function:        unused2
+  - String:          ''''
+...
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: basic3.c, Line: 16, Column: 0 }
+Function:        bar
+Args:
+  - NumStackBytes:   '0'
+  - String:          ' stack bytes in function '''
+  - Function:        bar
+  - String:          ''''
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+DebugLoc:        { File: basic3.c, Line: 8, Column: 14 }
+Function:        unused2
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          ADRP
+  - String:          ': '
+  - INST_ADRP:       '1'
+  - String:          "\n"
+  - String:          LDRWui
+  - String:          ': '
+  - INST_LDRWui:     '1'
+  - String:          "\n"
+  - String:          TCRETURNdi
+  - String:          ': '
+  - INST_TCRETURNdi: '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: basic3.c, Line: 7, Column: 0 }
+Function:        unused2
+Args:
+  - NumInstructions: '3'
+  - String:          ' instructions in function'
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+DebugLoc:        { File: basic3.c, Line: 0, Column: 0 }
+Function:        bar
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          ADRP
+  - String:          ': '
+  - INST_ADRP:       '1'
+  - String:          "\n"
+  - String:          Bcc
+  - String:          ': '
+  - INST_Bcc:        '1'
+  - String:          "\n"
+  - String:          LDRWui
+  - String:          ': '
+  - INST_LDRWui:     '1'
+  - String:          "\n"
+  - String:          SUBSWri
+  - String:          ': '
+  - INST_SUBSWri:    '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+DebugLoc:        { File: basic3.c, Line: 12, Column: 10 }
+Function:        bar
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      if.then
+  - String:          "\n"
+  - String:          ADDWri
+  - String:          ': '
+  - INST_ADDWri:     '1'
+  - String:          "\n"
+  - String:          RET
+  - String:          ': '
+  - INST_RET:        '1'
+  - String:          "\n"
+  - String:          STRWui
+  - String:          ': '
+  - INST_STRWui:     '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+DebugLoc:        { File: basic3.c, Line: 19, Column: 18 }
+Function:        bar
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      if.end
+  - String:          "\n"
+  - String:          ADDWrs
+  - String:          ': '
+  - INST_ADDWrs:     '1'
+  - String:          "\n"
+  - String:          TCRETURNdi
+  - String:          ': '
+  - INST_TCRETURNdi: '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: basic3.c, Line: 16, Column: 0 }
+Function:        bar
+Args:
+  - NumInstructions: '9'
+  - String:          ' instructions in function'
+...
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.empty.arm64.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.empty.arm64.o
new file mode 100644
index 0000000..c4c1738
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.empty.arm64.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.empty.arm64.opt.bitstream
similarity index 100%
copy from llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.opt.bitstream
copy to llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/basic3.macho.remarks.empty.arm64.opt.bitstream
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86.o
new file mode 100644
index 0000000..14094fa
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86.o
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86_64.opt.yaml b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86_64.opt.yaml
new file mode 100644
index 0000000..e6aa345
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86_64.opt.yaml
@@ -0,0 +1,53 @@
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: fat.macho.remarks.x86.c, Line: 14, Column: 0 }
+Function:        main
+Args:
+  - NumStackBytes:   '8'
+  - String:          ' stack bytes in function '''
+  - Function:        main
+  - String:          ''''
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+Function:        main
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          MOV32mi
+  - String:          ': '
+  - INST_MOV32mi:    '1'
+  - String:          "\n"
+  - String:          MOV64rr
+  - String:          ': '
+  - INST_MOV64rr:    '1'
+  - String:          "\n"
+  - String:          POP64r
+  - String:          ': '
+  - INST_POP64r:     '1'
+  - String:          "\n"
+  - String:          PUSH64r
+  - String:          ': '
+  - INST_PUSH64r:    '1'
+  - String:          "\n"
+  - String:          RET64
+  - String:          ': '
+  - INST_RET64:      '1'
+  - String:          "\n"
+  - String:          XOR32rr
+  - String:          ': '
+  - INST_XOR32rr:    '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: fat.macho.remarks.x86.c, Line: 14, Column: 0 }
+Function:        main
+Args:
+  - NumInstructions: '6'
+  - String:          ' instructions in function'
+...
diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86_64h.opt.yaml b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86_64h.opt.yaml
new file mode 100644
index 0000000..e6aa345
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/private/tmp/remarks/fat.macho.remarks.x86_64h.opt.yaml
@@ -0,0 +1,53 @@
+--- !Analysis
+Pass:            prologepilog
+Name:            StackSize
+DebugLoc:        { File: fat.macho.remarks.x86.c, Line: 14, Column: 0 }
+Function:        main
+Args:
+  - NumStackBytes:   '8'
+  - String:          ' stack bytes in function '''
+  - Function:        main
+  - String:          ''''
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionMix
+Function:        main
+Args:
+  - String:          'BasicBlock: '
+  - BasicBlock:      entry
+  - String:          "\n"
+  - String:          MOV32mi
+  - String:          ': '
+  - INST_MOV32mi:    '1'
+  - String:          "\n"
+  - String:          MOV64rr
+  - String:          ': '
+  - INST_MOV64rr:    '1'
+  - String:          "\n"
+  - String:          POP64r
+  - String:          ': '
+  - INST_POP64r:     '1'
+  - String:          "\n"
+  - String:          PUSH64r
+  - String:          ': '
+  - INST_PUSH64r:    '1'
+  - String:          "\n"
+  - String:          RET64
+  - String:          ': '
+  - INST_RET64:      '1'
+  - String:          "\n"
+  - String:          XOR32rr
+  - String:          ': '
+  - INST_XOR32rr:    '1'
+  - String:          "\n"
+...
+--- !Analysis
+Pass:            asm-printer
+Name:            InstructionCount
+DebugLoc:        { File: fat.macho.remarks.x86.c, Line: 14, Column: 0 }
+Function:        main
+Args:
+  - NumInstructions: '6'
+  - String:          ' instructions in function'
+...
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.arm64 b/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.arm64
new file mode 100755
index 0000000..fcf72f4
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.arm64
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.empty.arm64 b/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.empty.arm64
new file mode 100755
index 0000000..49c0a27
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.empty.arm64
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.empty.x86_64 b/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.empty.x86_64
deleted file mode 100755
index a730148..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.empty.x86_64
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.x86_64 b/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.x86_64
deleted file mode 100755
index 59e53c8..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic.macho.remarks.x86_64
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.o b/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.o
deleted file mode 100644
index aef4841..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.empty.x86_64.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.x86_64.o b/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.x86_64.o
deleted file mode 100644
index f8aa441..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.x86_64.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.x86_64.opt.bitstream
deleted file mode 100644
index ac700c3..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic1.macho.remarks.x86_64.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.empty.x86_64.o b/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.empty.x86_64.o
deleted file mode 100644
index 9bb1405..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.empty.x86_64.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.empty.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.empty.x86_64.opt.bitstream
deleted file mode 100644
index e69de29..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.empty.x86_64.opt.bitstream
+++ /dev/null
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.x86_64.o b/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.x86_64.o
deleted file mode 100644
index 880926b..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.x86_64.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.x86_64.opt.bitstream
deleted file mode 100644
index ee71291..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic2.macho.remarks.x86_64.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.empty.x86_64.o b/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.empty.x86_64.o
deleted file mode 100644
index fff0a71..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.empty.x86_64.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.empty.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.empty.x86_64.opt.bitstream
deleted file mode 100644
index e69de29..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.empty.x86_64.opt.bitstream
+++ /dev/null
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.x86_64.o b/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.x86_64.o
deleted file mode 100644
index 9508c97..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.x86_64.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.x86_64.opt.bitstream
deleted file mode 100644
index e29471c..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/basic3.macho.remarks.x86_64.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.i386.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.i386.opt.bitstream
deleted file mode 100644
index 3abbc03..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.i386.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86 b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86
index a0ebc45..7e529f7 100755
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86
+++ b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.c b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.c
index 8153fc2..8304561 100644
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.c
+++ b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.c
@@ -4,11 +4,11 @@
  * - fat.macho.remarks.x86
  */
 
-/* for ARCH in x86_64 x86_64h i386
+/* for ARCH in x86_64 x86_64h
  * do
  *   clang -gline-tables-only -c fat.macho.remarks.x86.c -fsave-optimization-record=bitstream -foptimization-record-file=fat.macho.remarks."$ARCH".opt.bitstream -mllvm -remarks-section -arch "$ARCH"
  * done
- * lipo -create -output fat.macho.remarks.x86.o fat.macho.remarks.x86_64.o fat.macho.remarks.x86_64h.o fat.macho.remarks.i386.o
- * clang -gline-tables-only fat.macho.remarks.x86.o -arch x86_64 -arch x86_64h -arch i386 -o fat.macho.remarks.x86
+ * lipo -create -output fat.macho.remarks.x86.o fat.macho.remarks.x86_64.o fat.macho.remarks.x86_64h.o
+ * clang -gline-tables-only fat.macho.remarks.x86.o -arch x86_64 -arch x86_64h -o fat.macho.remarks.x86
  */
 int main(void) { return 0; }
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.o b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.o
deleted file mode 100644
index 2f5b8f3..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.o
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.opt.bitstream
deleted file mode 100644
index 72e5999..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86_64.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86_64.opt.bitstream
deleted file mode 100644
index c710786..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86_64.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86_64h.opt.bitstream b/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86_64h.opt.bitstream
deleted file mode 100644
index c710786..0000000
--- a/llvm/test/tools/dsymutil/Inputs/remarks/fat.macho.remarks.x86_64h.opt.bitstream
+++ /dev/null
Binary files differ
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test
deleted file mode 100644
index 4768746..0000000
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test
+++ /dev/null
@@ -1,13 +0,0 @@
-RUN: rm -rf %t
-RUN: mkdir -p %t
-RUN: cat %p/../Inputs/remarks/basic.macho.remarks.empty.x86_64 > %t/basic.macho.remarks.empty.x86_64
-
-RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.empty.x86_64
-
-Check that the remark file in the bundle does not exist:
-RUN: not cat %t/basic.macho.remarks.empty.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.empty.x86_64 2>&1
-
-RUN: dsymutil --linker parallel -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.empty.x86_64
-
-Check that the remark file in the bundle does not exist:
-RUN: not cat %t/basic.macho.remarks.empty.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.empty.x86_64 2>&1
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test
deleted file mode 100644
index d85cd54..0000000
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test
+++ /dev/null
@@ -1,67 +0,0 @@
-RUN: rm -rf %t
-RUN: mkdir -p %t
-RUN: cat %p/../Inputs/remarks/basic.macho.remarks.x86_64 > %t/basic.macho.remarks.x86_64
-
-RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
-
-Check that the remark file in the bundle exists and is sane:
-RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s
-
-RUN: dsymutil --linker parallel -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
-
-Check that the remark file in the bundle exists and is sane:
-RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s
-
-Now emit it in a different format: YAML.
-RUN: dsymutil -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
-RUN: cat %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s --check-prefix=CHECK-YAML
-
-RUN: dsymutil --linker parallel -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
-RUN: cat %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s --check-prefix=CHECK-YAML
-
-CHECK: <Meta
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK: <Remark Num
-CHECK-NOT: <Remark Num
-
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Missed
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-YAML:--- !Analysis
-CHECK-NOT: --- !
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test b/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
index 79302e8..b2605a73 100644
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
+++ b/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
@@ -1,25 +1,26 @@
 REQUIRES: system-darwin
 
 RUN: rm -rf %t
-RUN: mkdir -p %t
+RUN: mkdir -p %t/private/tmp/remarks
 RUN: cat %p/../Inputs/remarks/fat.macho.remarks.x86 > %t/fat.macho.remarks.x86
+RUN: llvm-remarkutil yaml2bitstream %p/../Inputs/private/tmp/remarks/fat.macho.remarks.x86_64.opt.yaml -o %t/private/tmp/remarks/fat.macho.remarks.x86_64.opt.bitstream
+RUN: llvm-remarkutil yaml2bitstream %p/../Inputs/private/tmp/remarks/fat.macho.remarks.x86_64h.opt.yaml -o %t/private/tmp/remarks/fat.macho.remarks.x86_64h.opt.bitstream
 
-RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/fat.macho.remarks.x86
+RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%t %t/fat.macho.remarks.x86
 
 Check that the remark files in the bundle exist and are all sane:
 RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64h | FileCheck %s
 RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64 | FileCheck %s
-RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-i386 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-i386
 
-RUN: dsymutil --linker parallel -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/fat.macho.remarks.x86
+RUN: dsymutil --linker parallel -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%t %t/fat.macho.remarks.x86
 
 Check that the remark files in the bundle exist and are all sane:
 RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64h | FileCheck %s
 RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64 | FileCheck %s
-RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-i386 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-i386
 
 CHECK: <Meta
 CHECK: <Remark Num
 CHECK: <Remark Num
-CHECK-i386: <Remark Num
+CHECK: <Remark Num
 CHECK-NOT: <Remark Num
+CHECK: <Meta
diff --git a/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.bitstream b/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.bitstream
index 2a52843..49ba47e 100644
--- a/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.bitstream
+++ b/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.bitstream
Binary files differ
diff --git a/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.v0.bitstream b/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.v0.bitstream
new file mode 100644
index 0000000..2a52843
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/Inputs/two-remarks.v0.bitstream
Binary files differ
diff --git a/llvm/test/tools/llvm-remarkutil/convert.test b/llvm/test/tools/llvm-remarkutil/convert.test
index 0d7ab8e..2b1bdcb 100644
--- a/llvm/test/tools/llvm-remarkutil/convert.test
+++ b/llvm/test/tools/llvm-remarkutil/convert.test
@@ -1,8 +1,11 @@
 RUN: llvm-remarkutil bitstream2yaml %p/Inputs/two-remarks.bitstream -o %t.yaml
 RUN: FileCheck %s -strict-whitespace < %t.yaml
+RUN: not llvm-remarkutil bitstream2yaml %p/Inputs/two-remarks.v0.bitstream 2>&1 -o - | FileCheck %s --check-prefix=ERR
 RUN: llvm-remarkutil yaml2bitstream %p/Inputs/two-remarks.yaml -o %t.bitstream
 RUN: llvm-remarkutil bitstream2yaml %t.bitstream -o - | FileCheck %s -strict-whitespace
 
+; ERR: error: Unsupported remark container version (expected: 1, read: 0). Please upgrade/downgrade your toolchain to read this container.
+
 ; CHECK: --- !Analysis
 ; CHECK-NEXT: Pass:            prologepilog
 ; CHECK-NEXT: Name:            StackSize
diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
index 138c5d0..b91c27e 100644
--- a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
+++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
@@ -291,6 +291,7 @@
         [&](StringRef FileName) { BinHolder.eraseObjectEntry(FileName); });
 
     Error E = RL.link(*ErrorOrObj);
+    // FIXME: Remark parsing errors are not propagated to the user.
     if (Error NewE = handleErrors(
             std::move(E), [&](std::unique_ptr<FileError> EC) -> Error {
               return remarksErrorHandler(Obj, *this, std::move(EC));
diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp
index b3d7185..a2327fb 100644
--- a/llvm/tools/llc/llc.cpp
+++ b/llvm/tools/llc/llc.cpp
@@ -387,13 +387,13 @@
   // Set a diagnostic handler that doesn't exit on the first error
   Context.setDiagnosticHandler(std::make_unique<LLCDiagnosticHandler>());
 
-  Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
+  Expected<LLVMRemarkFileHandle> RemarksFileOrErr =
       setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
                                    RemarksFormat, RemarksWithHotness,
                                    RemarksHotnessThreshold);
   if (Error E = RemarksFileOrErr.takeError())
     reportError(std::move(E), RemarksFilename);
-  std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
+  LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);
 
   if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
     reportError("input language must be '', 'IR' or 'MIR'");
diff --git a/llvm/tools/llvm-remarkutil/RemarkConvert.cpp b/llvm/tools/llvm-remarkutil/RemarkConvert.cpp
index 207c5e0..203c826 100644
--- a/llvm/tools/llvm-remarkutil/RemarkConvert.cpp
+++ b/llvm/tools/llvm-remarkutil/RemarkConvert.cpp
@@ -80,8 +80,8 @@
   if (!MaybeOF)
     return MaybeOF.takeError();
   auto OF = std::move(*MaybeOF);
-  auto MaybeSerializer = createRemarkSerializer(
-      OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab));
+  auto MaybeSerializer =
+      createRemarkSerializer(OutputFormat, OF->os(), std::move(StrTab));
   if (!MaybeSerializer)
     return MaybeSerializer.takeError();
   auto Serializer = std::move(*MaybeSerializer);
@@ -110,8 +110,7 @@
   if (!MaybeOF)
     return MaybeOF.takeError();
   auto OF = std::move(*MaybeOF);
-  auto MaybeSerializer = createRemarkSerializer(
-      OutputFormat, SerializerMode::Standalone, OF->os());
+  auto MaybeSerializer = createRemarkSerializer(OutputFormat, OF->os());
   if (!MaybeSerializer)
     return MaybeSerializer.takeError();
 
diff --git a/llvm/tools/opt/optdriver.cpp b/llvm/tools/opt/optdriver.cpp
index 26902b2..d4fa6eb 100644
--- a/llvm/tools/opt/optdriver.cpp
+++ b/llvm/tools/opt/optdriver.cpp
@@ -510,7 +510,7 @@
   if (!DisableDITypeMap)
     Context.enableDebugTypeODRUniquing();
 
-  Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
+  Expected<LLVMRemarkFileHandle> RemarksFileOrErr =
       setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
                                    RemarksFormat, RemarksWithHotness,
                                    RemarksHotnessThreshold);
@@ -518,7 +518,7 @@
     errs() << toString(std::move(E)) << '\n';
     return 1;
   }
-  std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
+  LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);
 
   // Load the input module...
   auto SetDataLayout = [&](StringRef IRTriple,
diff --git a/llvm/unittests/Remarks/BitstreamRemarksFormatTest.cpp b/llvm/unittests/Remarks/BitstreamRemarksFormatTest.cpp
index ea61691..ddf7445 100644
--- a/llvm/unittests/Remarks/BitstreamRemarksFormatTest.cpp
+++ b/llvm/unittests/Remarks/BitstreamRemarksFormatTest.cpp
@@ -21,7 +21,7 @@
 
 // This should be updated whenever any of the tests below are modified.
 TEST(BitstreamRemarksFormat, ContainerVersion) {
-  EXPECT_EQ(remarks::CurrentContainerVersion, 0UL);
+  EXPECT_EQ(remarks::CurrentContainerVersion, 1UL);
 }
 
 // The values of the current blocks should not change over time.
diff --git a/llvm/unittests/Remarks/BitstreamRemarksParsingTest.cpp b/llvm/unittests/Remarks/BitstreamRemarksParsingTest.cpp
index 6234931..f5973f5 100644
--- a/llvm/unittests/Remarks/BitstreamRemarksParsingTest.cpp
+++ b/llvm/unittests/Remarks/BitstreamRemarksParsingTest.cpp
@@ -14,7 +14,7 @@
 
 using namespace llvm;
 
-template <size_t N> void parseGood(const char (&Buf)[N]) {
+template <size_t N> static void parseGood(const char (&Buf)[N]) {
   // 1. Parse the YAML remark -> FromYAMLRemark
   // 2. Serialize it to bitstream -> BSStream
   // 3. Parse it back -> FromBSRemark
@@ -48,11 +48,11 @@
   std::string BSBuf;
   raw_string_ostream BSStream(BSBuf);
   Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =
-      remarks::createRemarkSerializer(remarks::Format::Bitstream,
-                                      remarks::SerializerMode::Standalone,
-                                      BSStream, std::move(BSStrTab));
+      remarks::createRemarkSerializer(remarks::Format::Bitstream, BSStream,
+                                      std::move(BSStrTab));
   EXPECT_FALSE(errorToBool(BSSerializer.takeError()));
   (*BSSerializer)->emit(*FromYAMLRemark);
+  (*BSSerializer)->finalize();
 
   // 3.
   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =
@@ -256,11 +256,11 @@
   std::string BSBuf;
   raw_string_ostream BSStream(BSBuf);
   Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =
-      remarks::createRemarkSerializer(remarks::Format::Bitstream,
-                                      remarks::SerializerMode::Standalone,
-                                      BSStream, std::move(BSStrTab));
+      remarks::createRemarkSerializer(remarks::Format::Bitstream, BSStream,
+                                      std::move(BSStrTab));
   EXPECT_FALSE(errorToBool(BSSerializer.takeError()));
   (*BSSerializer)->emit(ToSerializeRemark);
+  (*BSSerializer)->finalize();
 
   StringRef Buf = BSStream.str();
   LLVMRemarkParserRef Parser =
diff --git a/llvm/unittests/Remarks/BitstreamRemarksSerializerTest.cpp b/llvm/unittests/Remarks/BitstreamRemarksSerializerTest.cpp
index 8113d35..3b46096 100644
--- a/llvm/unittests/Remarks/BitstreamRemarksSerializerTest.cpp
+++ b/llvm/unittests/Remarks/BitstreamRemarksSerializerTest.cpp
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Bitcode/BitcodeAnalyzer.h"
-#include "llvm/Remarks/BitstreamRemarkSerializer.h"
 #include "llvm/Remarks/Remark.h"
+#include "llvm/Remarks/RemarkSerializer.h"
 #include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 #include <optional>
@@ -34,23 +34,24 @@
   EXPECT_EQ(OutputOS.str(), Expected);
 }
 
-static void check(remarks::SerializerMode Mode, const remarks::Remark &R,
-                  StringRef ExpectedR, std::optional<StringRef> ExpectedMeta,
-                  std::optional<remarks::StringTable> StrTab) {
+static void check(const remarks::Remark &R, StringRef ExpectedR,
+                  std::optional<StringRef> ExpectedMeta = std::nullopt,
+                  std::optional<remarks::StringTable> StrTab = std::nullopt) {
   // Emit the remark.
   std::string InputBuf;
   raw_string_ostream InputOS(InputBuf);
   Expected<std::unique_ptr<remarks::RemarkSerializer>> MaybeSerializer = [&] {
     if (StrTab)
-      return createRemarkSerializer(remarks::Format::Bitstream, Mode, InputOS,
+      return createRemarkSerializer(remarks::Format::Bitstream, InputOS,
                                     std::move(*StrTab));
     else
-      return createRemarkSerializer(remarks::Format::Bitstream, Mode, InputOS);
+      return createRemarkSerializer(remarks::Format::Bitstream, InputOS);
   }();
   EXPECT_FALSE(errorToBool(MaybeSerializer.takeError()));
   std::unique_ptr<remarks::RemarkSerializer> Serializer =
       std::move(*MaybeSerializer);
   Serializer->emit(R);
+  Serializer->finalize();
 
   // Analyze the serialized remark.
   checkAnalyze(InputOS.str(), ExpectedR);
@@ -66,20 +67,6 @@
   }
 }
 
-static void check(const remarks::Remark &R, StringRef ExpectedR,
-                  StringRef ExpectedMeta,
-                  std::optional<remarks::StringTable> StrTab = std::nullopt) {
-  return check(remarks::SerializerMode::Separate, R, ExpectedR, ExpectedMeta,
-               std::move(StrTab));
-}
-
-static void
-checkStandalone(const remarks::Remark &R, StringRef ExpectedR,
-                std::optional<remarks::StringTable> StrTab = std::nullopt) {
-  return check(remarks::SerializerMode::Standalone, R, ExpectedR,
-               /*ExpectedMeta=*/std::nullopt, std::move(StrTab));
-}
-
 TEST(BitstreamRemarkSerializer, SeparateRemarkFileNoOptionals) {
   remarks::Remark R;
   R.RemarkType = remarks::Type::Missed;
@@ -89,19 +76,21 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=1 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=14 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=8 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'remark\\x00pass\\x00function\\x00'\n"
-        "  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n");
 }
 
@@ -118,19 +107,21 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=1 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=2 op2=1 op3=0/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=14 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=8 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'function\\x00pass\\x00remark\\x00'\n"
-        "  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n",
         std::move(StrTab));
 }
@@ -148,20 +139,22 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"
         "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=15 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=9 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'remark\\x00pass\\x00function\\x00path\\x00'\n"
-        "  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n");
 }
 
@@ -175,20 +168,22 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=3 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"
         "  <Remark hotness codeid=7 abbrevid=6 op0=999999999/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=14 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=8 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'remark\\x00pass\\x00function\\x00'\n"
-        "  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n");
 }
 
@@ -204,20 +199,22 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=2 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"
         "  <Argument codeid=9 abbrevid=8 op0=3 op1=4/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=16 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=10 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'remark\\x00pass\\x00function\\x00key\\x00value\\x00'\n"
-        "  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n");
 }
 
@@ -237,21 +234,23 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"
         "  <Argument with debug location codeid=8 abbrevid=7 op0=3 op1=4 op2=5 "
         "op3=99 op4=55/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=17 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=11 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'remark\\x00pass\\x00function\\x00key\\x00value\\x00path\\x00'\n"
-        "  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n");
 }
 
@@ -276,7 +275,7 @@
   check(R,
         "<BLOCKINFO_BLOCK/>\n"
         "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=8 BlockCodeSize=4>\n"
@@ -285,14 +284,17 @@
         "  <Remark hotness codeid=7 abbrevid=6 op0=999999999/>\n"
         "  <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 "
         "op3=11 op4=66/>\n"
-        "</Remark>\n",
-        "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=19 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"
-        "  <String table codeid=3 abbrevid=5/> blob data = "
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=13 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
         "'remark\\x00pass\\x00function\\x00path\\x00key\\x00value\\x00argpa"
-        "th\\x00'\n  <External File codeid=4 abbrevid=6/> blob data = "
-        "'" EXTERNALFILETESTPATH"'\n"
+        "th\\x00'\n"
+        "</Meta>\n",
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=7 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=0/>\n"
+        "  <External File codeid=4 abbrevid=5/> blob data = "
+        "'" EXTERNALFILETESTPATH "'\n"
         "</Meta>\n");
 }
 
@@ -323,15 +325,12 @@
   R.Args.back().Loc->SourceFilePath = "argpath";
   R.Args.back().Loc->SourceLine = 11;
   R.Args.back().Loc->SourceColumn = 66;
-  checkStandalone(
+  check(
       R,
       "<BLOCKINFO_BLOCK/>\n"
-      "<Meta BlockID=8 NumWords=15 BlockCodeSize=3>\n"
-      "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+      "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
+      "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
       "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
-      "  <String table codeid=3 abbrevid=6/> blob data = "
-      "'pass\\x00remark\\x00function\\x00path\\x00key\\x00value\\x00argpath\\x0"
-      "0'\n"
       "</Meta>\n"
       "<Remark BlockID=9 NumWords=8 BlockCodeSize=4>\n"
       "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
@@ -339,6 +338,11 @@
       "  <Remark hotness codeid=7 abbrevid=6 op0=999999999/>\n"
       "  <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 "
       "op3=11 op4=66/>\n"
-      "</Remark>\n",
-      std::move(StrTab));
+      "</Remark>\n"
+      "<Meta BlockID=8 NumWords=13 BlockCodeSize=3>\n"
+      "  <String table codeid=3 abbrevid=6/> blob data = "
+      "'pass\\x00remark\\x00function\\x00path\\x00key\\x00value\\x00argpath\\x0"
+      "0'\n"
+      "</Meta>\n",
+      std::nullopt, std::move(StrTab));
 }
diff --git a/llvm/unittests/Remarks/RemarksLinkingTest.cpp b/llvm/unittests/Remarks/RemarksLinkingTest.cpp
index 89de9e8..54942ff 100644
--- a/llvm/unittests/Remarks/RemarksLinkingTest.cpp
+++ b/llvm/unittests/Remarks/RemarksLinkingTest.cpp
@@ -133,16 +133,18 @@
         "...\n",
         remarks::Format::Bitstream,
         "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+        "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
-        "  <String table codeid=3 abbrevid=6/> blob data = "
-        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
         "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
-        "</Remark>\n");
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=10 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
+        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
+        "</Meta>\n");
 
   // Check that we keep remarks without debug info.
   check(remarks::Format::YAML,
@@ -153,15 +155,17 @@
         "...\n",
         remarks::Format::Bitstream,
         "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=10 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+        "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
-        "  <String table codeid=3 abbrevid=6/> blob data = "
-        "'inline\\x00NoDefinition\\x00foo\\x00'\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=1 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
-        "</Remark>\n");
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=8 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
+        "'inline\\x00NoDefinition\\x00foo\\x00'\n"
+        "</Meta>\n");
 
   // Check that we deduplicate remarks.
   check(remarks::Format::YAML,
@@ -179,16 +183,18 @@
         "...\n",
         remarks::Format::Bitstream,
         "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+        "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
-        "  <String table codeid=3 abbrevid=6/> blob data = "
-        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
         "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
-        "</Remark>\n");
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=10 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
+        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
+        "</Meta>\n");
 }
 
 TEST(Remarks, LinkingGoodStrTab) {
@@ -209,11 +215,9 @@
         "...\n",
         remarks::Format::Bitstream,
         "<BLOCKINFO_BLOCK/>\n"
-        "<Meta BlockID=8 NumWords=13 BlockCodeSize=3>\n"
-        "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+        "<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=1 op1=1/>\n"
         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
-        "  <String table codeid=3 abbrevid=6/> blob data = "
-        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00Ok\\x00'\n"
         "</Meta>\n"
         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=1 op1=4 op2=0 op3=2/>\n"
@@ -222,7 +226,11 @@
         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
         "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
-        "</Remark>\n");
+        "</Remark>\n"
+        "<Meta BlockID=8 NumWords=11 BlockCodeSize=3>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
+        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00Ok\\x00'\n"
+        "</Meta>\n");
 }
 
 // Check that we propagate parsing errors.
diff --git a/llvm/unittests/Remarks/YAMLRemarksSerializerTest.cpp b/llvm/unittests/Remarks/YAMLRemarksSerializerTest.cpp
index 7e994ac..112cd92 100644
--- a/llvm/unittests/Remarks/YAMLRemarksSerializerTest.cpp
+++ b/llvm/unittests/Remarks/YAMLRemarksSerializerTest.cpp
@@ -23,23 +23,23 @@
 using namespace llvm;
 
 static void check(remarks::Format SerializerFormat,
-                  remarks::SerializerMode Mode, ArrayRef<remarks::Remark> Rs,
-                  StringRef ExpectedR, std::optional<StringRef> ExpectedMeta,
+                  ArrayRef<remarks::Remark> Rs, StringRef ExpectedR,
+                  std::optional<StringRef> ExpectedMeta,
                   std::optional<remarks::StringTable> StrTab = std::nullopt) {
   std::string Buf;
   raw_string_ostream OS(Buf);
   Expected<std::unique_ptr<remarks::RemarkSerializer>> MaybeS = [&] {
     if (StrTab)
-      return createRemarkSerializer(SerializerFormat, Mode, OS,
-                                    std::move(*StrTab));
+      return createRemarkSerializer(SerializerFormat, OS, std::move(*StrTab));
     else
-      return createRemarkSerializer(SerializerFormat, Mode, OS);
+      return createRemarkSerializer(SerializerFormat, OS);
   }();
   EXPECT_FALSE(errorToBool(MaybeS.takeError()));
   std::unique_ptr<remarks::RemarkSerializer> S = std::move(*MaybeS);
 
   for (const remarks::Remark &R : Rs)
     S->emit(R);
+  S->finalize();
   EXPECT_EQ(OS.str(), ExpectedR);
 
   if (ExpectedMeta) {
@@ -54,8 +54,7 @@
 static void check(remarks::Format SerializerFormat, const remarks::Remark &R,
                   StringRef ExpectedR, StringRef ExpectedMeta,
                   std::optional<remarks::StringTable> StrTab = std::nullopt) {
-  return check(SerializerFormat, remarks::SerializerMode::Separate,
-               ArrayRef(&R, &R + 1), ExpectedR, ExpectedMeta,
+  return check(SerializerFormat, ArrayRef(&R, &R + 1), ExpectedR, ExpectedMeta,
                std::move(StrTab));
 }
 
@@ -63,8 +62,7 @@
 checkStandalone(remarks::Format SerializerFormat, const remarks::Remark &R,
                 StringRef ExpectedR,
                 std::optional<remarks::StringTable> StrTab = std::nullopt) {
-  return check(SerializerFormat, remarks::SerializerMode::Standalone,
-               ArrayRef(&R, &R + 1), ExpectedR,
+  return check(SerializerFormat, ArrayRef(&R, &R + 1), ExpectedR,
                /*ExpectedMeta=*/std::nullopt, std::move(StrTab));
 }
 
diff --git a/mlir/include/mlir/Remark/RemarkStreamer.h b/mlir/include/mlir/Remark/RemarkStreamer.h
index 8bfd176..170d6b4 100644
--- a/mlir/include/mlir/Remark/RemarkStreamer.h
+++ b/mlir/include/mlir/Remark/RemarkStreamer.h
@@ -26,14 +26,15 @@
   createToFile(llvm::StringRef path, llvm::remarks::Format fmt);
 
   void streamOptimizationRemark(const Remark &remark) override;
-  void finalize() override {}
+  void finalize() override;
   ~LLVMRemarkStreamer() override;
 
 private:
   LLVMRemarkStreamer() = default;
 
-  std::unique_ptr<class llvm::remarks::RemarkStreamer> remarkStreamer;
   std::unique_ptr<class llvm::ToolOutputFile> file;
+  // RemarkStreamer must be destructed before file is destroyed!
+  std::unique_ptr<class llvm::remarks::RemarkStreamer> remarkStreamer;
 };
 } // namespace mlir::remark::detail
 
diff --git a/mlir/lib/Remark/RemarkStreamer.cpp b/mlir/lib/Remark/RemarkStreamer.cpp
index 8e3544f..d213a1a 100644
--- a/mlir/lib/Remark/RemarkStreamer.cpp
+++ b/mlir/lib/Remark/RemarkStreamer.cpp
@@ -20,8 +20,7 @@
   if (ec)
     return failure();
 
-  auto serOr = llvm::remarks::createRemarkSerializer(
-      fmt, llvm::remarks::SerializerMode::Separate, f->os());
+  auto serOr = llvm::remarks::createRemarkSerializer(fmt, f->os());
   if (!serOr) {
     llvm::consumeError(serOr.takeError());
     return failure();
@@ -50,6 +49,12 @@
   if (file && remarkStreamer)
     file->keep();
 }
+
+void LLVMRemarkStreamer::finalize() {
+  if (!remarkStreamer)
+    return;
+  remarkStreamer->releaseSerializer();
+}
 } // namespace mlir::remark::detail
 
 namespace mlir::remark {
diff --git a/offload/plugins-nextgen/common/src/JIT.cpp b/offload/plugins-nextgen/common/src/JIT.cpp
index 07ef05e..881e27da 100644
--- a/offload/plugins-nextgen/common/src/JIT.cpp
+++ b/offload/plugins-nextgen/common/src/JIT.cpp
@@ -180,9 +180,10 @@
 JITEngine::backend(Module &M, const std::string &ComputeUnitKind,
                    unsigned OptLevel) {
 
-  auto RemarksFileOrErr = setupLLVMOptimizationRemarks(
-      M.getContext(), /*RemarksFilename=*/"", /*RemarksPasses=*/"",
-      /*RemarksFormat=*/"", /*RemarksWithHotness=*/false);
+  Expected<LLVMRemarkFileHandle> RemarksFileOrErr =
+      setupLLVMOptimizationRemarks(
+          M.getContext(), /*RemarksFilename=*/"", /*RemarksPasses=*/"",
+          /*RemarksFormat=*/"", /*RemarksWithHotness=*/false);
   if (Error E = RemarksFileOrErr.takeError())
     return std::move(E);
   if (*RemarksFileOrErr)