Skip to content

Commit dfbd76b

Browse files
[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: #156715
1 parent eede476 commit dfbd76b

File tree

83 files changed

+1276
-764
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+1276
-764
lines changed

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/Frontend/Driver/CodeGenOptions.h"
3030
#include "llvm/IR/DataLayout.h"
3131
#include "llvm/IR/DebugInfo.h"
32+
#include "llvm/IR/LLVMRemarkStreamer.h"
3233
#include "llvm/IR/LegacyPassManager.h"
3334
#include "llvm/IR/Module.h"
3435
#include "llvm/IR/ModuleSummaryIndex.h"
@@ -1384,6 +1385,10 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
13841385
Conf.CGFileType = getCodeGenFileType(Action);
13851386
break;
13861387
}
1388+
1389+
// FIXME: Both ExecuteAction and thinBackend set up optimization remarks for
1390+
// the same context.
1391+
finalizeLLVMOptimizationRemarks(M->getContext());
13871392
if (Error E =
13881393
thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
13891394
ModuleToDefinedGVSummaries[M->getModuleIdentifier()],

clang/lib/CodeGen/CodeGenAction.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "clang/Lex/Preprocessor.h"
3030
#include "clang/Serialization/ASTWriter.h"
3131
#include "llvm/ADT/Hashing.h"
32+
#include "llvm/ADT/ScopeExit.h"
3233
#include "llvm/Bitcode/BitcodeReader.h"
3334
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
3435
#include "llvm/Demangle/Demangle.h"
@@ -259,19 +260,18 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
259260
Ctx.setDefaultTargetCPU(TargetOpts.CPU);
260261
Ctx.setDefaultTargetFeatures(llvm::join(TargetOpts.Features, ","));
261262

262-
Expected<std::unique_ptr<llvm::ToolOutputFile>> OptRecordFileOrErr =
263-
setupLLVMOptimizationRemarks(
264-
Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses,
265-
CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness,
266-
CodeGenOpts.DiagnosticsHotnessThreshold);
263+
Expected<LLVMRemarkFileHandle> OptRecordFileOrErr =
264+
setupLLVMOptimizationRemarks(
265+
Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses,
266+
CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness,
267+
CodeGenOpts.DiagnosticsHotnessThreshold);
267268

268269
if (Error E = OptRecordFileOrErr.takeError()) {
269270
reportOptRecordError(std::move(E), Diags, CodeGenOpts);
270271
return;
271272
}
272273

273-
std::unique_ptr<llvm::ToolOutputFile> OptRecordFile =
274-
std::move(*OptRecordFileOrErr);
274+
LLVMRemarkFileHandle OptRecordFile = std::move(*OptRecordFileOrErr);
275275

276276
if (OptRecordFile && CodeGenOpts.getProfileUse() !=
277277
llvm::driver::ProfileInstrKind::ProfileNone)
@@ -1173,7 +1173,7 @@ void CodeGenAction::ExecuteAction() {
11731173
Ctx.setDefaultTargetCPU(TargetOpts.CPU);
11741174
Ctx.setDefaultTargetFeatures(llvm::join(TargetOpts.Features, ","));
11751175

1176-
Expected<std::unique_ptr<llvm::ToolOutputFile>> OptRecordFileOrErr =
1176+
Expected<LLVMRemarkFileHandle> OptRecordFileOrErr =
11771177
setupLLVMOptimizationRemarks(
11781178
Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses,
11791179
CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness,
@@ -1183,8 +1183,7 @@ void CodeGenAction::ExecuteAction() {
11831183
reportOptRecordError(std::move(E), Diagnostics, CodeGenOpts);
11841184
return;
11851185
}
1186-
std::unique_ptr<llvm::ToolOutputFile> OptRecordFile =
1187-
std::move(*OptRecordFileOrErr);
1186+
LLVMRemarkFileHandle OptRecordFile = std::move(*OptRecordFileOrErr);
11881187

11891188
emitBackendOutput(CI, CI.getCodeGenOpts(),
11901189
CI.getTarget().getDataLayoutString(), TheModule.get(), BA,

flang/lib/Frontend/FrontendActions.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,7 +1352,7 @@ void CodeGenAction::executeAction() {
13521352
std::make_unique<BackendRemarkConsumer>(remarkConsumer));
13531353

13541354
// write optimization-record
1355-
llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
1355+
llvm::Expected<llvm::LLVMRemarkFileHandle> optRecordFileOrErr =
13561356
setupLLVMOptimizationRemarks(
13571357
llvmModule->getContext(), codeGenOpts.OptRecordFile,
13581358
codeGenOpts.OptRecordPasses, codeGenOpts.OptRecordFormat,
@@ -1364,8 +1364,7 @@ void CodeGenAction::executeAction() {
13641364
return;
13651365
}
13661366

1367-
std::unique_ptr<llvm::ToolOutputFile> optRecordFile =
1368-
std::move(*optRecordFileOrErr);
1367+
llvm::LLVMRemarkFileHandle optRecordFile = std::move(*optRecordFileOrErr);
13691368

13701369
if (optRecordFile) {
13711370
optRecordFile->keep();

llvm/docs/Remarks.rst

Lines changed: 21 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -152,26 +152,6 @@ Other tools that support remarks:
152152
.. option:: -opt-remarks-format=<format>
153153
.. option:: -opt-remarks-with-hotness
154154

155-
Serialization modes
156-
===================
157-
158-
There are two modes available for serializing remarks:
159-
160-
``Separate``
161-
162-
In this mode, the remarks and the metadata are serialized separately. The
163-
client is responsible for parsing the metadata first, then use the metadata
164-
to correctly parse the remarks.
165-
166-
``Standalone``
167-
168-
In this mode, the remarks and the metadata are serialized to the same
169-
stream. The metadata will always come before the remarks.
170-
171-
The compiler does not support emitting standalone remarks. This mode is
172-
more suited for post-processing tools like linkers, that can merge the
173-
remarks for one whole project.
174-
175155
.. _yamlremarks:
176156

177157
YAML remarks
@@ -374,27 +354,11 @@ This block can contain the following records:
374354
The remark container
375355
--------------------
376356

377-
Bitstream remarks are designed to be used in two different modes:
378-
379-
``The separate mode``
380-
381-
The separate mode is the mode that is typically used during compilation. It
382-
provides a way to serialize the remark entries to a stream while some
383-
metadata is kept in memory to be emitted in the product of the compilation
384-
(typically, an object file).
385-
386-
``The standalone mode``
387-
388-
The standalone mode is typically stored and used after the distribution of
389-
a program. It contains all the information that allows the parsing of all
390-
the remarks without having any external dependencies.
391-
392-
In order to support multiple modes, the format introduces the concept of a
393-
bitstream remark container type.
357+
The bitstream remark container supports multiple types:
394358

395-
.. _bitstreamremarksseparateremarksmeta:
359+
.. _bitstreamremarksfileexternal:
396360

397-
``SeparateRemarksMeta: the metadata emitted separately``
361+
``RemarksFileExternal: a link to an external remarks file``
398362

399363
This container type expects only a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
400364

@@ -406,84 +370,33 @@ bitstream remark container type.
406370
clients to retrieve remarks and their associated metadata directly from
407371
intermediate products.
408372

409-
``SeparateRemarksFile: the remark entries emitted separately``
373+
The container versions of the external separate container should match in order to
374+
have a well-formed file.
410375

411-
This container type expects only a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
412-
413-
* :ref:`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>`
414-
* :ref:`RECORD_META_REMARK_VERSION <bitstreamremarksrecordmetaremarkversion>`
376+
.. _bitstreamremarksfile:
415377

416-
This container type expects 0 or more :ref:`REMARK_BLOCK <bitstreamremarksremarkblock>`.
378+
``RemarksFile: a standalone remarks file``
417379

418-
Typically, this is emitted in a side-file alongside an object file, and is
419-
made to be able to stream to without increasing the memory consumption of
420-
the compiler. This is referenced by the :ref:`RECORD_META_EXTERNAL_FILE
421-
<bitstreamremarksrecordmetaexternalfile>` entry in the
422-
:ref:`SeparateRemarksMeta <bitstreamremarksseparateremarksmeta>` container.
380+
This container type expects a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
423381

424-
When the parser tries to parse a container that contains the metadata for the
425-
separate remarks, it should parse the version and type, then keep the string
426-
table in memory while opening the external file, validating its metadata and
427-
parsing the remark entries.
382+
* :ref:`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>`
383+
* :ref:`RECORD_META_REMARK_VERSION <bitstreamremarksrecordmetaremarkversion>`
428384

429-
The container versions from the separate container should match in order to
430-
have a well-formed file.
385+
Then, this container type expects 1 or more :ref:`REMARK_BLOCK <bitstreamremarksremarkblock>`.
386+
If no remarks are emitted, the meta blocks are also not emitted, so the file is empty.
431387

432-
``Standalone: the metadata and the remark entries emitted together``
388+
After the remark blocks, another :ref:`META_BLOCK <bitstreamremarksmetablock>` is emitted, containing:
389+
* :ref:`RECORD_META_STRTAB <bitstreamremarksrecordmetastrtab>`
433390

434-
This container type expects only a :ref:`META_BLOCK <bitstreamremarksmetablock>` containing only:
391+
When the parser reads this container type, it jumps to the end of the file
392+
to read the string table before parsing the individual remarks.
435393

436-
* :ref:`RECORD_META_CONTAINER_INFO <bitstreamremarksrecordmetacontainerinfo>`
437-
* :ref:`RECORD_META_REMARK_VERSION <bitstreamremarksrecordmetaremarkversion>`
438-
* :ref:`RECORD_META_STRTAB <bitstreamremarksrecordmetastrtab>`
394+
Standalone remarks files can be referenced by the
395+
:ref:`RECORD_META_EXTERNAL_FILE <bitstreamremarksrecordmetaexternalfile>`
396+
entry in the :ref:`RemarksFileExternal
397+
<bitstreamremarksfileexternal>` container.
439398

440-
This container type expects 0 or more :ref:`REMARK_BLOCK <bitstreamremarksremarkblock>`.
441-
442-
A complete output of :program:`llvm-bcanalyzer` on the different container types:
443-
444-
``SeparateRemarksMeta``
445-
446-
.. code-block:: none
447-
448-
<BLOCKINFO_BLOCK/>
449-
<Meta BlockID=8 NumWords=13 BlockCodeSize=3>
450-
<Container info codeid=1 abbrevid=4 op0=5 op1=0/>
451-
<String table codeid=3 abbrevid=5/> blob data = 'pass\\x00key\\x00value\\x00'
452-
<External File codeid=4 abbrevid=6/> blob data = '/path/to/file/name'
453-
</Meta>
454-
455-
``SeparateRemarksFile``
456-
457-
.. code-block:: none
458-
459-
<BLOCKINFO_BLOCK/>
460-
<Meta BlockID=8 NumWords=3 BlockCodeSize=3>
461-
<Container info codeid=1 abbrevid=4 op0=0 op1=1/>
462-
<Remark version codeid=2 abbrevid=5 op0=0/>
463-
</Meta>
464-
<Remark BlockID=9 NumWords=8 BlockCodeSize=4>
465-
<Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>
466-
<Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>
467-
<Remark hotness codeid=7 abbrevid=6 op0=999999999/>
468-
<Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 op3=11 op4=66/>
469-
</Remark>
470-
471-
``Standalone``
472-
473-
.. code-block:: none
474-
475-
<BLOCKINFO_BLOCK/>
476-
<Meta BlockID=8 NumWords=15 BlockCodeSize=3>
477-
<Container info codeid=1 abbrevid=4 op0=5 op1=2/>
478-
<Remark version codeid=2 abbrevid=5 op0=30/>
479-
<String table codeid=3 abbrevid=6/> blob data = 'pass\\x00remark\\x00function\\x00path\\x00key\\x00value\\x00argpath\\x00'
480-
</Meta>
481-
<Remark BlockID=9 NumWords=8 BlockCodeSize=4>
482-
<Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>
483-
<Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>
484-
<Remark hotness codeid=7 abbrevid=6 op0=999999999/>
485-
<Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 op3=11 op4=66/>
486-
</Remark>
399+
.. FIXME: Add complete output of :program:`llvm-bcanalyzer` on the different container types (once format changes are completed)
487400
488401
opt-viewer
489402
==========

llvm/include/llvm/IR/LLVMRemarkStreamer.h

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/Remarks/Remark.h"
1818
#include "llvm/Support/Compiler.h"
1919
#include "llvm/Support/Error.h"
20+
#include "llvm/Support/ToolOutputFile.h"
2021
#include <memory>
2122
#include <optional>
2223
#include <string>
@@ -82,20 +83,81 @@ struct LLVMRemarkSetupFormatError
8283
LLVMRemarkSetupFormatError>::LLVMRemarkSetupErrorInfo;
8384
};
8485

85-
/// Setup optimization remarks that output to a file.
86-
LLVM_ABI Expected<std::unique_ptr<ToolOutputFile>> setupLLVMOptimizationRemarks(
86+
/// RAII handle that manages the lifetime of the ToolOutputFile used to output
87+
/// remarks. On destruction (or when calling releaseFile()), this handle ensures
88+
/// that the optimization remarks are finalized and the RemarkStreamer is
89+
/// correctly deregistered from the LLVMContext.
90+
class LLVMRemarkFileHandle final {
91+
struct Finalizer {
92+
LLVMContext *Context;
93+
94+
Finalizer(LLVMContext *Ctx) : Context(Ctx) {}
95+
96+
Finalizer(const Finalizer &) = delete;
97+
Finalizer &operator=(const Finalizer &) = delete;
98+
99+
Finalizer(Finalizer &&Other) : Context(Other.Context) {
100+
Other.Context = nullptr;
101+
}
102+
103+
Finalizer &operator=(Finalizer &&Other) {
104+
std::swap(Context, Other.Context);
105+
return *this;
106+
}
107+
108+
~Finalizer() { finalize(); }
109+
110+
LLVM_ABI void finalize();
111+
};
112+
113+
std::unique_ptr<ToolOutputFile> OutputFile;
114+
Finalizer Finalize;
115+
116+
public:
117+
LLVMRemarkFileHandle() : OutputFile(nullptr), Finalize(nullptr) {}
118+
119+
LLVMRemarkFileHandle(std::unique_ptr<ToolOutputFile> OutputFile,
120+
LLVMContext &Ctx)
121+
: OutputFile(std::move(OutputFile)), Finalize(&Ctx) {}
122+
123+
ToolOutputFile *get() { return OutputFile.get(); }
124+
explicit operator bool() { return bool(OutputFile); }
125+
126+
/// Finalize remark emission and release the underlying ToolOutputFile.
127+
std::unique_ptr<ToolOutputFile> releaseFile() {
128+
finalize();
129+
return std::move(OutputFile);
130+
}
131+
132+
void finalize() { Finalize.finalize(); }
133+
134+
ToolOutputFile &operator*() { return *OutputFile; }
135+
ToolOutputFile *operator->() { return &*OutputFile; }
136+
};
137+
138+
/// Set up optimization remarks that output to a file. The LLVMRemarkFileHandle
139+
/// manages the lifetime of the underlying ToolOutputFile to ensure \ref
140+
/// finalizeLLVMOptimizationRemarks() is called before the file is destroyed or
141+
/// released from the handle. The handle must be kept alive until all remarks
142+
/// were emitted through the remark streamer.
143+
LLVM_ABI Expected<LLVMRemarkFileHandle> setupLLVMOptimizationRemarks(
87144
LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
88145
StringRef RemarksFormat, bool RemarksWithHotness,
89146
std::optional<uint64_t> RemarksHotnessThreshold = 0);
90147

91-
/// Setup optimization remarks that output directly to a raw_ostream.
92-
/// \p OS is managed by the caller and should be open for writing as long as \p
93-
/// Context is streaming remarks to it.
148+
/// Set up optimization remarks that output directly to a raw_ostream.
149+
/// \p OS is managed by the caller and must be open for writing until
150+
/// \ref finalizeLLVMOptimizationRemarks() is called.
94151
LLVM_ABI Error setupLLVMOptimizationRemarks(
95152
LLVMContext &Context, raw_ostream &OS, StringRef RemarksPasses,
96153
StringRef RemarksFormat, bool RemarksWithHotness,
97154
std::optional<uint64_t> RemarksHotnessThreshold = 0);
98155

156+
/// Finalize optimization remarks and deregister the RemarkStreamer from the \p
157+
/// Context. This must be called before closing the (file) stream that was used
158+
/// to set up the remarks.
159+
LLVM_ABI void finalizeLLVMOptimizationRemarks(LLVMContext &Context);
160+
99161
} // end namespace llvm
100162

101163
#endif // LLVM_IR_LLVMREMARKSTREAMER_H

llvm/include/llvm/LTO/LTO.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#ifndef LLVM_LTO_LTO_H
1616
#define LLVM_LTO_LTO_H
1717

18+
#include "llvm/IR/LLVMRemarkStreamer.h"
1819
#include "llvm/Support/Compiler.h"
1920
#include <memory>
2021

@@ -91,7 +92,7 @@ LLVM_ABI std::string getThinLTOOutputFile(StringRef Path, StringRef OldPrefix,
9192
StringRef NewPrefix);
9293

9394
/// Setup optimization remarks.
94-
LLVM_ABI Expected<std::unique_ptr<ToolOutputFile>> setupLLVMOptimizationRemarks(
95+
LLVM_ABI Expected<LLVMRemarkFileHandle> setupLLVMOptimizationRemarks(
9596
LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
9697
StringRef RemarksFormat, bool RemarksWithHotness,
9798
std::optional<uint64_t> RemarksHotnessThreshold = 0, int Count = -1);
@@ -579,7 +580,7 @@ class LTO {
579580
DenseSet<GlobalValue::GUID> DynamicExportSymbols;
580581

581582
// Diagnostic optimization remarks file
582-
std::unique_ptr<ToolOutputFile> DiagnosticOutputFile;
583+
LLVMRemarkFileHandle DiagnosticOutputFile;
583584
};
584585

585586
/// The resolution for a symbol. The linker must provide a SymbolResolution for

llvm/include/llvm/LTO/LTOBackend.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ thinBackend(const Config &C, unsigned Task, AddStreamFn AddStream, Module &M,
6565
AddStreamFn IRAddStream = nullptr,
6666
const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>());
6767

68-
LLVM_ABI Error
69-
finalizeOptimizationRemarks(std::unique_ptr<ToolOutputFile> DiagOutputFile);
68+
LLVM_ABI Error finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile);
7069

7170
/// Returns the BitcodeModule that is ThinLTO.
7271
LLVM_ABI BitcodeModule *findThinLTOModule(MutableArrayRef<BitcodeModule> BMs);

llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ struct LTOCodeGenerator {
244244
bool ShouldInternalize = EnableLTOInternalization;
245245
bool ShouldEmbedUselists = false;
246246
bool ShouldRestoreGlobalsLinkage = false;
247-
std::unique_ptr<ToolOutputFile> DiagnosticOutputFile;
247+
LLVMRemarkFileHandle DiagnosticOutputFile;
248248
std::unique_ptr<ToolOutputFile> StatsFile = nullptr;
249249
std::string SaveIRBeforeOptPath;
250250

0 commit comments

Comments
 (0)